-
15. Re: Implementing the same of @RequestParameter in WELD
agori Jan 3, 2010 10:55 PM (in response to agori)
Gavin King wrote on Jan 03, 2010 04:47:
It is possible to write a portable extension that adds the @Inject annotation to everything annotated @RequestParameter.Ok, I messed things up a little with ProcessAnnotatedType event and found this probably bugged solution
(but maybe useful for someone, so I paste it here):
public void processIt(@Observes ProcessAnnotatedType<?> pat) { final AnnotationLiteral<Inject> injectAnn = new AnnotationLiteral<Inject>() {}; final AnnotatedType originalAt = pat.getAnnotatedType(); final Set<AnnotatedField<?>> injectableFieldList = new HashSet<AnnotatedField<?>>(); for (AnnotatedField<?> field : pat.getAnnotatedType().getFields()) { if (field.isAnnotationPresent(Param.class) && field.isAnnotationPresent(Inject.class) == false) { injectableFieldList.add(field); } } AnnotatedType decoratedAnnotatedType = new AnnotatedType() { @Override public Set getConstructors() { return originalAt.getConstructors(); } @Override public Set getFields() { Set fields = new HashSet(); for (Object obj : originalAt.getFields()) { final AnnotatedField a = (AnnotatedField) obj; if (injectableFieldList.contains(a)) { fields.add(new AnnotatedField() { @Override public Field getJavaMember() { return a.getJavaMember(); } @Override public AnnotatedType getDeclaringType() { return a.getDeclaringType(); } @Override public boolean isStatic() { return a.isStatic(); } @Override public <T extends Annotation> T getAnnotation( Class<T> annotationType) { if (annotationType.equals(Inject.class)) { return (T) injectAnn; } return a.getAnnotation(annotationType); } @Override public Set<Annotation> getAnnotations() { if (!a.getAnnotations().contains(injectAnn)) { Set<Annotation> result = new HashSet<Annotation>(a.getAnnotations()); result.add(injectAnn); return result; } return a.getAnnotations(); } @Override public Type getBaseType() { return a.getBaseType(); } @Override public Set<Type> getTypeClosure() { return a.getTypeClosure(); } @Override public boolean isAnnotationPresent( Class<? extends Annotation> annotationType) { if (annotationType.equals(Inject.class)) { return true; } return a.isAnnotationPresent(annotationType); } }); } else { fields.add(a); } } return fields; } @Override public Class getJavaClass() { return originalAt.getJavaClass(); } @Override public Set getMethods() { return originalAt.getMethods(); } @Override public <T extends Annotation> T getAnnotation( Class<T> annotationType) { return originalAt.getAnnotation(annotationType); } @Override public Set<Annotation> getAnnotations() { return originalAt.getAnnotations(); } @Override public Type getBaseType() { return originalAt.getBaseType(); } @Override public Set<Type> getTypeClosure() { return originalAt.getTypeClosure(); } @Override public boolean isAnnotationPresent( Class<? extends Annotation> annotationType) { return originalAt.isAnnotationPresent(annotationType); } }; pat.setAnnotatedType(decoratedAnnotatedType); }
Writing extensions is quite verbose ;)
-
16. Re: Implementing the same of @RequestParameter in WELD
agori Jan 3, 2010 11:11 PM (in response to agori)
Matt Drees wrote on Jan 03, 2010 21:09:
The other approach, which I would opt for, is to have the producer method bean scoped @Dependent instead of @RequestScoped, and at the injection point use Instance<String> instead of String. So:@Produces @Dependent @HttpParam("") public String produceHttpParam(InjectionPoint ip) { ... }
Have you tried the code? InjectionPoint is not injected (pass null) into the producer method.
-
17. Re: Implementing the same of @RequestParameter in WELD
swd847 Jan 3, 2010 11:34 PM (in response to agori)If you want to make your extension smaller have a look at org.jboss.extensions.util.reannotated.ReannotatedType in weld-extensions.
-
18. Re: Implementing the same of @RequestParameter in WELD
gavin.king Jan 4, 2010 12:28 AM (in response to agori)
Alberto Gori wrote on Jan 03, 2010 23:11:
Have you tried the code? InjectionPoint is not injected (pass null) into the producer method.Yes, that's right. InjectionPoint only works with direct injection, not with programmatic lookup.
I recommend you inject a Parameter object with a get() method. Have the @Produces method return Parameter.
-
19. Re: Implementing the same of @RequestParameter in WELD
matt.drees Jan 4, 2010 1:02 AM (in response to agori)
Alberto Gori wrote on Jan 03, 2010 23:11:
Have you tried the code?I haven't tried it, no.
InjectionPoint is not injected (pass null) into the producer method.Bummer. On a re-read of the spec, it doesn't explicitly require this, but I think it ought to. Maybe this can be addressed in a maintenance release.
So, here's a third option:
@Dependent @HttpParam("") public class RequestParameter implements Provider<String> { @Inject InjectionPoint ip; @Inject Instance<ServletRequest> request; public String get() { String paramName = ip.getAnnotation(HttpParam.class).value(); String[] paramValues = request.get().getParameterValues(paramName); if (paramValues.length != 1) throw new MyInvalidParameterException(paramName, paramValues); return paramValues[0]; } } @SessionScoped class MySessionScopedBean { @Inject HttpParam("foo") Provider<String> fooParam; //use as before }
(No, I didn't test this either. But I think it should work. :-) )
Depending on your needs, this may be even better, because you could add some methods to RequestParameter:
- boolean isAvailable() for optional parameters
- List<String> asList() for multivalued parameters
and inject RequestParameter, not Provider<String>, if you need access to them.
Thoughts?
-
20. Re: Implementing the same of @RequestParameter in WELD
meetoblivion Jan 4, 2010 1:03 AM (in response to agori)So I'm fairly certain the example's broken.
Here's my code (and forgive the System.out's)
public class RequestParamProducer { @Produces @HttpParam("") public ParamHolder produceHttpParam(InjectionPoint ip) { if(ip == null) System.out.println("IP IS NULL."); FacesContext fc = FacesContext.getCurrentInstance(); System.out.println("Requesting http param: "+ip.getAnnotated().getAnnotation(HttpParam.class).value()); ParamHolder ph = new ParamHolder(); ph.setValue( fc.getExternalContext().getRequestParameterMap().get(ip.getAnnotated().getAnnotation(HttpParam.class).value()) ); return ph; } } public class ParamHolder implements java.io.Serializable{ private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } } @Named("mybean") @SessionScoped public class MyBean implements java.io.Serializable{ @Inject @HttpParam("beanValue") ParamHolder beanValue; public String getValue() { if(beanValue == null) return "null"; return beanValue.getValue().toString(); } } @Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface HttpParam { @Nonbinding public String value(); } <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> Hello from Facelets <h:outputText value="#{mybean.value}"/> </h:body> </html>
Here's the behavior
On first request, beanValue gets set. The resulting ParamHolder is dependent on the Injection point. I was able to verify this by changing the producer component's scoping to RequestScope to verify that it remains dependent on the injection point. I had to use FacesContext to get the parameter. I cannot for the life of me inject a ServletRequest, though I figure we need an extension that produces it for us. The value of beanValue does not change when I change the value in the URL.
-
21. Re: Implementing the same of @RequestParameter in WELD
meetoblivion Jan 4, 2010 1:13 AM (in response to agori)
Matt Drees wrote on Jan 04, 2010 01:02:
So, here's a third option: snippedLooks good, but doesn't work. It seems like we might be missing something somewhere, as I cannot inject ServletRequest anywhere.
Caused by: org.jboss.weld.DeploymentException: Injection point has ambiguous dependencies. Injection point: field com.tad.cdi.reqparamtest.requestparamtry.RequestParameter.request; Qualifiers: [@javax.enterprise.inject.Default()]; Possible dependencies: [Built-in implicit javax.inject.Instance bean, org.jboss.weld.bean-/RequestParamTry/target/RequestParamTry/-ManagedBean-class com.tad.cdi.reqparamtest.requestparamtry.RequestParameter]
-
22. Re: Implementing the same of @RequestParameter in WELD
gavin.king Jan 4, 2010 1:23 AM (in response to agori)
Bummer. On a re-read of the spec, it doesn't explicitly require this, but I think it ought to. Maybe this can be addressed in a maintenance release.The problem is that if you might call Instance.select(), in which case the type and qualifiers of the injection point don't reflect the type and qualifiers used by the resolution algorithm. And it doesn't seem right to me to mess with the InjectionPoint object to reflect that. We would need some other object, some kind of DependencySpecification object that specifies just the type and qualifiers. Not sure that that's really worth the effort.
So, here's a third option...That approach looks great to me.
-
23. Re: Implementing the same of @RequestParameter in WELD
gavin.king Jan 4, 2010 1:26 AM (in response to agori)
John Ament wrote on Jan 04, 2010 01:13:
Matt Drees wrote on Jan 04, 2010 01:02:
So, here's a third option: snipped
Looks good, but doesn't work. It seems like we might be missing something somewhere, as I cannot inject ServletRequest anywhere.Add an @Alternative annotation, and enable RequestParameter in beans.xml. There is an ambiguity with the built-in Instance impl.
BTW, it's not really necessary to implement Provider<String>. Just inject RequestParameter instead.
-
24. Re: Implementing the same of @RequestParameter in WELD
meetoblivion Jan 4, 2010 1:51 AM (in response to agori)Still missing something...
This is my beans.xml
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> <alternatives> <class>com.tad.cdi.reqparamtest.requestparamtry.RequestParameter</class> </alternatives> </beans>
And this is what my facelet tells me
Can not set javax.enterprise.inject.Instance field com.tad.cdi.reqparamtest.requestparamtry.RequestParameter.request to com.tad.cdi.reqparamtest.requestparamtry.RequestParameterSo looks to me like it's trying to inject a RequestParameter instance into the request object, which is Instance<ServletRequest>
-
25. Re: Implementing the same of @RequestParameter in WELD
matt.drees Jan 4, 2010 3:32 AM (in response to agori)I don't understand why Weld would be trying to inject a RequestParameter into RequestParameter.request. Seems like a bug to me. But, let me check an assumption I've made. I was assuming you had set up a mechanism to inject the current ServletRequest. It isn't provided by default. Have you?
(If you haven't, I think you should be getting an UnsatisfiedResolutionException, though, not the weirdness you're currently getting.)
-
26. Re: Implementing the same of @RequestParameter in WELD
gavin.king Jan 4, 2010 3:41 AM (in response to agori)Let's see the actual code.
-
27. Re: Implementing the same of @RequestParameter in WELD
meetoblivion Jan 4, 2010 3:51 AM (in response to agori)No, I do not. To be honest, my understanding was that there was no way to provide a ServletRequest that was independent - every version I've seen involved getting the FacesContext (since that has a getCurrentInstance()). Obviously that's not portable to a non JSF application.
I believe the exception I got was something like an Unsatisfied resolution before I switched to Alternative.
Caused by: org.jboss.weld.DeploymentException: Injection point has ambiguous dependencies. Injection point: field com.tad.cdi.reqparamtest.requestparamtry.MyBean.reqParam; Qualifiers: [@com.tad.cdi.reqparamtest.requestparamtry.HttpParam(value=beanValue)]; Possible dependencies: [Built-in implicit javax.inject.Instance bean, org.jboss.weld.bean-/RequestParamTry/target/RequestParamTry/-ManagedBean-class com.tad.cdi.reqparamtest.requestparamtry.RequestParameter]
I created a servlet request producer
public class ServletRequestProducer { @Produces @RequestScoped public ServletRequest produceServletRequest() { return (ServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest(); } }
I turned off the alternative, not sure if you were expecting it to be on or not. Personally, I don't get what that has to do with this injection, other than maybe a resolution error due to generics, but even then I'm now @Inject ServletRequest request.
-
28. Re: Implementing the same of @RequestParameter in WELD
matt.drees Jan 4, 2010 5:40 AM (in response to agori)
To be honest, my understanding was that there was no way to provide a ServletRequest that was independentTry this:
@RequestScoped public class ServletRequestProducer { private ServletRequest request; public void initRequest(@Observes @Initialized ServletRequest request) { this.request = request; } @Produces @Dependent public ServletRequest produceRequest() { if (request == null) throw new IllegalStateException("request was not initialized!"); return request; } } @WebListener public class RequestListenerToEventBridge implements ServletRequestListener { { @Inject @Initialized Event<ServletRequest> initializeRequest; requestInitialized(ServletRequestEvent sre) { initializeRequest.fire(sre.getServletRequest()); } ... }
where you set up the qualifier @Initialized. (I haven't tried this)
But the producer method in your post ought to work fine in faces requests.
I don't think @Alternative is needed.
As Gavin said, it'd be helpful if you post the full code.
-
29. Re: Implementing the same of @RequestParameter in WELD
nickarls Jan 4, 2010 9:53 AM (in response to agori)Is that running code? I would assume there is no injection into servlet listeners and you would have to get the manager from JNDI and fire it yourself?
You could probably do the same with a @Filter but you would have the same injection-issue there.