/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 static 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=" + valueName(key) + " value="+valueName(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 list if (isDebug) { log.debug("Read value="+valueName(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=" + valueName(obj)); } return obj; } private String valueName(Object obj) { if (obj == null) { return "null"; } else if (obj instanceof String) { return (String) obj; } else { return "Object of class = " + obj.getClass().getName(); } } /** * 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); } }