Sunday, September 19, 2010

JSR 330 and @Inject

The background (why the hell create a new IoC DI framework)
First of i would like to explain that the initial intention from my side was to create a very simple InjectionContainer that could extend Spring/Guice when needed and be replaces as soon as Spring/Guice released their version that supports JSR-330.

After waiting and not understanding how and when Spring and Guice was actually going to do this I started to play with the JSR 330 TCK. Once the TCK was implemented with the @Inject IoC I realized that this is quite powerful and that I wanted and needed extensions for it to be useful in a real world application.

The primary reason for this research from my side was to get a deep understanding of IoC DI and to finally implement a generic solution of the many "simplifications" I have always created in a application when I start to develop for a customer.

Basic project info/note
I am a Maven2 addicted developer (its not all good but its worth it in the end), so all projects are Maven'ized and all releases are deployed to central Maven repository (thanks to Sonatype).

Release 1.1.0 of Simple Java (with Config/i18n and Inject implementations bundled)

The Different projects

- Config
Java properties has been the same for ages and are very not very useful. This is an extension to those properties with Generics and override (via System parameters) support.
- i18n
Java Format packages has a lot of features but they are sometimes hard to "find" and use in a good way. This package does that, it gathers the formatters and configures them in a more useful way. And it also fixes an irritating bug that Sun refuses to solve with the wrong blank-space being used as a thousand separator.

- @Inject (the org.hrodberath.inject)
The Injection framework implementation is a certified (not official yet though) version of the JSF 330 specification. The TCK test suite can be found in the code base.

The @Inject framework today implements a small but similar feature set to Guice

Current feature set (in release 1.1.0)
  • support for new and singelton scope
    - also thread and inheritable-thread but these are custom
  • support for javax.inject.Provider (very useful interface)
  • support for configuration of older non @inject friendly factories via the InjectionFactory
  • Has extensive registering support (with "inheritable" modules), this is the intended strong point of this framework.
    - Has scanning support
    - Has module "extends" support (like inheritance)
    - Simple System.out printing of active configuration
Planned additions (in release 1.2.0)
  • support for attaching older non @inject friendly variable factory via a VariableInjectionFactory
  • SPI support for adding custom @Inject and @PostConstruct and @Scope annotations
  • Service Configuration live exporting to XML
  • More and better support for multi module configuration "mixing" where the exact results of these mixes can be seen and understood easily.
    - Implement the @Extends annotation that was added in 1.1
    - Support multiple "extends" (multi inheritance)
    - Support advance configuration where support for standard / customer / custom variations are supported and easy to understand how to configure. Add some way to switch between these config variations.
- Extensions for @Inject (the org.hrodberath.inject.extension)
I have planned the following extensions for release soon.

Ongoing features (Has SNAPSHOT code that is close to working as intended)

Transaction (JDBC and JPA support, not JTA or XA)
  • Uses EJB annotations with AspectJ
  • All transaction types from EJB implemented and supported
  • JPA --> JDBC shared connection support implemented
  • JDBC Helper package included
  • Junit Runner
    - Transacted and Container manager runner implemented
  • TODO: create more test suites for mixed transactions
JSF (@Inject and Transaction supported JSF with no-config setup)
  • JSF Injection Provider for @Inject implemented
  • OpenTransactionInView via a Filter is implemented
  • TODO: no config, need inspiration here: use a Annotation? naming?
Planned features (has no SNAPSHOTS or source code at this moment)

EJB 3.0 JUnit Framework (with Mocking help connected to mockito)
  • Extreme bootstrap speed (less than 1s for a standard project with 100 services or so)
  • @Inject and Transaction support for an EJBJunitRunner built on @Runwith
  • Support for @EJB and @Resource
    • Not entitys, use hypersonic with JPA for this, its loads simpler.
NetBeans RCP
  • @Inject and Transaction support
Eclipse RPC
  • @Inject and Transaction support
GWT (still unsure if this will be supported, as I don't "get it" GWT at this moment)
  • @Inject and Transaction support
Spring

Connect the @Inject container to Spring managed beans to support the usage of any Spring bean inside the container.
  • JMS Bean usage support verified
  • WebFlow beans support verified
  • RMI Bean usage support verified
Example Application for JSF (built on the superb JSF framework PrimeFaces)

This is intended as the showcase to prove that the coding can be done very fast and simple.
Points to sonatypes snapshot repository for the snapshot dependencies.
Usage examples

All projects are syncronized to central Maven and can be used without a extra addition of repository. Snapshots need the sonatype repository though,
https://oss.sonatype.org/content/repositories/snapshots/

See http://code.google.com/p/java-simple-utils/

Simple Java Config


public class AnyApplicationConfig extends ConfigBase {
private static final String DEFAULT_CONFIG = "classpath:/basicConfig.properties";

public static void initConfig() throws ParseException {
    initConfig(DEFAULT_CONFIG);
}
public static void initConfig(String resource) throws ParseException {
    String externalConfig = System.getProperty("config.externalfile");
    if(!StringUtil.isBlank(externalConfig)){
        // This initilized (reads) the config
        new AnyApplicationConfig(resource, externalConfig);
    }else{
        // This initilized (reads) the config
        new AnyApplicationConfig(resource, null);
    }
}

public interface ApplicationState {
    ConfigItem<String> A_STRING = new ConfigItem<String>(String.class, "anyapp.astring");
    ConfigItem<Date> A_DATE = new ConfigItem<Date>(Date.class, "anyapp.adate");
    ConfigItem<Integer> A_INTEGER = new ConfigItem<Integer>(Integer.class, "anyapp.ainteger");
    ConfigItem<Long> A_LONG = new ConfigItem<Long>(Long.class, "anyapp.along");
}


Simple Java i18n
Wiki Guide: http://code.google.com/p/java-simple-utils/wiki/i18n

// This mimics what can be done in a software using the profile provider.
Locale testProviderLocale = new Locale("en", "US");
LocaleProvider.setThreadLocaleProvider();
// This mimics what can be done in a RequestFilter for example
LocaleProvider.setProfile(new LocaleProfile(testProviderLocale));
// All calls to the provider from now on in the current thread will return this locale
LocaleProvider.getProfile().getLocale();

// The following code would now follow this locale
String testDate = "2010-01-01";
Formatter<Date> formatter = Formatter.getFormatter(Date.class);
Date aDate = formatter.convertToObject(testDate);
String aStringDate = formatter.convertToString(aDate);


Simple Java Injection (@Inject)
Wiki Guide: http://code.google.com/p/java-simple-utils/wiki/Injection
JSR 330 Annotation supported Container Configuration example:

InjectionRegisterJava registerJava = new InjectionRegisterJava();
registerJava.register(new RegistrationModuleSimple() {
    public void registrations() {
        register(AnyService.class).annotated(DoNothing.class)
            .scopeAs(ScopeContainer.Scope.SINGLETON)
            .with(AnyServiceDoNothingImpl.class);
        register(AnyService.class)
            .registeredAs(SimpleInjection.RegisterType.FINAL)
            .with(AnyServiceDoSomethingImpl.class);
    }
});

JSF and JUnit example setup with Injection support

@InjectionContainerContext(ModuleContainerForTests.class)
@RunWith(InjectionJUnitTestRunner.class)
@TransactionAttribute
public class TestJPATransactionManager {

    @Inject
    private TransactedApplication application;
...
}



The details about this code is
1. @InjectionContainerContext
- This is the InjectionContainer JUnitRunner Container Context
2. @RunWith
- This is the JUnit annotation that connects the Container to JUnit and adds transaction support when needed.
3. @TransactionAttribute
- This is the EJB3 annotation and it activates transaction support for all tests in this class. InjectionJUnitTestRunner is the class that actually does this.

A JSF Bean with Injected services

@ManagedBean(name = "personBean")
@RequestScoped
public class WebInjectBean {

    @Inject
    private ServiceInject serviceInject;
}


Config in web.xml for this to work
<context-param>
<param-name>com.sun.faces.injectionProvider</param-name>
<param-value>org.hrodberaht.webexample.web.ApplicationJSFResolverImpl</param-value>
</context-param>


A very simple example Application with the Injected transaction manager for a JPA configured application.
See the Google Code link

public class JPATransactedApplication implements TransactedApplication {

    @Inject
    private Provider<EntityManager>

    @TransactionAttribute
    public void createPerson(Person person) {      
        EntityManager em = entityManager.get();
        em.persist(person);
    }
}


The Application with the Injected transaction manager for a JDBC configured application.
See the Google Code link

public class JDBCHelperApplication {

    @Inject
    private JDBCService jdbcService;
  
    public void createAdvancedModel(AdvanceModel advanceModel) {
    Insert insert = jdbcService.createInsert("advanceModel");
        insert.field("id", advanceModel.getId());
        updateAllFields(advanceModel, insert);
        jdbcService.insert(insert);
    }

    public AdvanceModel findAdvancedModel(Long id) {
        String sql = "select * from AdvanceModel where id = ?";
        return jdbcService.querySingle(sql, new AdvanceModelIterator(), id);
    }
    ...
}


Example configuration from the proof of concept project
See Google Code link

InjectionRegisterScan registerJava = new InjectionRegisterScan();
registerJava.registerBasePackageScan("org.hrodberaht.webexample.service.impl");

InjectionRegisterModule register = new InjectionRegisterModule(registerJava);
final TransactionManager transactionManager =
new TransactionManagerJPAImpl(Persistence.createEntityManagerFactory("web-example-jpa"));
register.register(new TransactionManagerModule(transactionManager, register));