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.getLocalPart());
68  
69                  if (type != null)
70                  {
71                      Object writeObj = type.readObject(childReader, context);
72      
73                      writeProperty(name.getLocalPart(), 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.getLocalPart());
84  
85                  if (type != null)
86                  {
87                      Object writeObj = type.readObject(childReader, context);
88      
89                      writeProperty(name.getLocalPart(), 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(String name, Object object, Object property)
121         throws XFireFault
122     {
123         try
124         {
125             PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(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             String name = (String) 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;
160                 
161                 if (type.isAbstract())
162                 {
163                     cwriter = writer.getAttributeWriter(name, getSchemaType().getNamespaceURI());
164                 }
165                 else
166                 {
167                     cwriter = writer.getAttributeWriter(name, type.getSchemaType().getNamespaceURI());
168                 }
169 
170                 type.writeObject(value, cwriter, context);
171     
172                 cwriter.close();
173             }
174         }
175         
176         for (Iterator itr = info.getElements(); itr.hasNext(); )
177         {
178             String name = (String) itr.next();
179 
180             Object value = readProperty(object, name);
181             
182             Type type = getType( info, name );
183             MessageWriter cwriter;
184             
185             // Write the outer element
186             if (type.isAbstract())
187             {
188                 cwriter = writer.getElementWriter(name, getSchemaType().getNamespaceURI());
189             }
190             else
191             {
192                 cwriter = writer.getElementWriter(name, type.getSchemaType().getNamespaceURI());
193             }
194 
195             // Write the value if it is not null.
196             if ( value != null)
197             {
198                 if ( type == null )
199                     throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
200 
201                 type.writeObject(value, cwriter, context);
202             }
203             else if (info.isNillable(name))
204             {
205                 // Write the xsi:nil if it is null.
206                 MessageWriter attWriter = cwriter.getAttributeWriter("nil", SoapConstants.XSI_NS);
207                 attWriter.writeValue("true");
208                 attWriter.close();
209             }
210             
211             cwriter.close();
212         }
213     }
214 
215     protected Object readProperty(Object object, String name)
216     {
217         try
218         {
219             PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name);
220             return desc.getReadMethod().invoke(object, new Object[0]);
221         }
222         catch (Exception e)
223         {
224             throw new XFireRuntimeException( "Couldn't get property " + name, e );
225         }
226     }
227 
228     /***
229      * @see org.codehaus.xfire.aegis.type.Type#writeSchema()
230      */
231     public void writeSchema(Element root)
232     {
233         BeanTypeInfo info = getTypeInfo();
234         
235         Element complex = new Element(SoapConstants.XSD_PREFIX + ":complexType",
236                                       SoapConstants.XSD);
237         complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
238         root.appendChild(complex);
239 
240         Element seq = null;
241         
242         // Write out schema for elements
243         for (Iterator itr = info.getElements(); itr.hasNext();)
244         {
245             if (seq == null)
246             {
247                 seq = new Element(SoapConstants.XSD_PREFIX + ":sequence", SoapConstants.XSD);
248                 complex.appendChild(seq);
249             }
250                             
251             String name = (String) itr.next();
252             
253             Element element = new Element(SoapConstants.XSD_PREFIX + ":element",
254                                           SoapConstants.XSD);
255             seq.appendChild(element);
256             
257             Type type = getType(info, name);
258             
259             String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), 
260                                                             type.getSchemaType().getNamespaceURI() );
261             
262             writeTypeReference(name, element, type, prefix);
263         }
264         
265         // Write out schema for attributes
266         for (Iterator itr = info.getAttributes(); itr.hasNext();)
267         {
268             String name = (String) itr.next();
269             
270             Element element = new Element(SoapConstants.XSD_PREFIX + ":attribute",
271                                           SoapConstants.XSD);
272             complex.appendChild(element);
273             
274             Type type = getType(info, name);
275 
276             String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), 
277                                                             type.getSchemaType().getNamespaceURI() );
278             
279             element.addAttribute(new Attribute("name", name));
280             element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
281         }
282     }
283 
284     private Type getType(BeanTypeInfo info, String name)
285     {
286         Type type = info.getType(name);
287         
288         if (type == null)
289         {
290             throw new NullPointerException("Couldn't find type for" + name + " in class " + 
291                                            getTypeClass().getName());
292         }
293         
294         return type;
295     }
296 
297     private void writeTypeReference(String name, Element element, Type type, String prefix)
298     {
299         if (type.isAbstract())
300         {
301             element.addAttribute(new Attribute("name", name));
302             element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
303             
304             if (getTypeInfo().isNillable(name))
305             {
306                 element.addAttribute(new Attribute("nillable", "true"));
307             }
308         }
309         else
310         {
311             element.addAttribute(new Attribute("ref", prefix + ":" + type.getSchemaType().getLocalPart()));
312         }
313     }
314     
315     /***
316      * We need to write a complex type schema for Beans, so return true.
317      * 
318      * @see org.codehaus.xfire.aegis.type.Type#isComplex()
319      */
320     public boolean isComplex()
321     {
322         return true;
323     }
324 
325     public Set getDependencies()
326     {
327         Set deps = new HashSet();
328 
329         BeanTypeInfo info = getTypeInfo();
330         
331         for (Iterator itr = info.getAttributes(); itr.hasNext(); )
332         {
333             String name = (String) itr.next();
334 
335             deps.add(info.getType(name));
336         }
337 
338         for (Iterator itr = info.getElements(); itr.hasNext(); )
339         {
340             String name = (String) itr.next();
341 
342             deps.add(info.getType(name));
343         }
344         
345         return deps;
346     }
347 
348     public BeanTypeInfo getTypeInfo()
349     {
350         if (_info == null)
351         {
352             _info = createTypeInfo();
353         }
354         
355         // Delay initialization so things work in recursive scenarios (XFIRE-117)
356         if (!_info.isInitialized())
357         {
358             _info.initialize();
359         }
360         
361         return _info;
362     }
363 
364     public BeanTypeInfo createTypeInfo()
365     {
366         BeanTypeInfo info = new BeanTypeInfo(getTypeClass());
367 
368         info.setTypeMapping(getTypeMapping());
369 
370         return info;
371     }
372 }