/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.serial.io;
import org.jboss.serial.objectmetamodel.DataContainer;
import org.jboss.serial.objectmetamodel.DataContainerConstants;
import org.jboss.serial.objectmetamodel.DefaultObjectDescriptorStrategy;
import org.jboss.serial.objectmetamodel.ObjectDescriptorStrategy;
import org.jboss.serial.objectmetamodel.ObjectSubstitutionInterface;
import org.jboss.serial.objectmetamodel.safecloning.SafeCloningRepository;
import org.jboss.serial.classmetamodel.ClassDescriptorStrategy;
import org.jboss.serial.classmetamodel.DefaultClassDescriptorStrategy;
import org.jboss.serial.exception.SerializationException;
import org.jboss.serial.util.StringUtilBuffer;
import org.jboss.serial.util.StringUtil;
import java.io.*;
import java.lang.reflect.Field;
/**
* @author <a href="mailto:clebert.suconic@jboss.com">Clebert Suconic</a>
*/
public class JBossObjectOutputStream extends ObjectOutputStream implements DataContainerConstants
{
OutputStream output;
DataOutputStream dataOutput;
boolean checkSerializableClass=false;
boolean standardReplacement=false;
ClassDescriptorStrategy classDescriptorStrategy = new DefaultClassDescriptorStrategy();
ObjectDescriptorStrategy objectDescriptorStrategy = new DefaultObjectDescriptorStrategy();
static Field fieldEnableReplace;
/** one of the optimizations we do is to reuse byte arrays on writeUTF operations, look at {@link org.jboss.serial.util.StringUtil}.
* StringUtil has also the capability of creating Buffers on ThreadLocal over demand, but having these buffers pre-created
* is more efficient */
StringUtilBuffer buffer;
static
{
try {
fieldEnableReplace = ObjectOutputStream.class.getDeclaredField("enableReplace");
fieldEnableReplace.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
protected ObjectSubstitutionInterface getSubstitutionInterface() throws IOException
{
try
{
if (fieldEnableReplace.getBoolean(this))
{
return new ObjectSubstitutionInterface()
{
public Object replaceObject(Object obj) throws IOException {
return JBossObjectOutputStream.this.replaceObject(obj);
}
};
} else
{
return null;
}
}
catch (IllegalAccessException ex)
{
throw new SerializationException(ex.getMessage(),ex);
}
}
/**Creates an OutputStream, that by default doesn't require */
public JBossObjectOutputStream(OutputStream output) throws IOException
{
this(output,false);
}
/**Creates an OutputStream, that by default doesn't require */
public JBossObjectOutputStream(OutputStream output,StringUtilBuffer buffer) throws IOException
{
this(output,false,buffer);
}
public JBossObjectOutputStream(OutputStream output, boolean checkSerializableClass) throws IOException
{
this(output,checkSerializableClass,null);
}
public JBossObjectOutputStream(OutputStream output, boolean checkSerializableClass,StringUtilBuffer buffer) throws IOException
{
super();
this.buffer=buffer;
this.output=output;
this.checkSerializableClass=checkSerializableClass;
writeStreamHeader();
if (output instanceof DataOutputStream)
{
dataOutput = (DataOutputStream) output;
}
else
{
dataOutput = new DataOutputStream(output);
}
}
public void writeObjectUsingDataContainer(Object obj) throws IOException {
DataContainer dataContainer = new DataContainer(null,this.getSubstitutionInterface(),checkSerializableClass,buffer,classDescriptorStrategy,objectDescriptorStrategy);
if (output instanceof DataOutputStream)
{
dataOutput = (DataOutputStream) output;
}
else
{
dataOutput = new DataOutputStream(output);
}
ObjectOutput objectOutput = dataContainer.getOutput();
objectOutput.writeObject(obj);
//objectOutput.flush();
dataContainer.saveData(dataOutput);
//this.flush();
}
protected void writeObjectOverride(Object obj) throws IOException {
DataContainer dataContainer = new DataContainer(null,this.getSubstitutionInterface(),checkSerializableClass,buffer,classDescriptorStrategy,objectDescriptorStrategy);
if (output instanceof DataOutputStream)
{
dataOutput = (DataOutputStream) output;
}
else
{
dataOutput = new DataOutputStream(output);
}
dataContainer.setStringBuffer(buffer);
ObjectOutput objectOutput = dataContainer.getDirectOutput(this.dataOutput);
objectOutput.writeObject(obj);
//objectOutput.flush();
//dataContainer.saveData(dataOutput);
//this.flush();
}
public void writeUnshared(Object obj) throws IOException {
writeObjectOverride(obj);
}
public void defaultWriteObject() throws IOException {
}
public void writeFields() throws IOException {
}
public void reset() throws IOException {
}
protected void writeStreamHeader() throws IOException {
if (output!=null)
{
output.write(openSign);
}
}
protected void writeClassDescriptor(ObjectStreamClass desc)
throws IOException
{
}
protected ClassDescriptorStrategy getClassDescriptorStrategy()
{
return classDescriptorStrategy;
}
protected void setClassDescriptorStrategy(ClassDescriptorStrategy classDescriptorStrategy)
{
this.classDescriptorStrategy = classDescriptorStrategy;
}
protected ObjectDescriptorStrategy getObjectDescriptorStrategy()
{
return objectDescriptorStrategy;
}
protected void setObjectDescriptorStrategy(ObjectDescriptorStrategy objectDescriptorStrategy)
{
this.objectDescriptorStrategy = objectDescriptorStrategy;
}
protected boolean isStandardReplacement()
{
return standardReplacement;
}
protected void setStandardReplacement(boolean standardReplacement)
{
this.standardReplacement = standardReplacement;
}
/**
* Writes a byte. This method will block until the byte is actually
* written.
*
* @param val the byte to be written to the stream
* @throws IOException If an I/O error has occurred.
*/
public void write(int val) throws IOException {
dataOutput.write(val);
}
/**
* Writes an array of bytes. This method will block until the bytes are
* actually written.
*
* @param buf the data to be written
* @throws IOException If an I/O error has occurred.
*/
public void write(byte[] buf) throws IOException {
dataOutput.write(buf);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (buf == null) {
throw new SerializationException("buf parameter can't be null");
}
dataOutput.write(buf, off, len);
}
/**
* Flushes the stream. This will write any buffered output bytes and flush
* through to the underlying stream.
*
* @throws IOException If an I/O error has occurred.
*/
public void flush() throws IOException {
if (dataOutput!=null)
{
dataOutput.flush();
}
else
{
output.flush();
}
}
protected void drain() throws IOException {
//bout.drain();
}
public void close() throws IOException {
flush();
dataOutput.close();
}
public void writeBoolean(boolean val) throws IOException {
dataOutput.writeBoolean(val);
}
public void writeByte(int val) throws IOException {
dataOutput.writeByte(val);
}
public void writeShort(int val) throws IOException {
dataOutput.writeShort(val);
}
public void writeChar(int val) throws IOException {
dataOutput.writeChar(val);
}
public void writeInt(int val) throws IOException {
dataOutput.writeInt(val);
}
public void writeLong(long val) throws IOException {
dataOutput.writeLong(val);
}
public void writeFloat(float val) throws IOException {
dataOutput.writeFloat(val);
}
public void writeDouble(double val) throws IOException {
dataOutput.writeDouble(val);
}
public void writeBytes(String str) throws IOException {
dataOutput.writeBytes(str);
}
public void writeChars(String str) throws IOException {
dataOutput.writeChars(str);
}
public void writeUTF(String str) throws IOException {
StringUtil.saveString(dataOutput,str,buffer);
}
/** Reuses every primitive value to recreate another object. */
public Object smartClone(Object obj) throws IOException
{
return smartClone(obj,null,Thread.currentThread().getContextClassLoader());
}
/** Reuses every primitive value to recreate another object. */
public Object smartClone(Object obj, SafeCloningRepository safeToReuse) throws IOException
{
return smartClone(obj,safeToReuse,Thread.currentThread().getContextClassLoader());
}
/** Reuses every primitive value to recreate another object.
* and if safeToReuse!=null, it can reuse the entire object */
public Object smartClone(Object obj, SafeCloningRepository safeToReuse, ClassLoader loader) throws IOException
{
DataContainer container = new DataContainer(loader,this.getSubstitutionInterface(),safeToReuse,checkSerializableClass,buffer);
ObjectOutput output = container.getOutput();
output.writeObject(obj);
output.flush();
ObjectInput input = container.getInput();
try
{
return input.readObject();
}
catch (ClassNotFoundException e)
{
throw new SerializationException (e.getMessage(),e);
}
}
}