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

Java-based (JDBC) data connectivity to SaaS, NoSQL, and Big Data. Download Now.

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

If there is a question on building such software, you would get adifferent 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, sandwitched 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 screw driver.

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.

Identity and Access Management Application Design


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:

Provider-API Module











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.

Model-API User











Now this is the Java feature called ServiceLoader API:

ServiceLoader API




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.

SPI Defines Providers






SPI File Structure




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

SPI Configuration






SPI Configuration



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


After changing the user provider to ‘jpa



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:

KeycloakWith 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 theexisting system. Plus your system has become highly modular since JARs are the unit of modularity now.

Connect any Java based application to your SaaS data.  Over 100+ Java-based data source connectors.

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 }}