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);
}