You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Sonny Gill <so...@gmail.com> on 2011/09/28 11:26:47 UTC
Injecting domain object into all pages
Hi guys,
I am new to Tapestry.
Over the past couple of weeks, I have migrated a simple Tapestry application
we had to T 5.3 and really enjoyed the experience.
Now I am evaluating Tapestry for some further development by writing a
prototype application.
One of the things I want to be able to do is inject a domain object inside
all pages that have a field of that type.
The situation is this :-
1. One tapestry app will serve multiple sites
2. There is a domain object called Site which contains configuration for
site, and there is a mechanism to identify the Site for current request
based on information in the URL used (let us say a query parameter called
site, but in reality this will be the domain name used to access the
application).
What I am trying to do is intercept the request, look up the Site and inject
the Site object inside the target page which may look like this :-
class MyPage {
private Site site;
}
It appears that the first two steps can be accomplished either by using a
Decorator or a HttpServletRequestFilter. After that how to I inject the Site
object inside the target page?
I have searched through the Nabble forum, Tapestry documentation, Tapestry
quickstart etc. but I can't see a clear way of how to do this.
If anyone can provide an answer, or point me to some demo code or another
resource, that would help me greatly.
Thanks!
Cheers,
Sonny Gill
Re: Injecting domain object into all pages
Posted by Cezary Biernacki <ce...@gmail.com>.
Hi,
I am still using Tapestry 5.2.6, so I am not sure what changed in Tapestry
5.3. However I believe, that "field.inject(new Site());" line is wrong. It
inserts a constant value to the field. I believe (if online documentation is
still relevant) that you should use field.setConduit() instead, and provide
FieldConduit<Site> instance that selects in get() method a correct Site
based on a request.
See
http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/plastic/PlasticField.html#setConduit(org.apache.tapestry5.plastic.FieldConduit)
http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/plastic/FieldConduit.html
However, I would recommend avoiding using such advanced techniques with a
beta version of Tapestry. More mundane approach with normal injection of a
service, that has a single method returning Site would work on all versions
of Tapestry 5 without any headaches.
Best regards,
Cezary
On Wed, Oct 5, 2011 at 7:07 PM, Sonny Gill <so...@gmail.com> wrote:
> Hi guys,
>
> I tried this a few days ago, ran into a few problems, and then tried again
> today.
>
> Unfortunately I could not get this to work.
>
> On changing the method name to contributeSiteInjectionProvider, it was
> detected by Tapestry, and the provider and FieldValueConduit is created,
> but
> the ReadOnlyFieldValueConduit.get() was never called.
>
> Then I upgraded to Tapestry 5.3-beta-16, and it looks
> like ReadOnlyFieldValueConduit has been removed.
>
>
> Finally, I tried to follow
> http://wiki.apache.org/tapestry/JEE-Annotationand added the following
> :-
>
> public class SiteAnnotationWorker implements ComponentClassTransformWorker2
> {
>
> @Override
> public void transform(PlasticClass plasticClass, TransformationSupport
> transformationSupport, MutableComponentModel mutableComponentModel) {
> for (PlasticField field :
> plasticClass.getFieldsWithAnnotation(Inject.class)) {
> if (Site.class.getName().equals(field.getTypeName())) {
> field.inject(new Site());
> field.claim(Inject.class);
> }
> }
> }
>
> }
>
> and in AppModule,
>
> @Contribute(ComponentClassTransformWorker2.class)
> @Primary
> public static void
>
> provideClassTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2>
> configuration) {
> configuration.addInstance("site", SiteAnnotationWorker.class,
> "before:Property");
> }
>
> With this, the field is injected correctly (great!).
>
> The problem now is that Tapestry continues to keep the injected object
> around and injects the same object for every request.
> This is probably correct for the use case
> for ComponentClassTransformWorker(s), but I need to inject a different
> object on each HTTP request.
>
> How can I achieve that?
>
> Again.. I am very thankful for the help received so far. I would have been
> completely lost without it.
>
>
> Best regards,
> Sonny
>
>
>
>
> On Wed, Sep 28, 2011 at 2:59 PM, Cezary Biernacki <cezary.bi@gmail.com
> >wrote:
>
> > Hi,
> > I built a website with similar requirements, and it is not hard to do
> that
> > with Tapestry. A simple approach is to built a service that returns
> correct
> > 'Site' instance, and use it everywhere where you need to 'site'.
> >
> > So instead having
> > private Site site;
> >
> > you would have, e.g.
> > @Inject
> > private SiteLookup lookup;
> >
> >
> > and instead using 'site', you would use 'lookup.getSite()'. SiteLookup
> can
> > depend on 'Request' service, so there is no need to pass it as argument
> to
> > getSite() method.
> >
> > But, if you are not afraid of some more advanced machinery, you can
> > contribute your own InjectionProvider, and inject 'Site' directly,
> > something
> > like:
> >
> >
> > public class SiteInjectionProvider implements InjectionProvider {
> > private final Request request;
> >
> > public SiteInjectionProvider (Request request) {
> > this.request = request;
> > }
> >
> > @Override
> > public boolean provideInjection(String fieldName,
> > @SuppressWarnings("rawtypes") Class fieldType,
> > ObjectLocator locator, ClassTransformation transformation,
> > MutableComponentModel componentModel) {
> >
> > if (!Site.class.equals(fieldType)) {
> > return false;
> > }
> >
> > TransformField field = transformation.getField(fieldName);
> >
> > ComponentValueProvider<FieldValueConduit> provider =
> > createProvider(fieldName);
> >
> > field.replaceAccess(provider);
> >
> > return true;
> > }
> >
> > private ComponentValueProvider<FieldValueConduit> createProvider(final
> > String fieldName) {
> >
> > return new ComponentValueProvider<FieldValueConduit>() {
> >
> > public FieldValueConduit get(final ComponentResources
> resources)
> > {
> >
> > return new ReadOnlyFieldValueConduit(resources, fieldName)
> {
> > public Object get() {
> > return ....; // <--- here implement selecting
> > correct Site based on Request
> > }
> > };
> > }
> >
> > };
> > }
> >
> > }
> >
> >
> > Remember to contribute your injection provider in your AppModule
> >
> > @Contribute(InjectionProvider.class)
> > public static void
> > setupInjectingSite(OrderedConfiguration<InjectionProvider> configuration)
> {
> > configuration.addInstance("site", SiteInjectionProvider .class,
> > "after:Default");
> >
> > }
> >
> >
> > After that, you would be able to use
> > @Inject
> > private Site site;
> >
> > on your pages and components.
> >
> > Best regards,
> > Cezary
> >
> >
> >
> >
> >
> >
> >
> > On Wed, Sep 28, 2011 at 2:39 PM, Sonny Gill <so...@gmail.com>
> > wrote:
> >
> > > Thanks Barry.
> > >
> > > Site is a domain layer object and knows nothing about Request/Response
> > etc.
> > > There will be a limited number of Site objects, one for each site
> > > supported,
> > > created and configured at the application start up.
> > >
> > > I could set it up as a Service for Tapestry application.
> > > But can I then provide a Service lookup method that can look at the
> > current
> > > request, get the correct Site using some criteria, and hand it over to
> > > Tapestry to inject into the page for current request?
> > >
> > > So, something like :-
> > >
> > > public Service lookupSiteService(Request request) {
> > > String id = ...get site id from the request in some way...;
> > >
> > > SiteRepository repository = ...; // Can I inject an implementation of
> > > SiteRepository (also a domain layer object) here?
> > >
> > > return repository.getSite(id);
> > > }
> > >
> > >
> >
>
>
>
> --
> http://sonnygill.net/
> http://twitter.com/sonny_gill
> http://www.linkedin.com/in/sonnygill
>
Re: Injecting domain object into all pages
Posted by Sonny Gill <so...@gmail.com>.
Hi guys,
I tried this a few days ago, ran into a few problems, and then tried again
today.
Unfortunately I could not get this to work.
On changing the method name to contributeSiteInjectionProvider, it was
detected by Tapestry, and the provider and FieldValueConduit is created, but
the ReadOnlyFieldValueConduit.get() was never called.
Then I upgraded to Tapestry 5.3-beta-16, and it looks
like ReadOnlyFieldValueConduit has been removed.
Finally, I tried to follow
http://wiki.apache.org/tapestry/JEE-Annotationand added the following
:-
public class SiteAnnotationWorker implements ComponentClassTransformWorker2
{
@Override
public void transform(PlasticClass plasticClass, TransformationSupport
transformationSupport, MutableComponentModel mutableComponentModel) {
for (PlasticField field :
plasticClass.getFieldsWithAnnotation(Inject.class)) {
if (Site.class.getName().equals(field.getTypeName())) {
field.inject(new Site());
field.claim(Inject.class);
}
}
}
}
and in AppModule,
@Contribute(ComponentClassTransformWorker2.class)
@Primary
public static void
provideClassTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2>
configuration) {
configuration.addInstance("site", SiteAnnotationWorker.class,
"before:Property");
}
With this, the field is injected correctly (great!).
The problem now is that Tapestry continues to keep the injected object
around and injects the same object for every request.
This is probably correct for the use case
for ComponentClassTransformWorker(s), but I need to inject a different
object on each HTTP request.
How can I achieve that?
Again.. I am very thankful for the help received so far. I would have been
completely lost without it.
Best regards,
Sonny
On Wed, Sep 28, 2011 at 2:59 PM, Cezary Biernacki <ce...@gmail.com>wrote:
> Hi,
> I built a website with similar requirements, and it is not hard to do that
> with Tapestry. A simple approach is to built a service that returns correct
> 'Site' instance, and use it everywhere where you need to 'site'.
>
> So instead having
> private Site site;
>
> you would have, e.g.
> @Inject
> private SiteLookup lookup;
>
>
> and instead using 'site', you would use 'lookup.getSite()'. SiteLookup can
> depend on 'Request' service, so there is no need to pass it as argument to
> getSite() method.
>
> But, if you are not afraid of some more advanced machinery, you can
> contribute your own InjectionProvider, and inject 'Site' directly,
> something
> like:
>
>
> public class SiteInjectionProvider implements InjectionProvider {
> private final Request request;
>
> public SiteInjectionProvider (Request request) {
> this.request = request;
> }
>
> @Override
> public boolean provideInjection(String fieldName,
> @SuppressWarnings("rawtypes") Class fieldType,
> ObjectLocator locator, ClassTransformation transformation,
> MutableComponentModel componentModel) {
>
> if (!Site.class.equals(fieldType)) {
> return false;
> }
>
> TransformField field = transformation.getField(fieldName);
>
> ComponentValueProvider<FieldValueConduit> provider =
> createProvider(fieldName);
>
> field.replaceAccess(provider);
>
> return true;
> }
>
> private ComponentValueProvider<FieldValueConduit> createProvider(final
> String fieldName) {
>
> return new ComponentValueProvider<FieldValueConduit>() {
>
> public FieldValueConduit get(final ComponentResources resources)
> {
>
> return new ReadOnlyFieldValueConduit(resources, fieldName) {
> public Object get() {
> return ....; // <--- here implement selecting
> correct Site based on Request
> }
> };
> }
>
> };
> }
>
> }
>
>
> Remember to contribute your injection provider in your AppModule
>
> @Contribute(InjectionProvider.class)
> public static void
> setupInjectingSite(OrderedConfiguration<InjectionProvider> configuration) {
> configuration.addInstance("site", SiteInjectionProvider .class,
> "after:Default");
>
> }
>
>
> After that, you would be able to use
> @Inject
> private Site site;
>
> on your pages and components.
>
> Best regards,
> Cezary
>
>
>
>
>
>
>
> On Wed, Sep 28, 2011 at 2:39 PM, Sonny Gill <so...@gmail.com>
> wrote:
>
> > Thanks Barry.
> >
> > Site is a domain layer object and knows nothing about Request/Response
> etc.
> > There will be a limited number of Site objects, one for each site
> > supported,
> > created and configured at the application start up.
> >
> > I could set it up as a Service for Tapestry application.
> > But can I then provide a Service lookup method that can look at the
> current
> > request, get the correct Site using some criteria, and hand it over to
> > Tapestry to inject into the page for current request?
> >
> > So, something like :-
> >
> > public Service lookupSiteService(Request request) {
> > String id = ...get site id from the request in some way...;
> >
> > SiteRepository repository = ...; // Can I inject an implementation of
> > SiteRepository (also a domain layer object) here?
> >
> > return repository.getSite(id);
> > }
> >
> >
>
--
http://sonnygill.net/
http://twitter.com/sonny_gill
http://www.linkedin.com/in/sonnygill
Re: Injecting domain object into all pages
Posted by cqasker <cq...@yahoo.com>.
That is really cool. Did not know you can do that with Tapestry. I'm just
lurking but thanks -- I have a similar requirement for my app too.
--
View this message in context: http://tapestry.1045711.n5.nabble.com/Injecting-domain-object-into-all-pages-tp4848523p4854537.html
Sent from the Tapestry - User mailing list archive at Nabble.com.
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org
Re: Injecting domain object into all pages
Posted by Sonny Gill <so...@gmail.com>.
Hi Cezary,
Thanks a lot for the detailed response.
That InjectionProvider example looks like exactly what I am looking for.
I will give that a go.
Cheers,
Sonny
On Wed, Sep 28, 2011 at 2:59 PM, Cezary Biernacki <ce...@gmail.com>wrote:
> Hi,
> I built a website with similar requirements, and it is not hard to do that
> with Tapestry. A simple approach is to built a service that returns correct
> 'Site' instance, and use it everywhere where you need to 'site'.
>
> So instead having
> private Site site;
>
> you would have, e.g.
> @Inject
> private SiteLookup lookup;
>
>
> and instead using 'site', you would use 'lookup.getSite()'. SiteLookup can
> depend on 'Request' service, so there is no need to pass it as argument to
> getSite() method.
>
> But, if you are not afraid of some more advanced machinery, you can
> contribute your own InjectionProvider, and inject 'Site' directly,
> something
> like:
>
>
> public class SiteInjectionProvider implements InjectionProvider {
> private final Request request;
>
> public SiteInjectionProvider (Request request) {
> this.request = request;
> }
>
> @Override
> public boolean provideInjection(String fieldName,
> @SuppressWarnings("rawtypes") Class fieldType,
> ObjectLocator locator, ClassTransformation transformation,
> MutableComponentModel componentModel) {
>
> if (!Site.class.equals(fieldType)) {
> return false;
> }
>
> TransformField field = transformation.getField(fieldName);
>
> ComponentValueProvider<FieldValueConduit> provider =
> createProvider(fieldName);
>
> field.replaceAccess(provider);
>
> return true;
> }
>
> private ComponentValueProvider<FieldValueConduit> createProvider(final
> String fieldName) {
>
> return new ComponentValueProvider<FieldValueConduit>() {
>
> public FieldValueConduit get(final ComponentResources resources)
> {
>
> return new ReadOnlyFieldValueConduit(resources, fieldName) {
> public Object get() {
> return ....; // <--- here implement selecting
> correct Site based on Request
> }
> };
> }
>
> };
> }
>
> }
>
>
> Remember to contribute your injection provider in your AppModule
>
> @Contribute(InjectionProvider.class)
> public static void
> setupInjectingSite(OrderedConfiguration<InjectionProvider> configuration) {
> configuration.addInstance("site", SiteInjectionProvider .class,
> "after:Default");
>
> }
>
>
> After that, you would be able to use
> @Inject
> private Site site;
>
> on your pages and components.
>
> Best regards,
> Cezary
>
>
Re: Injecting domain object into all pages
Posted by Cezary Biernacki <ce...@gmail.com>.
Hi,
I built a website with similar requirements, and it is not hard to do that
with Tapestry. A simple approach is to built a service that returns correct
'Site' instance, and use it everywhere where you need to 'site'.
So instead having
private Site site;
you would have, e.g.
@Inject
private SiteLookup lookup;
and instead using 'site', you would use 'lookup.getSite()'. SiteLookup can
depend on 'Request' service, so there is no need to pass it as argument to
getSite() method.
But, if you are not afraid of some more advanced machinery, you can
contribute your own InjectionProvider, and inject 'Site' directly, something
like:
public class SiteInjectionProvider implements InjectionProvider {
private final Request request;
public SiteInjectionProvider (Request request) {
this.request = request;
}
@Override
public boolean provideInjection(String fieldName,
@SuppressWarnings("rawtypes") Class fieldType,
ObjectLocator locator, ClassTransformation transformation,
MutableComponentModel componentModel) {
if (!Site.class.equals(fieldType)) {
return false;
}
TransformField field = transformation.getField(fieldName);
ComponentValueProvider<FieldValueConduit> provider =
createProvider(fieldName);
field.replaceAccess(provider);
return true;
}
private ComponentValueProvider<FieldValueConduit> createProvider(final
String fieldName) {
return new ComponentValueProvider<FieldValueConduit>() {
public FieldValueConduit get(final ComponentResources resources)
{
return new ReadOnlyFieldValueConduit(resources, fieldName) {
public Object get() {
return ....; // <--- here implement selecting
correct Site based on Request
}
};
}
};
}
}
Remember to contribute your injection provider in your AppModule
@Contribute(InjectionProvider.class)
public static void
setupInjectingSite(OrderedConfiguration<InjectionProvider> configuration) {
configuration.addInstance("site", SiteInjectionProvider .class,
"after:Default");
}
After that, you would be able to use
@Inject
private Site site;
on your pages and components.
Best regards,
Cezary
On Wed, Sep 28, 2011 at 2:39 PM, Sonny Gill <so...@gmail.com> wrote:
> Thanks Barry.
>
> Site is a domain layer object and knows nothing about Request/Response etc.
> There will be a limited number of Site objects, one for each site
> supported,
> created and configured at the application start up.
>
> I could set it up as a Service for Tapestry application.
> But can I then provide a Service lookup method that can look at the current
> request, get the correct Site using some criteria, and hand it over to
> Tapestry to inject into the page for current request?
>
> So, something like :-
>
> public Service lookupSiteService(Request request) {
> String id = ...get site id from the request in some way...;
>
> SiteRepository repository = ...; // Can I inject an implementation of
> SiteRepository (also a domain layer object) here?
>
> return repository.getSite(id);
> }
>
>
Re: Injecting domain object into all pages
Posted by Sonny Gill <so...@gmail.com>.
Thanks Barry.
Site is a domain layer object and knows nothing about Request/Response etc.
There will be a limited number of Site objects, one for each site supported,
created and configured at the application start up.
I could set it up as a Service for Tapestry application.
But can I then provide a Service lookup method that can look at the current
request, get the correct Site using some criteria, and hand it over to
Tapestry to inject into the page for current request?
So, something like :-
public Service lookupSiteService(Request request) {
String id = ...get site id from the request in some way...;
SiteRepository repository = ...; // Can I inject an implementation of
SiteRepository (also a domain layer object) here?
return repository.getSite(id);
}
Cheers,
Sonny
On Wed, Sep 28, 2011 at 1:33 PM, Barry Books <tr...@gmail.com> wrote:
> I have a site that works this way also. I'm sure what you are trying
> to do could be done but I just do this.
>
> 1. Make Site a service
>
> 2. class MyPage {
>
> @Inject
> private Site site;
> }
>
> I guess it's a bit more typing but it's easy. Since site is a service
> it has access to the Request and whatever else you might need. You
> don't need to intercept the request. Depending on how it works you
> might want the service to be per thread.
>
> If you really want to automate it I suspect it can be done with
> TransformField in a ComponentClassTransformWorker, but Site would
> still need to be a service.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>
--
http://sonnygill.net/
http://twitter.com/sonny_gill
http://www.linkedin.com/in/sonnygill
Re: Injecting domain object into all pages
Posted by Barry Books <tr...@gmail.com>.
I have a site that works this way also. I'm sure what you are trying
to do could be done but I just do this.
1. Make Site a service
2. class MyPage {
@Inject
private Site site;
}
I guess it's a bit more typing but it's easy. Since site is a service
it has access to the Request and whatever else you might need. You
don't need to intercept the request. Depending on how it works you
might want the service to be per thread.
If you really want to automate it I suspect it can be done with
TransformField in a ComponentClassTransformWorker, but Site would
still need to be a service.
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org