001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.io.FormatIdOutputStream
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.io;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import org.apache.derby.iapi.services.info.JVMInfo;
027:
028: import java.io.DataOutputStream;
029: import java.io.IOException;
030: import java.io.ObjectOutput;
031: import java.io.ObjectOutputStream;
032: import java.io.OutputStream;
033: import java.io.Serializable;
034:
035: /**
036: A stream for serializing objects with format id tags.
037:
038: <P>An ObjectOutput (henceforth 'out') preceeds objects it writes with
039: a format id. The companion FormatIdInputStream (henceforth 'in')
040: uses these format ids in parsing the stored data. The stream
041: can be thought of as containing a sequence of (formatId,object) pairs
042: interspersed with other data. The assumption is that out.writeObject()
043: produces these pairs and in.readObject() uses the format ids to
044: construct objects from the pairs that out.writeObject produced.
045: The description below describes each supported pair and how in.readObject()
046: processes it.
047:
048: <OL>
049: <LI> (NULL_FORMAT_ID, nothing) in.readObject() returns null.
050: <LI> (SRING_FORMAT_ID, UTF8 encoded string)in.readObject reads and
051: returns this string.
052: <LI> (SERIALIZABLE_FORMAT_ID,serialized object) in.readObject() reads
053: the object using java serialization and returns it.
054: <LI> (A format id for a Storable, isNull flag and object if isNull == false)
055: (see note 1) in.readObject() reads the boolean isNull flag. If is null
056: is true, in.readObject() returns a Storable object of the correct
057: class which is null. If ifNull is false, in.readObject() restores
058: the object using its readExternal() method.
059: <LI> (A format id for a Formatable which is not Storable, the stored object)
060: (see note 1) in.readObject restores the object using its
061: readExternal() method.
062: </OL>
063:
064: <P>Note 1: The FormatIdInputStream uses
065: Monitor.newInstanceFromIdentifier(format id) to get the class.
066: <P>Note 2: An object may support more than one of the following
067: interfaces Storable, Formatable, Serializable. In this case out.writeObject
068: use the first of these interfaces which the object supports (based on the order
069: listed here) to determine how to write the object.
070: */
071: public class FormatIdOutputStream extends DataOutputStream implements
072: ObjectOutput, ErrorInfo {
073:
074: /**
075: Constructor for a FormatIdOutputStream
076:
077: @param out output goes here.
078: */
079: public FormatIdOutputStream(OutputStream out) {
080: super (out);
081: }
082:
083: /**
084: Write a format id for the object provied followed by the
085: object itself to this FormatIdOutputStream.
086:
087: @param ref a reference to the object.
088: @exception java.io.IOException the exception.
089: */
090: public void writeObject(Object ref) throws IOException {
091: if (ref == null) {
092: FormatIdUtil.writeFormatIdInteger(this ,
093: StoredFormatIds.NULL_FORMAT_ID);
094: return;
095: }
096:
097: if (ref instanceof String) {
098: // String's are special cased to use writeUTF which is more
099: // efficient than the default writeObject(String), but the format
100: // can only store 65535 bytes. The worst case size conversion is
101: // 3 bytes for each unicode character in a String, so limiting
102: // writeUTF optimization to strings smaller than 20000 should
103: // insure that we won't call writeUTF() and produce more than
104: // 65535 bytes.
105:
106: String str = (String) ref;
107:
108: if (str.length() <= 20000) {
109: FormatIdUtil.writeFormatIdInteger(this ,
110: StoredFormatIds.STRING_FORMAT_ID);
111:
112: this .writeUTF((String) ref);
113: return;
114: }
115: }
116:
117: // Add debugging code to read-in every formatable that we write
118: // to ensure that it can be read and it's correctly registered.
119: OutputStream oldOut = null;
120: if (SanityManager.DEBUG) {
121:
122: if (ref instanceof Formatable) {
123:
124: oldOut = this .out;
125:
126: this .out = new DebugByteTeeOutputStream(oldOut);
127: }
128: }
129:
130: if (ref instanceof Storable) {
131: Storable s = (Storable) ref;
132:
133: int fmtId = s.getTypeFormatId();
134:
135: if (fmtId != StoredFormatIds.SERIALIZABLE_FORMAT_ID) {
136: FormatIdUtil.writeFormatIdInteger(this , fmtId);
137: boolean isNull = s.isNull();
138: writeBoolean(isNull);
139: if (!isNull) {
140: s.writeExternal(this );
141: }
142: if (SanityManager.DEBUG) {
143: ((DebugByteTeeOutputStream) this .out)
144: .checkObject(s);
145: this .out = oldOut;
146: }
147: return;
148: }
149: } else if (ref instanceof Formatable) {
150: Formatable f = (Formatable) ref;
151: int fmtId = f.getTypeFormatId();
152:
153: if (fmtId != StoredFormatIds.SERIALIZABLE_FORMAT_ID) {
154: FormatIdUtil.writeFormatIdInteger(this , fmtId);
155: f.writeExternal(this );
156:
157: if (SanityManager.DEBUG) {
158: ((DebugByteTeeOutputStream) this .out)
159: .checkObject(f);
160: this .out = oldOut;
161: }
162: return;
163: }
164: }
165:
166: /*
167: ** Otherwise we assume (ref instanceof Serializable).
168: ** If it isn't we'll get an error, which is what
169: ** we would expect if someone uses something that
170: ** doesn't support Serializable/Externalizable/Formattable
171: ** when it should.
172: */
173: {
174:
175: /*
176: ** If we are debugging (SerializeTrace), we are
177: ** going to print out every unexpected serialized
178: ** class. We print them out to stdout to help
179: ** in debugging (so they cause diffs in test runs).
180: ** This is only active in a SANE server.
181: */
182: if (SanityManager.DEBUG) {
183: if (SanityManager.DEBUG_ON("SerializedTrace")) {
184: String name = ref.getClass().getName();
185: if (!name.startsWith("java.lang")
186: && !name.startsWith("java.math")) {
187: SanityManager.DEBUG("SerializedTrace",
188: "...writing serialized class: " + name);
189: System.out
190: .println("...writing serialized class: "
191: + name);
192: }
193: }
194: }
195:
196: FormatIdUtil.writeFormatIdInteger(this ,
197: StoredFormatIds.SERIALIZABLE_FORMAT_ID);
198: ObjectOutputStream oos = new ObjectOutputStream(this );
199: oos.writeObject(ref);
200: oos.flush();
201:
202: if (SanityManager.DEBUG && ref instanceof Formatable) {
203: ((DebugByteTeeOutputStream) this .out)
204: .checkObject((Formatable) ref);
205: this .out = oldOut;
206: }
207: }
208: }
209:
210: /**
211: Set the OutputStream for this FormatIdOutputStream to the stream
212: provided. It is the responsibility of the caller to flush or
213: close (as required) the previous stream this class was attached to.
214:
215: @param out The new output stream.
216: */
217: public void setOutput(OutputStream out) {
218: this .out = out;
219: this .written = 0;
220: }
221:
222: /* Methods of ErrorInfo, used here for SQLData error reporting */
223:
224: public String getErrorInfo() {
225: return null;
226: }
227:
228: public Exception getNestedException() {
229: return null;
230: }
231:
232: }
|