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 }