1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.io.FormatIdOutputStream |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.services.io; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | import org.apache.derby.iapi.services.info.JVMInfo; |
26 | |
27 | import java.io.DataOutputStream; |
28 | import java.io.IOException; |
29 | import java.io.ObjectOutput; |
30 | import java.io.ObjectOutputStream; |
31 | import java.io.OutputStream; |
32 | import java.io.Serializable; |
33 | |
34 | |
35 | /** |
36 | A stream for serializing objects with format id tags. |
37 | |
38 | <P>An ObjectOutput (henceforth 'out') preceeds objects it writes with |
39 | a format id. The companion FormatIdInputStream (henceforth 'in') |
40 | uses these format ids in parsing the stored data. The stream |
41 | can be thought of as containing a sequence of (formatId,object) pairs |
42 | interspersed with other data. The assumption is that out.writeObject() |
43 | produces these pairs and in.readObject() uses the format ids to |
44 | construct objects from the pairs that out.writeObject produced. |
45 | The description below describes each supported pair and how in.readObject() |
46 | processes it. |
47 | |
48 | <OL> |
49 | <LI> (NULL_FORMAT_ID, nothing) in.readObject() returns null. |
50 | <LI> (SRING_FORMAT_ID, UTF8 encoded string)in.readObject reads and |
51 | returns this string. |
52 | <LI> (SERIALIZABLE_FORMAT_ID,serialized object) in.readObject() reads |
53 | the object using java serialization and returns it. |
54 | <LI> (A format id for a Storable, isNull flag and object if isNull == false) |
55 | (see note 1) in.readObject() reads the boolean isNull flag. If is null |
56 | is true, in.readObject() returns a Storable object of the correct |
57 | class which is null. If ifNull is false, in.readObject() restores |
58 | the object using its readExternal() method. |
59 | <LI> (A format id for a Formatable which is not Storable, the stored object) |
60 | (see note 1) in.readObject restores the object using its |
61 | readExternal() method. |
62 | </OL> |
63 | |
64 | <P>Note 1: The FormatIdInputStream uses |
65 | Monitor.newInstanceFromIdentifier(format id) to get the class. |
66 | <P>Note 2: An object may support more than one of the following |
67 | interfaces Storable, Formatable, Serializable. In this case out.writeObject |
68 | use the first of these interfaces which the object supports (based on the order |
69 | listed here) to determine how to write the object. |
70 | */ |
71 | public class FormatIdOutputStream |
72 | extends DataOutputStream implements ObjectOutput, ErrorInfo |
73 | { |
74 | |
75 | /** |
76 | Constructor for a FormatIdOutputStream |
77 | |
78 | @param out output goes here. |
79 | */ |
80 | public FormatIdOutputStream(OutputStream out) |
81 | { |
82 | super(out); |
83 | } |
84 | |
85 | /** |
86 | Write a format id for the object provied followed by the |
87 | object itself to this FormatIdOutputStream. |
88 | |
89 | @param ref a reference to the object. |
90 | @exception java.io.IOException the exception. |
91 | */ |
92 | public void writeObject(Object ref) throws IOException |
93 | { |
94 | if (ref == null) |
95 | { |
96 | FormatIdUtil.writeFormatIdInteger(this, StoredFormatIds.NULL_FORMAT_ID); |
97 | return; |
98 | } |
99 | |
100 | if (ref instanceof String) |
101 | { |
102 | // String's are special cased to use writeUTF which is more |
103 | // efficient than the default writeObject(String), but the format |
104 | // can only store 65535 bytes. The worst case size conversion is |
105 | // 3 bytes for each unicode character in a String, so limiting |
106 | // writeUTF optimization to strings smaller than 20000 should |
107 | // insure that we won't call writeUTF() and produce more than |
108 | // 65535 bytes. |
109 | |
110 | String str = (String) ref; |
111 | |
112 | if (str.length() <= 20000) |
113 | { |
114 | FormatIdUtil.writeFormatIdInteger( |
115 | this, StoredFormatIds.STRING_FORMAT_ID); |
116 | |
117 | this.writeUTF((String)ref); |
118 | return; |
119 | } |
120 | } |
121 | |
122 | // Add debugging code to read-in every formatable that we write |
123 | // to ensure that it can be read and it's correctly registered. |
124 | OutputStream oldOut = null; |
125 | if (SanityManager.DEBUG) { |
126 | |
127 | if (ref instanceof Formatable) { |
128 | |
129 | oldOut = this.out; |
130 | |
131 | this.out = new DebugByteTeeOutputStream(oldOut); |
132 | } |
133 | } |
134 | |
135 | if (ref instanceof Storable) |
136 | { |
137 | Storable s = (Storable)ref; |
138 | |
139 | int fmtId = s.getTypeFormatId(); |
140 | |
141 | if (fmtId != StoredFormatIds.SERIALIZABLE_FORMAT_ID) { |
142 | FormatIdUtil.writeFormatIdInteger(this, fmtId); |
143 | boolean isNull = s.isNull(); |
144 | writeBoolean(isNull); |
145 | if (!isNull) |
146 | { |
147 | s.writeExternal(this); |
148 | } |
149 | if (SanityManager.DEBUG) { |
150 | ((DebugByteTeeOutputStream) this.out).checkObject(s); |
151 | this.out = oldOut; |
152 | } |
153 | return; |
154 | } |
155 | } |
156 | else if (ref instanceof Formatable) |
157 | { |
158 | Formatable f = |
159 | (Formatable) ref; |
160 | int fmtId = f.getTypeFormatId(); |
161 | |
162 | if (fmtId != StoredFormatIds.SERIALIZABLE_FORMAT_ID) { |
163 | FormatIdUtil.writeFormatIdInteger(this,fmtId); |
164 | f.writeExternal(this); |
165 | |
166 | if (SanityManager.DEBUG) { |
167 | ((DebugByteTeeOutputStream) this.out).checkObject(f); |
168 | this.out = oldOut; |
169 | } |
170 | return; |
171 | } |
172 | } |
173 | |
174 | /* |
175 | ** Otherwise we assume (ref instanceof Serializable). |
176 | ** If it isn't we'll get an error, which is what |
177 | ** we would expect if someone uses something that |
178 | ** doesn't support Serializable/Externalizable/Formattable |
179 | ** when it should. |
180 | */ |
181 | { |
182 | |
183 | /* |
184 | ** If we are debugging (SerializeTrace), we are |
185 | ** going to print out every unexpected serialized |
186 | ** class. We print them out to stdout to help |
187 | ** in debugging (so they cause diffs in test runs). |
188 | ** This is only active in a SANE server. |
189 | */ |
190 | if (SanityManager.DEBUG) |
191 | { |
192 | if (SanityManager.DEBUG_ON("SerializedTrace")) |
193 | { |
194 | String name = ref.getClass().getName(); |
195 | if ( |
196 | !name.startsWith("java.lang") && |
197 | !name.startsWith("java.math")) |
198 | { |
199 | SanityManager.DEBUG("SerializedTrace", |
200 | "...writing serialized class: "+name); |
201 | System.out.println( |
202 | "...writing serialized class: "+name); |
203 | } |
204 | } |
205 | } |
206 | |
207 | FormatIdUtil.writeFormatIdInteger(this, StoredFormatIds.SERIALIZABLE_FORMAT_ID); |
208 | ObjectOutputStream oos = new ObjectOutputStream(this); |
209 | oos.writeObject(ref); |
210 | oos.flush(); |
211 | |
212 | if (SanityManager.DEBUG && ref instanceof Formatable) { |
213 | ((DebugByteTeeOutputStream) this.out).checkObject((Formatable) ref); |
214 | this.out = oldOut; |
215 | } |
216 | } |
217 | } |
218 | |
219 | /** |
220 | Set the OutputStream for this FormatIdOutputStream to the stream |
221 | provided. It is the responsibility of the caller to flush or |
222 | close (as required) the previous stream this class was attached to. |
223 | |
224 | @param out The new output stream. |
225 | */ |
226 | public void setOutput(OutputStream out) |
227 | { |
228 | this.out = out; |
229 | this.written = 0; |
230 | } |
231 | |
232 | |
233 | /* Methods of ErrorInfo, used here for SQLData error reporting */ |
234 | |
235 | public String getErrorInfo() |
236 | { |
237 | return null; |
238 | } |
239 | |
240 | public Exception getNestedException() |
241 | { |
242 | return null; |
243 | } |
244 | |
245 | } |