001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2005 Emic Networks
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Marc Herbert
019: * Contributor(s): ______________________.
020: */package org.continuent.sequoia.common.exceptions.driver.protocol;
021:
022: import java.io.IOException;
023: import java.io.PrintStream;
024: import java.io.PrintWriter;
025:
026: import org.continuent.sequoia.common.stream.DriverBufferedInputStream;
027: import org.continuent.sequoia.common.stream.DriverBufferedOutputStream;
028:
029: /**
030: * This class implements our own Exception chain, overriding Throwable as much
031: * as possible, relying on our own SerializableStackTraceElement instead of
032: * java.lang.StackStraceElement.
033: * <p>
034: * The main reason for this class is that java.lang.StackStraceElement is not
035: * serializable in JDK 1.4 (the constructor is missing, whereas 1.5 has it).
036: * Unfortunately we cannot override everything we would like to because
037: * StackTraceElement is also final and so SerializableStackTraceElement cannot
038: * subtype+override it.
039: *
040: * @author <a href="mailto:Marc.Herbert@emicnetworks.com">Marc Herbert</a>
041: * @version 1.0
042: */
043: public class SerializableException extends Exception {
044: private static final long serialVersionUID = -7676999309139924676L;
045:
046: private String sqlState;
047: private int vendorCode;
048:
049: private SerializableStackTraceElement[] stackTrace;
050:
051: /**
052: * @see Exception#Exception(java.lang.Throwable)
053: */
054: SerializableException(String message, SerializableException cause) {
055: super (message, cause);
056: }
057:
058: /**
059: * Converts a chain of Throwables to a chain of
060: * <code>SerializableException</code>. The resulting chain has the same
061: * length.
062: *
063: * @param start head of chain to convert
064: */
065:
066: SerializableException(Throwable start) {
067: this (start.getMessage(), null == start.getCause() ? null
068: : new SerializableException(start.getCause())); // recursion
069:
070: convertStackTrace(start);
071: }
072:
073: void convertStackTrace(Throwable regularEx) {
074: StackTraceElement[] regularST = regularEx.getStackTrace();
075: stackTrace = new SerializableStackTraceElement[regularST.length];
076: for (int i = 0; i < regularST.length; i++)
077: stackTrace[i] = new SerializableStackTraceElement(
078: regularST[i]);
079:
080: // nullifies super's, non-serializable stack trace since we don't want to
081: // use it anymore
082: setStackTrace(null /* ignored arg */);
083:
084: }
085:
086: /* ***** Serialization / Networking ********* */
087:
088: /**
089: * Constructs/reads a new SerializableException chain from the stream
090: */
091: SerializableException(DriverBufferedInputStream in)
092: throws IOException {
093: // receive message and next exception (recursive)
094: // (left to right evaluation is guaranteed by JLS bible)
095: super (in.readLongUTF(),
096: in.readBoolean() ? new SerializableException(in) : null);
097:
098: // receive stackTrace
099: stackTrace = new SerializableStackTraceElement[in.readInt()];
100: for (int i = 0; i < stackTrace.length; i++)
101: stackTrace[i] = new SerializableStackTraceElement(in);
102:
103: // receive SQL fields
104: setSQLState(in.readLongUTF());
105: setErrorCode(in.readInt());
106:
107: }
108:
109: /**
110: * Send the Serializable chain to the given stream.
111: *
112: * @param out destination stream
113: * @throws IOException stream error
114: */
115: public void sendToStream(DriverBufferedOutputStream out)
116: throws IOException {
117: // send message
118: out.writeLongUTF(getMessage());
119:
120: // send next exception if any (chaining)
121: if (null != getCause()) {
122: out.writeBoolean(true);
123: ((SerializableException) getCause()).sendToStream(out); // recursion
124: } else
125: out.writeBoolean(false); // stop condition
126:
127: // send stack trace
128: out.writeInt(stackTrace.length);
129: for (int i = 0; i < stackTrace.length; i++)
130: stackTrace[i].sendToStream(out);
131:
132: // send SQL fields
133: out.writeLongUTF(getSQLState());
134: out.writeInt(getErrorCode());
135:
136: out.flush();
137: }
138:
139: /**
140: * @see java.lang.Throwable#printStackTrace()
141: */
142: public void printStackTrace() {
143: printStackTrace(System.err);
144: }
145:
146: /**
147: * Prints this throwable and its backtrace to the specified print stream.
148: *
149: * @param s <code>PrintStream</code> to use for output
150: */
151: public void printStackTrace(PrintStream s) {
152: synchronized (s) {
153: s.println(this );
154: for (int i = 0; i < stackTrace.length; i++)
155: s.println("\tAt: " + stackTrace[i]);
156:
157: SerializableException ourCause = (SerializableException) getCause();
158: if (ourCause != null) {
159: s.println("Caused by");
160: ourCause.printStackTrace(s);
161: }
162: }
163: }
164:
165: /**
166: * Prints this throwable and its backtrace to the specified print writer.
167: *
168: * @param s <code>PrintWriter</code> to use for output
169: */
170: public void printStackTrace(PrintWriter s) {
171: synchronized (s) {
172: s.println(this );
173: for (int i = 0; i < stackTrace.length; i++)
174: s.println("\tat " + stackTrace[i]);
175:
176: SerializableException ourCause = (SerializableException) getCause();
177: if (ourCause != null) {
178: s.println("Caused-by");
179: ourCause.printStackTrace(s);
180: }
181: }
182: }
183:
184: /**
185: * @deprecated
186: * @see java.lang.Throwable#fillInStackTrace()
187: */
188: public synchronized Throwable fillInStackTrace() {
189: setStackTrace(null);
190: return this ;
191: }
192:
193: /**
194: * Please use getSerializedStackTrace() instead. Unfortunately
195: * StackTraceElement has no constructor in 1.4 and cannot be overriden
196: * (final).
197: *
198: * @deprecated
199: * @see java.lang.Throwable#getStackTrace()
200: */
201: public StackTraceElement[] getStackTrace() {
202: return new StackTraceElement[0];
203: }
204:
205: /**
206: * Returns our private stack trace, the one which is serializable.
207: *
208: * @return our private stack trace
209: */
210: public SerializableStackTraceElement[] getSerializableStackTrace() {
211: return (SerializableStackTraceElement[]) stackTrace.clone();
212: }
213:
214: /**
215: * This method is deprecated and erases the regular, non serializable stack
216: * trace. Please use setSerializedStackTrace() instead. Unfortunately
217: * StackTraceElement has no constructor in 1.4 and cannot be overriden
218: * (final).
219: *
220: * @deprecated
221: * @see java.lang.Throwable#setStackTrace(java.lang.StackTraceElement[])
222: */
223: public void setStackTrace(StackTraceElement[] ignored) {
224: super .setStackTrace(new StackTraceElement[0]);
225: }
226:
227: /**
228: * Sets the vendorCode value.
229: *
230: * @param vendorCode The vendorCode to set.
231: */
232: void setErrorCode(int vendorCode) {
233: this .vendorCode = vendorCode;
234: }
235:
236: /**
237: * Returns the vendorCode value.
238: *
239: * @return Returns the vendorCode.
240: */
241: public int getErrorCode() {
242: return vendorCode;
243: }
244:
245: /**
246: * Sets the sQLState value.
247: *
248: * @param sQLState The sQLState to set.
249: */
250: public void setSQLState(String sQLState) {
251: this .sqlState = sQLState;
252: }
253:
254: /**
255: * Returns the sQLState value.
256: *
257: * @return Returns the sQLState.
258: */
259: public String getSQLState() {
260: return sqlState;
261: }
262:
263: /**
264: * Override super, adding an extra check because we do not want a mixed chain.
265: *
266: * @see java.lang.Throwable#initCause(java.lang.Throwable)
267: */
268: public Throwable initCause(Throwable cause) {
269: throwsIfNotSerializable(cause);
270:
271: super .initCause(cause);
272: return this ;
273: }
274:
275: private void throwsIfNotSerializable(Throwable cause)
276: throws IllegalArgumentException {
277: if (null == cause)
278: return;
279:
280: if (!(cause instanceof SerializableException))
281: throw new IllegalArgumentException(
282: "The cause of SerializableException has to be a SerializableException");
283: }
284:
285: }
|