1 2 3 Previous Next 44 Replies Latest reply on Feb 12, 2010 11:23 AM by kenglxn Go to original post
      • 15. Re: Implementing the same of @RequestParameter in WELD
        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

          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

            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

              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

                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

                  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

                    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.


                    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

                      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

                        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

                          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.RequestParameter

                          So 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

                            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

                              Let's see the actual code.

                              • 27. Re: Implementing the same of @RequestParameter in WELD
                                meetoblivion

                                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

                                  To be honest, my understanding was that there was no way to provide a ServletRequest that was independent

                                  Try 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

                                    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.