Showing posts with label guice. Show all posts
Showing posts with label guice. Show all posts

Monday, May 17, 2021

Simple Java/Scala Configuration Injection with Guice

Problem and Audience

One of the things every microservice needs is a mechanism for injecting configuration, so we developed a little json configuration helper for our littleware scala code that overlays a hierarchy of json configuration objects, and integrates with our module runtime and dependency-injection framework.

Configuration in Littleware

Littleware has a simple ServiceLoader based module runtime system that integrates with a guice dependency injection container. In practice what that means is that each java or scala jar includes a Module class that implements a simple callback interface for defining configuration injection bindings, and registering application event listeners (startup and shutdown). We have now augmented this platform with a json configuration helper that allows a module developer to provide configuration defaults on the classpath in the jar file, and the stack operator to override those defaults with a json file on an environment-defined search path or with json in an environment variable.

Here's how it works. The JsonConfigLoader provides a loadConfig helper that takes a key as an argument and returns a JsonObject (we use the gson json library).

The JsonConfigLoader also provides a bindKeys method that consumes a json object and a guice binder, converts the json to a list of (key, value) pairs, maps the values back to strings, and binds each key to its string value using guice's @Named binding facility.

So in the Module.scala (or .java) file described above, the module bootstrap code does something like this:

littleware.scala.JsonConfigLoader.loadConfig(CONFIG_KEY).map(
  {
    jsConfig =>
    littleware.scala.JsonConfigLoader.bindKeys(binder, jsConfig)
  }
)

Finally, a configuration provider can consume the bound configuration strings - like this:

@inject.Singleton()
    class ConfigProvider @inject.Inject() (
        @inject.name.Named("little.cloudmgr.sessionmgr.awsconfig") configStr:String,
        gs: gson.Gson
    ) extends inject.Provider[Config] {
        lazy val singleton: Config = {
            val js = gs.fromJson(configStr, classOf[gson.JsonObject])
            Config(
              js.getAsJsonPrimitive("oidcJwksUrl").getAsString(),
              Option(js.getAsJsonPrimitive("kmsSigningKey")).map({ _.getAsString() }),
              js.getAsJsonArray("kmsPublicKeys").asScala.map({ jsIt => jsIt.getAsJsonPrimitive().getAsString() }).toSet
            )
        }

        override def get():Config = singleton
    }

In the cloudmgr module above the configuration key is LITTLE_CLOUDMGR, so the config loader first loads littleware/config/LITTLE_CLOUDMGR.json off the classpath - which provides some developer defaults. The loader then searches the folders from the LITTLE_CONFIG_PATH environment (or system) variable until it finds a LITTLE_CLOUDMGR.json file, and it loads that, and does a shallow json merge. Finally, the config loader looks for a LITTLE_CLOUDMGR system (or environment) variable, and again merges the keys.

What does our configuration look like? We want to avoid collisions between binding keys from different modules, so the keys in a config json follow the java package reverse-dns pattern. Also, I like to have simple patterns that I can follow, so each service implementation in the module that requires configuration defines its own Config class and Provider[Config] that consumes a particular configuration key (that can be individually overriden via the configuration merge process described above). For example, the cloudmgr module has two service implementation, LocalKeySessionMgr and AwsSessionMgr, and the json configuration for the module looks like this:

{
    "little.cloudmgr.domain" : "test-cloud.frickjack.com",
    "little.cloudmgr.sessionmgr.type": "local",
    "little.cloudmgr.sessionmgr.localconfig": {
        "signingKey": { "kid": "testkey", "pem": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs02I2exqJsdAoHef\n54/cjmlRvww903MKp0AOPqlRRXqhRANCAATWdeIowEmJ5lxpm7gE8GtvBnB1FBTI\nlcZHdD1FPM90oeEAraGGtnluYYEdPiJP3r29n3qFcGTgvqDAE49bc4om\n-----END PRIVATE KEY-----" }, 
        "verifyKeys": [ 
            { "kid": "testkey", "pem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1nXiKMBJieZcaZu4BPBrbwZwdRQU\nyJXGR3Q9RTzPdKHhAK2hhrZ5bmGBHT4iT969vZ96hXBk4L6gwBOPW3OKJg==\n-----END PUBLIC KEY-----" } 
        ],
        "oidcJwksUrl": "https://www.googleapis.com/oauth2/v3/certs" 
    }, 
    "little.cloudmgr.sessionmgr.awsconfig": {
        "kmsPublicKeys": [ 
            "alias/littleware/api/api-frickjack-com/sessMgrSigningKey", 
            "alias/littleware/api/api-frickjack-com/sessMgrOldKey" 
        ], 
        "kmsSigningKey": "alias/littleware/api/api-frickjack-com/sessMgrSigningKey", 
        "oidcJwksUrl": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_860PcgyKN/.well-known/jwks.json"
    },
    "little.cloudmgr.sessionmgr.lambdaconfig": {
        "corsDomainWhiteList": [ ".frickjack.com" ],
        "cookieDomain": ".frickjack.com"
    }
}

Summary

We developed a little json configuration helper for our littleware scala code that overlays a hierarchy of json configuration objects, and integrates with our module runtime and dependency-injection framework.

Sunday, August 07, 2016

Custom JUnit4 TestRunner with Guice

It's easy to write your own JUnit test-runner. A developer on the java-platform often writes code in the context of some framework or container platform (Spring, Play, java-EE, OSGi, Guice, whatever) that provides facilities for application bootstrap and dependency injection. When at some point she wants to write a JUnit test suite for some subset of code the developer is faced with the problem of how to recreate the application framework's runtime context within JUnit's runtime context - which assumes a simple no-argument constructor for test-harness classes.

Fortunately - JUnit provides a serviceable solution for augmenting the test suite runtime context in the form of test runners. A developer uses an annotation on his test class to indicate that the tests require a custom runner. For example - Spring provides its SpringJUnit4ClassRunner which allows a test developer to use Spring annotations in her test suties - like this:

@RunWith(SpringJUnit4ClassRunner.class)
class WhateverTest {

     @Service(name="whatever")
     Whatever service;

     @Test
     public void testSomething() {
         assertTrue( ... );
     }
     ...
}



The Littleware project includes a system that extends Guice with a simple module and bootstrap mechanism that I was able to integrate with JUnit with a simple LittleTestRunner that follows the same trick that Spring's test runner uses of simply extending JUnit's default BlockJUnit4ClassRunner - overriding the createTest method - easy cheesey:

package littleware.test;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import littleware.bootstrap.LittleBootstrap;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

/**
 * JUnit4 test runner enabled with littleware.bootstrap Guice bootrap and
 * injection. Imitation of SpringJunit4TestRunner:
 *
 */
public class LittleTestRunner extends BlockJUnit4ClassRunner {
    private static final Logger log = Logger.getLogger(LittleTestRunner.class.getName());

    /**
     * Disable BlockJUnit4ClassRunner test-class constructor rules
     */
    @Override
    protected void validateConstructor( List<Throwable> errors ) {}
    
    /**
     * Construct a new {@code LittleTestRunner} and initialize a
     * {@link LittleBootstrap} to provide littleware testing functionality to
     * standard JUnit tests.
     *
     * @param clazz the test class to be run
     * @see #createTestContextManager(Class)
     */
    public LittleTestRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "constructor called with [{0}]", clazz);
        }
    }

    /**
     * This is where littleware hooks in
     * 
     * @return an instance of getClass constructed via the littleware managed Guice injector
     */
    @Override
    protected Object createTest() {
        try {
            return LittleBootstrap.factory.lookup(this.getTestClass().getJavaClass());
        } catch ( RuntimeException ex ) {
            log.log( Level.SEVERE, "Test class construction failed", ex );
            throw ex;
        }
    }
}

Now I can write tests in Guice's "inject dependencies into the constructor" style - like this:


/**
 * Just run UUIDFactory implementations through a simple test
 */
@RunWith(LittleTestRunner.class)
public class UUIDFactoryTester {

    private final Provider<UUID> uuidFactory;

    /**
     * Constructor stashes UUIDFactory to run test
     * against
     */
    @Inject()
    public UUIDFactoryTester(Provider<UUID> uuidFactory) {
        this.uuidFactory = uuidFactory;
    }

    /**
     * Just get a couple UUID's, then go back and forth to the string
     * representation
     */
    @Test
    public void testUUIDFactory() {

       //...
    }
}

Friday, June 28, 2013

CDI and Guice with jsr-300 javax.inject In Same Webapp

Things can get weird when java SE code developed with IOC is deployed into a java EE container that implements CDI. For example - the littleware code I work with implements simple modules and life cycle management with guice, so when I deploy that code in a webapp to a glassfish server I want guice's runtime to manage littleware's dependency injection, but I might also want CDI to inject a glassfish-managed JPA EntityManager into a servlet for me, and both guice and CDI are scanning for the same javax.inject runtime annotations. Fortunately - this kind of thing worked fine for me until recently, since CDI 1 as introduced in java EE 6 ignored jars that did not include a META-INF/beans.xml file.

Java Enterprise Edition 6 introduced JSR-330 (javax.inject annotations and provider interfaces) and JSR-299 (CDI - contexts and dependency injection) to the java-ee platform. The javax.inject package standardized the annotations and interfaces used by java's various inversion of control and dependency-injection systems (guice, spring, dagger, pico container, ...), so that code written with one IOC system in mind could be more easily used in an application that deployed to another system.

CDI introduced an IOC implementation for java enterprise edition (EE) technologies (servlets, JSF, EJB, JPA, ...) deployed in EE containers like glassfish, jboss, weblogic, and websphere.

I discovered CDI's behavior changed slightly in the new java EE 7 runtime when a webapp that ran fine in glassfish version 3 (a java EE 6 container) failed to deploy under glassfish 4 (the EE-7 reference implementation). The errors in the glassfish-4 logs lead me to this ticket in guava's bug database. It turns out that CDI's new version 1.1 runtime automatically scans all jars deployed with an enterprise application, and guava-14+ includes a couple classes with javax.inject annotations that CDI processed in an unintended way (littleware still uses guice's com.google.inject package annotations and interfaces, so CDI ignored those).

I eventually found one easy solution to this javax.inject - CDI 1.1 mix-up is to add a META-INF/beans.xml CDI config file to jars that include javax.inject annotations that require special or no handling by CDI. I added the following META-INF/beans.xml with a bean-discover-mode attribute set to "none" to the guava jar in my webapp, and that solved the problem. Hopefully guava's maintainers will add a similar beans.xml file to guava's binary distribution on maven central.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="none">
</beans>

Wednesday, January 25, 2012

Simple Session Scope with Guice

I've been a big fan of the Guice dependency injection library for a while, but I missed support for session-scoped injection until I figured out a way to achieve the same effect using child injectors. I was inspired by the PicoContainer project that I stumbled upon several months ago. I haven't actually used PicoContainer, but it appears to actually be better thought out than Guice - implementing arbitrary dependency injection scopes and lifecycle management via hierarchies of IOC containers.

I had PicoContainer's hierarchy approach bouncing around in my head when I stumbled up Guice's createChildInjector method - which supports a similar approach to managing object scopes.

Long story short - I introduced the notion of a "SessionModule" that defines session-scoped bindings to littleware's module system in my development repository clone. Littleware's module system also uses OSGi in an unorthodox way to manage the application life cycle. I wish I would have known about PicoContainer earlier - it appears to implement a nice approach for a combined IOC and application life cycle container. At some point I need to take a look at Spring too - to steal ideas if nothing else!