You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@uima.apache.org by Richard Eckart de Castilho <ec...@tk.informatik.tu-darmstadt.de> on 2011/04/02 17:15:48 UTC

Re: Dependency Injection support?

Hello everybody,

thinking more about a UIMA/Spring (or potentially any other popular DI framework) integration, I came up with several scenarios:

1) a UIMA component should access objects from an external context as external resources, e.g. a Spring application context
2) a UIMA component should be initialized by a DI framework, e.g. by applying Spring BeanPostProcessors
3) a UIMA component should be described through means provided by a DI framework, e.g. using a Spring Bean Configuration file.

I have implemented 1) and 2) and made them available as uimaFIT-spring at http://code.google.com/p/uimafit/source/browse/#svn%2Ftrunk%2FuimaFIT-spring
The example code below for 1) and 2) is actually taken from the unit tests of uimaFIT-spring.
Scenario 3) has been implemented by Roberto but so far is not available. For more sample code see his last mail to this thread.

I will address these scenarios now and illustrate solutions in conjunction with Spring, pros and cons for each.
I have omitted modifiers such as static, private or public.

Before going into the details, I would like to remark that while UIMA is in principle designed in a very configurable and modifiable way (almost everything works via interfaces and factories), the door has been shut quite tightly for anybody who actually wants to reconfigure UIMA. Critical fields, methods and classes are private
and static. After searching for a considerable time, I think I have managed to get away with a minimum of using reflection to circumvent this. With a few changes to the
core framework, UIMA could be made much open to integration into other DI/IOC frameworks - and actually some of the cons listed below would then disappear.

Now to the dirty details...

1) a UIMA component should access objects from an external context as external resources

Pros:
- works without a global application context
- user can actually inject different contexts into different analysis engines
- user has control over mapping external bean names to component resource keys (binding)
- analysis engine is pure UIMA (+uimaFIT if desired) - no dependency on external DI framework

Cons:
- uses reflection to access private UIMA class (ResourceRegistration) (cf. Jira UIMA-2102)

    // Acquire application context
    ApplicationContext ctx = getApplicationContext();

    // Create resource manager
    SpringContextResourceManager resMgr = new SpringContextResourceManager();
    resMgr.setApplicationContext(ctx);

    // Create component description
    AnalysisEngineDescription desc = createPrimitiveDescription(MyAnalysisEngine.class);
    bindExternalResource(desc, "injectedBean", "springBean");

    // Instantiate component
    AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(desc, resMgr, null);

    // Test that injection works
    ae.process(ae.newJCas());

    class MyAnalysisEngine extends org.uimafit.component.JCasAnnotator_ImplBase {
        @ExternalResource(key = "injectedBean") Object injectedBean;

    	void process(JCas aJCas) throws AnalysisEngineProcessException {
            assertEquals("BEAN", injectedBean);
        }
    }

http://code.google.com/p/uimafit/source/browse/trunk/uimaFIT-spring/src/test/java/org/uimafit/spring/SpringContextResourceManagerTest.java


2) a UIMA component should be initialized by a DI framework

Pros:
- any component produced by UIMA is passed through the DI framework
- interfaces such as ApplicationContextAware, BeanFactoryAware, etc. can be used
- annotations such as @Inject, @Resource or @Autowired can be used

Cons:
- application context is globally injected into the UIMA framework (UIMA is a singleton)

Caveats:
- Spring application context is only set after UIMA's initialize() has been invoked - initialization logic requiring both UIMA and Spring beans has to happen in afterPropertiesSet()
- component is initialized by Spring, but its life-cycle is managed by UIMA, not by Spring

    // Create application context
    final GenericApplicationContext ctx = new GenericApplicationContext(); 
    AnnotationConfigUtils.registerAnnotationConfigProcessors(ctx); 
    ctx.registerBeanDefinition("otherBean", BeanDefinitionBuilder.genericBeanDefinition(String.class).addConstructorArgValue("BEAN").getBeanDefinition());
    ctx.registerBeanDefinition("analysisEngineFactory", BeanDefinitionBuilder.genericBeanDefinition(AnalysisEngineFactory_impl.class).getBeanDefinition()); 

    // Configure UIMA for this context
    new UIMAFramework_impl() { { 
        CompositeResourceFactory_impl factory = (CompositeResourceFactory_impl) getResourceFactory(); 
        factory.registerFactory(ResourceCreationSpecifier.class, ctx(AnalysisEngineFactory_impl.class)); 
    } }; 

    // Instantiate component
    AnalysisEngine ae = createPrimitive(MyAnalysisEngine.class);

    // Test that injection works
    ae.process(ae.newJCas());

    class MyAnalysisEngine extends JCasAnnotator_ImplBase {
        @Autowired @Qualifier("otherBean") Object injectedBean;

        void process(JCas aJCas) throws AnalysisEngineProcessException {
            assertEquals("BEAN", injectedBean);
        }
    }

http://code.google.com/p/uimafit/source/browse/trunk/uimaFIT-spring/src/test/java/org/uimafit/spring/UimaFactoryInjectionTest.java


3) a UIMA component should be described through means provided by a DI framework

This is taken from Roberto's mail, slightly reduced and I have added two more cons.

Pros:
- casProcessors, consumers and reader are POJO:
- can use spring (Roberto)
- seamless your business logic: db access, internal api (Roberto)
- easy to test and inject mocks (Roberto)

Cons:
- no different classloader (no PEAR) (Roberto)
- maybe lack of other  functionality, which I don't use :) (Roberto)
- works for primitives, but requires more invasive work for aggregates (Richard)

    <bean name="regExpTokenizer-0" class="uima.bean.CasProcessorFactoryBean" parent="baseAnnotator">
        <property name="component">
            <bean class="uima.annotators.language.RegExpTokenizer" parent="baseCasProcessor">
                <property name="resourcesPattern" value="commandsTokenizer_${key}.xml" /> <!--- a String--->
                <property name="fileRepo" ref="fileSystem" /> <!--- a reference to a bean--->
            </bean>
        </property>
    </bean>

Happy for any comments.

Cheers,

Richard

-- 
------------------------------------------------------------------- 
Richard Eckart de Castilho
Technical Lead
Ubiquitous Knowledge Processing Lab 
FB 20 Computer Science Department      
Technische Universität Darmstadt 
Hochschulstr. 10, D-64289 Darmstadt, Germany 
phone +49 (6151) 16-7477, fax -5455, room S2/02/E225
eckartde@tk.informatik.tu-darmstadt.de 
www.ukp.tu-darmstadt.de 
-------------------------------------------------------------------