blob: d2ad38745d22e4514fb32b1b86ed54d9d6698631 [file] [log] [blame]
/*
* 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);
}
}