Integrating a third party library with CDI - or developping a CDI support for a library - can be done in two ways:

  • cheap: just provide a lookup solution to get CDI beans but keep the framework configuration
  • complete: make CDI the first class citizen of the framework and bring the framework to CDI

Both solutions are valid even if drastically opposed.

In this post serie we'll investigate the last one since the first one is generally "easy".

One example: Apache Shiro

Before starting with code snippets, let's use this first post to really understand the difference between both.

Let's have a look to a common way to setup and use Apache Shiro before adding CDI into the game:

  • you add the needed dependencies (shiro-web for instance). This part is one which will not change with or without CDI.
  • you configure the web integration through web.xml add shiro filter and environment intializer
  • you configure shiro itself in shiro.ini
  • you use the programmatic API or annotation API to validate the user (Subject in Shiro semantic) context

Shiro configuration

Here is a sample for shiro.ini (taken from shiro master):

[main]
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdUrlRewritingEnabled = false

securityManager.sessionManager = $sessionManager
securityManager.cacheManager = $cacheManager

[urls]
/** = authcBasic[permissive]

[users]
root = secret,admin
guest = guest,guest
presidentskroob = 12345,president
darkhelmet = ludicrousspeed,darklord,schwartz
lonestarr = vespa,goodguy,schwartz

[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

What do we observe?

  • The main part defines the wiring of the runtime. This is a shiro specific IoC.
  • The other parts just configure some special beans (in real life users/roles are not in this file but in a database or ldap for instance)

So what would it mean to migrate it to CDI? After previous analyzis we identify that CDI integration will happen mainly at [main] part. The first challenge will be to use CDI as the main IoC for Shiro and not rely on a custom one (the one Shiro built on top of shiro.ini).

If you pause a second and check the proposed model in spring you will quickly see it is close to that philosophy:

@Configuration
@Import({ShiroBeanConfiguration.class})
public class ShiroConfiguration extends AbstractShiroConfiguration {

    @Bean
    @Override
    protected SessionsSecurityManager securityManager(List<Realm> realms) {
        return super.securityManager(realms);
    }

    @Bean
    @Override
    protected SessionManager sessionManager() {
        return super.sessionManager();
    }

    @Bean
    @Override
    protected SubjectDAO subjectDAO() {
        return super.subjectDAO();
    }

    @Bean
    @Override
    protected SessionStorageEvaluator sessionStorageEvaluator() {
        return super.sessionStorageEvaluator();
    }

    @Bean
    @Override
    protected SubjectFactory subjectFactory() {
        return super.subjectFactory();
    }

    @Bean
    @Override
    protected SessionFactory sessionFactory() {
        return super.sessionFactory();
    }

    @Bean
    @Override
    protected SessionDAO sessionDAO() {
        return super.sessionDAO();
    }

    @Bean
    @Override
    protected Authorizer authorizer() {
        return super.authorizer();
    }

    @Bean
    @Override
    protected AuthenticationStrategy authenticationStrategy() {
        return super.authenticationStrategy();
    }

    @Bean
    @Override
    protected Authenticator authenticator() {
        return super.authenticator();
    }

    @Bean
    @Override
    protected RememberMeManager rememberMeManager() {
        return super.rememberMeManager();
    }
}

All the goal of shiro.ini is to build a Shiro SecurityManager fully intiialized. Using CDI as backbone just consists to build it based on CDI beans.

Shiro usage

Once you have a SecurityManager and it is initialized properly (I'll skip the ThreadContext setup there since it is very specific to Shiro and far from our original topic), you can use the programmatic or annotation based API:

@ApplicationScoped
public class Service {
    @RequiresRoles("admin")
    public Result stopBatch(BatchId id) {
        // ...
    }
}

What's the challenge there in CDI? Shiro not being CDI integrated you need an @InterceptorBinding which means the actual service will look like:

@ApplicationScoped
public class BatchService {
    @Shiro
    @RequiresRoles("admin")
    public Result stopBatch(BatchId id) {
        // ...
    }
}

Adding this marker will slowly result in a lot of boilerplate (suppose you use multiple frameworks for the same method).

Here is our second challenge: avoid to require the user to use other annotations.

Conclusion

Integrating with CDI a third party library has mainly two challenges:

  • replace the library initialization by CDI using CDI beans directly and avoiding any custom configuration not integrated to CDI or requiring some specific files
  • avoid to add new API/annotations

Next posts will deal with these two aspects still using Shiro example.

From the same author:

In the same category: