You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Kalle Korhonen <ka...@gmail.com> on 2014/04/03 08:44:07 UTC

Re: First stab at CDI module for tapestry

Just to complete the loop on my side regarding making Tapestry services as
CDI beans, it is almost, kind of, possible via an SPI extension. However,
you run into issues with the lifecycle management and bean requirements
(no-arg constructor etc.) as well as dependency loops. I followed
http://rmannibucau.wordpress.com/2013/08/19/adding-legacy-beans-to-cdi-context-a-cdi-extension-sample/for
inspiration (the author also has his code up on github at
https://github.com/rmannibucau/cdi-light-config/). A reasonable solution
could be useful for Tapestry as more JEE libraries expect a CDI layer. CDI
is great but still feels bulky and needlessly cumbersome compared to the
lightweight and easy-to-grasp nature of tapestry-ioc (for example, just
compare serviceResources.autobuild() to DeltaSpike's BeanBuilder). Perhaps
a future version of CDI will address this betters as the big boys (Spring,
Guice) are on the same boat (see
http://antoniogoncalves.org/2011/01/12/bootstrapping-cdi-in-several-environments/,
http://jglue.org/2011/11/29/cdi-for-guice-users/ for more info).

Kalle


On Sat, Mar 9, 2013 at 2:34 PM, Kalle Korhonen
<ka...@gmail.com>wrote:

> Hey Magnus,
>
> as part of your tapestry-cdi work, did you look into implementing a
> Tapestry CDI SPI, i.e. an implementation of
> javax.enterprise.inject.spi.BeanManager? This is related to my earlier
> thread about injectable entitylisteners (
> http://mail-archives.apache.org/mod_mbox/tapestry-dev/201210.mbox/%3CCA+=EWnB+Tjv01fiyZsDEd3U5iyOp3wcf1r3PRhqtDjrM7ixTdA@mail.gmail.com%3E).
> JPA 2.1 is required the check the SPI (see
> http://java.dzone.com/articles/cdi-extensions-you-can-build,
> http://java.net/downloads/jpa-spec/JavaPersistencePFD.pdf,
> http://stackoverflow.com/questions/12951701/how-to-get-entity-manager-or-transaction-in-jpa-listener,
>
> http://stackoverflow.com/questions/11740322/cdi-injection-is-not-working-in-servlets).
> The CDI is a solid spec, we should start thinking about offering
> tapestry-cdi or similar as a core Tapestry module.
>
> Kalle
>
>
>
> On Thu, Aug 18, 2011 at 11:59 AM, Magnus Kvalheim <ma...@kvalheim.dk>wrote:
>
>> Yes, I've noticed. Great work Igor :)
>>
>> I've intentionally not done anything for tapestry-cdi in terms of
>> supporting
>> javax.inject.@Inject.
>>
>> As Tap @inject and javax @inject are interchangeable - cdi bean injection
>> with either @inject should 'just work' with tapestry-cdi and T5.3 :)
>>
>> On Thu, Aug 18, 2011 at 12:56 PM, Igor Drobiazko
>> <ig...@gmail.com>wrote:
>>
>> > Just as a side note: starting with T5.3 it's possible to use JSR 330 for
>> > injection.
>> >
>> > Check this out:
>> > http://tapestry.apache.org/using-jsr-330-standard-annotations.html
>> >
>> > On Wed, Jun 8, 2011 at 1:56 PM, Magnus Kvalheim <ma...@kvalheim.dk>
>> > wrote:
>> >
>> > > Hi all,
>> > >
>> > > We're looking into moving our apps from a 'traditional' servlet
>> container
>> > > with spring into a Java EE web profile server like glassfish 3.1.
>> > > Motivations for doing this is to utilize cdi(jsr 299, 330), ejb3 and
>> > more.
>> > > Not just for the tapestry app, but also the other applications in
>> > > our portfoleo which share common core business logic.
>> > >
>> > > For reference on previous discussions:
>> > >
>> > >
>> >
>> http://tapestry.1045711.n5.nabble.com/Java-based-spring-configuration-td3394086.html
>> > > http://tapestry.1045711.n5.nabble.com/Discussion-td2421783i20.html
>> > >
>> > > Now, I've tried running the tapestry quickstart app in glassfish 3.1
>> > (with
>> > > the eclipse connector for publishing).
>> > > This works ok - although I cannot make live class reloading work. :(
>> > >
>> > > Glassfish uses Weld, so the CDIModule is basically an objectprovider
>> for
>> > > injecting Weld managed beans.
>> > > (As you probably know CDI/Weld can also be used outside jee as
>> > alternative
>> > > to tapestry-ioc, spring, etc)
>> > >
>> > > *CDIModule class*
>> > > *public class CDIModule { *
>> > > * public static void bind(ServiceBinder binder) {*
>> > > *    binder.bind(ObjectProvider.class,
>> > > CDIObjectProvider.class).withId("CDIObjectProvider");        *
>> > > *    } *
>> > > * public static BeanManager buildBeanManager(Logger log) { *
>> > > * try {*
>> > > * BeanManager beanManager = (BeanManager) new
>> > > InitialContext().lookup("java:comp/BeanManager");*
>> > > * return beanManager; *
>> > > * } catch (NamingException e) {*
>> > > * log.error("Could not lookup jndi resource: java:comp/BeanManager",
>> e);*
>> > > * }*
>> > > * return null;*
>> > > * } *
>> > > * public static void contributeMasterObjectProvider(*
>> > > * @InjectService("CDIObjectProvider") ObjectProvider cdiProvider,*
>> > > * OrderedConfiguration<ObjectProvider> configuration) { *
>> > > *// configuration.add("cdiProvider", cdiProvider,
>> > >
>> > >
>> >
>> "after:Service,after:AnnotationBasedContributions,after:Alias,after:Autobuild");
>> > > *
>> > > * configuration.add("cdiProvider", cdiProvider, "after:*"); *
>> > > * } *
>> > > *}*
>> > > *
>> > > *
>> > > The beanmanager is expected to be found in jndi. If the beans.xml is
>> > > present
>> > > it will be available at this point.
>> > > The BeanManager is also exposed as a service and injectable for other
>> > > services or components.
>> > > I've tested by adding the *@SubModule(CDIModule.class) *to my
>> quickstart
>> > > appmodule.
>> > > *
>> > > *
>> > > *CDIObjectProvider class*
>> > > *public class CDIObjectProvider implements ObjectProvider { *
>> > > * private BeanManager beanManager;*
>> > > * private Logger log;*
>> > > * *
>> > > * @SuppressWarnings({ "unchecked", "rawtypes" })*
>> > > * private Set allowedScopes = CollectionFactory.newSet(*
>> > > * ApplicationScoped.class,*
>> > > * Singleton.class);*
>> > > *
>> > > *
>> > > * public CDIObjectProvider(*
>> > > * Logger log,*
>> > > * @InjectService("BeanManager") BeanManager manager) {*
>> > > * this.beanManager = manager;*
>> > > * this.log = log;*
>> > > * }*
>> > > * @SuppressWarnings("unchecked")*
>> > > * public <T> T provide(Class<T> objectType,*
>> > > * AnnotationProvider annotationProvider, ObjectLocator locator) {*
>> > > * Set<Bean<?>> beans =  beanManager.getBeans(objectType);*
>> > > * if(beans!=null && beans.size()>0) {*
>> > > * Bean<T> bean = (Bean<T>) beans.iterator().next(); *
>> > > * if(hasValidScope(bean)) {*
>> > > * CreationalContext<T> ctx =
>> beanManager.createCreationalContext(bean);*
>> > > * T o = (T) beanManager.getReference(bean, objectType, ctx); *
>> > > * log.info("Found and returning: "+objectType.getCanonicalName());*
>> > > * return o; *
>> > > * }*
>> > > * }*
>> > > * return null;*
>> > > * } *
>> > > * protected <T> boolean hasValidScope(Bean<T> bean) {*
>> > > * return bean!=null && allowedScopes.contains(bean.getScope());*
>> > > * }*
>> > > *}*
>> > >
>> > > I've limited the scope to singleton/applicationscoped. Perhaps also
>> > Default
>> > > could be accepted though.
>> > > Until now I've only tested this with pojo's and not ejb's - but for
>> that
>> > > it's working as expected.
>> > > I can inject CDI beans into pages and components using*
>> > >  org.apache.tapestry5.ioc.annotations.Inject*
>> > >
>> > > I'm no expert to tapestry internals - so there could be
>> > > other considerations that needs to be addressed.
>> > > In fact in seemed just a little to easy to implement - so I must have
>> > > missed
>> > > something. - Or perhaps it's just that easy to do in Tapestry :)
>> > >
>> > > Thoughts, comments?
>> > >
>> >
>> >
>> >
>> > --
>> > Best regards,
>> >
>> > Igor Drobiazko
>> > http://tapestry5.de
>> >
>>
>
>