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]

Reply via email to