Great news! Ok, so I've made some good progress again after getting involved in this thread: http://tapestry.1045711.n5.nabble.com/Supporting-EJB-annotation-in-Tapestry-classes-td4696836.html
So the problem was: When contributing to MasterObjectProvider my CDIObjectprovider will resolve any tapestry services which also qualifies as a cdi bean. This is because the MasterObjectProvider will ask Objectlocator for a service after all providers have been asked. I have instead contributed an CDIInjectionProvider. *public static void contributeInjectionProvider(* * OrderedConfiguration<InjectionProvider> configuration,* * @Local CDIFactory cdiFactory) {* * configuration.add("CDI", new CDIInjectionProvider(cdiFactory), "after:*,before:Service");* *}* So attempts to find a cdi bean is no longer through the MasterObjectProvider but directly by the InjectWorker (through the chain of command). The conundrum is: I want to ask CDIInjectionProvider last, but ServiceInjectionProvider will 'blow up' if it fails to resolve a service/bean. So what I've done for now is this 'hack': *@Override* *public boolean provideInjection(String fieldName, Class fieldType,* * ObjectLocator locator, ClassTransformation transformation,* * MutableComponentModel componentModel) {* * * * /*** * * Problem: in many cases a tapestry service will qualify as a cdi bean.* * * In order to prevent cdi for managing a service that should be provided by tapestry we check if locator has the service.* * */* * try {* * if(locator.getService(fieldType)!=null)* * return false;* * } catch (RuntimeException e) { * //it blew up, which means there is a chance cdi can resolve it * }* * * * TransformField field = transformation.getField(fieldName); * * final Object injectionValue = cdiFactory.get(fieldType); * * if(injectionValue!=null) {* * field.inject(injectionValue);* * return true;* * }* * return false;* *}* It checks to see if a service could be resolved and does nothing as it should be handled by ServiceInjectionProvider which is next in chain. It does the job. A bit hacky, but I cannot think of another way of doing it. For Tapestry-CDI I've implemented two means of injection: * using @inject (either tapestry's or jsr-330). This goes through the Injectionprovider chain * using @CDI - this is handled by own ComponentClassTransformWorker. I'll host the code somewhere, perhaps github, in case others would be interested in giving it a spin as well. ..and please let me know if you have any suggestion for a cleaner approach --magnus On Thu, Jun 9, 2011 at 10:10 AM, Magnus Kvalheim <mag...@kvalheim.dk> wrote: > Thanks Howard, great to hear I'm on the right track :) > > I haven't given much thought to exposing tapestry services to cdi. > It's not something we currently require for our projects, but if it's > requested by others I could look into that as well! > > *Question* > I have one issue related to the CDIObjectProvider though. > While testing the quickstart (which uses the CDIModule) I created and > registered a tapestry service - which accidentally also satisfies as a CDI > managed bean. > *From quickstart appmodule* > *public static void bind(ServiceBinder binder){* > * binder.bind(AnotherService.class).withId("AnotherSrv"); * > *}* > > As posted earlier the CDIProvider is contributed to the > MasterObjectProvider as last (after:*)? > *configuration.add("cdiProvider", cdiProvider, "after:*");* > > AnotherService is injected into a component/page > *@Inject private AnotherService another;* > * > * > What I would want is the cdiprovider to be asked last - so that if Tapestry > itself cannot provide the service it will eventually get delegated to CDI. > In this case - tapestry should provide the service. > However, the cdiprovider is asked to provide the bean. (This is why I've > specifically test for scope now.) > > So the question is. *How can I make sure that the CDIProvider is asked > last in chain?* > Do I have to contribute to InjectionProvider as well? > > thanks in advance > Magnus > > On Wed, Jun 8, 2011 at 6:47 PM, Howard Lewis Ship <hls...@gmail.com>wrote: > >> Nope, it's designed to be easy ... though contributions into >> MasterObjectProvider are always tricky since you can get into >> dependency cycles very easily. You've properly side-stepped this using >> @InjectService (using the special @Local marker annotation is always >> very important). >> >> The hard part, which is still difficult with Spring, is proper >> co-dependence: allowing Tapestry services to be injected into Spring >> beans as well as the reverse. It comes down to a turf battle about >> which framework is "in charge". >> >> On Wed, Jun 8, 2011 at 4:56 AM, Magnus Kvalheim <mag...@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? >> > >> >> >> >> -- >> Howard M. Lewis Ship >> >> Creator of Apache Tapestry >> >> The source for Tapestry training, mentoring and support. Contact me to >> learn how I can get you up and productive in Tapestry fast! >> >> (971) 678-5210 >> http://howardlewisship.com >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >> For additional commands, e-mail: users-h...@tapestry.apache.org >> >> >