Over a million developers have joined DZone.

Who Cares What Tools You Use, Just Keep Your Application Lightweight

DZone 's Guide to

Who Cares What Tools You Use, Just Keep Your Application Lightweight

See how to build a Identity and Access Management example app that is modular, loosely coupled, highly configurable, highly testable, and easily extensible.

· Java Zone ·
Free Resource

Software applications are judged by many facets, for example, easy maintainability, easily altering of software behavior via configuration, speedy incorporation of a new feature without affecting existing features, how performant the application is, etc. 

If there is a question on building such software, you would get a different answer based on whom you approach. Spring evangelists would say "Spring is the framework for all you needDump the Java EE Standards since Java EE Lost the Competition and Spring Won" while Java EE adherents would respond by saying "It's not Spring anymore, it is the summer of Java EE 7 since the age of frameworks is over hence move from spring to Java EE.

However, many times we neither need a heavy framework nor a heavy application server to create a highly configurable/modular/testable lightweight, loosely coupled application, and yes it is possible. It's neither science fiction nor fairytale. Just utilize core Java features, sandwiched 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 the right tool at the right place. It is all about knowing when to use a hammer and when to use a screwdriver.

Problem Scenario

Let’s say we are going to build an IAM (Identity and Access Management) Application, which is, in fact, an absolutely essential tool for every organization regardless of size. It requires (but is not limited to) the following functionality:

  • Password Management
  • Self Service
  • Delegated Administration
  • Provisioning
  • Audit and Compliance
  • Federation and Single-Sign-on using SAML, OAuth, OpenId etc.
  • Role Based Access Control (RBAC)
  • Social Login
  • Service Oriented Architecture/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 give a 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 makes them available to the clients via Session Component.

 Image title

Provider SPI/API

There are three main interfaces, ProviderSpi, ProviderFactory, and Provider. The entire application would be centered around these interfaces.

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:

Image title


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

Image title


Now this is the Java feature called ServiceLoader API:
 Image title




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

Image title

Image title



The main config file is shown below, based on this configuration.

Image title

Image title


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 the 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());

            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);

        factories.put(factory.getId(), factory);


As can you can see here in our main class, we are doing two things: first inserting user and then scheduling some tasks. Depending upon the Config, it would either store in-memory or in a database.

public class Main {

    public static void main(String[] args) {
        ConsoleApplication application = new ConsoleApplication();

This time the user provider is ‘mem

Image title

After changing the user provider to ‘jpa

Image title


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 of the Keycloak Application (Identity and Access Management Application from Jboss), Here are some of the modules:

Image title

With this approach we end up creating lots of interfaces, so different components depend upon abstractions instead of concrete classes. Your system is open for extensions and closed for modifications since any specific implementation can be worked on separately and included in the system without affecting the existing system. Plus your system has become highly modular since JARs are the unit of modularity now.

java ,architecture

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}