View Javadoc

1   package org.apache.ojb.broker.metadata.fieldaccess;
2   
3   /* Copyright 2003-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.beans.BeanInfo;
19  import java.beans.IntrospectionException;
20  import java.beans.Introspector;
21  import java.beans.PropertyDescriptor;
22  import java.lang.reflect.Method;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.ojb.broker.core.proxy.ProxyHelper;
28  import org.apache.ojb.broker.metadata.MetadataException;
29  import org.apache.ojb.broker.util.ClassHelper;
30  import org.apache.ojb.broker.util.logging.Logger;
31  
32  /**
33   * A {@link PersistentField} implementation using
34   * JavaBeans compliant calls only to access persistent attributes.
35   * No Reflection is needed. But for each attribute xxx there must be
36   * public getXxx() and setXxx() methods. In metadata the field name must be
37   * the bean compliant 'xxx'.
38   *
39   * @version $Id: PersistentFieldIntrospectorImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
40   */
41  public class PersistentFieldIntrospectorImpl extends PersistentFieldBase
42  {
43      private static final long serialVersionUID = 8805309492150404444L;
44      private Class type;
45      private transient List propertyGraph;
46  
47      public PersistentFieldIntrospectorImpl()
48      {
49          super();
50      }
51  
52      public PersistentFieldIntrospectorImpl(Class aClass, String aPropertyName)
53      {
54          super(aClass, aPropertyName);
55      }
56  
57      public Class getType()
58      {
59          if (type == null)
60          {
61              type = getPropertyDescriptor().getPropertyType();
62          }
63          return type;
64      }
65  
66      public void set(Object target, Object value) throws MetadataException
67      {
68          if(target == null) return;
69          List propertyDescriptors = getPropertyGraph();
70          int size = propertyDescriptors.size() - 1;
71          PropertyDescriptor pd;
72          for (int i = 0; i < size; i++)
73          {
74              Object attribute;
75              pd = (PropertyDescriptor) propertyDescriptors.get(i);
76              attribute = getValueFrom(pd, target);
77              if (attribute != null || value != null)
78              {
79                  if (attribute == null)
80                  {
81                      try
82                      {
83                          attribute = ClassHelper.newInstance(pd.getPropertyType());
84                      }
85                      catch (Exception e)
86                      {
87                          throw new MetadataException("Can't instantiate nested object of type '"
88                                  + pd.getPropertyType() + "' for field '"
89                                  + pd.getName() + "'", e);
90                      }
91                  }
92                  setValueFor(pd, target, attribute);
93              }
94              else
95              {
96                  return;
97              }
98              target = attribute;
99          }
100         pd = (PropertyDescriptor) propertyDescriptors.get(size);
101         setValueFor(pd, target, value);
102     }
103 
104     public Object get(Object target) throws MetadataException
105     {
106         List propertyDescriptors = getPropertyGraph();
107         for (int i = 0; i < propertyDescriptors.size(); i++)
108         {
109             PropertyDescriptor pd = (PropertyDescriptor) propertyDescriptors.get(i);
110             target = getValueFrom(pd, target);
111             if (target == null) break;
112         }
113         return target;
114     }
115 
116     private Object getValueFrom(PropertyDescriptor pd, Object target)
117     {
118         if (target == null) return null;
119         Method m = pd.getReadMethod();
120         if (m != null)
121         {
122             try
123             {
124                 return m.invoke(ProxyHelper.getRealObject(target), null);
125             }
126             catch (Throwable e)
127             {
128                 logProblem(pd, target, null, "Can't read value from given object");
129                 throw new MetadataException("Error invoking method:" + m.getName() + " in object " + target.getClass().getName(), e);
130             }
131         }
132         else
133         {
134             throw new MetadataException("Can't get ReadMethod for property:" + pd.getName() + " in object " + target.getClass().getName());
135         }
136     }
137 
138     private void setValueFor(PropertyDescriptor pd, Object target, Object value)
139     {
140         Method m = pd.getWriteMethod();
141         Object[] args = {value};
142         if (m != null)
143         {
144             try
145             {
146                 /**
147                  * MBAIRD: it is safe to call getParameterTypes()[0] because this is
148                  * the "set" method and it needs to take one parameter only.
149                  * we need to be able to set values to null. We can only set something to null if
150                  * the type is not a primitive (assignable from Object).
151                  */
152                 if ((value != null) || !m.getParameterTypes()[0].isPrimitive())
153                 {
154                     m.invoke(ProxyHelper.getRealObject(target), args);
155                 }
156             }
157             catch (Throwable e)
158             {
159                 logProblem(pd, target, value, "Can't set value on given object.");
160                 throw new MetadataException("Error invoking method:" + m.getName() + " in object:" + target.getClass().getName(), e);
161             }
162         }
163         else
164         {
165             throw new MetadataException("Can't get WriteMethod for property:" + pd.getName() + " in object:" + target.getClass().getName());
166         }
167     }
168 
169     private List getPropertyGraph()
170     {
171         if (propertyGraph == null)
172         {
173             propertyGraph = buildPropertyGraph();
174         }
175         return propertyGraph;
176     }
177 
178     private List buildPropertyGraph()
179     {
180         List result = new ArrayList();
181         String[] fields = StringUtils.split(getName(), PATH_TOKEN);
182         PropertyDescriptor pd = null;
183         for (int i = 0; i < fields.length; i++)
184         {
185             String fieldName = fields[i];
186             if (pd == null)
187             {
188                 pd = findPropertyDescriptor(getDeclaringClass(), fieldName);
189             }
190             else
191             {
192                 pd = findPropertyDescriptor(pd.getPropertyType(), fieldName);
193             }
194             result.add(pd);
195         }
196         return result;
197     }
198 
199     /**
200      * Get the PropertyDescriptor for aClass and aPropertyName
201      */
202     protected static PropertyDescriptor findPropertyDescriptor(Class aClass, String aPropertyName)
203     {
204         BeanInfo info;
205         PropertyDescriptor[] pd;
206         PropertyDescriptor descriptor = null;
207 
208         try
209         {
210             info = Introspector.getBeanInfo(aClass);
211             pd = info.getPropertyDescriptors();
212             for (int i = 0; i < pd.length; i++)
213             {
214                 if (pd[i].getName().equals(aPropertyName))
215                 {
216                     descriptor = pd[i];
217                     break;
218                 }
219             }
220             if (descriptor == null)
221             {
222                 /*
223 				 * Daren Drummond: 	Throw here so we are consistent
224 				 * 					with PersistentFieldDefaultImpl.
225 				 */
226                 throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName());
227             }
228             return descriptor;
229         }
230         catch (IntrospectionException ex)
231         {
232             /*
233 			 * Daren Drummond: 	Throw here so we are consistent
234 			 * 					with PersistentFieldDefaultImpl.
235 			 */
236             throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName(), ex);
237         }
238     }
239 
240     /**
241      * Returns the PropertyDescriptor.
242      *
243      * @return java.beans.PropertyDescriptor
244      */
245     protected PropertyDescriptor getPropertyDescriptor()
246     {
247         return (PropertyDescriptor) getPropertyGraph().get(getPropertyGraph().size() - 1);
248     }
249 
250     /**
251      * This implementation returns always 'false'.
252      */
253     public boolean makeAccessible()
254     {
255         return false;
256     }
257 
258     /**
259      * Always returns 'false'.
260      *
261      * @see PersistentField#usesAccessorsAndMutators
262      */
263     public boolean usesAccessorsAndMutators()
264     {
265         return true;
266     }
267 
268     /**
269      * Let's give the user some hints as to what could be wrong.
270      */
271     protected void logProblem(PropertyDescriptor pd, Object anObject, Object aValue, String msg)
272     {
273         Logger logger = getLog();
274         logger.error("Error in [PersistentFieldPropertyImpl], " + msg);
275         logger.error("Declaring class [" + getDeclaringClass().getName() + "]");
276         logger.error("Property Name [" + getName() + "]");
277         logger.error("Property Type [" + pd.getPropertyType().getName() + "]");
278 
279         if (anObject != null)
280         {
281             logger.error("anObject was class [" + anObject.getClass().getName() + "]");
282         }
283         else
284         {
285             logger.error("anObject was null");
286         }
287         if (aValue != null)
288         {
289             logger.error("aValue was class [" + aValue.getClass().getName() + "]");
290         }
291         else
292         {
293             logger.error("aValue was null");
294         }
295     }
296 }