Browse DevX
Sign up for e-mail newsletters from DevX


POJO-Based Solutions for LDAP Access: One Good, One Better : Page 4

Find out how to employ dependency injection, annotation, and aspect-oriented programming to enable POJO-based application development.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Comparing the Two Solutions

The most noticeable difference between the class diagrams of the two solutions is the dependency: in the first version, the Account class is directly dependent on the DAO interface, and in the second, the aspect depends on the DAO (See Figure 3). The annotated version of the Account and the aspect are both dependent on annotation PropertyKey in the second version.

Figure 3. Class Diagrams for Solution 2: The first solution is enhanced with annotation and AOP.

First of all, the new version makes unit testing easier. To remove the dependency on LDAP, the first version would require you to create a mock POJO implementation of the property client:

&public class MockPropertyDAO implements PropertyDAO { @Override public Object getProperty(String[] key, PropertyType type) { return keyValues.get(Arrays.toString(key)); } public void setProperty(String[] key, Object value) { keyValues.put(Arrays.toString(key), value); } private Map<String, Object> keyValues = new HashMap<String, Object>(); }

The test case for the Account class then has the mock DAO injected into the Account class so that you could set different threshold values for different test scenarios:

public class AccountTest { @BeforeClass public static void setUpBeforeClass() throws Exception { account.setDao(dao); } @Test public void testThreshold1() { dao.setProperty(thresholdKey, 1000L); assertEquals(1000L, account.getThreshold()); account.businessLogicUsingThreshold(); // validation logic... } private static Account account = new Account(); private static MockPropertyDAO dao = new MockPropertyDAO(); private String[] thresholdKey = new String[] { "dc", "com", "dc", "my-domain", "cn", "app1", "cn", "threshold"}; }

In contrast, the second version would require you to expose the LDAP configuration data as a POJO property. Either by excluding the aspect from the compilation of the test classes or with modifications of the pointcut, the test case can just call the setter to set the property to whatever value the test scenarios need.

For example, this modified pointcut renders the aspect advice non-operational if the getters are called from JUnit4 test cases:

pointcut propertyGetter(PropertyKey key) : @annotation(key) && !cflow(junit4TestCase()); pointcut junit4TestCase() : execution(@Test * *()) || execution(@BeforeClass * *()) || execution(@AfterClass * *()) || execution(@Before * *()) || execution(@After * *());

Compared with AccountTest, the test for annotated Account is simpler and more intuitive because you do not need to deal with DAO and you manipulate the desired property directly through the annotated Account:

public class AnnotatedAccountTest { @Test public void testThreshold1() { account.setThreshold(1000L); assertEquals(1000L, account.getThreshold()); account.businessLogicUsingThreshold(); // validation logic... } private AnnotatedAccount account = new AnnotatedAccount(); }

What other benefits does the new solution bring? Compare the two versions of the getters:

// version 1 private long getThreshold() { String[] key = new String[] { "dc", "com", "dc", "my-domain", "cn", "app1", "cn", "threshold"}; return ((Long)dao.getProperty(key, PropertyType.LONG)).longValue(); } // version 2 @PropertyKey({"dc", "com", "dc", "my-domain", "cn", "app1", "cn", "threshold"}) private long getThreshold() { return threshold; }

As you can see, the annotated version becomes more readable because it is succinct. A less obvious benefit is that with annotation, the style of accessing LDAP changes from imperative to declarative. The annotation specifies the additional information regarding the getter, not how it is going to be consumed. It further raises the abstraction level for dealing with configuration data. So even if the DAO interface changes from String array to comma-separated-value String, the client does not need to change.

With the help of the aspect, the expected return type is captured by the joint point context. So, the client does not need to pass in explicitly the type value that is already in the getter signature. Moreover, because the around advice automatically casts the return type to match the annotated method return type, the need for explicit type casting (as required by the first version) is eliminated, further simplifying the calling syntax. This results in client code that is both easy to read and change. These benefits stand out even more with a larger code base that has many LDAP-based configuration values.

From a Good Solution to a Better Solution

All the good code qualities—such as testability, simplicity, readability, and maintainability—derive from the fact that the client code is POJO-based. Through the combined use of DI, annotation, and AOP, the Account class manages to remain a POJO even when faced with multiple application concerns.

When running unit tests, you can easily remove the dependency on LDAP by performing dependency injection with different configurable values. Instead of explicitly calling out to read the LDAP value, annotation marks the getter and specifies the property key. This prevents the infrastructure service-related code from tangling with the core business logic.

Finally, the aspect consumes the annotation and the getter signature and makes the calls to the DAO from a single place. This prevents LDAP access code from scattering. Because the separate concerns are neatly handled in a modular manner, code becomes clear, and changes are isolated and easier to make.

The initial solution—building on DI-based Spring LDAP and following the DAO pattern—is already reasonably good. By relentlessly eliminating redundancy and reducing complexity, and armed with good understanding of advanced techniques such as annotation and AOP, you will discover a simpler and more modular solution.

Walter Jia is a senior solution architect at TELUS communications and has been developing enterprise software for 15 years. He is passionate about writing maintainable code that continuously satisfies real business needs. In his spare time, he enjoys studying programming languages and reading good literature.
Thanks for your registration, follow us on our social networks to keep up-to-date