1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2000-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9package com.sleepycat.bind.serial; 10 11import java.io.ByteArrayOutputStream; 12import java.io.IOException; 13import java.io.ObjectOutputStream; 14import java.io.ObjectStreamClass; 15import java.io.ObjectStreamConstants; 16import java.io.OutputStream; 17 18import com.sleepycat.db.DatabaseException; 19import com.sleepycat.util.RuntimeExceptionWrapper; 20 21/** 22 * A specialized <code>ObjectOutputStream</code> that stores class description 23 * information in a <code>ClassCatalog</code>. It is used by 24 * <code>SerialBinding</code>. 25 * 26 * <p>This class is used instead of an {@link ObjectOutputStream}, which it 27 * extends, to write a compact object stream. For writing objects to a 28 * database normally one of the serial binding classes is used. {@link 29 * SerialOutput} is used when an {@link ObjectOutputStream} is needed along 30 * with compact storage. A {@link ClassCatalog} must be supplied, however, to 31 * stored shared class descriptions.</p> 32 * 33 * <p>The {@link ClassCatalog} is used to store class definitions rather than 34 * embedding these into the stream. Instead, a class format identifier is 35 * embedded into the stream. This identifier is then used by {@link 36 * SerialInput} to load the class format to deserialize the object.</p> 37 * 38 * @see <a href="SerialBinding.html#evolution">Class Evolution</a> 39 * 40 * @author Mark Hayes 41 */ 42public class SerialOutput extends ObjectOutputStream { 43 44 /* 45 * Serialization version constants. Instead of hardcoding these we get them 46 * by creating a SerialOutput, which itself guarantees that we'll always 47 * use a PROTOCOL_VERSION_2 header. 48 */ 49 private final static byte[] STREAM_HEADER; 50 static { 51 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 52 try { 53 new SerialOutput(baos, null); 54 } catch (IOException e) { 55 throw new RuntimeExceptionWrapper(e); 56 } 57 STREAM_HEADER = baos.toByteArray(); 58 } 59 60 private ClassCatalog classCatalog; 61 62 /** 63 * Creates a serial output stream. 64 * 65 * @param out is the output stream to which the compact serialized objects 66 * will be written. 67 * 68 * @param classCatalog is the catalog to which the class descriptions for 69 * the serialized objects will be written. 70 */ 71 public SerialOutput(OutputStream out, ClassCatalog classCatalog) 72 throws IOException { 73 74 super(out); 75 this.classCatalog = classCatalog; 76 77 /* guarantee that we'll always use the same serialization format */ 78 79 useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2); 80 } 81 82 // javadoc is inherited 83 protected void writeClassDescriptor(ObjectStreamClass classdesc) 84 throws IOException { 85 86 try { 87 byte[] id = classCatalog.getClassID(classdesc); 88 writeByte(id.length); 89 write(id); 90 } catch (DatabaseException e) { 91 /* 92 * Do not throw IOException from here since ObjectOutputStream 93 * will write the exception to the stream, which causes another 94 * call here, etc. 95 */ 96 throw new RuntimeExceptionWrapper(e); 97 } catch (ClassNotFoundException e) { 98 throw new RuntimeExceptionWrapper(e); 99 } 100 } 101 102 /** 103 * Returns the fixed stream header used for all serialized streams in 104 * PROTOCOL_VERSION_2 format. To save space this header can be removed and 105 * serialized streams before storage and inserted before deserializing. 106 * {@link SerialOutput} always uses PROTOCOL_VERSION_2 serialization format 107 * to guarantee that this header is fixed. {@link SerialBinding} removes 108 * this header from serialized streams automatically. 109 * 110 * @return the fixed stream header. 111 */ 112 public static byte[] getStreamHeader() { 113 114 return STREAM_HEADER; 115 } 116} 117