Any chance of getting this into a tapestry-module? Perhaps tapestry-cdi 1.0?
On Aug 18, 2011, at 5:13 AM, Magnus Kvalheim <[email protected]> wrote: > 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 <[email protected]> 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 <[email protected]>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 <[email protected]> >>> 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: [email protected] >>> For additional commands, e-mail: [email protected] >>> >>> >> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
