When you get started with IVAAP’s backend SDK, the first API that you will probably encounter is its “Lookup” system. A lookup system is a basic component of a pluggable architecture. Within such architecture, when a program needs to perform an action, it is not aware of the specifics of this action’s implementation, it just knows how to find this implementation and execute it. There are many benefits to separating service definition from implementation. A program might have one default implementation that is overridden by a plugin. Clients can customize an application’s behavior without having access to the code of this application. “Looking up” the concrete implementation of a service is an effective way to propose options without cluttering the code with “if” statements that you need to change each time a new option is offered. IVAAP was not just meant to be a web application “built for purpose”—we wanted it to be a platform that customers can extend on their own. With this goal is mind, the first component that we picked for IVAAP’s architecture was a “lookup” system.
The Java language has a standard way of performing such dependency injections. Java’s ServiceLoader class is central to this mechanism but it is a bit outdated and maintenance-heavy. To plug classes for a ServiceLoader, you need to edit a separate META-INF/services text file. This file contains the name of the class you want to plug. It doesn’t offer protections against typos and if a class name changes, the injection breaks unless you remember to update this service file. This design violates the concept that “what changes together should belong together.”
Unlike the ServiceLoader, IVAAP uses Java annotations to register classes into its lookup. These annotations belong in same class they register and they don’t break when that class name changes. For example, here is how the built-in entitlements controller is registered:
@SelfRegistration(lookupClass = AbstractEntitlementsController.class, position = 50) public class DefaultEntitlementsController extends AbstractEntitlementsController {
The IVAAP SDK also has the option to perform this registration programmatically. This is the equivalent registration using code instead of annotations:
Lookup.Factory.getInstance().register(AbstractEntitlementsController.class, new DefaultEntitlementsController(), 50);
In many ways, this registration is very similar to what ServiceLoaders require. You can unregister classes, too. For example, here is how a customer would override how IVAAP controls entitlements:
@ClassUnregistration(lookupClass = AbstractEntitlementsController.class, registeredClass = DefaultEntitlementsController.class) @SelfRegistration(lookupClass = AbstractEntitlementsController.class, position = 100) public class MyEntitlementsController extends AbstractEntitlementsController {
This is the equivalent registration using code instead of annotations:
Lookup.Factory.getInstance().unregisterClass(AbstractEntitlementsController.class, DefaultEntitlementsController.class); Lookup.Factory.getInstance().register(AbstractEntitlementsController.class, new MyEntitlementsController(), 100);
Each registration has a position. This is mostly useful when several classes of the same type need to be registered in the lookup. By setting the position attribute, you can customize which class will be found first when the content of the lookup is inspected. In other words, the position controls the order in which all “if” statements will be executed. It also has performance tuning use cases. For example, there are many service handler classes registered, each one representing a microservice. You can decide the order in which they will be matched to a URL, optimizing for the most frequently used ones.
When annotations became part of the Java ecosystem, they were widely adopted as an alternative to XML configuration files. This approach has sometimes been overused, resulting in multiple types of annotations, carrying numerous attributes, becoming just as indecipherable as the XML configuration files they were attempting to replace. The IVAAP backend avoids this pitfall by using the same annotation across all option types, which means is only one set of annotations to learn for a programmer extending the platform.
Another nice feature of IVAAP’s lookup system occurs at startup. When the classpath is inspected for lookup annotations, the classes found are logged. There are 300 modules in IVAAP, and no two customers pick the same options. When troubleshooting is needed, these logs make an unambiguous way to learn which options are actually in play for a specific deployment.
Inspecting jar files for lookup registrations at startup takes time. For performance reasons, you might elect to ignore jars that are known to be registration-free. You typically exclude external libraries by adding lookup.ignore configuration files along with your jars. These files use regex expressions to exclude jars by name. You also have the option to set environment variables to achieve the same result. The later method is actually quite useful in the context of Docker deployments. This gives devops the option to create one Docker instance with all jars of the platform and customize how this instance behaves just by setting environment variables—you can reuse the same Docker image in multiple deployment contexts.
The Java ecosystem has many dependency injection libraries available. They tend to require configuration and many keystrokes, essentially interrupting the developer while coding is underway. IVAAP’s backend doesn’t just propose an easy way to customize how it behaves, it also proposes an unobtrusive way to create pluggable behaviors while developers are working. Actually, the simplicity of the lookup system is the reason why so many aspects of IVAAP are pluggable. When this system is introduced to new developers, they enjoy that it’s easy to learn, yet versatile. Devops appreciate that they can leverage it when a fast turnaround is needed.
Visit our products page for more information about IVAAP or contact us for a demo.