Saturday, April 24, 2010

Favorite fifteen tips from “Rework” book by Jason Fried and DHH

I have been a long admirer of Jason Fried of 37Signals and read his first book Getting Real. Jason along with DHH have put forth many of the ideas from that book along with other ideas from their blog Signal vs. Noise into a new book Rework. I just finished reading it and though it reiterates many ideas from the earlier book “Getting Real” and their blogs, it’s worth re-reading those ideas as many of business companies today still runs on old fallacies. The book consists of thirteen sections and over eighty ideas, here are my favorite ideas from the book:


Failure is not a rite of passage




I have heared the advice from startup folks about “Fail early and fail often.” On the contrary, this book shows people who learn from mistakes will make new mistakes, instead success shows what actually works. Another related avice in the book is “Reason to quit”, which shows when you can quit and choose something else. When I read Founders at Work: Stories of Startups’ Early Days, it also showed that most startups don’t stick to their original ideas and move to other ideas based on early feedback.



Planning is Guessing




This is related to another advice from the book “Your estimates suck” as Planning and Estimation is hard especially in software business. I have written about Software Estimation in my earlier blogs, however most places still equate estimates with commitments. Jason and DHH reminds us again that estimates are just guesses that were made based on the best information available at the time.


Workaholism




This is another unorthodox advice that is contradictory to how most software projects are run. Most companies measure workers’ dedication on how many hours he/she put even when they are not actually producing. This is also common when managers treat estimates as commitments and refuse to admit reality when things change. We are all familiar with iron triangle of schedule/cost/functionality or sometime referred to as cost/quality/schedule or cost/resourcs/schedule. Often business folks are unwilling to change schedule and functionality, which often requires working late hours. This is also related to Heroism, which I have blogged before and go to sleep, as workholism can result in sleep deprivation, which reduces creativity and productivity.



Scratch your own itch


Most successful businesses started with hobbies or personal interests or problems and there are tons of examples of this. This advice is also related to eat your own dog food, though not mentioned in this book.


Start making something




Jason and DHH reminds us another great point that ideas are cheap and the real question is how well you execute them.


Draw a line in the sand




One of the key characteristics of Ruby on Rails software that DHH produced is having strong opinions that limits variations. Similarly, 37Signals is known for their simple design and limited features. You can differentiate yourself from others by standing for something.



Outside money is Plan Z




Both DHH and Jason often talked about downside of getting money from venture capitalists and I agree that these days you can start most software startups with minimal money and raising money can be very distracting. Another related tip that “building a flip is building to flop”, which is often what startup founders hope to get out.


Start at the epicenter




This book advices you to focus on your core product. Though, this book briefly mentiosn this topic but there is a great presentation of Video of Geoffrey Moore at Business of Software 2009 that talks about similar topic. This advice is also reated to other tips from the book such as “don’t copy”, “decommoditize your product”, “focus on you instead of they”, i.e., focus on your core strengths and not your competitors.



Focus on what won’t change




This is great advice for building business that will last. I remember when I started working at Amazon, we were told the core values of Amazon that included having a large selection, cheap prices, customer service and everything we built started from outside-in focus, i.e., it started with customers.


Get it out here




This is similar to common advice from the startup and agile community, i.e. release early and release often.


Interruption is the enemy of productivity





More and more research is showing that our brain can’t focus on onething at a time, and constant interruption and multi-tasking hampers your productivity. This is also somewhat related to office space is setup as many agile practices encourage more open space with pair programming and I have found that it prevents concentration. I found that private office offered from Organizational Patterns of Agile Software Development provides less interruption.


Meetings are toxic




This is another hallmark idea of 37Signals and the book contains a number of tips on making your productive such as fixed time, fewer people, clear agenda, beginning with a specific problem and ending with action items and making someone responsible for them.


Good enough is fine





37Signals is known for their simple design and fewer features. This is related other advice in the book such as “embrace the constraints”, “throw less at the problem”, “underdo your competitor”, “say no” and “be a curator”. When you have limited resources, you can become more creative. Also, you are better off building half a product, not a half assed product.


Make tiny decisions




The authors encourage to make tiny decisions as big decisions are hard to make and hard to change. This advice is related to other tips such as “decisions are progress”, which encourages you to always make progress and “quick wins”, which encourages you to build momentum by accomplishing small tasks.


Build an audience




The authors encourage to build audience that come back to you by writing blogs, tweets and speaking. This is also reated to “sell your by-products”, “emulate chefs”, “emulate drug dealers” and “out-teach your competitors”.



Conclusion


Though, I skipped many gems of advice on hiring, culture and marketing but I suggest you read the book to build long lasting and successful business.

Wednesday, February 3, 2010

A few recipes for reprocessing messages in Dead-Letter-Queue using ActiveMQ

A few recipes for reprocessing messages in Dead-Letter-Queue using ActiveMQ




Messaging based asynchronous processing is a key component of any complexed software especially in transactional environment. There are a number of solutions that provide high performance and reliable messaging in Java space such as ActiveMQ, FUSE broker, JBossMQ, SonicMQ, Weblogic, Websphere, Fiorano, etc. These providers support JMS specification, which provides abstraction for queues, message providers and message consumers. In this blog, I will go over some recipes for recovering messages from dead letter queue when using ActiveMQ.


What is Dead Letter Queue


Generally, when a consumer fails to process a message within a transaction or does not send acknowledgement back to the broker, the message is put back to the queue. The message is then delivered upto certain number of times based on configuration and finally the message is put to dead letter queue when that limit is exceeded. The ActiveMQ documentation recommends following settings for defining dead letter queues:



 <broker...>
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- Set the following policy on all queues using the '>' wildcard -->
<policyEntry queue=">">

<deadLetterStrategy>
<individualDeadLetterStrategy
queuePrefix="DLQ." useQueueForQueueMessages="true" />
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>

</destinationPolicy>
...
</broker>

and you can control redlivery policy as follows:


 RedeliveryPolicy policy = connection.getRedeliveryPolicy();
policy.setInitialRedeliveryDelay(500);
policy.setBackOffMultiplier(2);
policy.setUseExponentialBackOff(true);
policy.setMaximumRedeliveries(2);

It is important that you create dlq per queue, otherwise ActiveMQ puts them into a single dead letter queue.



Getting the QueueViewMBean Handle


ActiveMQ provides QueueViewMBean to invoke administration APIs on the queues. The easiest way to get this handle is to use BrokerFacadeSupport class, which is extended by RemoteJMXBrokerFacade and LocalBrokerFacade. You can use RemoteJMXBrokerFacade if you are connecting to remote ActiveMQ server, e.g. here is Spring configuration for setting it up:


     <bean id="brokerQuery" class="org.apache.activemq.web.RemoteJMXBrokerFacade" autowire="constructor" destroy-method="shutdown">
<property name="configuration">
<bean class="org.apache.activemq.web.config.SystemPropertiesConfiguration"/>
</property>

<property name="brokerName"><null/></property>
</bean>

Alternatively, you can use LocalBrokerFacade if you are running embedded ActiveMQ server, e.g. below is Spring configuration for it:


     <bean id="brokerQuery" class="org.apache.activemq.web.LocalBrokerFacade" autowire="constructor" scope="prototype"/>


Getting number of messages from the queue


Once you got handle to QueueViewMBean, you can use following API to find the number of messages in the queue:


 1     public long getQueueSize(final String dest) {

2 try {
3 return brokerQuery.getQueue(dest).getQueueSize();
4 } catch (Exception e) {
5 throw new RuntimeException(e);

6 }
7 }
8


Copying Messages using JMS APIs


The JMS specification provides APIs to browse queue in read mode and then you can send the messages to another queue, e.g.


  1 import java.util.Enumeration;

2

3 import javax.jms.Connection;
4 import javax.jms.ConnectionFactory;
5 import javax.jms.JMSException;

6 import javax.jms.Message;
7 import javax.jms.Queue;
8 import javax.jms.QueueBrowser;

9 import javax.jms.Session;
10 import javax.jms.TextMessage;
11 import javax.management.openmbean.CompositeData;
12

13 import org.apache.activemq.broker.jmx.QueueViewMBean;
14 import org.apache.activemq.web.BrokerFacadeSupport;
15 import org.springframework.beans.factory.annotation.Autowired;
16 import org.springframework.jms.core.BrowserCallback;

17 import org.springframework.jms.core.JmsTemplate;
18 import org.springframework.jms.core.MessageCreator;
19

20 public class DlqReprocessor {

21 @Autowired
22 private JmsTemplate jmsTemplate;
23

24 @Autowired
25 BrokerFacadeSupport brokerQuery;
26

27 @Autowired
28 ConnectionFactory connectionFactory;
29

30
31 @SuppressWarnings("unchecked")
32 void redeliverDLQUsingJms(final String brokerName, final String from,

33 final String to) {
34 Connection connection = null;
35 Session session = null;
36

37 try {
38 connection = connectionFactory.createConnection();
39 connection.start();
40 session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
41 Queue dlq = session.createQueue(from);

42 QueueBrowser browser = session.createBrowser(dlq);
43

44 Enumeration<Message> e = browser.getEnumeration();
45
46 while (e.hasMoreElements()) {

47 Message message = e.nextElement();
48 final String messageBody = ((TextMessage) message).getText();
49 jmsTemplate.send(to, new MessageCreator() {
50 @Override

51 public Message createMessage(final Session session)
52 throws JMSException {
53 return session.createTextMessage(messageBody);

54 }
55 });
56 }
57 } catch (Exception e) {
58 throw new RuntimeException(e);

59 } finally {
60 try {
61 session.close();
62 } catch (Exception e) {

63 }
64 try {
65 connection.close();
66 } catch (Exception e) {

67 }
68 }
69 }
70 // . . .

71 }
72

The downside of above approach is that it leaves the original messages in the dead letter queue.


Copying Messages using Spring’s JmsTemplate APIs


You can effectively do the same thing with JmsTemplate provided by Spring with a bit less code, e.g.


  1    void redeliverDLQUsingJmsTemplateBrowse(final String from, final String to) {

2 try {
3 jmsTemplate.browse(from, new BrowserCallback() {
4

5 @SuppressWarnings("unchecked")

6 @Override
7 public Object doInJms(Session session, QueueBrowser browser)
8 throws JMSException {
9 Enumeration<Message> e = browser.getEnumeration();

10 while (e.hasMoreElements()) {
11 Message message = e.nextElement();
12 final String messageBody = ((TextMessage) message)
13 .getText();
14 jmsTemplate.send(to, new MessageCreator() {

15 @Override
16 public Message createMessage(final Session session)
17 throws JMSException {
18 return session.createTextMessage(messageBody);

19 }
20 });
21 }
22 return null;
23 }

24 });
25 } catch (Exception e) {
26 throw new RuntimeException(e);
27 }

28 }
29


Moving Messages using receive/send APIs


As I mentioned, the above approaches leave messages in the DLQ, which may not be what you want. Thus, another simple approach would be to consume messages from the dead letter queue and send it to another,e.g.


  1   public void redeliverDLQUsingJmsTemplateReceive(final String from,

2 final String to) {
3 try {
4 jmsTemplate.setReceiveTimeout(100);
5 Message message = null;

6 while ((message = jmsTemplate.receive(from)) != null) {
7 final String messageBody = ((TextMessage) message).getText();
8 jmsTemplate.send(to, new MessageCreator() {

9 @Override
10 public Message createMessage(final Session session)
11 throws JMSException {

12 return session.createTextMessage(messageBody);
13 }
14 });
15 }
16 } catch (Exception e) {

17 throw new RuntimeException(e);
18 }
19 }
20


Moving Messages using ActiveMQ’s API


Finally, the best approach I found waas to use ActiveMQ’s APIs to move messags, e.g.


  1     public void redeliverDLQUsingJMX(final String brokerName, final String from,

2 final String to) {
3 try {
4 final QueueViewMBean queue = brokerQuery.getQueue(from);

5 for (int i = 0; i < 10 && queue.getQueueSize() > 0; i++) {
6 CompositeData[] compdatalist = queue.browse();

7 for (CompositeData cdata : compdatalist) {
8 String messageID = (String) cdata.get("JMSMessageID");
9 queue.moveMessageTo(messageID, to);
10 }

11 }
12 } catch (Exception e) {
13 throw new RuntimeException(e);
14 }

15 }
16


I have been using this approach and have found to be reliable for reprocessing dead letter queue, though these techniques an also be used for general queues. I am sure there are tons of alternatives including using full-fledged enterprise service bus route. Let me know if you have interesting solutions to this problem.

Wednesday, January 20, 2010

PlexRBAC: an open source project for providing powerful role based security (II)

This is continuation of my previous blog on my open source project PlexRBAC for managing role based access control. Last time, I covered REST APIs and in this blog I will cover internal domain model, RBAC APIs in Java and examples of instance or dynamic based security.


Layers


PlexRBAC consists of following layers


Business Domain Layer



This layer defines core classes that are part of the RBAC based security domain such as:



  • Domain – As described previously, the domain allows you to support multiple applications or realms.
  • Subject – The subject represents users who are defined in an application.
  • Role – A role represents job title or function.
  • Permission – A permission is composed of operation, target and an expression that is used for dynamic or instance based security.
  • SecurityError – Upon a permission failure, you can choose to store them in the database using SecurityError.

Repository Layer


This layer is responsible for accessing or storing above objects in the database. PlexRBAC uses Berkley DB for persistence and each domain is stored as a separate database, which allows you to segregate permissions and roles for distinct domains. Following are list of repositories supported by PlexRBAC:



  • DomainRepository – provides database access for Domains.

  • PermissionRepository – provides database access for Permissions.
  • SubjectRepository – provides database access for Subjects.
  • SecurityErrorRepository – provides database access for SecurityErrors.
  • RoleRepository – provides database access for Roles.
  • SecurityMappingRepository – provides APIs to map permissions with roles and to map subject with roles.
  • RepositoryFactory – provides factory methods to create above repositories.

Security Layer


This class defines PermissionManager for authorizing permissions.


Evaluation Layer


This layer proivdes evaluation engine for instance based security.



Service Layer


This layer defines REST services such as:



  • DomainService – this service provides REST APIs for accessing Domains.
  • PermissionService – this service provides REST APIs for accessing Permissions.
  • SubjectService – this service provides REST APIs for accessing Subjects.
  • RoleService – this service provides REST APIs for accessing Roles.
  • AuthenticationService – this service provides REST APIs for authenticating users.
  • AuthorizationService – this service provides REST APIs for authorizing permissions.
  • RolePermissionService – this service provides REST APIs for mapping permissions with roles.
  • SubjectRolesService – this service provides REST APIs for mapping subjects with roles.

JMX Layer



This layer defines JMX helper classes for managing services and configuration remotely.


Caching Layer


This layer provides caching security permissions to improve performance.


Metrics Layer


This layer provides performance measurement classes such as Timing class to measure method invocation benchmarks.


Utility Layer


This layer provides helper classes.



Web Layer


This layer provides filters for enforcing authentication and authorization when accessing REST APIs.


Example


Let’s use the same example that we described last time but with addition of instance based security. Let’s assume there are five roles: Teller, Customer-Service-Representative (CSR), Account, AccountingManager and LoanOfficer, where



  • A teller can modify customer deposit accounts — but only if customer and teller live in same region
  • A customer service representative can create or delete customer deposit accounts — but only if customer and teller live in same region
  • An accountant can create general ledger reports — but only if year is == current year
  • An accounting manager can modify ledger-posting rules — but only if year is == current year
  • A loan officer can create and modify loan accounts – but only if account balance is < 10000


In addition, following classes will be used to add domain specific security:


  1
2 class User {

3

4 private String id;
5 private String region;
6

7 User() {
8 }
9

10 public User(String id, String region) {
11 this.id = id;

12 this.region = region;
13 }
14

15 public void setRegion(String region) {
16 this.region = region;

17 }
18

19 public String getRegion() {
20 return region;
21 }

22
23 public void setId(String id) {
24 this.id = id;
25 }
26

27 public String getId() {
28 return id;
29 }
30 }
31

32 class Customer extends User {
33

34 public Customer(String id, String region) {
35 super(id, region);

36 }
37 }
38

39 class Employee extends User {
40

41 public Employee(String id, String region) {
42 super(id, region);
43 }
44 }
45

46 class Account {
47

48 private String id;
49 private double balance;

50
51 Account() {
52 }
53

54 public Account(String id, double balance) {

55 this.id = id;
56 this.balance = balance;
57 }
58

59 /**

60 * @return the id

61 */
62 public String getId() {

63 return id;
64 }
65

66 /**
67 * @param id

68 * the id to set

69 */
70 public void setId(String id) {

71 this.id = id;
72 }
73

74 public void setBalance(double balance) {

75 this.balance = balance;
76 }
77

78 public double getBalance() {
79 return balance;

80 }
81 }
82

83

Bootstrapping


Let’s create handle to repository-factory as:


 1
2 private static final String TEST_DB_DIR = "test_db_dir_perms";

3 RepositoryFactory repositoryFactory = new RepositoryFactoryImpl(TEST_DB_DIR);


And instance of permission manager as:


 1 PermissionManager permissionManager = new PermissionManagerImpl(repositoryFactory,

2 new JavascriptEvaluator());

Creating a domain



Now, let’s create a domain for banking:


 1     private static final String BANKING = "banking";

2 repositoryFactory.getDomainRepository().save(new Domain(BANKING, ""));


Creating Users


Next step is to create users for the domain or application so let’s define accounts for tom, cassy, ali, mike and larry, i.e.,


 1         final SubjectRepository subjectRepo = repositoryFactory

2 .getSubjectRepository(BANKING);
3 Subject tom = subjectRepo.save(new Subject("tom", "pass"));
4 Subject cassy = subjectRepo.save(new Subject("cassy", "pass"));

5 Subject ali = subjectRepo.save(new Subject("ali", "pass"));
6 Subject mike = subjectRepo.save(new Subject("mike", "pass"));

7 Subject larry = subjectRepo.save(new Subject("larry", "pass"));
8


Creating Roles


Now, we will create roles for Teller, CSR, Accountant, AccountManager and LoanManager:


  1         final RoleRepository roleRepo = repositoryFactory

2 .getRoleRepository(BANKING);
3 Role employee = roleRepo.save(new Role("Employee"));
4 Role teller = roleRepo.save(new Role("Teller", employee));

5 Role csr = roleRepo.save(new Role("CSR", teller));
6 Role accountant = roleRepo.save(new Role("Accountant", employee));

7 Role accountantMgr = roleRepo.save(new Role("AccountingManager",
8 accountant));
9 Role loanOfficer = roleRepo

10 .save(new Role("LoanOfficer", accountantMgr));
11


Creating Permissions


We can then create new permissions and save them in the database as follows:


  1         final PermissionRepository permRepo = repositoryFactory

2 .getPermissionRepository(BANKING);
3 Permission cdDeposit = permRepo.save(new Permission("(create|delete)",
4 "DepositAccount",

5 "employee.getRegion().equals(customer.getRegion())")); // 1

6 Permission ruDeposit = permRepo.save(new Permission("(read|modify)",
7 "DepositAccount",

8 "employee.getRegion().equals(customer.getRegion())")); // 2

9 Permission cdLoan = permRepo.save(new Permission("(create|delete)",
10 "LoanAccount", "account.getBalance() < 10000")); // 3

11 Permission ruLoan = permRepo.save(new Permission("(read|modify)",
12 "LoanAccount", "account.getBalance() < 10000")); // 4

13
14 Permission rdLedger = permRepo.save(new Permission("(read|create)",
15 "GeneralLedger", "year == new Date().getFullYear()")); // 5

16
17 Permission rGlpr = permRepo
18 .save(new Permission("read", "GeneralLedgerPostingRules",
19 "year == new Date().getFullYear()")); // 6

20
21 Permission cmdGlpr = permRepo.save(new Permission(
22 "(create|modify|delete)", "GeneralLedgerPostingRules",
23 "year == new Date().getFullYear()")); // 7

24

Mapping Subjects/Permissions to Roles


Now we will map subjects to roles as follows:


 1         final SecurityMappingRepository smr = repositoryFactory

2 .getSecurityMappingRepository(BANKING);
3

4 // Mapping Users to Roles
5 smr.addRolesToSubject(tom, teller);
6 smr.addRolesToSubject(cassy, csr);
7 smr.addRolesToSubject(ali, accountant);

8 smr.addRolesToSubject(mike, accountantMgr);
9 smr.addRolesToSubject(larry, loanOfficer);
0


Then we will map permissions to roles as follows:


 1         smr.addPermissionsToRole(teller, ruDeposit);
2 smr.addPermissionsToRole(csr, cdDeposit);

3 smr.addPermissionsToRole(accountant, rdLedger);
4 smr.addPermissionsToRole(accountant, ruLoan);
5 smr.addPermissionsToRole(accountantMgr, cdLoan);
6 smr.addPermissionsToRole(accountantMgr, rGlpr);
7 smr.addPermissionsToRole(loanOfficer, cmdGlpr);
8


Authorization


Now the fun part of authorization, let’s check if user “tom” can view deposit-accounts, e.g.


  1    public static Map<String, Object> toMap(final Object... keyValues) {

2 Map<String, Object> map = new HashMap<String, Object>();
3 for (int i = 0; i < keyValues.length - 1; i += 2) {

4 map.put(keyValues[i].toString(), keyValues[i + 1]);
5 }
6 return map;
7 }

8 @Test
9 public void testReadDepositByTeller() {
10 initDatabase();
11 permissionManager.check(new PermissionRequest(BANKING, "tom", "read",

12 "DepositAccount", toMap("employee", new Employee("tom",
13 "west"), "customer", new Customer("zak", "west"))));

14 }
15

16

Note that above test method builds a PermissionRequest that encapsulates domain, subject, operation, target and context and then calls check method of SecurityManager, which throws SecurityException if permission fails.


Then we check if tom, the teller can delete deposit-account, e.g.


 1     @Test(expected = SecurityException.class)

2 public void testDeleteByTeller() {
3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "tom", "delete",

5 "DepositAccount", toMap("employee", new Employee("tom",
6 "west"), "customer", new Customer("zak", "west"))));

7 }
8


Which would throw security exception.


Now let’s check if cassy, the CSR can delete deposit-account, e.g.


 1     @Test
2 public void testDeleteByCsr() {

3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "cassy",
5 "delete", "DepositAccount", toMap("employee",

6 new Employee("cassy", "west"), "customer",
7 new Customer("zak", "west"))));

0


Which works as CSR have permissions for deleting deposit-account. Now, let’s check if ali, the accountant can view general-ledger, e.g.


 1    @Test
2 public void testReadLedgerByAccountant() {

3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "ali", "read",
5 "GeneralLedger", toMap("year", 2010, "account",

6 new Account("zak", 500))));
7 }
8

9

Which works as expected. Next we check if ali can delete general-ledger:


 1     @Test(expected = SecurityException.class)

2 public void testDeleteLedgerByAccountant() {
3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "ali", "delete",

5 "GeneralLedger", toMap("year", 2010, "account",
6 new Account("zak", 500))));
7 }

8

9

Which would fail as only account-manager can delete. Next we check if mike, the account-manager can create general-ledger, e.g.


 1     @Test
2 public void testCreateLedgerByAccountantManager() {

3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "mike",
5 "create", "GeneralLedger", toMap("year", 2010,

6 "account", new Account("zak", 500))));
7 }
8


Which works as expected. Now we check if mike can create posting-rules of general-ledger, e.g.


 1     @Test(expected = SecurityException.class)

2 public void testPostLedgingRulesByAccountantManager() {
3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "mike",

5 "create", "GeneralLedgerPostingRules", toMap("year",
6 2010, "account", new Account("zak", 500))));

7 }
8


Which fails authorization. Then we check if larry, the loan officer can create posting-rules of general-ledger, e.g.


 1     @Test
2 public void testPostLedgingRulesByLoanManager() {

3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "larry",
5 "create", "GeneralLedgerPostingRules", toMap("year",

6 2010, "account", new Account("zak", 500))));
7 }
8


Which works as expected. Now, let’s check the same permission but with different year, e.g.


 1     @Test(expected = SecurityException.class)

2 public void testPostLedgingRulesByLoanManagerWithExceededAmount() {
3 initDatabase();
4 permissionManager.check(new PermissionRequest(BANKING, "larry",

5 "create", "GeneralLedgerPostingRules", IDUtils.toMap("year",
6 2011)));
7 }
8


Which fails as year doesn’t match.


Summary


Above examples demonstrate how PlexRBAC API can be used along with instance or dynamic based security. In next post, I will describe caching and how PlexRBAC can be integrated with J2EE and Spring security.