DEPENDENCY injection in Analytic suite

guice by google

CONSTRUCTOR INJECTION

public class myClass {
    
    private Service myService;

    public myClass(URL url) {
        this.myService = new Service(url);
    }
    ...
}
public class myController {
    
    private final Service myService;

    @Inject
    public myController(Service service) {
        this.myService = service;
    }
    ...
}

Before

After

Modules

public class serviceModule extends AbstractService {
    
    public configure() {
        bind(Service.class).to(ServiceImpl.class);
    }
    ...
}



public interface Service {
    ...
}

public class ServiceImpl implements Service {
    ...
}

Modules Annotation

public class serviceModule extends AbstractService {
    
    public configure() {
        bind(Service.class)
            .annotatedWith(Service1.class)
            .to(ServiceImpl1.class);
        bind(Service.class)
            .annotatedWith(Service2.class)
            .to(ServiceImpl2.class);
    }
    ...
}

public interface Service {
    ...
}

public class ServiceImpl1 implements Service {
    ...
}

public class ServiceImpl2 implements Service {
    ...
}
@Inject
public myClass(
    @Service1 Service service) {
    this.service = service;
}

Providers

public class serviceModule extends AbstractService {
    
    public configure() {
        bind(Service.class).toProvider(ServiceProvider.class);
    }
    ...
}



public class ServiceProvider implements Provider<Service> {
    
    public Service get() {
        return new Service();
    }

}

Singleton with providers

public class serviceModule extends AbstractService {
    
    public configure() {
        bind(Service.class).toProvider(ServiceProvider.class).in(Singleton.class);
    }
    ...
}



public class ServiceProvider implements Provider<Service> {
    
    private final Service service;

    public ServiceProvider() {
        this.service = new Service();
    }

    public Service get() {
        return this.service;
    }

}

Throwing providers

public class serviceModule extends AbstractService {
    
    public configure() {
        ThrowingProviderBinder.create(binder())
                .bind(ServiceProvider.class, Service.class)
                .to(ServiceProviderImpl.class)
                .in(Singleton.class);
    }
    ...
}

public interface ServiceProvider<T> extends CheckedProvider<T> {
  T get() throws ServiceException;
}

public class ServiceProviderImpl implements ServiceProvider<Service> {
    
    public Service get() throws ServiceException {
        try {
            return new Service();
        } catch(MalformedURLException e) {
            throw new ServiceException("cannot connect", e);
        }
    }
}

guice With play

controller instance

public class VersionCTRL extends Controller {
    private static final Logger.ALogger LOG = Logger.of(VersionCTRL.class);

    /**
     * Get the current commit version
     *
     * @return a Result object with the commit version
     */
    public static Result get() {
        LOG.debug("get the project version");

        try {
            return ok(toJson(new VersionOutput(Configuration.getApplicationVersion())));
        } catch (Exception e) {
            LOG.error("an error occurred", e);
            return internalServerError();
        }
    }
}

Before

controller instance

public class VersionCTRL extends Controller {
    private static final Logger.ALogger LOG = Logger.of(VersionCTRL.class);

    private final String version;

    @Inject
    public VersionCTRL(@ConfigParam(Property.APPLICATION_VERSION) String version) {
        this.version = version;
    }

    /**
     * Get the current commit version
     *
     * @return a Result object with the commit version
     */
    public Result get() {
        LOG.debug("get the project version");
        return ok(toJson(new VersionOutput(version)));
    }
}

After

guice In analytic suite

Configuration

No more class Configuration

@Inject
 public VersionCTRL(@ConfigParam(Property.APPLICATION_VERSION) String version)

Use ConfigParam annotation

public enum Property {
    ...
APPLICATION_VERSION("application.version",
            "Version of the application",
            new TypeLiteral<String>() {
            },
            "x.x.x"),
    ...
}

Add configuration in enum Property

application.version = "0.9-SNAPSHOT"

Add line in configuration file

globals.configuration.Property

conf/application.conf

Tests

Mock it all!

public class ControllerTest extends WithAuthenticatedApplication {

    private Service mockService = mock(Service.class);

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder()
            .in(new File("."))
            .global(new GlobalSettings())
            .bindings(new AuthModule(), new ConfigurationModule(), new TestModule())
            .build();
    }

    //test methods
    ...

    class TestModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(Service.class).toInstance(mockService);
        }
    }
}

BONUS : Initializers

new global

public class Global extends GlobalSettings {

    @Override
    public void onStart(Application application) {
        Logger.info("Starting app");
        startApp();
        Logger.info("Starting done");
    }

    private void startApp() {
        MainInitializer mainInitializer = new MainInitializer();
        try {
            mainInitializer.init();
            Logger.info("STARTUP SERVICE :)");
        } catch (InitializerException e) {
            stopApplication();
        }
    }

    private void stopApplication() {
        Logger.error("Exiting the application");
        System.exit(1);
    }
}

Main initializer

public class MainInitializer implements Initializer {
    protected static final Logger.ALogger LOG = Logger.of(MainInitializer.class);

    private final List<Initializer> initializers;

    public MainInitializer() {
        this.initializers = new LinkedList<>();
        LOG.debug("Initializing initializers");
        initializers.add(getInitializer(DatabaseInitializer.class));
        initializers.add(getInitializer(MsHadoopInitializer.class));
        initializers.add(getInitializer(MsIndexInitializer.class));
        initializers.add(getInitializer(UserInitializer.class));
        initializers.add(getInitializer(ImpalaInitializer.class));
    }

    private Initializer getInitializer(Class<? extends Initializer> clazz) {
        return Play.application().injector().instanceOf(clazz);
    }

    @Override
    public void init() throws InitializerException {
        final ArrayList<Throwable> throwables = new ArrayList<>();
        LOG.info("Launching initializers");
        initializers.forEach(i -> {
            try {
                i.init();
                LOG.info("" + i.getClass().getName() + " OK");

            } catch (InitializerException e) {
                throwables.add(e);
            }
        });

        if (!throwables.isEmpty()) {
            InitializerException e = new InitializerException("Main initializer throwable");
            throwables.forEach(e::addSuppressed);
            throw e;
        }
    }

MS INDEX INITIALIZER

public class MsHadoopInitializer implements Initializer {

    private final HadoopClientProvider hadoopClientProvider;
    private final TemporaryRepositoryInitializer temporaryRepositoryProvider;
    @Inject
    public MsHadoopInitializer(
            HadoopClientProvider hadoopClientProvider,
            TemporaryRepositoryInitializer temporaryRepositoryProvider
            ) {
        this.hadoopClientProvider = hadoopClientProvider;
        this.temporaryRepositoryProvider = temporaryRepositoryProvider;
    }

    @Override
    public void init() throws InitializerException {
        try {
            hadoopClientProvider.get();
        } catch (ConfigurationException e) {
            throw new InitializerException("Bad MS URI format in configuration", e);
        }
        try {
            temporaryRepositoryProvider.init();
        } catch (InitializerException ie) {
            InitializerException e = new InitializerException("Cannot create temporary repository");
            e.addSuppressed(ie);
            throw e;
        }
    }
}

DEPENDENCY injection in Analytic suite

By Erwann Cloarec

DEPENDENCY injection in Analytic suite

  • 474