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