0

In JSF 2 I have declared function inspect which takes one argument of type java.lang.reflect.Method and based on this argument it performs some annotation inspection and returns true or false. The catch is I want to call this function inspect from JSF EL to be able to modify UI according the return value but I am not able to get a reference of target method to pass it as an argument of the function, so I would like to ask how to do it?

Example

package some.pkg;

@ManagedBean( name = "someClass" )
public class SomeClass {

     @MyAnnotation
     public void someMethod( String arg1, Integer arg2 ) { /* ... */ }
}

JSF function declaration

<function>
    <function-name>inspect</function-name>
    <function-class>some.pkg.Inspector</function-class>
    <function-signature>boolean inspect(java.lang.reflect.Method)</function-signature>
</function>

Desired invocation from JSF, but it doesn't work

 <h:outputText 
    value="someMethod is annotated by @MyAnnotation" 
    rendered="#{inspect(someClass.someMethod)}"
 />

Acceptable would be also this, but it is less comfortable variant

 <h:outputText 
    value="someMethod is annotated by @MyAnnotation" 
    rendered="#{inspect(some.pkg.SomeClass.someMethod)}"
 />
Xtreme Biker
  • 28,480
  • 12
  • 120
  • 195
Gaim
  • 6,289
  • 4
  • 35
  • 58

2 Answers2

0

If you are using EL > 2.2 you don't need your custom EL-function. You can call the method from your ManagedBean directly with parameters:

#{someClass.someMethod('foo', 42)}

Otherwise you must declare a namespace and use it before your function:

#{namespace:inspect(someClass.someMethod)}

You can find a good explanation here.

But I'm not sure if this will work in your case. Even if it's possible to pass java.lang.reflect.Method as Parameter (never tried) how should the Method get their parameters? Nobody is passing them.

Community
  • 1
  • 1
lefloh
  • 9,828
  • 3
  • 24
  • 46
  • Thank you for answer, but you completely misunderstood my problem. 1) I don't want to call `someMethod`, I want to inspect it via reflection to check its annotations. 2) I can call JSF function in general, but I can't pass a method reference as an argument - which is what you don't know either. – Gaim Jul 18 '13 at 07:27
  • Sorry! Just tried it and it does not work. Think the problem is that the EL evalutation either expects a property or a method with parenthesis. And if you pass a method with paranthesis it will be called so you will just pass the result of this method to `inspect()`. Perhaps you could try to pass the methodName as String to `inspect` and call it there via reflection. – lefloh Jul 18 '13 at 07:49
  • Yea, methodName as String is the only option I have as an alternative, but it is not as cool as using EL options, ... if any. – Gaim Jul 18 '13 at 07:52
0

Why don't you try it just in server side? You know before rendering the page if the method is annotated in the current bean or not, so:

@ManagedBean( name = "someClass" )
public class SomeClass {

    boolean annotated = false;

    public boolean isAnnotated(){
        return annotated;
    }

    @PostConstruct
    public void postConstruct(){
        if (inspect(this.getClass().getMethod("someMethod")){
            annotated=true;
        }
    }

}

And in your xhtml page:

<h:outputText 
    value="someMethod is annotated by @MyAnnotation" 
    rendered="#{someClass.annotated}"
 />

You can even adapt it to use a parameter and calculate it on-the-fly:

//Notice in this case we're using a METHOD, not a GETTER
public boolean annotated(String methodName){
    return inspect(this.getClass().getMethod(methodName);
}

Calling it like that:

<h:outputText 
        value="someMethod is annotated by @MyAnnotation" 
        rendered="#{someClass.annotated('methodName')}"
     />

Or you can use an @ApplicationScoped managed bean to have access to it from every single view:

@ManagedBean
@ApplicationScoped
public class InspectorBean{

    public boolean inspectMethod(String className, String methodName){
        return inspect(Class.forName(className).getMethod(methodName));
    }

}

Then you can access like that from all your views:

<h:outputText 
        value="someMethod is annotated by @MyAnnotation" 
        rendered="#{inspectorBean.inspectMethod('completeClassName','methodName')}"
     />
Community
  • 1
  • 1
Xtreme Biker
  • 28,480
  • 12
  • 120
  • 195
  • Thanks for try, but I cannot do this. I extremely simplified my actual issue. I need to do this evaluation for multiple beans and methods and it differs every request so I cannot eagerly evaluate it for all methods and beans, because there might be hundreds of such methods and I need to evaluate only couple. Let's say, that this approach with lazy evaluation would be possible, but it would largely increased my coding overhead. – Gaim Jul 18 '13 at 12:45
  • If you have to use reflection hundreds of times while the application runs, then you probably are going into a several design issue. Actual view should take care only about its bean and not for other ones. – Xtreme Biker Jul 18 '13 at 12:54
  • Well you are out of the scope of question but I can make short clarification for you. The reflection is not for invocation, only for inspection. The evaluation is not hundred times per request but only couple times (for example 1-5). And finally this is research project of adaptive systems, so there is no optimization at this time. Later, the inspection will be cached, evaluation not. – Gaim Jul 18 '13 at 13:52
  • I provided a solution which works for the problem you wrote, so I think actually I'm in the scope of the question. Your explanations are ok, but I don't understand how your project works, I only say that if you're calling methods of non-related beans from your view, you have a design problem with it. Maybe you should consider rewriting your question. – Xtreme Biker Jul 18 '13 at 14:04
  • Well, I didn't say, that I invoke them and neither that they are not related. That for clarification. And back to the issue: I asked how to **pass a method** to function `inspect`, so the question is fine. You provided **alternative** solution, it is nice, but doesn't work for me. Anyway thanks for try – Gaim Jul 18 '13 at 14:14
  • Check my last answer. Obviuously you need to provide the class name, as you don't know which class stores the method. If you keep it in `@ApplicationScoped` it'll be accesible from everywhere. Forget about passing the method itself, it seems imposible at least with EL 2.2 and makes no sense when you can achieve it in server side using Java directly. – Xtreme Biker Jul 19 '13 at 06:03
  • Yea, this is the only solution I figured out too. Well, not the `@ApplicationScoped` but passing the parameters as `String` into `inspect` function with updated signature. Anyway, if you are sure about impossibility of passing the method reference directly, than it is what I was interested about and you answered the question. Thanks – Gaim Jul 19 '13 at 07:29
  • If you edit your answer with statement, that the direct reference cannot be passed in in EL 2.2 then I accept it. – Gaim Jul 19 '13 at 07:30
  • If you have a look at [EL language specs](http://download.oracle.com/otndocs/jcp/expression_language-2.2-mrel-eval-oth-JSpec/) it says in the section 1.18 of the document that you can force an argument to any type T. Moreover, I'm sure that if you have the method reference already stored in your bean you can pass it via view. What I nearly discard is the possibility to declare an String as a `Method` object using EL. That's what you're pretending and you can both search the whole internet or try an approach similar I suggested with. Good luck ;-) – Xtreme Biker Jul 19 '13 at 07:41