You are viewing a plain text version of this content. The canonical link for it is here.
Posted to axis-cvs@ws.apache.org by sc...@apache.org on 2007/09/19 18:56:49 UTC

svn commit: r577367 [3/5] - in /webservices/axis2/trunk/java/modules: jaxws/src/org/apache/axis2/jaxws/client/ jaxws/src/org/apache/axis2/jaxws/message/util/ kernel/src/org/apache/axis2/addressing/ kernel/src/org/apache/axis2/client/ kernel/src/org/apa...

Added: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/MessageExternalizeUtils.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/MessageExternalizeUtils.java?rev=577367&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/MessageExternalizeUtils.java (added)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/MessageExternalizeUtils.java Wed Sep 19 09:56:47 2007
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2004,2007 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.axis2.context.externalize;
+
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.impl.builder.StAXBuilder;
+import org.apache.axiom.om.util.StAXUtils;
+import org.apache.axiom.soap.SOAPEnvelope;
+import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.builder.BuilderUtil;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.MessageFormatter;
+import org.apache.axis2.transport.TransportUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.xml.stream.XMLStreamReader;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.OutputStream;
+
+/**
+ * Utility to read/write the Message of a MessageContext
+ * Message Object Format.
+ * 
+ * <tt>
+ * Format := Prolog {DataBlocks} EndBlocks
+ * 
+ * Prolog :=
+ *   NAME      (UTF) 
+ *   REVISION  (INT)
+ *   ACTIVE    (BOOL)
+ *     [OPTIMIZED (BOOL)]  
+ *        [OPTIMIZED_CONTENT_TYPE (UTF)]    <--- If OPTIMIZED=TRUE
+ *     [CHARSET   (UTF)] 
+ *     [NAMESPACE (UTF)]
+ *   
+ * DataBlock :=
+ *   SIZE (INT >0)
+ *   DATA (BYTES)
+ *   
+ * EndBlocks
+ *   SIZE (INT)   {0 indicates end -1 indicates failure}
+ *     
+ *   
+ * </tt>
+ */
+public class MessageExternalizeUtils  implements ExternalizeConstants {
+    static final Log log = LogFactory.getLog(MessageExternalizeUtils.class);
+
+    /*
+     * @serial Tracks the revision level of a class to identify changes to the
+     * class definition that are compatible to serialization/externalization.
+     * If a class definition changes, then the serialization/externalization
+     * of the class is affected.
+     * Refer to the writeExternal() and readExternal() methods.
+     */
+    // supported revision levels, add a new level to manage compatible changes
+    private static final int REVISION_2 = 2;
+    // current revision level of this object
+    private static final int revisionID = REVISION_2;
+    
+    
+    /**
+     * Private Constructor.
+     * This class only supports static methods
+     */
+    private MessageExternalizeUtils() {}
+    
+    /**
+     * Write out the Message
+     * @param out
+     * @param mc
+     * @param correlationIDString
+     * @param outputFormat
+     * @throws IOException
+     */
+    public static void writeExternal(ObjectOutput out, 
+                                     MessageContext mc,
+                                     String correlationIDString,
+                                     OMOutputFormat outputFormat) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":writeExternal(): start");
+        }
+        SOAPEnvelope envelope = mc.getEnvelope();
+        if (envelope == null) {
+            // Case: No envelope
+            out.writeUTF("NULL_ENVELOPE");
+            out.writeInt(revisionID);
+            out.writeBoolean(EMPTY_OBJECT); // Not Active
+            out.writeInt(0);  // EndBlocks
+            if (log.isDebugEnabled()) {
+                log.debug(correlationIDString + ":writeExternal(): end: msg is Empty");
+            }
+            return;
+        }
+        
+        // Write Prolog
+        String msgClass = envelope.getClass().getName();
+        out.writeUTF(msgClass);
+        out.writeInt(revisionID);
+        out.writeBoolean(ACTIVE_OBJECT);
+        if (outputFormat.isOptimized()) {
+            out.writeBoolean(true);
+            // Write out the contentType.
+            out.writeUTF(outputFormat.getContentType());
+        } else {
+            out.writeBoolean(false);
+        }
+        out.writeUTF(outputFormat.getCharSetEncoding());
+        out.writeUTF(envelope.getNamespace().getNamespaceURI());
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":writeExternal(): " + 
+                      "optimized=[" + outputFormat.isOptimized() + "]  " +
+                      "optimizedContentType " + outputFormat.getContentType() + "]  " +
+                      "charSetEnc=[" + outputFormat.getCharSetEncoding() + "]  " +
+                      "namespaceURI=[" + envelope.getNamespace().getNamespaceURI() + "]");
+        }
+        
+        // Write DataBlocks
+        // MessageOutputStream writes out the DataBlocks in chunks
+        // BufferedOutputStream buffers the data to prevent numerous, small blocks
+        MessageOutputStream mos = new MessageOutputStream(out);  
+        BufferedOutputStream bos = new BufferedOutputStream(mos);   
+        boolean errorOccurred = false;
+        try { 
+            // Write out the message using the same logic as the 
+            // transport layer.
+            MessageFormatter msgFormatter = TransportUtils.getMessageFormatter(mc);
+            msgFormatter.writeTo(mc, outputFormat, bos, 
+                                 true); // Preserve the original message
+            
+        } catch (IOException e) {
+            throw e;
+        } catch (Throwable t) {
+            throw AxisFault.makeFault(t);
+        } finally {
+            bos.flush();
+            bos.close();
+        }
+        
+        // Write End of Data Blocks
+        if (errorOccurred) {
+            out.writeInt(-1);
+        } else {
+            out.writeInt(0);
+        }
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":writeExternal(): end");
+        }
+    }
+    
+    /**
+     * Read the Message
+     * @param in
+     * @param mc
+     * @param correlationIDString
+     * @return
+     * @throws IOException
+     */
+    public static SOAPEnvelope readExternal(ObjectInput in,
+                                            MessageContext mc,
+                                            String correlationIDString) throws IOException, ClassNotFoundException {
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":readExternal(): start");
+        }
+        SOAPEnvelope envelope = null;
+        
+        // Read Prolog
+        // Read the class name and object state
+        String name = in.readUTF();
+        int revision = in.readInt();
+        
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":readExternal(): name= " + name  +
+                      " revision= " + revision);
+        }
+        // make sure the object data is in a revision level we can handle
+        if (revision != REVISION_2) {
+            throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_REVID);
+        }
+        
+        
+        boolean gotMsg = in.readBoolean();
+        if (gotMsg != ACTIVE_OBJECT) {
+            if (log.isDebugEnabled()) {
+                log.debug(correlationIDString + ":readExternal(): end:" +
+                                "no message present");
+            }
+            in.readInt(); // Read end of data blocks
+            return envelope;
+        }
+        
+        // Read optimized, optimized content-type, charset encoding and namespace uri
+        boolean optimized= in.readBoolean();
+        String optimizedContentType = null;
+        if (optimized) {
+            optimizedContentType = in.readUTF();
+        }
+        String charSetEnc = in.readUTF();
+        String namespaceURI = in.readUTF();
+        if (log.isDebugEnabled()) {
+            log.debug(correlationIDString + ":readExternal(): " +
+                      "optimized=[" + optimized + "]  " +
+                      "optimizedContentType=[" + optimizedContentType + "]  " +
+                      "charSetEnc=[" + charSetEnc + "]  " +
+                      "namespaceURI=[" + namespaceURI + "]");
+        }
+        
+        MessageInputStream mis = new MessageInputStream(in);
+        StAXBuilder  builder = null;
+        try {
+            if (optimized) {
+                boolean isSOAP = true;
+                builder =
+                    BuilderUtil.getAttachmentsBuilder(mc,
+                                                      mis,
+                                                      optimizedContentType,
+                                                      isSOAP);
+                envelope = (SOAPEnvelope) builder.getDocumentElement();
+                envelope.buildWithAttachments();
+            } else {
+                XMLStreamReader xmlreader = StAXUtils.createXMLStreamReader(mis, charSetEnc);
+                builder = new StAXSOAPModelBuilder(xmlreader, namespaceURI);
+                envelope = (SOAPEnvelope) builder.getDocumentElement();
+                envelope.build();
+            }
+        } catch (Exception ex) {
+            // TODO: what to do if can't get the XML stream reader
+            // For now, log the event
+            log.error(correlationIDString
+                      + ":readExternal(): Error when deserializing persisted envelope: ["
+                      + ex.getClass().getName() + " : " + ex.getLocalizedMessage() + "]", ex);
+            envelope = null;
+        } finally {
+            if (builder != null) {
+                builder.close();
+            }
+            // Close the message input stream.  This will ensure that the
+            // underlying stream is advanced past the message.
+            mis.close();
+            if (log.isDebugEnabled()) {
+                log.debug(correlationIDString + ":readExternal(): end");
+            }
+        }
+        return envelope;
+    }
+    
+    /**
+     * MessageOutputStream writes DataBlock chunks to the ObjectOutput.
+     */
+    private static class MessageOutputStream extends OutputStream {
+        ObjectOutput out;
+        boolean isDebug;
+        MessageOutputStream(ObjectOutput out) {
+            this.out = out;
+            isDebug = log.isDebugEnabled();
+        }
+        
+         
+        public void close() throws IOException {
+            // NOOP: ObjectOutput will be closed externally
+        }
+        
+        public void flush() throws IOException {
+            out.flush();
+        }
+
+        /** 
+         * Writes a chunk of data to the ObjectOutput
+         */
+        public void write(byte[] b, int off, int len) throws IOException {
+            if (len > 0) {
+                if (isDebug) {
+                    log.debug("Write data chunk with len=" + len);
+                }
+                // Write out the length and the data chunk
+                out.writeInt(len);
+                out.write(b, off, len);
+            }
+        }
+
+         
+        /** 
+         * Writes a chunk of data to the ObjectOutput
+         */
+        public void write(byte[] b) throws IOException {
+            if (b != null &&  b.length > 0) {
+                if (isDebug) {
+                    log.debug("Write data chunk with size=" + b.length);
+                }
+                // Write out the length and the data chunk
+                out.writeInt(b.length);
+                out.write(b);
+            }
+        }
+
+         
+        /** 
+         * Writes a single byte chunk of data to the ObjectOutput
+         */
+        public void write(int b) throws IOException {
+            if (isDebug) {
+                log.debug("Write one byte data chunk");
+            }
+            // Write out the length and the data chunk
+            out.writeInt(1);
+            out.write(b);
+        }
+    }
+       
+    /**
+     * Provides a InputStream interface over ObjectInput.
+     * MessageInputStream controls the reading of the DataBlock chunks
+     *
+     */
+    private static class MessageInputStream extends InputStream {
+        
+        ObjectInput in;
+        boolean isDebug;
+        int chunkAvail = 0;
+        boolean isEOD = false;
+        
+        /**
+         * Constructor
+         * @param in
+         */
+        MessageInputStream(ObjectInput in) {
+            this.in = in;
+            isDebug = log.isDebugEnabled();
+        }
+
+         
+        /**
+         * Read a single logical byte
+         */
+        public int read() throws IOException {
+            if (isDebug) {
+                log.debug("invoking read()");
+            }
+            // Determine how many bytes are left in the current data chunk
+            updateChunkAvail();
+            int ret = 0;
+            if (isEOD) {
+                ret = -1;
+            } else {
+                chunkAvail--;
+                ret = in.readByte();
+            }
+            log.debug("returning " + ret);
+            return ret;
+        }
+
+         
+        /**
+         * Read an array of logical bytes
+         */
+        public int read(byte[] b, int off, int len) throws IOException {
+            if (isDebug) {
+                log.debug("invoking read with off=" + off + " and len=" + len);
+            }
+            if (isEOD) {
+                if (isDebug) {
+                    log.debug("EOD returning -1");
+                }
+                return -1;
+            }
+            int bytesRead = 0;
+            while ((len >0 && !isEOD)) {
+                // Determine how many bytes are left in the current data chunk
+                updateChunkAvail();
+                if (!isEOD) {
+                    // Read the amount of bytes requested or the number of bytes available in the current chunk
+                    int readLength = len < chunkAvail ? len : chunkAvail;
+                    int br = in.read(b, off, readLength);
+                    if (br < 0) {
+                        throw new IOException("End of File encountered");
+                    }
+                    // Update state with the number of bytes read
+                    off += br;
+                    len -= br;
+                    chunkAvail -= br;
+                    bytesRead += br;
+                }
+            }
+            if (isDebug) {
+                log.debug("bytes read = " + bytesRead);
+            }
+            return bytesRead;
+        }
+
+         
+        public int read(byte[] b) throws IOException {
+            return read(b, 0, b.length);
+        }
+        
+        public void close() throws IOException {
+            if (isDebug) {
+                log.debug("start close");
+            }
+            // Keep reading chunks until EOD
+            if (!isEOD) {
+                byte[] tempBuffer = new byte[4 * 1024];
+                while (!isEOD) {
+                    read(tempBuffer);
+                }
+            }
+            if (isDebug) {
+                log.debug("end close");
+            }
+        }
+        
+        /**
+         * updateChunkAvail updates the chunkAvail field with the
+         * amount of data in the chunk.
+         * @throws IOException
+         */
+        private void updateChunkAvail() throws IOException {
+            
+            // If there are no more bytes in the current chunk,
+            // read the size of the next datablock
+            if (chunkAvail == 0 && !isEOD) {
+                chunkAvail = in.readInt();
+                if (isDebug) {
+                    log.debug("New DataBlock with size=" + chunkAvail);
+                }
+                if (chunkAvail <= 0) {
+                    if (isDebug) {
+                        log.debug("End of data");
+                    }
+                    isEOD = true;
+                    chunkAvail = 0;
+                }
+            }
+        }
+    }
+}

Added: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/ObjectInputStreamWithCL.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/ObjectInputStreamWithCL.java?rev=577367&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/ObjectInputStreamWithCL.java (added)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/ObjectInputStreamWithCL.java Wed Sep 19 09:56:47 2007
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2004,2007 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.axis2.context.externalize;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+
+/**
+ * An ObjectInputStream that is constructed with a ClassLoader or ClassResolver.
+ * The default behavior is to use the ContextClassLoader
+ */
+public class ObjectInputStreamWithCL extends java.io.ObjectInputStream
+{
+
+    /**
+     * <p>
+     * This interface is used to resolve OSGi declared serializable classes.
+     * </p>
+     */
+    public interface ClassResolver
+    {
+        /**
+         * Attempt to load the specified class.
+         * 
+         * @param className
+         *            The classname.
+         * @return The class, or null if not found.
+         */
+        public Class resolveClass(String className);
+    }
+
+    private static final HashMap primClasses = new HashMap(8, 1.0F);
+
+    /** The class resolver */
+
+    protected static ClassResolver resolver;
+    static
+    {
+        primClasses.put("boolean", boolean.class);
+        primClasses.put("byte", byte.class);
+        primClasses.put("char", char.class);
+        primClasses.put("short", short.class);
+        primClasses.put("int", int.class);
+        primClasses.put("long", long.class);
+        primClasses.put("float", float.class);
+        primClasses.put("double", double.class);
+        primClasses.put("void", void.class);
+
+    }
+
+    protected ClassLoader classloader;
+
+    protected String name;
+
+    /**
+     * Construct using ContextClassLoader
+     * @param is
+     * @throws IOException
+     */
+    public ObjectInputStreamWithCL(InputStream is) throws IOException
+    {
+        super(is);
+        
+        classloader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction()
+        {
+            public Object run()
+            {
+                return Thread.currentThread().getContextClassLoader();                
+            }          
+        });                
+    }
+
+    /**
+     * Constructor that accepts a ClassLoader
+     * @param is
+     * @param cl
+     * @throws IOException
+     */
+    public ObjectInputStreamWithCL(InputStream is, ClassLoader cl) throws IOException
+    {
+        super(is);
+        classloader = cl;
+    }
+    
+    /**
+     * Constructor that accepts a ClassResolver
+     * @param is
+     * @param r ClassResolver
+     * @throws IOException
+     */
+    public ObjectInputStreamWithCL(InputStream is, ClassResolver r) throws IOException
+    {
+        super(is);
+        resolver = r;
+    }
+
+    /**
+     * Override resolveClass so that we can use our own ClassLoader
+     */
+    protected Class resolveClass(ObjectStreamClass objStrmClass) throws ClassNotFoundException
+    {
+        return resolveClass(objStrmClass.getName());
+    }
+
+    private Class resolveClass(String name) throws ClassNotFoundException
+    {
+        try
+        {
+            this.name = name;
+            return (Class) AccessController.doPrivileged(loadAction);
+        }
+        catch (java.security.PrivilegedActionException pae)
+        {
+            Exception wrapped = pae.getException();
+            if (wrapped instanceof ClassNotFoundException) throw (ClassNotFoundException) wrapped;
+            throw new ClassNotFoundException(name);
+        }
+    }
+
+    java.security.PrivilegedExceptionAction loadAction = 
+        new java.security.PrivilegedExceptionAction()
+    {
+        public java.lang.Object run() throws Exception
+        {
+            try
+            {
+                Class clazz = null;
+                // If the resolver is set
+                if (resolver != null)
+                {
+                    // use the resolver to load the class.
+                    clazz = resolver.resolveClass(name);
+                }
+
+                // if the class is not loadable
+                if (clazz == null)
+                {
+                    clazz = loadClass(name, classloader); // d296416
+                }
+
+                return clazz;
+            }
+            catch (ClassNotFoundException cnf)
+            {
+                Class c = (Class) primClasses.get(name);
+                if (c != null)
+                {
+                    return c;
+                }
+               
+                throw cnf;
+                
+            } 
+        }
+    };
+
+    // d296416: Use runtime bundle classloader (current) to resolve a class when
+    // the class could not be resolved using the specified classloader.
+    // A serializable class in a bundle should specify via
+    // <com.ibm.ws.runtime.serializable> bundle extension point
+    // that it is deserializable outside the current bundle.
+    // NOTE: Looking up current classloader is only a tactical solution,
+    // and could be deprecated in future.
+    // 
+    private java.lang.Class loadClass(String name, ClassLoader loader) throws ClassNotFoundException
+    {
+        try
+        {
+            return Class.forName(name, true, loader);
+        }
+        catch (ClassNotFoundException cnf)
+        {
+            return Class.forName(name);
+        }
+    }
+
+    
+    /**
+     * Override to provide our own resolution
+     */
+    protected Class resolveProxyClass(String[] interfaces) throws ClassNotFoundException
+    {
+        if (interfaces.length == 0)
+        {
+            throw new ClassNotFoundException("zero-length interfaces array");
+        }
+
+        Class nonPublicClass = null;
+
+        Class[] classes = new Class[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++)
+        {
+            classes[i] = resolveClass(interfaces[i]);
+
+            if ((classes[i].getModifiers() & Modifier.PUBLIC) == 0)
+            {
+                // "if more than one non-public interface class loader is
+                // encountered, an IllegalAccessError is thrown"
+                if (nonPublicClass != null)
+                {
+                    throw new IllegalAccessError(nonPublicClass + " and " + classes[i] + " both declared non-public");
+                }
+
+                nonPublicClass = classes[i];
+            }
+        }
+
+        // The javadocs for this method say:
+        //
+        // "Unless any of the resolved interfaces are non-public, this same
+        // value of loader is also the class loader passed to
+        // Proxy.getProxyClass; if non-public interfaces are present, their
+        // class loader is passed instead"
+        //
+        // Unfortunately, we don't have a single classloader that we can use.
+        // Call getClassLoader() on either the non-public class (if any) or the
+        // first class.
+        proxyClass = nonPublicClass != null ? nonPublicClass : classes[0];
+        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(proxyClassLoaderAction);
+
+        // "If Proxy.getProxyClass throws an IllegalArgumentException,
+        // resolveProxyClass will throw a ClassNotFoundException containing the
+        // IllegalArgumentException."
+        try
+        {
+            return Proxy.getProxyClass(loader, classes);
+        }
+        catch (IllegalArgumentException ex)
+        {
+            throw new ClassNotFoundException(ex.getMessage(), ex);
+        }
+    }
+
+    private Class proxyClass;
+    PrivilegedAction proxyClassLoaderAction = new PrivilegedAction()
+    {
+        public Object run()
+        {
+            return proxyClass.getClassLoader();
+        }
+    };
+}

Added: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectInputStream.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectInputStream.java?rev=577367&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectInputStream.java (added)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectInputStream.java Wed Sep 19 09:56:47 2007
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2004,2007 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.axis2.context.externalize;
+
+import org.apache.axis2.AxisFault;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamConstants;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A SafeObjectInputStream reads data that was written by SafeObjectOutputStream
+ * 
+ * @see SafeObjectInput
+ */
+public class SafeObjectInputStream implements ObjectInput, ObjectStreamConstants {
+
+    private static final Log log = LogFactory.getLog(SafeObjectInputStream.class);
+    private static final boolean isDebug = log.isDebugEnabled();
+    
+    // All data is read from the delegated ObjectInput
+    ObjectInput in = null;
+    final ObjectInput original;
+    
+    // A buffer is used when Object is persisted as bytes
+    private byte[] buffer = null;
+    private final int BUFFER_MIN_SIZE = 4096;
+    
+    /**
+     * Add the SafeObjectInputStream if necessary
+     * @param in
+     * @return
+     */
+    public static SafeObjectInputStream install(ObjectInput in) {
+        if (in instanceof SafeObjectInputStream) {
+            return (SafeObjectInputStream) in;
+        }
+        return new SafeObjectInputStream(in);
+    }
+    
+    
+    /**
+     * Intentionally private.  Callers should use the install method to add the SafeObjectInputStream
+     * into the stream.
+     * @param in
+     */
+    private SafeObjectInputStream(ObjectInput in) {
+        original = in;
+        if (log.isDebugEnabled()) {
+            this.in = new DebugObjectInput(original);
+        } else {
+            this.in = original;
+        }
+    }
+    
+    //  Delegated Methods
+    public int available() throws IOException {
+        return in.available();
+    }
+    public void close() throws IOException {
+        in.close();
+    }
+    public int read() throws IOException {
+        return in.read();
+    }
+    public int read(byte[] b, int off, int len) throws IOException {
+        return in.read(b, off, len);
+    }
+    public int read(byte[] b) throws IOException {
+        return in.read(b);
+    }
+    public boolean readBoolean() throws IOException {
+        return in.readBoolean();
+    }
+    public byte readByte() throws IOException {
+        return in.readByte();
+    }
+    public char readChar() throws IOException {
+        return in.readChar();
+    }
+    public double readDouble() throws IOException {
+        return in.readDouble();
+    }
+    public float readFloat() throws IOException {
+        return in.readFloat();
+    }
+    public void readFully(byte[] b, int off, int len) throws IOException {
+        in.readFully(b, off, len);
+    }
+    public void readFully(byte[] b) throws IOException {
+        in.readFully(b);
+    }
+    public int readInt() throws IOException {
+        return in.readInt();
+    }
+    public String readLine() throws IOException {
+        return in.readLine();
+    }
+    public long readLong() throws IOException {
+        return in.readLong();
+    }
+    public Object readObject() throws ClassNotFoundException, IOException {
+        return readObjectOverride();
+    }
+    public short readShort() throws IOException {
+        return in.readShort();
+    }
+    public int readUnsignedByte() throws IOException {
+        return in.readUnsignedByte();
+    }
+    public int readUnsignedShort() throws IOException {
+        return in.readUnsignedShort();
+    }
+    public String readUTF() throws IOException {
+        return in.readUTF();
+    }
+    public long skip(long n) throws IOException {
+        return in.skip(n);
+    }
+    public int skipBytes(int n) throws IOException {
+        return in.skipBytes(n);
+    }
+    
+    // Value Add Methods
+    
+    /**
+     * Read the input stream and place the key/value pairs in a hashmap
+     * @return HashMap or null
+     * @throws IOException
+     * @see SafeObjectOutputStream.writeMap()
+     */
+    public HashMap readHashMap() throws IOException  {
+        HashMap hashMap = new HashMap();
+        return (HashMap) readMap(hashMap);
+    }
+    
+    /**
+     * Read the input stream and place the key/value pairs in the
+     * indicated Map
+     * @param map input map
+     * @return map or null
+     * @throws IOException
+     * @see SafeObjectOutputStream.writeMap()
+     */
+    public Map readMap(Map map) throws IOException {
+        boolean isActive = in.readBoolean();
+        if (!isActive) {
+            return null;
+        }
+        
+        while (in.readBoolean()) {
+            Object key = null;
+            Object value = null;
+
+            boolean isObjectForm = in.readBoolean();
+            try {
+                if (isObjectForm) {
+                    if (isDebug) {
+                        log.debug(" reading using object form");
+                    }
+                    // Read the key and value directly
+                    key = in.readObject();
+                    value = in.readObject();
+                } else {
+                    if (isDebug) {
+                        log.debug(" reading using byte form");
+                    }
+                    // Get the byte stream
+                    ByteArrayInputStream bais = getByteStream(in);
+
+                    // Now get the real key and value
+                    ObjectInputStream tempOIS = createObjectInputStream(bais);
+                    key = tempOIS.readObject();
+                    value = tempOIS.readObject();
+                    tempOIS.close();
+                    bais.close();
+                }
+                // Put the key and value in the map
+                if (isDebug) {
+                    log.debug("Read key=" + key + " value="+value);
+                }
+                map.put(key, value);
+            } catch (ClassNotFoundException e) {
+                // Swallow the error and try to continue
+                log.error(e);
+            } catch (IOException e) {
+                throw e;
+            } catch (Exception e) {
+                throw AxisFault.makeFault(e);
+            }
+        }
+        return map;
+    }
+    
+    /**
+     * Read the input stream and place objects in an ArrayList
+     * @return ArrayList or null
+     * @throws IOException
+     * @see SafeObjectInputStream.writeList()
+     */
+    public ArrayList readArrayList() throws IOException {
+        List ll = new ArrayList();
+        return (ArrayList) readList(ll);
+    }
+    
+    /**
+     * Read the input stream and place objects in a LinkedList
+     * @return LinkedList or null
+     * @throws IOException
+     * @see SafeObjectInputStream.writeList()
+     */
+    public LinkedList readLinkedList() throws IOException {
+       List ll = new LinkedList();
+       return (LinkedList) readList(ll);
+    }
+    
+    /**
+     * Read hte input stream and place objects in the specified List
+     * @param list List
+     * @return List or null
+     * @throws IOException
+     * @see SafeObjectInputStream.writeList()
+     */
+    public List readList(List list) throws IOException {
+        boolean isActive = in.readBoolean();
+        if (!isActive) {
+            return null;
+        }
+        
+        while (in.readBoolean()) {
+            Object value;
+            boolean isObjectForm = in.readBoolean();
+            try {
+                if (isObjectForm) {
+                    if (isDebug) {
+                        log.debug(" reading using object form");
+                    }
+                    // Read the value directly
+                    value = in.readObject();
+                } else {
+                    if (isDebug) {
+                        log.debug(" reading using byte form");
+                    }
+                    // Get the byte stream
+                    ByteArrayInputStream bais = getByteStream(in);
+
+                    // Now get the real key and value
+                    ObjectInputStream tempOIS = createObjectInputStream(bais);
+                    value = tempOIS.readObject();
+                    tempOIS.close();
+                    bais.close();
+                }
+                // Put the key and value in the map
+                if (isDebug) {
+                    log.debug("Read value="+value);
+                }
+                list.add(value);
+            } catch (IOException e) {
+                throw e;
+            } catch (Exception e) {
+                throw AxisFault.makeFault(e);
+            }
+        }
+        return list;
+    }
+    
+    /**
+     * Reads the object using the same format that was written.
+     * 
+     * EXPECTED FORMATS
+     *   boolean=false
+     *   return null Object
+     *   
+     *   
+     *   boolean=true
+     *   boolean=true
+     *   return Object read from inputStream
+     *   
+     *   
+     *   boolean=true
+     *   boolean=false
+     *   int=nuber of bytes
+     *   bytes
+     *   return Object read from bytes
+     *   
+     * @return Object or null
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    private Object readObjectOverride() throws IOException, ClassNotFoundException {
+        boolean isActive = in.readBoolean();
+        if (!isActive) {
+            if (isDebug) {
+                log.debug("Read object=null");
+            }
+            return null;
+        }
+        Object obj = null;
+        boolean isObjectForm = in.readBoolean();
+        if (isObjectForm) {
+            // Read the object directly
+            if (isDebug) {
+                log.debug(" reading using object form");
+            }
+            obj = in.readObject();
+        } else {
+            if (isDebug) {
+                log.debug(" reading using byte form");
+            }
+            // Get the byte stream
+            ByteArrayInputStream bais = getByteStream(in);
+
+            // Now get the real object
+            ObjectInputStream tempOIS = createObjectInputStream(bais);
+            obj = tempOIS.readObject();
+            tempOIS.close();
+            bais.close();
+        }
+        
+        if (isDebug) {
+            log.debug("Read object=" + obj);
+        }
+        return obj;   
+        
+    }
+    
+    /**
+     * Get the byte stream for an object that is written using the 
+     * byte format.  
+     * EXPECTED format
+     *     int (number of bytes)
+     *     bytes 
+     *     
+     * @param in
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    private ByteArrayInputStream getByteStream(ObjectInput in) throws IOException, ClassNotFoundException {
+        
+        // Read the length
+        int size = in.readInt();
+        
+        // Fill our temporary buffer
+        if (buffer == null || buffer.length < size) {
+            int allocSize = (size > BUFFER_MIN_SIZE) ? size : BUFFER_MIN_SIZE;
+            buffer = new byte[allocSize];
+        }
+        in.readFully(buffer, 0, size);
+        
+        // Return a stream backed by the buffer
+        return new ByteArrayInputStream(buffer, 0, size);
+    }
+    
+    private ObjectInputStream createObjectInputStream(InputStream is) throws IOException {
+        // The created ObjectInputStream must use the same class/object resolution 
+        // code that is used by the original ObjectInput
+        return new ObjectInputStreamWithCL(is);
+    }
+}

Added: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectOutputStream.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectOutputStream.java?rev=577367&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectOutputStream.java (added)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeObjectOutputStream.java Wed Sep 19 09:56:47 2007
@@ -0,0 +1,757 @@
+/*
+ * Copyright 2004,2007 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.axis2.context.externalize;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamConstants;
+import java.io.Serializable;
+import java.io.ObjectOutputStream.PutField;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A SafeObjectOutputStream provides extra mechanisms to ensure that 
+ * objects can be safely serialized to the ObjectOutput.
+ * 
+ * If an Object is written to a normal ObjectOutput, the ObjectOutput is left in 
+ * an unknown state if a NotSerializableException occurs.
+ * 
+ * The SafeObjectOutputStream does some additonal checking to ensure that the Object can
+ * be safely written.  If the Object is suspicious, it is first written to a buffer to ensure
+ * that the underlying ObjectOutput is not corrupted.
+ * 
+ * In addition, SafeObjectOutputStream provides extra methods to write containers of Objects.
+ * For example the writeMap object will write the key and value pairs that are can be serialized.
+ * 
+ * @see SafeObjectInputStream
+ *
+ */
+public class SafeObjectOutputStream implements Closeable, Flushable, ObjectOutput,
+        ObjectStreamConstants, ExternalizeConstants {
+    
+    private static final Log log = LogFactory.getLog(SafeObjectOutputStream.class);
+    private static final boolean isDebug = log.isDebugEnabled();
+    
+    // Actual Stream 
+    private ObjectOutput out = null;
+    
+    // There are two ways to write out an object, a series of bytes or an Object.  
+    // These flags are embedded in the stream so that the reader knows which form was used.
+    private static final boolean FORM_BYTE = false;
+    private static final boolean FORM_OBJECT = true;
+    
+    // Temporary ObjectOutputStream for
+    MyOOS tempOOS = null;
+    
+    // As a way to improve performance and reduce trace logging with
+    // extra exceptions, keep a table of classes that are not serializable
+    // and only log the first time it that the class is encountered in
+    // an NotSerializableException
+    // note that the Hashtable is synchronized by Java so we shouldn't need to 
+    // do extra control over access to the table
+    public static Hashtable notSerializableList = new Hashtable();
+    
+    /**
+     * Add the SafeOutputStream if necessary.
+     * @param out Current ObjectOutput
+     * @return
+     * @throws IOException
+     */
+    public static SafeObjectOutputStream install(ObjectOutput out) throws IOException {
+        if (out instanceof SafeObjectOutputStream) {
+            return (SafeObjectOutputStream) out;
+        }
+        return new SafeObjectOutputStream(out);
+    }
+    
+    /**
+     * Intentionally private.  
+     * Callers should use the install method to add the SafeObjectOutputStream
+     * into the stream.
+     * @param oo
+     * @throws IOException
+     */
+    private SafeObjectOutputStream(ObjectOutput oo) throws IOException {
+        if (log.isDebugEnabled()) {
+            this.out = new DebugObjectOutputStream(oo);
+        } else {
+            this.out = oo;
+        }
+    }
+
+    // START DELEGATE METHODS
+    public void close() throws IOException {
+        if (tempOOS != null) {
+            tempOOS.close();
+            tempOOS = null;
+        }
+        out.close();
+    }
+
+    public void defaultWriteObject() throws IOException {
+        if (out instanceof ObjectOutputStream) {
+            ((ObjectOutputStream)out).defaultWriteObject();
+        }
+    }
+
+    public boolean equals(Object o) {
+        return out.equals(o);
+    }
+
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    public int hashCode() {
+        return out.hashCode();
+    }
+
+    public PutField putFields() throws IOException {
+        if (out instanceof ObjectOutputStream) {
+            return ((ObjectOutputStream)out).putFields();
+        } else {
+            throw new IOException("This method is not supported.");
+        }
+            
+    }
+
+    public void reset() throws IOException {
+        if (out instanceof ObjectOutputStream) {
+            ((ObjectOutputStream)out).reset();
+        }
+    }
+
+    public String toString() {
+        return out.toString();
+    }
+
+    public void useProtocolVersion(int version) throws IOException {
+        if (out instanceof ObjectOutputStream) {
+            ((ObjectOutputStream)out).useProtocolVersion(version);
+        }
+    }
+
+    public void write(byte[] buf, int off, int len) throws IOException {
+        out.write(buf, off, len);
+    }
+
+    public void write(byte[] buf) throws IOException {
+        out.write(buf);
+    }
+
+    public void write(int val) throws IOException {
+        out.write(val);
+    }
+
+    public void writeBoolean(boolean val) throws IOException {
+        out.writeBoolean(val);
+    }
+
+    public void writeByte(int val) throws IOException {
+        out.writeByte(val);
+    }
+
+    public void writeBytes(String str) throws IOException {
+        out.writeBytes(str);
+    }
+
+    public void writeChar(int val) throws IOException {
+        out.writeChar(val);
+    }
+
+    public void writeChars(String str) throws IOException {
+        out.writeChars(str);
+    }
+
+    public void writeDouble(double val) throws IOException {
+        out.writeDouble(val);
+    }
+
+    public void writeFields() throws IOException {
+        if (out instanceof ObjectOutputStream) {
+            ((ObjectOutputStream)out).writeFields();
+        }
+    }
+
+    public void writeFloat(float val) throws IOException {
+        out.writeFloat(val);
+    }
+
+    public void writeInt(int val) throws IOException {
+        out.writeInt(val);
+    }
+
+    public void writeLong(long val) throws IOException {
+        out.writeLong(val);
+    }
+
+    public void writeObject(Object obj) throws IOException {
+        writeObject(obj, false);  // Assume object is not safe
+    }
+
+    public void writeShort(int val) throws IOException {
+        out.writeShort(val);
+    }
+
+    public void writeUTF(String str) throws IOException {
+        out.writeUTF(str);
+    }
+
+    // END DELEGATE METHODS
+    
+    /**
+     * Write a map
+     * 
+     * FORMAT for null map
+     *     EMPTY_OBJECT
+     *     
+     * FORMAT for non-empty map
+     *     ACTIVE_OBJECT
+     *     for each contained key value pair
+     *        writePair
+     *     EMPTY_OBJECT (indicates end of the list
+     * 
+     * @param ll
+     * @return
+     * @throws IOException
+     */
+    public boolean writeMap(Map map) throws IOException {
+        
+        if (map == null) {
+            out.writeBoolean(EMPTY_OBJECT);
+            return false;
+        } else {
+            out.writeBoolean(ACTIVE_OBJECT);
+            // TODO Change to entry set to avoid second lookup
+            Iterator it = map.keySet().iterator();
+
+            while (it.hasNext()) {
+                Object key = it.next();
+                Object value = map.get(key);
+                writePair(key, false, value, false);
+            }
+            // Empty object indicates end of list
+            out.writeBoolean(EMPTY_OBJECT);
+        }
+        return true;
+    }
+    
+    /**
+     * Write a list.
+     * 
+     * FORMAT for null list
+     *     EMPTY_OBJECT
+     *     
+     * FORMAT for non-empty list
+     *     ACTIVE_OBJECT
+     *     for each contained object
+     *        ACTOVE_OBJECT
+     *        writeObject
+     *     EMPTY_OBJECT (indicates end of the list
+     * 
+     * @param ll
+     * @return
+     * @throws IOException
+     */
+    public boolean writeList(List al) throws IOException {
+        if (al == null) {
+            out.writeBoolean(EMPTY_OBJECT);
+            return false;
+        } else {
+            out.writeBoolean(ACTIVE_OBJECT);
+            Iterator it = al.iterator();
+
+            while (it.hasNext()) {
+                Object value = it.next();
+                writeItem(value, false);
+            }
+            // Empty object indicates end of list
+            out.writeBoolean(EMPTY_OBJECT);
+        }
+        return true;
+    }
+    
+    /**
+     * Writes an object to the stream.
+     * If the object is known (apriori) to be completely serializable it
+     * is "safe".  Safe objects are written directly to the stream.
+     * Objects that are not known are to be safe are tested for safety and 
+     * only written if they are deemed safe.  Unsafe objects are not written.
+     * Note: The java.io.ObjectOutputStream is left in an unrecoverable state
+     * if any object written to it causes a serialization error.  So please
+     * use the isSafe parameter wisely
+     * 
+     * FORMAT for NULL Object
+     *    EMPTY_OBJECT
+     *   
+     * FORMAT for non-serializable Object
+     *    EMPTY_OBJECT
+     *    
+     * FORMAT for safe serializable Object
+     *    ACTIVE_OBJECT
+     *    FORM_OBJECT
+     *    Object
+     *    
+     * FORMAT for other serializable Object
+     *    ACTIVE_OBJECT
+     *    FORM_BYTE
+     *    length of bytes
+     *    bytes representing the object
+     *   
+     * @param obj
+     * @param isSafe true if you know that object can be safely serialized. false if the 
+     * object needs to be tested for serialization.
+     * @returns true if written
+     * @throws IOException
+     */
+    private boolean writeObject(Object obj, 
+                       boolean isSafe) throws IOException {
+        
+        if (isDebug) {
+            log.debug("Writing object:" + obj);
+        }
+        
+        // Shortcut for null objects
+        if (obj == null) {
+            out.writeBoolean(EMPTY_OBJECT);
+            return false;
+        }
+        // Shortcut for non-serializable objects
+        if (!isSafe) {
+            if (!isSerializable(obj)) {
+                out.writeBoolean(EMPTY_OBJECT);
+                return false;
+            }
+        }
+        
+        // If not safe, see if there are characteristics of the Object
+        // that guarantee that it can be safely serialized 
+        // (for example Strings are always serializable)
+        if (!isSafe) {
+            isSafe = isSafeSerializable(obj);
+        }
+        if (isSafe) {
+            // Use object form
+            if (isDebug) {
+                log.debug("  write using object form");
+            }
+            out.writeBoolean(ACTIVE_OBJECT);  
+            out.writeBoolean(FORM_OBJECT);
+            out.writeObject(obj);
+        } else {
+
+            // Fall-back to byte form
+            if (isDebug) {
+                log.debug("  write using byte form");
+            }
+            MyOOS tempOOS;
+            try {
+                tempOOS = writeTempOOS(obj);
+            } catch (IOException e) {
+                // Put a EMPTY object in the file
+                out.writeBoolean(EMPTY_OBJECT);
+                throw e;
+            }
+            if (tempOOS == null) {
+                out.writeBoolean(EMPTY_OBJECT);
+                return false;
+            } 
+
+            out.writeBoolean(ACTIVE_OBJECT);   
+            out.writeBoolean(FORM_BYTE);
+            tempOOS.write(out);
+            resetOnSuccess();
+        }
+        return true;
+    }
+    
+    
+    /**
+     * Writes pair of objects to the stream.
+     * 
+     * If the objects are known (apriori) to be completely serializable they 
+     * are "safe".  Safe objects are written directly to the stream.
+     * Objects that are not known are to be safe are tested for safety and 
+     * only written if they are deemed safe.  Unsafe objects are not written.
+     * Note: The java.io.ObjectOutputStream is left in an unrecoverable state
+     * if any object written to it causes a serialization error.  So please
+     * use the isSafe parameter wisely
+     * 
+     *   
+     * FORMAT for non-serializable key/value pair
+     *    nothing is written
+     *    
+     * FORMAT for safe serializable key/value pair
+     *    ACTIVE_OBJECT
+     *    FORM_OBJECT
+     *    Object
+     *    
+     * FORMAT for other serializable key/value pair
+     *    ACTIVE_OBJECT
+     *    FORM_BYTE
+     *    length of bytes
+     *    bytes representing the object
+     *    
+     * @param obj1
+     * @param isSafe1 true if you know that object can be safely serialized. false if the 
+     * object needs to be tested for serialization.
+     * @param obj2
+     * @param isSafe2 true if you know that object can be safely serialized. false if the 
+     * object needs to be tested for serialization.
+     * @returns true if both are written to the stream
+     * @throws IOException
+     */
+    public boolean writePair(Object obj1, 
+                       boolean isSafe1, 
+                       Object obj2,
+                       boolean isSafe2) throws IOException {
+        
+        if (isDebug) {
+            log.debug("Writing key=" + obj1 + " value="+obj2);
+        }
+        // Shortcut for non-serializable objects
+        if ((!isSafe1 && !isSerializable(obj1)) ||
+             (!isSafe2 && !isSerializable(obj2))) {
+            return false;
+        }
+
+        boolean isSafe = (isSafe1 || isSafeSerializable(obj1)) && 
+            (isSafe2 || isSafeSerializable(obj2));
+
+        if (isSafe) {
+            if (isDebug) {
+                log.debug("  write using object form");
+            }
+            out.writeBoolean(ACTIVE_OBJECT);
+            out.writeBoolean(FORM_OBJECT);
+            out.writeObject(obj1);
+            out.writeObject(obj2);
+        } else {
+            if (isDebug) {
+                log.debug("  write using byte form");
+            }
+            MyOOS tempOOS = writeTempOOS(obj1, obj2);
+            if (tempOOS == null) {
+                return false;
+            } 
+            out.writeBoolean(ACTIVE_OBJECT);
+            out.writeBoolean(FORM_BYTE);
+            tempOOS.write(out);
+            resetOnSuccess();
+        }
+        return true;
+    }
+    
+    /**
+     * Writes pair of objects to the stream.
+     * 
+     * If the objects are known (apriori) to be completely serializable they 
+     * are "safe".  Safe objects are written directly to the stream.
+     * Objects that are not known are to be safe are tested for safety and 
+     * only written if they are deemed safe.  Unsafe objects are not written.
+     * Note: The java.io.ObjectOutputStream is left in an unrecoverable state
+     * if any object written to it causes a serialization error.  So please
+     * use the isSafe parameter wisely
+     * 
+     *   
+     * FORMAT for non-serializable key/value pair
+     *    nothing is written
+     *    
+     * FORMAT for safe serializable key/value pair
+     *    ACTIVE_OBJECT
+     *    FORM_OBJECT
+     *    Object
+     *    
+     * FORMAT for other serializable key/value pair
+     *    ACTIVE_OBJECT
+     *    FORM_BYTE
+     *    length of bytes
+     *    bytes representing the object
+     *    
+     * @param obj1
+     * @param isSafe1 true if you know that object can be safely serialized. false if the 
+     * object needs to be tested for serialization.
+     * @param obj2
+     * @param isSafe2 true if you know that object can be safely serialized. false if the 
+     * object needs to be tested for serialization.
+     * @returns true if both are written to the stream
+     * @throws IOException
+     */
+    public boolean writeItem(Object obj, 
+                       boolean isSafe) throws IOException {
+        
+        if (isDebug) {
+            log.debug("Writing obj=" + obj);
+        }
+        // Shortcut for non-serializable objects
+        if (!isSafe && !isSerializable(obj)) {
+            return false;
+        }
+
+        isSafe = (isSafe || isSafeSerializable(obj));
+
+        if (isSafe) {
+            if (isDebug) {
+                log.debug("  write using object form");
+            }
+            out.writeBoolean(ACTIVE_OBJECT);
+            out.writeBoolean(FORM_OBJECT);
+            out.writeObject(obj);
+        } else {
+            if (isDebug) {
+                log.debug("  write using byte form");
+            }
+            MyOOS tempOOS;
+            try {
+                tempOOS = writeTempOOS(obj);
+            } catch (RuntimeException e) {
+                return false;
+            }
+            if (tempOOS == null) {
+                return false;
+            } 
+            out.writeBoolean(ACTIVE_OBJECT);
+            out.writeBoolean(FORM_BYTE);
+            tempOOS.write(out);
+            resetOnSuccess();
+        }
+        return true;
+    }
+    
+    /**
+     * Does a quick check of the implemented interfaces to ensure that this 
+     * object is serializable
+     * @return true if the object is marked as Serializable 
+     */
+    private static boolean isSerializable(Object obj) {
+        boolean isSerializable = (obj == null) || obj instanceof Serializable;
+        if (!isSerializable) {
+            markNotSerializable(obj);
+        }
+        return isSerializable;
+    }
+    
+    /**
+     * Does a quick check of the implemented class is safe to serialize without 
+     * buffering.
+     * @return true if the object is marked as safe.
+     */
+    private static boolean isSafeSerializable(Object obj) {
+        
+        boolean isSafeSerializable = (obj == null) || 
+            obj instanceof SafeSerializable ||
+            obj instanceof String ||
+            obj instanceof Integer ||
+            obj instanceof Boolean ||
+            obj instanceof Long;
+        return isSafeSerializable;
+    }
+    
+    
+    /**
+     * Write the object to a temporary ObjectOutput
+     * @param obj
+     * @return ObjectOutput if successful
+     */
+    private MyOOS writeTempOOS(Object obj) throws IOException {
+        MyOOS oos = null;
+        
+        try {
+            oos = getTempOOS();
+            oos.writeObject(obj);
+            oos.flush();
+        } catch (NotSerializableException nse2) {
+            markNotSerializable(obj);
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+            throw nse2;
+        } catch (IOException e) {
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+            throw e;
+        } catch (RuntimeException e) {
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+            throw e;
+        }
+        return oos;
+    }
+    
+    /**
+     * Write the objects to a temporary ObjectOutput
+     * @param obj1
+     * @param obj2
+     * @return ObjectOutput if successful
+     */
+    private MyOOS writeTempOOS(Object obj1, Object obj2) throws IOException {
+        MyOOS oos = null;
+        boolean first = true;
+        try {
+            oos = getTempOOS();
+            oos.writeObject(obj1);
+            first = false;
+            oos.writeObject(obj2);
+            oos.flush();
+        } catch (NotSerializableException nse2) {
+            // This is okay and expected in some cases.
+            // Log the error and continue
+            markNotSerializable((first) ? obj1 :obj2);
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+        } catch (IOException e) {
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+            throw e;
+        } catch (RuntimeException e) {
+            if (oos != null) {
+                resetOnFailure();
+                oos = null;
+            }
+            throw e;
+        }
+        return oos;
+    }
+    
+    /**
+     * Get or create a temporary ObjectOutputStream
+     * @return MyOOS
+     * @throws IOException
+     */
+    private MyOOS getTempOOS() throws IOException {
+        if (tempOOS == null) {
+            tempOOS = new MyOOS(new MyBAOS());
+        }
+        return tempOOS;
+    }
+    
+    /**
+     * If a failure occurs, reset the temporary ObjectOutputStream
+     */
+    private void resetOnFailure() throws IOException {
+        if (tempOOS != null) {
+            tempOOS.close();
+            tempOOS = null;  // The ObjectOutput is in an unknown state and thus discarded
+        }
+    }
+    
+    /**
+     * Reset the temporary ObjectOutputStream
+     * @throws IOException
+     */
+    private void resetOnSuccess() throws IOException {
+        tempOOS.reset();
+    }
+    
+    private static void markNotSerializable(Object obj) {
+        if (!isDebug) {
+            return;
+        }
+        if (obj != null) {
+            String name = obj.getClass().getCanonicalName();
+            Object value = notSerializableList.get(name);
+            if (value == null) {
+                notSerializableList.put(name, name);
+                if (log.isTraceEnabled()) {
+                    log.trace("***NotSerializableException*** [" + name + "]");
+                }
+            }
+        }
+    }
+    
+    /**
+     * MyBAOS is a ByteArrayOutputStream with a few additions.
+     *
+     */
+    class MyBAOS extends ByteArrayOutputStream {
+        /**
+         * Return direct access to the buffer without creating a copy of the byte[]
+         * @return buf
+         */
+        public byte[] getBytes() {
+            return buf;
+        }
+        /**
+         * Reset to a specific index in the buffer
+         * @param count
+         */
+        public void reset(int count) {
+            this.count = count;
+        }
+    }
+    
+    
+    /**
+     * MyOOS is an ObjectOutputStream with a few performant additions.
+     *
+     */
+    class MyOOS extends ObjectOutputStream {
+        MyBAOS baos;
+        int dataOffset;
+        MyOOS(MyBAOS baos) throws IOException {
+            super(baos);
+            flush();
+            this.baos = baos;
+            
+            // Capture the data offset 
+            // (the location where data starts..which is after header information)
+            dataOffset = baos.size();
+        }
+        
+        /**
+         * Override the reset so that we can reset to the data offset.
+         */
+        public void reset() throws IOException {
+            super.reset();
+            // Reset the byte stream to the position past the headers
+            baos.reset(dataOffset);
+        }
+        
+        /**
+         * Write the contents of MyOOS to the indicated ObjectOutput.
+         * Note that this direct write avoids any byte[] buffer creation
+         * @param out
+         * @throws IOException
+         */
+        public void write(ObjectOutput out) throws IOException {
+            out.flush();
+            // out.writeObject(out.toByteArray());
+            out.writeInt(baos.size());
+            out.write(baos.getBytes(),0,baos.size());
+        }
+    }
+
+}

Added: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeSerializable.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeSerializable.java?rev=577367&view=auto
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeSerializable.java (added)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/context/externalize/SafeSerializable.java Wed Sep 19 09:56:47 2007
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004,2007 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.axis2.context.externalize;
+
+import java.io.Serializable;
+
+/**
+ * This is a marker interface that indicates that the Object
+ * and all contents of the Object can be serialized safely without
+ * buffering. 
+ * 
+ * @See SafeObjectOutputStream
+ *
+ */
+public interface SafeSerializable extends Serializable {
+
+}

Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/Parameter.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/Parameter.java?rev=577367&r1=577366&r2=577367&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/Parameter.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/Parameter.java Wed Sep 19 09:56:47 2007
@@ -18,12 +18,16 @@
 import org.apache.axiom.om.OMAbstractFactory;
 import org.apache.axiom.om.OMElement;
 import org.apache.axiom.om.impl.builder.StAXOMBuilder;
-import org.apache.axis2.util.ObjectStateUtils;
+import org.apache.axis2.context.externalize.ExternalizeConstants;
+import org.apache.axis2.context.externalize.SafeObjectInputStream;
+import org.apache.axis2.context.externalize.SafeObjectOutputStream;
+import org.apache.axis2.context.externalize.SafeSerializable;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamReader;
+
 import java.io.ByteArrayInputStream;
 import java.io.Externalizable;
 import java.io.IOException;
@@ -34,7 +38,7 @@
 /**
  * Class Parameter
  */
-public class Parameter implements Externalizable {
+public class Parameter implements Externalizable, SafeSerializable {
 
     /*
      * setup for logging
@@ -62,9 +66,9 @@
      * Refer to the writeExternal() and readExternal() methods.
      */
     // supported revision levels, add a new level to manage compatible changes
-    private static final int REVISION_1 = 1;
+    private static final int REVISION_2 = 2;
     // current revision level of this object
-    private static final int revisionID = REVISION_1;
+    private static final int revisionID = REVISION_2;
 
 
     /**
@@ -261,7 +265,8 @@
      * @param out The stream to write the object contents to
      * @throws IOException
      */
-    public void writeExternal(ObjectOutput out) throws IOException {
+    public void writeExternal(ObjectOutput o) throws IOException {
+        SafeObjectOutputStream out = SafeObjectOutputStream.install(o);
         // write out contents of this object
 
         //---------------------------------------------------------
@@ -281,8 +286,7 @@
 
         out.writeInt(type);
         out.writeBoolean(locked);
-
-        ObjectStateUtils.writeString(out, name, "Parameter.name");
+        out.writeObject(name);
 
         //---------------------------------------------------------
         // object fields
@@ -297,13 +301,8 @@
         if (parameterElement != null) {
             tmp = parameterElement.toString();
         }
-
-        // treat as an object, don't do UTF
-        ObjectStateUtils.writeObject(out, tmp, "Parameter.parameterElement");
-
-        // TODO: error handling if this can't be serialized
-        ObjectStateUtils.writeObject(out, value, "Parameter.value");
-
+        out.writeObject(tmp); // parameterElement
+        out.writeObject(value);
     }
 
 
@@ -318,7 +317,8 @@
      * @throws IOException
      * @throws ClassNotFoundException
      */
-    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+    public void readExternal(ObjectInput inObject) throws IOException, ClassNotFoundException {
+        SafeObjectInputStream in = SafeObjectInputStream.install(inObject);
         // trace point
         if (log.isTraceEnabled()) {
             log.trace(myClassName + ":readExternal():  BEGIN  bytes available in stream [" +
@@ -333,12 +333,12 @@
 
         // make sure the object data is in a version we can handle
         if (suid != serialVersionUID) {
-            throw new ClassNotFoundException(ObjectStateUtils.UNSUPPORTED_SUID);
+            throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_SUID);
         }
 
         // make sure the object data is in a revision level we can handle
-        if (revID != REVISION_1) {
-            throw new ClassNotFoundException(ObjectStateUtils.UNSUPPORTED_REVID);
+        if (revID != REVISION_2) {
+            throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_REVID);
         }
 
         //---------------------------------------------------------
@@ -347,8 +347,7 @@
 
         type = in.readInt();
         locked = in.readBoolean();
-
-        name = ObjectStateUtils.readString(in, "Parameter.name");
+        name = (String) in.readObject();
 
         //---------------------------------------------------------
         // object fields
@@ -359,7 +358,7 @@
         // to a String but will build the OMTree in the memory
 
         // treat as an object, don't do UTF
-        String tmp = (String) ObjectStateUtils.readObject(in, "Parameter.parameterElement");
+        String tmp = (String) in.readObject();
 
         // convert to an OMElement
         if (tmp != null) {
@@ -389,7 +388,7 @@
         }
 
         // TODO: error handling if this can't be serialized
-        value = ObjectStateUtils.readObject(in, "Parameter.value");
+        value = in.readObject();
 
         //---------------------------------------------------------
         // done

Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/ParameterIncludeImpl.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/ParameterIncludeImpl.java?rev=577367&r1=577366&r2=577367&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/ParameterIncludeImpl.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/description/ParameterIncludeImpl.java Wed Sep 19 09:56:47 2007
@@ -23,12 +23,16 @@
 import org.apache.axiom.om.OMAttribute;
 import org.apache.axiom.om.OMElement;
 import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.externalize.ExternalizeConstants;
+import org.apache.axis2.context.externalize.SafeObjectInputStream;
+import org.apache.axis2.context.externalize.SafeObjectOutputStream;
+import org.apache.axis2.context.externalize.SafeSerializable;
 import org.apache.axis2.deployment.DeploymentConstants;
-import org.apache.axis2.util.ObjectStateUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import javax.xml.namespace.QName;
+
 import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
@@ -41,7 +45,8 @@
 /**
  * Class ParameterIncludeImpl
  */
-public class ParameterIncludeImpl implements ParameterInclude, Externalizable {
+public class ParameterIncludeImpl 
+    implements ParameterInclude, Externalizable, SafeSerializable {
 
     /*
      * setup for logging
@@ -69,9 +74,9 @@
      * Refer to the writeExternal() and readExternal() methods.
      */
     // supported revision levels, add a new level to manage compatible changes
-    private static final int REVISION_1 = 1;
+    private static final int REVISION_2 = 2;
     // current revision level of this object
-    private static final int revisionID = REVISION_1;
+    private static final int revisionID = REVISION_2;
 
 
     /**
@@ -204,7 +209,8 @@
      * @param out The stream to write the object contents to
      * @throws IOException
      */
-    public void writeExternal(ObjectOutput out) throws IOException {
+    public void writeExternal(ObjectOutput o) throws IOException {
+        SafeObjectOutputStream out = SafeObjectOutputStream.install(o);
         // write out contents of this object
 
         //---------------------------------------------------------
@@ -221,7 +227,7 @@
         //---------------------------------------------------------
         // collection of parameters
         //---------------------------------------------------------
-        ObjectStateUtils.writeHashMap(out, parameters, "ParameterIncludeImpl.parameters");
+        out.writeMap(parameters);
 
     }
 
@@ -237,7 +243,8 @@
      * @throws IOException
      * @throws ClassNotFoundException
      */
-    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+    public void readExternal(ObjectInput inObject) throws IOException, ClassNotFoundException {
+        SafeObjectInputStream in = SafeObjectInputStream.install(inObject);
         // trace point
         if (log.isTraceEnabled()) {
             log.trace(myClassName + ":readExternal():  BEGIN  bytes available in stream [" +
@@ -252,35 +259,22 @@
 
         // make sure the object data is in a version we can handle
         if (suid != serialVersionUID) {
-            throw new ClassNotFoundException(ObjectStateUtils.UNSUPPORTED_SUID);
+            throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_SUID);
         }
 
         // make sure the object data is in a revision level we can handle
-        if (revID != REVISION_1) {
-            throw new ClassNotFoundException(ObjectStateUtils.UNSUPPORTED_REVID);
+        if (revID != REVISION_2) {
+            throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_REVID);
         }
 
         //---------------------------------------------------------
         // collection of parameters
         //---------------------------------------------------------
-
-        HashMap tmp = ObjectStateUtils.readHashMap(in, "ParameterIncludeImpl.parameters");
-
-        if (tmp != null) {
-            if (parameters != null) {
-                parameters.putAll(tmp);
-            } else {
-                if (log.isTraceEnabled()) {
-                    log.trace(myClassName +
-                            ":readExternal():  WARNING: parameters doesnot have a defined HashMap ");
-                }
-            }
-        }
+        in.readMap(parameters);
 
         //---------------------------------------------------------
         // done
         //---------------------------------------------------------
-
     }
 
 }

Modified: webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/util/MetaDataEntry.java
URL: http://svn.apache.org/viewvc/webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/util/MetaDataEntry.java?rev=577367&r1=577366&r2=577367&view=diff
==============================================================================
--- webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/util/MetaDataEntry.java (original)
+++ webservices/axis2/trunk/java/modules/kernel/src/org/apache/axis2/util/MetaDataEntry.java Wed Sep 19 09:56:47 2007
@@ -19,6 +19,10 @@
 
 package org.apache.axis2.util;
 
+import org.apache.axis2.context.externalize.SafeObjectInputStream;
+import org.apache.axis2.context.externalize.SafeObjectOutputStream;
+import org.apache.axis2.context.externalize.SafeSerializable;
+
 import javax.xml.namespace.QName;
 import java.io.Externalizable;
 import java.io.IOException;
@@ -31,14 +35,14 @@
  * An internal class for holding a set of information
  * about an object.
  */
-public class MetaDataEntry implements Externalizable {
+public class MetaDataEntry implements Externalizable, SafeSerializable {
     // serialization identifier
     private static final long serialVersionUID = 8978361069526299875L;
 
     // supported revision levels, add a new level to manage compatible changes
-    private static final int REVISION_1 = 1;
+    private static final int REVISION_2 = 2;
     // current revision level of this object
-    private static final int revisionID = REVISION_1;
+    private static final int revisionID = REVISION_2;
 
     // data to keep on an object
 
@@ -252,7 +256,8 @@
      * @param out The stream to write the object contents to
      * @throws IOException
      */
-    public void writeExternal(ObjectOutput out) throws IOException {
+    public void writeExternal(ObjectOutput o) throws IOException {
+        SafeObjectOutputStream out = SafeObjectOutputStream.install(o);
         // write out contents of this object
 
         //---------------------------------------------------------
@@ -269,10 +274,10 @@
         //---------------------------------------------------------
         // various simple fields
         //---------------------------------------------------------
-        ObjectStateUtils.writeString(out, className, "MetaDataEntry.className");
-        ObjectStateUtils.writeString(out, qnameAsString, "MetaDataEntry.qnameAsString");
-        ObjectStateUtils.writeString(out, extraName, "MetaDataEntry.extraName");
-        ObjectStateUtils.writeArrayList(out, children, "MetaDataEntry.list");
+        out.writeObject(className);
+        out.writeObject(qnameAsString);
+        out.writeObject(extraName);
+        out.writeList(children);
 
     }
 
@@ -288,7 +293,8 @@
      * @throws IOException
      * @throws ClassNotFoundException
      */
-    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+    public void readExternal(ObjectInput inObject) throws IOException, ClassNotFoundException {
+        SafeObjectInputStream in = SafeObjectInputStream.install(inObject);
 
         // serialization version ID
         long suid = in.readLong();
@@ -302,7 +308,7 @@
         }
 
         // make sure the object data is in a revision level we can handle
-        if (revID != REVISION_1) {
+        if (revID != REVISION_2) {
             throw new ClassNotFoundException(UNSUPPORTED_REVID);
         }
 
@@ -310,10 +316,10 @@
         // various simple fields
         //---------------------------------------------------------
 
-        className = ObjectStateUtils.readString(in, "MetaDataEntry.className");
-        qnameAsString = ObjectStateUtils.readString(in, "MetaDataEntry.qnameAsString");
-        extraName = ObjectStateUtils.readString(in, "MetaDataEntry.extraName");
-        children = ObjectStateUtils.readArrayList(in, "MetaDataEntry.list");
+        className = (String) in.readObject();
+        qnameAsString = (String) in.readObject();
+        extraName = (String) in.readObject();
+        children = in.readArrayList();
 
     }
 



---------------------------------------------------------------------
To unsubscribe, e-mail: axis-cvs-unsubscribe@ws.apache.org
For additional commands, e-mail: axis-cvs-help@ws.apache.org