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 }