Introduction
Software applications are judged by many facets, for example easy maintainability, easily altering software behavior via configuration, speedy incorporation of new feature without affecting existing features , how performant the application is , etc.
If there is a question of building such software, you would get different answer based on whom you approach, Spring Evangelist would say “Spring is the framework for all you need, Dump the JEE standards since JEE Lost the competition and Spring Won” while JEE adherent would respond saying It’s not Spring anymore, it is the summer of JEE7 since the age of framework is over hence move from spring to JEE
However many a times we neither need a heavy framework nor a heavy application server to create a highly configurable/modular/Testable light weight, loosely coupled Application, and yes it is possible, it is neither a science fiction nor a fairytale.
Just utilizing Core Java Features, Sand-witched in Design Patterns, stuffed with Design principle and sprinkled with Good Programming Idioms.
All that I am trying to convey is that we have to use right tool at the right place, it is all about knowing when to use a hammer and a screw driver.
Problem Scenario
Let’s say we are going to build an IAM (Identity and Access Management) Application, which is in fact absolutely essential tool for every organization regardless of size, it requires the following (but not limited to) functionality:
- Password Management
- Self Service
- Delegated Admin
- Provisioning
- Audit and Compliance
- Federation and Single Sign on using SAML, OAuth, OpenId etc
- Role Based Access Control (RBAC)
- Social Login
- SOA/Rest API security etc.
Design Elements
We will use the following:
- Interface Segregation Principle : Many specific interfaces are better than a single, general interface
- Open Closed Principle (OCP) : Classes should be open for extension, but closed for modification
- Dependency Inversion Principle: Depend upon abstractions. Do not depend upon concrete classes.
- Single Responsible Principle: Classes should change for only a single reason.
- Inversion of Control : Service Locator pattern using ServiceLoader API
- Simple Factory Programming idiom
To build an application, this would be:
- Modular
- Loosely Coupled
- Highly Configurable
- Highly Testable
- Easily extensible
Here are the main participants, which gives 30,000 feet overview:
- Provider SPI/API : Using these components Services are exposed
- Configuration: This is typically a JSON configuration file, which defines the services consumed by the application.
- Service Loader: This is typically the Application Session Factory, which loads the services based on the config and make it available to the clients via Session Component.

Provider SPI/API
There are three main interfaces, ProviderSpi, ProviderFactory and Provider, Entire application would be central around these interfaces, as you would be seeing, we would be extending and implementing these interfaces to get the job done. Note, you can find the source code here
public interface ProviderSpi {
String getName();
Class<? extends Provider> getProviderClass();
Class<? extends ProviderFactory> getProviderFactoryClass();
}
public interface ProviderFactory<T extends Provider> {
public T create(AppSession session);
public void init(Config.Scope config);
public void close();
public String getId();
}
public interface Provider {
void close();
}
Config holds the user configuration, and AppSession provides a way to get any particular instance of Provider to get the job done. Let’s see two more interfaces important in this aspect.
public interface AppSessionFactory {
AppSession create();
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz);
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
List<ProviderFactory> getProviderFactories(Class<? extends Provider> clazz);
void close();
}
public interface AppSession {
AppTransactionManager getTransaction();
<T extends Provider> T getProvider(Class<T> clazz);
<T extends Provider> T getProvider(Class<T> clazz, String id);
<T extends Provider> Set<String> listProviderIds(Class<T> clazz);
<T extends Provider> Set<T> getAllProviders(Class<T> clazz);
void enlistForClose(Provider provider);
AppSessionFactory getAppSessionFactory();
void close();
}
Here is our provider-api module

Let’s extend provider-api, to do something meaningful, and hence let’s create model-api, to keep things simple, let’s add only one model called User.

Here are the corresponding classes/Interfaces
public class UserSpi implements ProviderSpi {
public String getName() {
return "user";
}
public Class<? extends Provider> getProviderClass() {
return UserProvider.class;
}
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return UserProviderFactory.class;
}
}
public interface UserProviderFactory extends ProviderFactory<UserProvider> {
}
public interface UserProvider extends Provider {
UserModel addUser(UserModel user);
boolean removeUser(UserModel user);
UserModel getUserByUsername(String username);
void close();
}
public interface UserModel {
String getUsername();
void setUsername(String username);
boolean isEnabled();
void setEnabled(boolean enabled);
String getFirstName();
void setFirstName(String firstName);
String getLastName();
void setLastName(String lastName);
String getEmail();
void setEmail(String email);
}
Now this is the Java feature called ServiceLoader API

Here is two of the implementations of model-api, one is model-mem which would store the details in memory, while other one would store in database using jpa called model-jpa, you can have a third implementation called model-mongo, which would store the details in mongodb.


Configuration
The main config file is shown below, based on this configuration things would be driven.


Service Loader Component
Service Loader component is an implementation of AppSessionFactory, the main purpose of this component is to load the available service based on the configuration. For this purpose it uses JDK Service Loader API in combination Config Object. Here is the snippet code, which shows what is going on in the AppSessionFactory. The important piece is in the init method.
public class DefaultAppSessionFactory implements AppSessionFactory {
private Map<Class<? extends Provider>, String> provider = new HashMap<Class<? extends Provider>, String>();
private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
public void init() {
for (ProviderSpi spi : ServiceLoader.load(ProviderSpi.class)) {
Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
factoriesMap.put(spi.getProviderClass(), factories);
String provider = Config.getProvider(spi.getName());
if (provider != null) {
loadConfiguredProvider(spi, provider, factories);
} else {
loadUnconfiguredProvider(spi, factories);
}
}
}
private void loadUnconfiguredProvider(ProviderSpi spi, Map<String, ProviderFactory> factories) {
for (ProviderFactory factory : ServiceLoader.load(spi.getProviderFactoryClass())) {
Config.Scope scope = Config.scope(spi.getName(), factory.getId());
factory.init(scope);
factories.put(factory.getId(), factory);
}
if (factories.size() == 1) {
String provider = factories.values().iterator().next().getId();
this.provider.put(spi.getProviderClass(), provider);
}
}
private void loadConfiguredProvider(ProviderSpi spi, String provider, Map<String, ProviderFactory> factories) {
this.provider.put(spi.getProviderClass(), provider);
ProviderFactory factory = loadProviderFactory(spi, provider);
Config.Scope scope = Config.scope(spi.getName(), provider);
factory.init(scope);
factories.put(factory.getId(), factory);
}
………
}
Demo
As can be seen here in our main class, we are doing two things, first inserting user and then scheduling some tasks, depending upon the Config, either it would store in memory or in database.
public class Main {
public static void main(String[] args) {
ConsoleApplication application = new ConsoleApplication();
application.insertUser();
application.setupScheduledTasks();
}
}
This time user provider is ‘mem’

After changing the user provider to ‘jpa’

Conclusion
What I have demonstrated is the very basic infrastructure which can be extended further to take it to different levels, and can be used in any layer, whether it is the Web, Middle or data access layer. In fact what I have demonstrated is the design in Keycloak Application (Identity and Access Management Application from Jboss), Here are some of the modules

With this approach we end up creating lots of interfaces and hence different components depend upon abstractions instead of concrete classes, your system would become open of extensions and closed for modifications, since any specific implementation can be worked on separately and included in the system without affecting existing system plus your system would become highly modular (since jar is the unit of modularity)
References
- Keycloak Website
- Source code of this blog





