Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » net » axis » server » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource J2EE webOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   
    8   // $Id: EntityBeanDeserializer.java,v 1.2.4.2 2002/11/20 04:00:57 starksm Exp $
    9   
   10   package org.jboss.net.axis.server;
   11   
   12   import org.jboss.net.axis.ParameterizableDeserializer;
   13   
   14   import org.xml.sax.Attributes;
   15   import org.xml.sax.SAXException;
   16   
   17   import javax.xml.namespace.QName;
   18   
   19   import org.apache.axis.encoding.DeserializerImpl;
   20   import org.apache.axis.encoding.DeserializationContext;
   21   import org.apache.axis.encoding.Deserializer;
   22   import org.apache.axis.encoding.ser.SimpleDeserializer;
   23   import org.apache.axis.encoding.Target;
   24   import org.apache.axis.encoding.TypeMapping;
   25   import org.apache.axis.utils.JavaUtils;
   26   import org.apache.axis.utils.Messages;
   27   import org.apache.axis.Constants;
   28   import org.apache.axis.description.TypeDesc;
   29   import org.apache.axis.message.SOAPHandler;
   30   
   31   import java.lang.reflect.Method;
   32   import java.lang.reflect.InvocationTargetException;
   33   
   34   import javax.naming.InitialContext;
   35   import javax.ejb.EJBHome;
   36   import javax.naming.NamingException;
   37   
   38   import java.beans.PropertyDescriptor;
   39   import java.beans.IntrospectionException;
   40   import java.beans.Introspector;
   41   
   42   import java.util.Map;
   43   import java.util.List;
   44   import java.util.Collection;
   45   import java.util.Iterator;
   46   import java.util.StringTokenizer;
   47   
   48   /**
   49    * Server-side deserializer hitting an existing entity bean. Derived
   50    * from the axis BeanDeserializer. Currently relies on some
   51    * silly conventions that must be configurable in the deployment 
   52    * descriptor.
   53    * @author jung
   54    * @created 21.03.2002
   55    * @version $Revision: 1.2.4.2 $
   56    */
   57   
   58   public class EntityBeanDeserializer
   59      extends DeserializerImpl
   60      implements ParameterizableDeserializer {
   61   
   62      //
   63      // Attributes
   64      //
   65   
   66      protected Map options;
   67   
   68      protected Object home;
   69      protected Method findMethod;
   70      protected List findElements = new java.util.ArrayList(1);
   71      protected Object[] findObjects;
   72      protected TypeDesc typeDesc;
   73      protected QName xmlType;
   74      protected Class javaType;
   75      protected Map propertyMap = new java.util.HashMap(4);
   76      protected int collectionIndex = -1;
   77      protected Collection fieldSetters = new java.util.ArrayList(4);
   78      protected boolean initialized = false;
   79   
   80      /** 
   81       * Construct a new BeanSerializer
   82       * @param remoteType remote interface of the entity bean
   83       * @param xmlType fully-qualified xml tag-name of the corresponding xml structure
   84       */
   85   
   86      public EntityBeanDeserializer(Class remoteType, QName xmlType)
   87         throws Exception {
   88         // first the default constructor
   89         this.xmlType = xmlType;
   90         this.javaType = remoteType;
   91      }
   92   
   93      /** returns an option string with a default */
   94      protected String getStringOption(String key, String def) {
   95         String value = (String) options.get(key);
   96         if (value == null) {
   97            value = def;
   98         }
   99         return value;
  100      }
  101   
  102      /**
  103       * initialize the deserializer 
  104       */
  105   
  106      protected void initialize() throws SAXException {
  107         if (!initialized) {
  108            initialized = true;
  109   
  110            try {
  111               //
  112               // Extract home from jndiName
  113               //
  114               this.home =
  115                   new InitialContext().lookup(
  116                     getStringOption("JndiName", javaType.getName() + "Home"));
  117   
  118               //
  119               // Extract find method from name and sig
  120               //
  121   
  122               String findMethodName = getStringOption("FindMethodName", "findByPrimaryKey");
  123               String findMethodSignatureString =
  124                  getStringOption("FindMethodSignature", "java.lang.String");
  125               List findMethodSignatureClasses = new java.util.ArrayList(1);
  126               StringTokenizer tokenizer = new StringTokenizer(findMethodSignatureString, ";");
  127               while (tokenizer.hasMoreTokens()) {
  128                  findMethodSignatureClasses.add(
  129                     Thread.currentThread().getContextClassLoader().loadClass(
  130                        tokenizer.nextToken()));
  131               }
  132               this.findMethod =
  133                  home.getClass().getMethod(
  134                     findMethodName,
  135                     (Class[]) findMethodSignatureClasses.toArray(
  136                        new Class[findMethodSignatureClasses.size()]));
  137   
  138               //
  139               // Do some reasonable preprocessing
  140               //
  141   
  142               // Get a list of the bean properties
  143               BeanPropertyDescriptor[] pd = getPd(javaType);
  144               // loop through properties and grab the names for later
  145               for (int i = 0; i < pd.length; i++) {
  146                  BeanPropertyDescriptor descriptor = pd[i];
  147                  propertyMap.put(descriptor.getName(), descriptor);
  148                  propertyMap.put(JavaUtils.xmlNameToJava(descriptor.getName()), descriptor);
  149               }
  150               typeDesc = TypeDesc.getTypeDescForClass(javaType);
  151   
  152               //
  153               // Next prepare the elements we need to call the finder
  154               //
  155   
  156               String findMethodElements = getStringOption("FindMethodElements", "name");
  157               tokenizer = new StringTokenizer(findMethodElements, ";");
  158               while (tokenizer.hasMoreElements()) {
  159                  if (typeDesc != null) {
  160                     this.findElements.add(typeDesc.getAttributeNameForField(tokenizer.nextToken()));
  161                  } else {
  162                     this.findElements.add(new QName("", tokenizer.nextToken()));
  163                  }
  164               }
  165   
  166               this.findObjects = new Object[findElements.size()];
  167            } catch (NamingException e) {
  168               throw new SAXException("Could not lookup home.", e);
  169            } catch (ClassNotFoundException e) {
  170               throw new SAXException("Could not find signature class.", e);
  171            } catch (NoSuchMethodException e) {
  172               throw new SAXException("Could not find finder method.", e);
  173            }
  174   
  175         }
  176      }
  177   
  178      public void setOptions(Map options) {
  179         this.options = options;
  180      }
  181   
  182      public Map getOptions() {
  183         return options;
  184      }
  185   
  186      /**
  187       * Deserializer interface called on each child element encountered in
  188       * the XML stream.
  189       * @param namespace is the namespace of the child element
  190       * @param localName is the local name of the child element
  191       * @param prefix is the prefix used on the name of the child element
  192       * @param attributes are the attributes of the child element
  193       * @param context is the deserialization context.
  194       * @return is a Deserializer to use to deserialize a child (must be
  195       * a derived class of SOAPHandler) or null if no deserialization should
  196       * be performed.
  197       */
  198      public SOAPHandler onStartChild(
  199         String namespace,
  200         String localName,
  201         String prefix,
  202         Attributes attributes,
  203         DeserializationContext context)
  204         throws SAXException {
  205         BeanPropertyDescriptor propDesc = null;
  206   
  207         if (typeDesc != null) {
  208            QName elemQName = new QName(namespace, localName);
  209            String fieldName = typeDesc.getFieldNameForElement(elemQName,false);
  210            propDesc = (BeanPropertyDescriptor) propertyMap.get(fieldName);
  211         }
  212   
  213         if (propDesc == null) {
  214            // look for a field by this name.
  215            propDesc = (BeanPropertyDescriptor) propertyMap.get(localName);
  216         }
  217         if (propDesc == null) {
  218            // look for a field by the "adjusted" name.
  219            propDesc =
  220               (BeanPropertyDescriptor) propertyMap.get(JavaUtils.xmlNameToJava(localName));
  221         }
  222   
  223         if (propDesc == null) {
  224            // No such field
  225            throw new SAXException(
  226               Messages.getMessage("badElem00", javaType.getName(), localName));
  227         }
  228   
  229         // Determine the QName for this child element.
  230         // Look at the type attribute specified.  If this fails,
  231         // use the javaType of the property to get the type qname.
  232         QName qn = context.getTypeFromAttributes(namespace, localName, attributes);
  233   
  234         // get the deserializer
  235         Deserializer dSer = context.getDeserializerForType(qn);
  236   
  237         // If no deserializer, use the base DeserializerImpl.
  238         // There may not be enough information yet to choose the
  239         // specific deserializer.
  240         if (dSer == null) {
  241            dSer = new DeserializerImpl();
  242            // determine a default type for this child element
  243            TypeMapping tm = context.getTypeMapping();
  244            Class type = propDesc.getType();
  245            dSer.setDefaultType(tm.getTypeQName(type));
  246         }
  247   
  248         QName elementQName = new QName(namespace, localName);
  249         if (findElements.contains(elementQName)) {
  250            dSer.registerValueTarget(
  251               new FindPropertyTarget(findElements.indexOf(elementQName)));
  252         } else if (propDesc.getWriteMethod().getParameterTypes().length == 1) {
  253            // Success!  Register the target and deserializer.
  254            collectionIndex = -1;
  255            dSer.registerValueTarget(new BeanPropertyTarget(propDesc));
  256         } else {
  257            // Success! This is a collection of properties so use the index
  258            collectionIndex++;
  259            dSer.registerValueTarget(new BeanPropertyTarget(propDesc, collectionIndex));
  260         }
  261         return (SOAPHandler) dSer;
  262      }
  263   
  264      /**
  265       * Set the bean properties that correspond to element attributes.
  266       * 
  267       * This method is invoked after startElement when the element requires
  268       * deserialization (i.e. the element is not an href and the value is not nil.)
  269       * @param namespace is the namespace of the element
  270       * @param localName is the name of the element
  271       * @param qName is the prefixed qName of the element
  272       * @param attributes are the attributes on the element...used to get the type
  273       * @param context is the DeserializationContext
  274       */
  275      public void onStartElement(
  276         String namespace,
  277         String localName,
  278         String qName,
  279         Attributes attributes,
  280         DeserializationContext context)
  281         throws SAXException {
  282   
  283         initialize();
  284   
  285         if (typeDesc == null)
  286            return;
  287   
  288         // loop through the attributes and set bean properties that 
  289         // correspond to attributes
  290         for (int i = 0; i < attributes.getLength(); i++) {
  291            QName attrQName = new QName(attributes.getURI(i), attributes.getLocalName(i));
  292            String fieldName = typeDesc.getFieldNameForAttribute(attrQName);
  293            if (fieldName == null)
  294               continue;
  295   
  296            String attrName = attributes.getLocalName(i);
  297   
  298            // look for the attribute property
  299            BeanPropertyDescriptor bpd =
  300               (BeanPropertyDescriptor) propertyMap.get(fieldName);
  301            if (bpd != null) {
  302               if (bpd.getWriteMethod() == null)
  303                  continue;
  304   
  305               // determine the QName for this child element
  306               TypeMapping tm = context.getTypeMapping();
  307               Class type = bpd.getType();
  308               QName qn = tm.getTypeQName(type);
  309               if (qn == null)
  310                  throw new SAXException(Messages.getMessage("unregistered00", type.toString()));
  311   
  312               // get the deserializer
  313               Deserializer dSer = context.getDeserializerForType(qn);
  314               if (dSer == null)
  315                  throw new SAXException(Messages.getMessage("noDeser00", type.toString()));
  316               if (!(dSer instanceof SimpleDeserializer))
  317                  throw new SAXException(
  318                     Messages.getMessage("AttrNotSimpleType00", bpd.getName(), type.toString()));
  319   
  320               if (findElements.contains(attrQName)) {
  321                  dSer.registerValueTarget(
  322                     new FindPropertyTarget(findElements.indexOf(attrQName)));
  323               } else if (bpd.getWriteMethod().getParameterTypes().length == 1) {
  324                  // Success!  Create an object from the string and set
  325                  // it in the bean
  326                  try {
  327                     Object val = ((SimpleDeserializer) dSer).makeValue(attributes.getValue(i));
  328                     bpd.getWriteMethod().invoke(value, new Object[] { val });
  329                  } catch (Exception e) {
  330                     throw new SAXException(e);
  331                  }
  332               }
  333   
  334            } // if
  335         } // attribute loop
  336      }
  337   
  338      public void onEndElement(
  339         String namespace,
  340         String localName,
  341         DeserializationContext context)
  342         throws SAXException {
  343         try {
  344            value = findMethod.invoke(home, findObjects);
  345            Iterator allSetters = fieldSetters.iterator();
  346            while (allSetters.hasNext()) {
  347               ((BeanPropertyTarget) allSetters.next()).setReal(value);
  348            }
  349            fieldSetters = null;
  350         } catch (InvocationTargetException e) {
  351            throw new SAXException("Encountered exception " + e.getTargetException());
  352         } catch (IllegalAccessException e) {
  353            throw new SAXException("Encountered exception " + e);
  354         }
  355         super.onEndElement(namespace, localName, context);
  356      }
  357   
  358      public class FindPropertyTarget implements Target {
  359         int position;
  360   
  361         public FindPropertyTarget(int index) {
  362            this.position = index;
  363         }
  364   
  365         public void set(Object value) throws SAXException {
  366            findObjects[position] = value;
  367         }
  368      }
  369   
  370      /**
  371       * Class which knows how to update a bean property
  372       */
  373      public class BeanPropertyTarget implements Target {
  374         private BeanPropertyDescriptor pd;
  375         private int index = -1;
  376         Object value;
  377   
  378         /** 
  379          * This constructor is used for a normal property.
  380          * @param Object is the bean class
  381          * @param pd is the property
  382          **/
  383         public BeanPropertyTarget(BeanPropertyDescriptor pd) {
  384            this.pd = pd;
  385            this.index = -1; // disable indexing
  386         }
  387   
  388         /** 
  389          * This constructor is used for an indexed property.
  390          * @param Object is the bean class
  391          * @param pd is the property
  392          * @param i is the index          
  393          **/
  394         public BeanPropertyTarget(BeanPropertyDescriptor pd, int i) {
  395            this.pd = pd;
  396            this.index = i;
  397         }
  398   
  399         public void set(Object value) throws SAXException {
  400            this.value = value;
  401            if (fieldSetters != null) {
  402               fieldSetters.add(this);
  403            } else {
  404               setReal(EntityBeanDeserializer.this.value);
  405            }
  406         }
  407   
  408         public void setReal(Object target) throws SAXException {
  409            try {
  410               if (index < 0)
  411                  pd.getWriteMethod().invoke(target, new Object[] { value });
  412               else
  413                  pd.getWriteMethod().invoke(target, new Object[] { new Integer(index), value });
  414            } catch (Exception e) {
  415               Class type = pd.getReadMethod().getReturnType();
  416               value = JavaUtils.convert(value, type);
  417               try {
  418                  if (index < 0)
  419                     pd.getWriteMethod().invoke(target, new Object[] { value });
  420                  else
  421                     pd.getWriteMethod().invoke(target, new Object[] { new Integer(index), value });
  422               } catch (Exception ex) {
  423                  throw new SAXException(ex);
  424               }
  425            }
  426         }
  427      }
  428   
  429      static class BeanPropertyDescriptor {
  430         private String name;
  431         private Method getter;
  432         private Method setter;
  433   
  434         public BeanPropertyDescriptor(String _name, Method _getter, Method _setter) {
  435            name = _name;
  436            getter = _getter;
  437            setter = _setter;
  438         }
  439   
  440         public Method getReadMethod() {
  441            return getter;
  442         }
  443         public Method getWriteMethod() {
  444            return setter;
  445         }
  446         public String getName() {
  447            return name;
  448         }
  449         public Class getType() {
  450            return getter.getReturnType();
  451         }
  452   
  453         /** 
  454          * This method attempts to sort the property descriptors to match the 
  455          * order defined in the class.  This is necessary to support 
  456          * xsd:sequence processing, which means that the serialized order of 
  457          * properties must match the xml element order.  (This method assumes that the
  458          * order of the set methods matches the xml element order...the emitter 
  459          * will always order the set methods according to the xml order.)
  460          *
  461          * This routine also looks for set(i, type) and get(i) methods and adjusts the 
  462          * property to use these methods instead.  These methods are generated by the
  463          * emitter for "collection" of properties (i.e. maxOccurs="unbounded" on an element).
  464          * JAX-RPC is silent on this issue, but web services depend on this kind of behaviour.
  465          * The method signatures were chosen to match bean indexed properties.
  466          */
  467         static BeanPropertyDescriptor[] processPropertyDescriptors(
  468            PropertyDescriptor[] rawPd,
  469            Class cls) {
  470            BeanPropertyDescriptor[] myPd = new BeanPropertyDescriptor[rawPd.length];
  471   
  472            for (int i = 0; i < rawPd.length; i++) {
  473               myPd[i] =
  474                  new BeanPropertyDescriptor(
  475                     rawPd[i].getName(),
  476                     rawPd[i].getReadMethod(),
  477                     rawPd[i].getWriteMethod());
  478            }
  479   
  480            try {
  481               // Create a new pd array and index into the array
  482               int index = 0;
  483   
  484               // Build a new pd array
  485               // defined by the order of the get methods. 
  486               BeanPropertyDescriptor[] newPd = new BeanPropertyDescriptor[rawPd.length];
  487               Method[] methods = cls.getMethods();
  488               for (int i = 0; i < methods.length; i++) {
  489                  Method method = methods[i];
  490                  if (method.getName().startsWith("set")) {
  491                     boolean found = false;
  492                     for (int j = 0; j < myPd.length && !found; j++) {
  493                        if (myPd[j].getWriteMethod() != null
  494                           && myPd[j].getWriteMethod().equals(method)) {
  495                           found = true;
  496                           newPd[index] = myPd[j];
  497                           index++;
  498                        }
  499                     }
  500                  }
  501               }
  502               // Now if there are any additional property descriptors, add them to the end.
  503               if (index < myPd.length) {
  504                  for (int m = 0; m < myPd.length && index < myPd.length; m++) {
  505                     boolean found = false;
  506                     for (int n = 0; n < index && !found; n++) {
  507                        found = (myPd[m] == newPd[n]);
  508                     }
  509                     if (!found) {
  510                        newPd[index] = myPd[m];
  511                        index++;
  512                     }
  513                  }
  514               }
  515               // If newPd has same number of elements as myPd, use newPd.
  516               if (index == myPd.length) {
  517                  myPd = newPd;
  518               }
  519   
  520               // Get the methods of the class and look for the special set and
  521               // get methods for property "collections"
  522               for (int i = 0; i < methods.length; i++) {
  523                  if (methods[i].getName().startsWith("set")
  524                     && methods[i].getParameterTypes().length == 2) {
  525                     for (int j = 0; j < methods.length; j++) {
  526                        if ((methods[j].getName().startsWith("get")
  527                           || methods[j].getName().startsWith("is"))
  528                           && methods[j].getParameterTypes().length == 1
  529                           && methods[j].getReturnType() == methods[i].getParameterTypes()[1]
  530                           && methods[j].getParameterTypes()[0] == int.class
  531                           && methods[i].getParameterTypes()[0] == int.class) {
  532                           for (int k = 0; k < myPd.length; k++) {
  533                              if (myPd[k].getReadMethod() != null
  534                                 && myPd[k].getWriteMethod() != null
  535                                 && myPd[k].getReadMethod().getName().equals(methods[j].getName())
  536                                 && myPd[k].getWriteMethod().getName().equals(methods[i].getName())) {
  537                                 myPd[k] = new BeanPropertyDescriptor(myPd[k].getName(), methods[j], methods[i]);
  538                              }
  539                           }
  540                        }
  541                     }
  542                  }
  543               }
  544            } catch (Exception e) {
  545               // Don't process Property Descriptors if problems occur
  546               return myPd;
  547            }
  548            return myPd;
  549         }
  550      }
  551   
  552      /**
  553       * Create a BeanPropertyDescriptor array for the indicated class.
  554       */
  555      public static BeanPropertyDescriptor[] getPd(Class javaType) {
  556         BeanPropertyDescriptor[] pd;
  557         try {
  558            PropertyDescriptor[] rawPd =
  559               Introspector.getBeanInfo(javaType).getPropertyDescriptors();
  560            pd = BeanPropertyDescriptor.processPropertyDescriptors(rawPd, javaType);
  561         } catch (Exception e) {
  562            // this should never happen
  563            throw new RuntimeException(e.getMessage());
  564         }
  565         return pd;
  566      }
  567   
  568   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » net » axis » server » [javadoc | source]