You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by gd...@apache.org on 2001/05/04 21:24:51 UTC

cvs commit: xml-axis/java/src/org/apache/axis/message ElementRecorder.java MessageElement.java RPCElement.java RPCParam.java SOAPSAXHandler.java

gdaniels    01/05/04 12:24:51

  Modified:    java/src/org/apache/axis/encoding
                        DeserializationContext.java DeserializerBase.java
                        SOAPTypeMappingRegistry.java
                        ServiceDescription.java
               java/src/org/apache/axis/message ElementRecorder.java
                        MessageElement.java RPCElement.java RPCParam.java
                        SOAPSAXHandler.java
  Log:
  ID/HREF support.  Once again, needs some cleanup, but I've put it
  through some fairly nasty tests and it works.
  
  Basic pattern is when you start deserializing an element with an
  HREF on it, check to see if we have that ID in our table already.
  If we do, there should be either an ElementRecorder or a Deserializer
  in the table.  In the former case, we play the recorded events to our
  Deserializer, and in the latter, we just go grab the value.
  
  If we DON'T already have the ID, we add this deserializer to a
  fixup table in the DeserializationContext.  After that, whenever the
  SOAPSAXHandler encounters an element with an ID, it checks
  against the pending fixups before deciding to just record it.  If there
  is a waiting deserializer, it uses that instead.
  
  Deserializers have now changed to "push" their values once they
  have them to a set of "targets", which are right now target Fields
  of Objects, using the reflection API.  So we can do things like we
  do in DataSer (samples/encoding) to fixup a complete structure
  made up of parts which themselves need fixups.
  
  This all assumes local refs right now, next step is to abstract out the
  HREF dereferencing mechanism to be able to plug in arbitrary handlers
  for different types of references (cid:, http:, etc...).
  
  Tests are also a-coming.
  
  Revision  Changes    Path
  1.4       +51 -0     xml-axis/java/src/org/apache/axis/encoding/DeserializationContext.java
  
  Index: DeserializationContext.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/DeserializationContext.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DeserializationContext.java	2001/05/03 15:35:18	1.3
  +++ DeserializationContext.java	2001/05/04 19:24:37	1.4
  @@ -73,11 +73,62 @@
       public SOAPSAXHandler baseHandler;
       public Hashtable idMappings = new Hashtable();
       public TypeMappingRegistry mappingRegistry = new SOAPTypeMappingRegistry();
  +    public Hashtable fixups = new Hashtable();
  +    public boolean hasUnresolvedHrefs = false;
       
       public DeserializationContext(SOAPSAXHandler baseHandler)
       {
           this.baseHandler = baseHandler;
           mappingRegistry.setDeserializationContext(this);
  +    }
  +    
  +    public SOAPSAXHandler getSAXHandler()
  +    {
  +        return baseHandler;
  +    }
  +    
  +    public void addFixupHandler(String id, DeserializerBase handler)
  +    {
  +        DeserializerBase oldHandler = (DeserializerBase)fixups.get(id);
  +        
  +        /** !!! This is kind of messy.  Basically, if multiple references
  +         * to the same object occur, we need to make sure that when it gets
  +         * found, all of the fixup points are loaded with the correct value.
  +         * 
  +         * Right now this happens by checking the deserializer types and
  +         * copying the targets across to the new one.  To make this nicer,
  +         * we should integrate knowledge of the HREF attribute further up
  +         * the chain (in SOAPSAXHandler), to avoid even creating a new
  +         * deserializer once one is already registered for a given ID.
  +         * 
  +         */
  +        if (oldHandler != null) {
  +            // Make sure types match...
  +            if (!handler.getClass().equals(oldHandler.getClass())) {
  +                System.err.println("Non-compatible deserializers for multiple refs to ID " + id);
  +                return;
  +            }
  +            
  +            handler.copyValueTargets(oldHandler);
  +        }
  +        
  +        fixups.put(id, handler);
  +        hasUnresolvedHrefs = true;
  +    }
  +    
  +    public DeserializerBase getHandlerForID(String id)
  +    {
  +        DeserializerBase handler = (DeserializerBase)fixups.get(id);
  +        if (handler != null) {
  +            fixups.remove(id);
  +            hasUnresolvedHrefs = !fixups.isEmpty();
  +        }
  +        return handler;
  +    }
  +    
  +    public boolean unresolvedHrefs()
  +    {
  +        return hasUnresolvedHrefs;
       }
       
       public void pushElementHandler(DeserializerBase handler)
  
  
  
  1.4       +139 -9    xml-axis/java/src/org/apache/axis/encoding/DeserializerBase.java
  
  Index: DeserializerBase.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/DeserializerBase.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DeserializerBase.java	2001/05/03 15:35:21	1.3
  +++ DeserializerBase.java	2001/05/04 19:24:38	1.4
  @@ -57,23 +57,23 @@
   
   import org.xml.sax.*;
   import org.xml.sax.helpers.*;
  +import org.apache.axis.message.*;
   
  -/** A convenience base class for deserializers, which handles throwing
  - * exceptions when unexpected events occur.
  - * 
  - * !!! Can probably simplify this by just having all methods throw in
  - * here, then people can just overload what they allow.
  +import java.util.*;
  +import java.lang.reflect.*;
  +import java.lang.*;
  +
  +/** The Deserializer base class.
    * 
  - * The "value" object needs to be kept somewhere.  We can either do it
  - * here, which necessitates a deserializer instance for each deserialization,
  - * or could somehow store the state in the MessageElement which is
  - * being blessed with a value.  Might bear some investigating.
  + * Still needs some work.
    * 
    * @author Glen Daniels (gdaniels@allaire.com)
    */
   
   public class DeserializerBase extends DefaultHandler
   {
  +    private static final boolean DEBUG_LOG = false;
  +    
       protected Object value = null;
       protected DeserializationContext context = null;
       
  @@ -81,10 +81,140 @@
       {
           return value;
       }
  +    public void setValue(Object value)
  +    {
  +        this.value = value;
  +    }
  +
  +    /////////////////////////////////////////////////////////////
  +    //  Reflection-based insertion of values into target objects
  +    //  once deserialization is complete.
  +    //
  +    //  !!! This needs to be expanded to deal with accessor methods
  +    //      (i.e. Beans) as well, not to mention primitive types.
  +    //
  +    //
  +    class FieldTarget {
  +        public Object targetObject;
  +        public Field targetField;
  +        public FieldTarget(Object targetObject, Field targetField)
  +        {
  +            this.targetObject = targetObject;
  +            this.targetField = targetField;
  +        }
  +    }
  +    protected Vector targets = null;
  +    public void registerValueTarget(Object target, Field field)
  +    {
  +        if (targets == null)
  +            targets = new Vector();
  +        
  +        targets.addElement(new FieldTarget(target,field));
  +    }
  +    
  +    public void registerValueTarget(Object target, String fieldName)
  +    {
  +        try {
  +            Class cls = target.getClass();
  +            Field field = cls.getField(fieldName);
  +        
  +            registerValueTarget(target, field);
  +        } catch (NoSuchFieldException ex) {
  +            ex.printStackTrace();
  +        }
  +    }
       
  +    /** Add someone else's targets to our own (see DeserializationContext)
  +     * 
  +     */
  +    public void copyValueTargets(DeserializerBase other)
  +    {
  +        if (other.targets == null)
  +            return;
  +        
  +        Enumeration e = other.targets.elements();
  +        while (e.hasMoreElements()) {
  +            targets.addElement(e.nextElement());
  +        }
  +    }
  +    
  +    /** !!! Only works with object values right now.
  +     * TODO: Get primitives working
  +     */
  +    public void valueComplete() throws SAXException
  +    {
  +        if (targets == null)
  +            return;
  +        
  +        Enumeration e = targets.elements();
  +        while (e.hasMoreElements()) {
  +            FieldTarget target = (FieldTarget)e.nextElement();
  +            Field field = target.targetField;
  +            Object object = target.targetObject;
  +            
  +            try {
  +                field.set(object, value);
  +            } catch (IllegalAccessException accEx) {
  +                accEx.printStackTrace();
  +                throw new SAXException(accEx);
  +            } catch (IllegalArgumentException argEx) {
  +                argEx.printStackTrace();
  +                throw new SAXException(argEx);
  +            }
  +        }
  +    }
  +    
       public void setDeserializationContext(DeserializationContext context)
       {
           this.context = context;
  +    }
  +    
  +    /** Base-class startElement() handler.  Deals with HREFs
  +     */
  +    public void startElement(String namespace, String localName,
  +                             String qName, Attributes attributes)
  +        throws SAXException
  +    {
  +        String href = attributes.getValue("href");
  +        if (href != null) {
  +            if (DEBUG_LOG) {
  +                System.err.println("looking for href " + href);
  +            }
  +            
  +            MessageElement target = context.getElementByID(href.substring(1));
  +            if (target != null) {
  +                if (DEBUG_LOG)
  +                    System.out.println("found target " + target);
  +                
  +                DeserializerBase rec = target.getContentHandler();
  +                if (rec == null)
  +                    throw new SAXException("No handler in target element?");
  +                
  +                if (rec instanceof ElementRecorder) {
  +                    ((ElementRecorder)rec).publishChildrenToHandler(context.getSAXHandler());
  +                } else {
  +                    this.value = rec.value;
  +                }
  +                
  +                // Might want to somehow write deserialized version
  +                // back to the IDmapping table...
  +                
  +                return;
  +            }
  +            
  +            context.addFixupHandler(href.substring(1), this);
  +        }
  +    }
  +    
  +    public void endElement(String namespace, String localName,
  +                           String qName)
  +        throws SAXException
  +    {
  +        // By default, we're done when we're out of XML...
  +        // If the end element REALLY matters to subclasses, they should remember
  +        // to call valueComplete()...
  +        
  +        valueComplete();
       }
       
       /** Deserialization structure handlers
  
  
  
  1.5       +2 -5      xml-axis/java/src/org/apache/axis/encoding/SOAPTypeMappingRegistry.java
  
  Index: SOAPTypeMappingRegistry.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/SOAPTypeMappingRegistry.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- SOAPTypeMappingRegistry.java	2001/05/04 06:09:50	1.4
  +++ SOAPTypeMappingRegistry.java	2001/05/04 19:24:39	1.5
  @@ -22,14 +22,11 @@
       public static final QName SOAP_SHORT = new QName(Constants.URI_SOAP_ENC, "short");
       
       abstract class BasicDeser extends DeserializerBase {
  -        public void startElement(String namespace, String name, String qName,
  -                                 Attributes attributes)
  -        {
  -        }
  -        
           public void characters(char [] chars, int start, int end)
  +            throws SAXException
           {
               value = makeValue(new String(chars, start, end));
  +            valueComplete();
           }
           abstract Object makeValue(String source);
       }
  
  
  
  1.4       +8 -6      xml-axis/java/src/org/apache/axis/encoding/ServiceDescription.java
  
  Index: ServiceDescription.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ServiceDescription.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ServiceDescription.java	2001/04/29 00:38:25	1.3
  +++ ServiceDescription.java	2001/05/04 19:24:39	1.4
  @@ -164,12 +164,14 @@
        */
       public QName getParamTypeByName(String messageType, String paramName)
       {
  -        if (messageType.equals(REQUEST))
  -            return getInputParamTypeByName(paramName);
  -        if (messageType.equals(RESPONSE))
  -            return getOutputParamTypeByName(paramName);
  -        
  -        // Only understand these two at present...
  +        if (messageType != null) {
  +            if (messageType.equals(REQUEST))
  +                return getInputParamTypeByName(paramName);
  +            if (messageType.equals(RESPONSE))
  +                return getOutputParamTypeByName(paramName);
  +            
  +            // Only understand these two at present...
  +        }
           
           return null;
       }
  
  
  
  1.6       +24 -12    xml-axis/java/src/org/apache/axis/message/ElementRecorder.java
  
  Index: ElementRecorder.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/ElementRecorder.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ElementRecorder.java	2001/05/04 04:10:51	1.5
  +++ ElementRecorder.java	2001/05/04 19:24:45	1.6
  @@ -68,7 +68,7 @@
    * 
    * @author Glen Daniels (gdaniels@allaire.com)
    */
  -class ElementRecorder extends DeserializerBase
  +public class ElementRecorder extends DeserializerBase
   {
       private static final boolean DEBUG_LOG = false;
       
  @@ -77,6 +77,8 @@
       
       public ElementRecorder()
       {
  +        if (DEBUG_LOG)
  +            System.out.println("New ElementRecorder " + this);
       }
       
       public void startElement(String namespace, String localName,
  @@ -91,17 +93,6 @@
           _events.addElement(new StartElementEvent(namespace, localName, qName, attributes));
       }
       
  -    public void onStartChild(String namespace, String localName,
  -                             String qName, Attributes attributes)
  -    {
  -        if (DEBUG_LOG) {
  -            System.err.println("(rec) startChild ['" + namespace + "' " +
  -                           localName + "]");
  -        }
  -        
  -        _events.addElement(new StartElementEvent(namespace, localName, qName, attributes));
  -    }
  -    
       public void endElement(String namespace, String localName, String qName)
           throws SAXException
       {
  @@ -138,6 +129,27 @@
                   System.err.println("Publishing : " + event);
               }
               event.publishToHandler(handler);
  +        }
  +    }
  +
  +    public void publishChildrenToHandler(ContentHandler handler)
  +        throws SAXException
  +    {
  +        Enumeration e = _events.elements();
  +        SAXEvent event = null;
  +        
  +        // read the first element
  +        if (e.hasMoreElements()) e.nextElement();
  +
  +        // read the second element
  +        if (e.hasMoreElements()) event = (SAXEvent)e.nextElement();
  +
  +        while (e.hasMoreElements()) {
  +            if (DEBUG_LOG) {
  +                System.err.println("Publishing : " + event);
  +            }
  +            event.publishToHandler(handler);
  +            event = (SAXEvent)e.nextElement();
           }
       }
   
  
  
  
  1.14      +1 -25     xml-axis/java/src/org/apache/axis/message/MessageElement.java
  
  Index: MessageElement.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/MessageElement.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- MessageElement.java	2001/05/04 06:09:51	1.13
  +++ MessageElement.java	2001/05/04 19:24:46	1.14
  @@ -66,7 +66,7 @@
   import java.util.*;
   import java.io.*;
   
  -public class MessageElement
  +public class MessageElement extends DeserializerBase
   {
       private static final boolean DEBUG_LOG = false;
       
  @@ -78,12 +78,9 @@
       protected String    href;
       protected boolean   isRoot = false;
       protected SOAPEnvelope message = null;
  -    private DeserializationContext context = null;
  -    DeserializerBase deserializer;
       
       // The java Object value of this element.  This is either set by
       // deserialization, or by the user creating a message.
  -    protected Object value = null;
       protected QName typeQName = null;
       
       // String representation of this element.
  @@ -155,27 +152,6 @@
       public Element getAsDOM()
       {
           return null;
  -    }
  -    
  -    public Object getValue() throws IOException
  -    {
  -        if (value instanceof ElementRecorder) {
  -            // !!! Lazy deserialization here... We have the SAX events,
  -            //     but haven't run them through a deserializer yet.
  -            return null;
  -        }
  -        
  -        if (deserializer != null) {
  -            value = deserializer.getValue();
  -            deserializer = null;
  -        }
  -        
  -        return value;
  -    }
  -
  -    public void setValue(Object val)
  -    {
  -        value = val;
       }
       
       public DeserializerBase getContentHandler()
  
  
  
  1.5       +35 -35    xml-axis/java/src/org/apache/axis/message/RPCElement.java
  
  Index: RPCElement.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/RPCElement.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- RPCElement.java	2001/05/03 15:35:43	1.4
  +++ RPCElement.java	2001/05/04 19:24:46	1.5
  @@ -84,40 +84,7 @@
           return new RPCElementFactory();
       }
       
  -    class RPCContentHandler extends DeserializerBase
  -    {
  -        public void onStartChild(String namespace, String name, String qName,
  -                                 Attributes attributes)
  -            throws SAXException
  -        {
  -            // Start of an arg...
  -            RPCParam param = new RPCParam(namespace, name, attributes, context);
  -            
  -            params.addElement(param);
  -            if (param.getType() == null) {
  -                // No type inline, so check service description.
  -                ServiceDescription serviceDesc = getEnvelope().getServiceDescription();
  -                if (serviceDesc != null) {
  -                    param.setType(serviceDesc.getParamTypeByName(getEnvelope().getMessageType(),
  -                                                                 param.getName()));
  -                }
  -            } else {
  -                /** !!! If we have a service description and this is an
  -                 * explicitly-typed param, we might want to check here to
  -                 * see if the xsi:type val is indeed a subtype of the type
  -                 * we expect from the service description.
  -                 */
  -                
  -            }
  -            
  -            DeserializerBase handler = param.getContentHandler(context);
  -            
  -            handler.startElement(namespace, name, qName, attributes);
  -            
  -            context.pushElementHandler(handler);
  -        }
  -    }
  -    public DeserializerBase getContentHandler() { return new RPCContentHandler(); }
  +    public DeserializerBase getContentHandler() { return this; }
       
       ///////////////////////////////////////////////////////////////
       
  @@ -164,7 +131,40 @@
           this.methodName = methodName;
           this.name = methodName;
       }
  -    
  +
  +    /** *******************************************************
  +     *  Deserialization
  +     *  *******************************************************
  +     */
  +    public void onStartChild(String namespace, String name, String qName,
  +                             Attributes attributes)
  +        throws SAXException
  +    {
  +        // Start of an arg...
  +        RPCParam param = new RPCParam(namespace, name, attributes, context);
  +        
  +        params.addElement(param);
  +        if (param.getType() == null) {
  +            // No type inline, so check service description.
  +            ServiceDescription serviceDesc = getEnvelope().getServiceDescription();
  +            if (serviceDesc != null) {
  +                param.setType(serviceDesc.getParamTypeByName(getEnvelope().getMessageType(),
  +                                                             param.getName()));
  +            }
  +        } else {
  +            /** !!! If we have a service description and this is an
  +            * explicitly-typed param, we might want to check here to
  +            * see if the xsi:type val is indeed a subtype of the type
  +            * we expect from the service description.
  +            */
  +            
  +        }
  +        
  +        DeserializerBase handler = param.getContentHandler(context);
  +        
  +        context.pushElementHandler(handler);
  +    }
  +
       public void output(SerializationContext context)
           throws IOException
       {
  
  
  
  1.10      +1 -0      xml-axis/java/src/org/apache/axis/message/RPCParam.java
  
  Index: RPCParam.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/RPCParam.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- RPCParam.java	2001/05/04 06:09:52	1.9
  +++ RPCParam.java	2001/05/04 19:24:47	1.10
  @@ -71,6 +71,7 @@
   public class RPCParam extends MessageElement
   {
       private static boolean DEBUG_LOG = false;
  +    private DeserializerBase deserializer = null;
       
       // Who's your daddy?
       RPCElement myRPCElement;
  
  
  
  1.7       +21 -1     xml-axis/java/src/org/apache/axis/message/SOAPSAXHandler.java
  
  Index: SOAPSAXHandler.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/SOAPSAXHandler.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- SOAPSAXHandler.java	2001/05/03 15:35:47	1.6
  +++ SOAPSAXHandler.java	2001/05/04 19:24:47	1.7
  @@ -324,6 +324,7 @@
           
           if (!handlerContexts.empty()) {
               HandlerContext context = (HandlerContext)handlerContexts.pop();
  +            
               elementHandler = context.handler;
               recordingDepth = context.recordingDepth;
               //System.out.println("Popping handler (" + recordingDepth + ") " + elementHandler);
  @@ -416,8 +417,13 @@
           }
           
           if (elementHandler != null) {
  +            // This lets elements handle struct-like processing, and potentially
  +            // push another handler onto the element stack.
               elementHandler.onStartChild(namespace, localName, qName, attributes);
               
  +            // This may, therefore, refer to a different handler than the last line.
  +            elementHandler.startElement(namespace, localName, qName, attributes);
  +            
               recordingDepth++;
               
               return;
  @@ -519,8 +525,22 @@
                   }
                   
                   element.setEnvelope(envelope);
  -                pushElementHandler(element.getContentHandler());
                   element.setPrefix(namespaces.getPrefix(namespace));
  +                
  +                DeserializerBase handler = null;
  +                if (context.unresolvedHrefs() && (element.getID() != null)) {
  +                    handler = context.getHandlerForID(element.getID());
  +                    if (handler == null) {
  +                        handler = element.getContentHandler();
  +                    } /* else {
  +                        System.out.println("found it " + handler);
  +                    } */
  +                } else {
  +                    handler = element.getContentHandler();
  +                }
  +                
  +                pushElementHandler(handler);
  +
                   elementHandler.setDeserializationContext(context);
                   elementHandler.startElement(namespace, localName, qName, attributes);
               }