View Javadoc

1   package org.codehaus.xfire.aegis.type.basic;
2   
3   import java.beans.PropertyDescriptor;
4   import java.util.HashSet;
5   import java.util.Iterator;
6   import java.util.Map;
7   import java.util.Set;
8   
9   import javax.xml.namespace.QName;
10  
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.codehaus.xfire.MessageContext;
14  import org.codehaus.xfire.XFireRuntimeException;
15  import org.codehaus.xfire.aegis.MessageReader;
16  import org.codehaus.xfire.aegis.MessageWriter;
17  import org.codehaus.xfire.aegis.type.Type;
18  import org.codehaus.xfire.fault.XFireFault;
19  import org.codehaus.xfire.soap.SoapConstants;
20  import org.codehaus.xfire.util.NamespaceHelper;
21  import org.codehaus.yom.Attribute;
22  import org.codehaus.yom.Element;
23  
24  /***
25   * Serializes JavaBeans.
26   * 
27   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
28   */
29  public class BeanType
30      extends Type
31  {
32      private static final Log logger = LogFactory.getLog(BeanType.class);
33      
34      private static Map objectProperties = null;
35      
36      private BeanTypeInfo _info;
37      
38      public BeanType()
39      {
40          setNillable(true);
41      }
42      
43      public BeanType(BeanTypeInfo info)
44      {
45          this._info = info;
46          setNillable(true);
47          
48          this.setTypeClass(info.getTypeClass());
49      }
50  
51      public Object readObject(MessageReader reader, MessageContext context)
52          throws XFireFault
53      {
54          BeanTypeInfo info = getTypeInfo();
55          
56          try
57          {
58              Class clazz = getTypeClass();
59              Object object = clazz.newInstance();
60  
61              // Read attributes
62              while (reader.hasMoreAttributeReaders())
63              {
64                  MessageReader childReader = reader.getNextAttributeReader();
65                  QName name = childReader.getName();
66                  
67                  Type type = info.getType(name);
68  
69                  if (type != null)
70                  {
71                      Object writeObj = type.readObject(childReader, context);
72      
73                      writeProperty(name, object, writeObj);
74                  }
75              }
76  
77              // Read child elements
78              while( reader.hasMoreElementReaders() )
79              {
80                  MessageReader childReader = reader.getNextElementReader();
81                  QName name = childReader.getName();
82                  
83                  Type type = info.getType(name);
84  
85                  if (type != null)
86                  {
87                      Object writeObj = type.readObject(childReader, context);
88      
89                      writeProperty(name, object, writeObj);
90                  }
91                  else
92                  {
93                      readToEnd(childReader);
94                  }
95              }
96              
97              return object;
98          }
99          catch (IllegalAccessException e)
100         {
101             throw new XFireFault("Illegal access.", e, XFireFault.RECEIVER);
102         }
103 		catch (InstantiationException e)
104 		{
105             throw new XFireFault("Couldn't instantiate service.", e, XFireFault.SENDER);
106 		}
107     }
108 
109     private void readToEnd(MessageReader childReader)
110     {
111         while (childReader.hasMoreElementReaders())
112         {
113             readToEnd(childReader.getNextElementReader());
114         }
115     }
116 
117     /***
118      * Write the specified property to a field.
119      */
120     protected void writeProperty(QName name, Object object, Object property)
121         throws XFireFault
122     {
123         try
124         {
125             PropertyDescriptor desc = getTypeInfo().getPropertyDescriptor(name);
126             desc.getWriteMethod().invoke(object, new Object[] {property});
127         }
128         catch (Exception e)
129         {
130             throw new XFireFault("Couldn't set property " + name, e, XFireFault.SENDER);
131         }
132     }
133 
134 
135 
136     /***
137      * @see org.codehaus.xfire.aegis.type.Type#writeObject(java.lang.Object)
138      */
139     public void writeObject(Object object, MessageWriter writer, MessageContext context)
140         throws XFireFault
141     {
142         if (object == null)
143             return;
144         
145         BeanTypeInfo info = getTypeInfo();
146         
147     	for (Iterator itr = info.getAttributes(); itr.hasNext(); )
148         {
149             QName name = (QName) itr.next();
150 
151             Object value = readProperty(object, name);
152             if (value != null)
153             {
154                 Type type = getType( info, name );
155     
156                 if ( type == null )
157                     throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
158     
159                 MessageWriter cwriter = writer.getAttributeWriter(name);
160     
161                 type.writeObject(value, cwriter, context);
162     
163                 cwriter.close();
164             }
165         }
166         
167         for (Iterator itr = info.getElements(); itr.hasNext(); )
168         {
169             QName name = (QName) itr.next();
170 
171             Object value = readProperty(object, name);
172             MessageWriter cwriter = writer.getElementWriter(name);
173 
174             if ( value != null)
175             {
176                 Type type = getType( info, name );
177     
178                 if ( type == null )
179                     throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
180     
181                 type.writeObject(value, cwriter, context);
182             }
183             else if (info.isNillable(name))
184             {
185                 MessageWriter attWriter = cwriter.getAttributeWriter("nil", SoapConstants.XSI_NS);
186                 attWriter.writeValue("true");
187                 attWriter.close();
188             }
189             
190             cwriter.close();
191         }
192     }
193 
194     protected Object readProperty(Object object, QName name)
195     {
196         try
197         {
198             PropertyDescriptor desc = getTypeInfo().getPropertyDescriptor(name);
199             return desc.getReadMethod().invoke(object, new Object[0]);
200         }
201         catch (Exception e)
202         {
203             throw new XFireRuntimeException( "Couldn't get property " + name, e );
204         }
205     }
206 
207     /***
208      * @see org.codehaus.xfire.aegis.type.Type#writeSchema()
209      */
210     public void writeSchema(Element root)
211     {
212         BeanTypeInfo info = getTypeInfo();
213         
214         Element complex = new Element(SoapConstants.XSD_PREFIX + ":complexType",
215                                       SoapConstants.XSD);
216         complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
217         root.appendChild(complex);
218 
219         Element seq = null;
220         
221         for (Iterator itr = info.getElements(); itr.hasNext();)
222         {
223             if (seq == null)
224             {
225                 seq = new Element(SoapConstants.XSD_PREFIX + ":sequence", SoapConstants.XSD);
226                 complex.appendChild(seq);
227             }
228                             
229             QName name = (QName) itr.next();
230             
231             Element element = new Element(SoapConstants.XSD_PREFIX + ":element",
232                                           SoapConstants.XSD);
233             seq.appendChild(element);
234             
235             Type type = getType(info, name);
236             
237             String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), 
238                                                             type.getSchemaType().getNamespaceURI() );
239             
240             writeTypeReference(name, element, type, prefix);
241         }
242         
243         for (Iterator itr = info.getAttributes(); itr.hasNext();)
244         {
245             QName name = (QName) itr.next();
246             
247             Element element = new Element(SoapConstants.XSD_PREFIX + ":attribute",
248                                           SoapConstants.XSD);
249             complex.appendChild(element);
250             
251             Type type = getType(info, name);
252 
253             String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), 
254                                                             type.getSchemaType().getNamespaceURI() );
255             
256             element.addAttribute(new Attribute("name", name.getLocalPart()));
257             element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
258             
259             if (info.isNillable(name))
260             {
261                 element.addAttribute(new Attribute("nillable", "true"));
262             }
263         }
264     }
265 
266     private Type getType(BeanTypeInfo info, QName name)
267     {
268         Type type = info.getType(name);
269         
270         if (type == null)
271         {
272             throw new NullPointerException("Couldn't find type for" + name + " in class " + 
273                                            getTypeClass().getName());
274         }
275         
276         return type;
277     }
278 
279     private void writeTypeReference(QName name, Element element, Type type, String prefix)
280     {
281         if (type.isAbstract())
282         {
283             element.addAttribute(new Attribute("name", name.getLocalPart()));
284             element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
285             
286             if (type.isNillable())
287             {
288                 element.addAttribute(new Attribute("nillable", "true"));
289             }
290         }
291         else
292         {
293             element.addAttribute(new Attribute("ref", prefix + ":" + type.getSchemaType().getLocalPart()));
294         }
295     }
296     
297     /***
298      * We need to write a complex type schema for Beans, so return true.
299      * 
300      * @see org.codehaus.xfire.aegis.type.Type#isComplex()
301      */
302     public boolean isComplex()
303     {
304         return true;
305     }
306 
307     public Set getDependencies()
308     {
309         Set deps = new HashSet();
310 
311         BeanTypeInfo info = getTypeInfo();
312         
313         for (Iterator itr = info.getAttributes(); itr.hasNext(); )
314         {
315             QName name = (QName) itr.next();
316 
317             deps.add(info.getType(name));
318         }
319 
320         for (Iterator itr = info.getElements(); itr.hasNext(); )
321         {
322             QName name = (QName) itr.next();
323 
324             deps.add(info.getType(name));
325         }
326         
327         return deps;
328     }
329 
330     public BeanTypeInfo getTypeInfo()
331     {
332         if (_info == null)
333         {
334             _info = createTypeInfo();
335         }
336         
337         // Delay initialization so things work in recursive scenarios (XFIRE-117)
338         if (!_info.isInitialized())
339         {
340             _info.initialize();
341         }
342         
343         return _info;
344     }
345 
346     public BeanTypeInfo createTypeInfo()
347     {
348         BeanTypeInfo info = new BeanTypeInfo(getTypeClass(), getSchemaType().getNamespaceURI());
349 
350         info.setTypeMapping(getTypeMapping());
351 
352         return info;
353     }
354 }