Skip to main content

6. JSF: Weld CDI and Reflection. Using reflection on injected objects.

0. Introduction


Weld and Reflection are not good friends. When you inject a bean into another, Weld CDI makes a proxy of the injected object. So when you want to obtain information using Reflection, you cannot see the original injected object.

BalusC gives us the clue to introspect a Weld proxy bean. He uses BeanInfo Interface from the Java Beans API.

By means of this Bean API, we can retrieve the original object from the proxy object. It is stored in the attribute  targetInstance. Once we have this original object, we can use reflection. 


Note: To use Weld with Tomcat, we must include this dependency in the pom.xml file


<!-- https://mvnrepository.com/artifact/org.jboss.weld.servlet/weld-servlet-shaded -->
<dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet-shaded</artifactId>
    <version>3.0.5.Final</version>
</dependency>




1. A simple example

Let's consider 2 simple beans, Bean1 and Bean2, and Bean1 is injected into Bean2.

Bean1.java


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package org.ximodante.jsf.provesalfred;

import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Named @ToString @SessionScoped @NoArgsConstructor
public class Bean1 implements Serializable{
 private static final long serialVersionUID = 1L;
    
 @Getter @Setter private String field1="a";
 
}


Bean2.java


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;


import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Named @ToString @ViewScoped
public class Bean2 implements Serializable{
 private static final long serialVersionUID = 1L;
    
 @Inject @Getter @Setter private Bean1 b1;
    
 @Getter @Setter private String field2="33";
 
 public String go() {
  System.out.println("HELLO!");
  
  System.out.println("1. Displaying fields from beans");
  System.out.println("===============================");  
  System.out.println("b1="+ b1.toString());
  System.out.println("Bean2="+ this.toString()); 
  
  System.out.println("\n2. Displaying class names      ");
  System.out.println("===============================");  
  System.out.println("b1="+ b1.getClass().getName());
  System.out.println("Bean2="+ this.getClass().getName()); 
  
  System.out.println("\n3. Displaying information using Reflection API");
  System.out.println("==============================================");  
  System.out.println("b1="+ ReflectionUtil.getObjectInfoReflx(b1));
  System.out.println("b1="+ ReflectionUtil.getObjectInfoReflx(this));
  
  System.out.println("\n4. Displaying information using Bean Info API");
  System.out.println("==============================================");  
  System.out.println("b1="+ ReflectionUtil.getObjectInfoBean(b1));
  System.out.println("b1="+ ReflectionUtil.getObjectInfoBean(this));
  
  System.out.println("\n5. Displaying information using Both APIs");
  System.out.println("==============================================");  
  System.out.println("b1="+ ReflectionUtil.getObjectInfo(b1));
  System.out.println("b1="+ ReflectionUtil.getObjectInfo(this));
  
  return null;
 }
}


Notice:
  1. Bean1 is injected into Bean2 (line 15)
  2. When displaying b1 class name (line 29) the name of the proxy class is displayed.
  3. When displaying the fields using Reflection, (line 34) b1 fields are rather strange as it is a proxy.
  4. When displaying the fields using Bean Info (line 39), a field called targetInstance is the underlying object.
  5. With the new method getObjectInfo (line 44) that combines both API s>(Reflection and Bean) . we have solved the problem by means of using Reflection over the underlaying object when it is proxied.

ReflexionUtil.java

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package org.ximodante.jsf.provesalfred;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectionUtil {

 /**
  * A class whose name contanins "$Proxy$", is a class injected and proxied by CDI
  * @param cls
  * @return
  */
 public static boolean isProxy(Class<?> cls) {
  if (cls.getSimpleName().contains("Proxy$")) return true;
  else return false;
 }

 // Returns the underlying object proxied by CDI
 public static Object getProxiedObject(Object obj) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
  BeanInfo beanInfo;
  Object value=null;
 
  beanInfo = Introspector.getBeanInfo(obj.getClass());
  PropertyDescriptor oProperty = Arrays.stream(beanInfo.getPropertyDescriptors())
   .filter(e -> e.getName().equalsIgnoreCase("targetInstance"))
   .findFirst().get();
  Method getter = oProperty.getReadMethod();
  value = getter.invoke(obj);
 
  return value;
 }

 // Gets information from a class bypassing proxies
 public static String getObjectInfo(Object obj){
  String info="";
 
  Object myObj=obj;
  Class<?> cls=myObj.getClass();
 
  try {
   //Obtain original class (if stored in a proxy) 
   if (isProxy(cls)) { 
    myObj=getProxiedObject(myObj);
    cls=myObj.getClass();
   } 
   info =  getObjectInfoReflx(myObj);
  
  } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return info;

 }

 
 
 // Gets information from a class but cannot bypass proxies!!!!!
 public static String getObjectInfoReflx(Object obj) {
  String info="";
  int i=0;  
  try {
   
   info = info + "CLASS:" + obj.getClass().getName() + "\n";
   info = info + "--------------------------------------------------\n";
   for (Field f: obj.getClass().getDeclaredFields()){
    f.setAccessible(true);
   
    if (f!=null) {
    
     info= info + "  Field n." + i + "=" +  f.getName() + "  Type=" + f.getType();
     if (f.get(obj) != null) { 
      info= info + "  Value=" + f.get(obj).toString().trim(); 
     }
     i++;
     info=info + "\n";
    }            
   }    
 
  } catch (Exception e) {
   e.printStackTrace();
  } 
 
  return info;
 }
 
 /**
  * Retrieves information from an object using Bean API
  * @param obj
  * @return
  */
 public static String getObjectInfoBean(Object obj) {
  BeanInfo beanInfo;
  String info="";
  try {
   info = info + "CLASS:" + obj.getClass().getName() + "\n";
   info = info + "--------------------------------------------------\n";
   
  
   beanInfo = Introspector.getBeanInfo(obj.getClass());
   int i=0;
   for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
    String name = property.getName();
    Method getter = property.getReadMethod();
    Object value = getter.invoke(obj);
    String className=property.getPropertyType().getName();
    
    info= info + "  Field n." + i + "=" +  name + "  Type=" + value.getClass().getName();
    if (value != null) { 
     info= info + "  Value=" + value.toString().trim(); 
    }
    info=info + "\n";
    i++;
   } 
  } catch (Exception e) {
   e.printStackTrace();
  }
  return info;
 }
}

Notice:
  1. When a class is a proxy, its class name includes the string "Proxy$".
  2. Notice how field information is retrieved in Reflection and Bean APIs.
  3. How the field information is extracted taking into account if the class is proxied or not.

The xhtml page is

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:f="http://java.sun.com/jsf/core" 
      xmlns:p="http://primefaces.org/ui">  
    <h:head>  
    </h:head>  
      
    <h:body>  
      <h:form>  
        <p:panel header="TEST CDI REFLECTION">    
    <h3>Reflection with CDI</h3>
         
       field1:<h:inputText value="#{bean1.field1}"/> <br />
       
       field2:<h:inputText value="#{bean2.field2}"/>
       
     </p:panel>
     <h:commandButton value="Go" action="#{bean2.go}"/>  
      </h:form>
    </h:body>  
</html>



Look the window




And look carefully the results


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
HELLO!
1. Displaying fields from beans
===============================
b1=Bean1(field1=a)
Bean2=Bean2(b1=Bean1(field1=a), field2=33)

2. Displaying class names      
===============================
b1=org.ximodante.jsf.provesalfred.Bean1$Proxy$_$$_WeldClientProxy
Bean2=org.ximodante.jsf.provesalfred.Bean2

3. Displaying information using Reflection API
==============================================
b1=CLASS:org.ximodante.jsf.provesalfred.Bean1$Proxy$_$$_WeldClientProxy
--------------------------------------------------
  Field n.0=BEAN_ID_FIELD  Type=interface org.jboss.weld.serialization.spi.BeanIdentifier  Value=WELD%ManagedBean%STATIC_INSTANCE|/JSFv02_/WEB-INF/classes|org.ximodante.jsf.provesalfred.Bean1|null|false
  Field n.1=weld$$$53  Type=class java.lang.reflect.Method  Value=public abstract void org.jboss.weld.interceptor.proxy.LifecycleMixin.lifecycle_mixin_$$_preDestroy()
  Field n.2=weld$$$52  Type=class java.lang.reflect.Method  Value=public abstract void org.jboss.weld.interceptor.proxy.LifecycleMixin.lifecycle_mixin_$$_postConstruct()
  Field n.3=weld$$$55  Type=class java.lang.reflect.Method  Value=public abstract java.lang.Class org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy.getTargetClass()
  Field n.4=weld$$$54  Type=class java.lang.reflect.Method  Value=public abstract java.lang.Object org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy.getTargetInstance()
  Field n.5=methodHandler  Type=class org.jboss.weld.bean.proxy.ProxyMethodHandler  Value=org.jboss.weld.bean.proxy.ProxyMethodHandler@62ec8a98
  Field n.6=constructed  Type=boolean  Value=true

b1=CLASS:org.ximodante.jsf.provesalfred.Bean2
--------------------------------------------------
  Field n.0=serialVersionUID  Type=long  Value=1
  Field n.1=b1  Type=class org.ximodante.jsf.provesalfred.Bean1  Value=Bean1(field1=a)
  Field n.2=field2  Type=class java.lang.String  Value=33


4. Displaying information using Bean Info API
==============================================
b1=CLASS:org.ximodante.jsf.provesalfred.Bean1$Proxy$_$$_WeldClientProxy
--------------------------------------------------
  Field n.0=class  Type=java.lang.Class  Value=class org.ximodante.jsf.provesalfred.Bean1$Proxy$_$$_WeldClientProxy
  Field n.1=field1  Type=java.lang.String  Value=a
  Field n.2=handler  Type=org.jboss.weld.bean.proxy.ProxyMethodHandler  Value=org.jboss.weld.bean.proxy.ProxyMethodHandler@62ec8a98
  Field n.3=metadata  Type=org.jboss.weld.bean.proxy.ProxyMethodHandler  Value=org.jboss.weld.bean.proxy.ProxyMethodHandler@62ec8a98
  Field n.4=targetClass  Type=java.lang.Class  Value=class org.ximodante.jsf.provesalfred.Bean1
  Field n.5=targetInstance  Type=org.ximodante.jsf.provesalfred.Bean1  Value=Bean1(field1=a)

b1=CLASS:org.ximodante.jsf.provesalfred.Bean2
--------------------------------------------------
  Field n.0=b1  Type=org.ximodante.jsf.provesalfred.Bean1$Proxy$_$$_WeldClientProxy  Value=Bean1(field1=a)
  Field n.1=class  Type=java.lang.Class  Value=class org.ximodante.jsf.provesalfred.Bean2
  Field n.2=field2  Type=java.lang.String  Value=33


5. Displaying information using Both APIs
==============================================
b1=CLASS:org.ximodante.jsf.provesalfred.Bean1
--------------------------------------------------
  Field n.0=serialVersionUID  Type=long  Value=1
  Field n.1=field1  Type=class java.lang.String  Value=a

b1=CLASS:org.ximodante.jsf.provesalfred.Bean2
--------------------------------------------------
  Field n.0=serialVersionUID  Type=long  Value=1
  Field n.1=b1  Type=class org.ximodante.jsf.provesalfred.Bean1  Value=Bean1(field1=a)
  Field n.2=field2  Type=class java.lang.String  Value=33

Now we can use reflection on injected beans if we take into account these ideas.

Have a nice programming session!

Comments

Popular posts from this blog

10. JSF How to access my resources files?

Sometimes it is really difficult to access any resources form different Java applications. So let's see some scenarios: 1. Executable jar application in a NO WEB environment. Take into account that this is not a JSF application !!! It is desired to have a property file outside the "jar file" so that we can change properties when needed. For instance, we will place the properties file in the same folder that the "jar file" lies. To access the folder where the jar is, the trick is to access the folder where the compiled classed are placed and scale 3 folders . (Oh! rather strange, if anybody knows a better idea.., he will be welcome). Here is a simple code. The name of the class containing the code is ThisClass, and we have created a Property class that feeds with a file in the same folder that the" jar file" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /** * Gets the folder where resides the executable...

9. JSF: Solving common problems

1. Target Unreachable, identifier [bean] resolved to null When you reference a bean in a xhtml file with "#{bean.method}" and get this error, you should verify that: The bean implements Serializable . The references (javax.servlet-api.4.0.0, javax.faces.2.3.3,  javax.el.api.3.0.1, org.primefaces.6.1) have been selected in your pom.xml You have created the files bean.xml and faces-config.xml in the webapp/WEB-INF folder You have used the correct annotations from CDI  (javax.inject.Named and javax.enterprise.context.SessionScoped, ...) and not ManagedBean or jsf2.2 scopes. 2. Bean declaring a passivating scope must be passivation capable This error causes server not starting and fills up the console with long chained exceptions. It is very annoying. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 SEVERE: A child container failed during start java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to star...

2. Create a Maven JSF project and a simple login page

Updated on Oct-25-2107 0. Introduction Remember to install previously: Java JDK 8 Eclipse Oxygen Lombok  Apache Tomcat 9 1. Create a Maven Project In the top menu select : File - New - Maven Project Press Next Now fill the next form as follows It is important to select: Group id: org.ximodante.jsf  (or another packet you like) Packaging: war Artifact Id and Name: JSFv02 or any name for the project Press Finish 2. The pom.xml file Eclipse complains about the simple generated file. It seems that a web.xml is required if the packaging is selected to war. The simple generated file is  1 2 3 4 5 6 7 8 9 <project xmlns= "http://maven.apache.org/POM/4.0.0" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion> 4.0.0 </modelVersion> <groupId> org.ximodante...