/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.openejb.core.transaction; import java.io.ByteArrayOutputStream; import java.io.ObjectStreamException; import java.io.PrintStream; import java.io.PrintWriter; /** * Subclass of javax.transaction.TransactionRolledbackException which adds init cause to the exception. */ public class TransactionRolledbackException extends javax.transaction.TransactionRolledbackException { private Throwable cause = this; public TransactionRolledbackException() { super(); fillInStackTrace(); } public TransactionRolledbackException(final String detailMessage) { super(detailMessage); } /** * Constructs a new instance of this class with its walkback, message and * cause filled in. * * @param detailMessage String The detail message for the exception. * @param throwable The cause of this Throwable */ public TransactionRolledbackException(final String detailMessage, final Throwable throwable) { super(detailMessage); cause = throwable; } public TransactionRolledbackException(final Throwable throwable) { super(throwable == null ? null : throwable.toString()); cause = throwable; } /** * Answers the extra information message which was provided when the * throwable was created. If no message was provided at creation time, then * answer null. Subclasses may override this method to answer localized text * for the message. * * @return String The receiver's message. */ @Override public String getLocalizedMessage() { return getMessage(); } /** * Outputs a printable representation of the receiver's walkback on the * System.err stream. */ @Override public void printStackTrace() { printStackTrace(System.err); } /** * Count the number of duplicate stack frames, starting from * the end of the stack. * * @param currentStack a stack to compare * @param parentStack a stack to compare * @return the number of duplicate stack frames. */ private static int countDuplicates(final StackTraceElement[] currentStack, final StackTraceElement[] parentStack) { int duplicates = 0; int parentIndex = parentStack.length; for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0; ) { final StackTraceElement parentFrame = parentStack[parentIndex]; if (parentFrame.equals(currentStack[i])) { duplicates++; } else { break; } } return duplicates; } /** * Outputs a printable representation of the receiver's walkback on the * stream specified by the argument. * * @param err PrintStream The stream to write the walkback on. */ @Override public void printStackTrace(final PrintStream err) { err.println(toString()); // Don't use getStackTrace() as it calls clone() // Get stackTrace, in case stackTrace is reassigned final StackTraceElement[] stack = getStackTrace(); for (int i = 0; i < stack.length; i++) { err.println("\tat " + stack[i]); } StackTraceElement[] parentStack = stack; Throwable throwable = getCause(); while (throwable != null) { err.print("Caused by: "); err.println(throwable); final StackTraceElement[] currentStack = throwable.getStackTrace(); final int duplicates = countDuplicates(currentStack, parentStack); for (int i = 0; i < currentStack.length - duplicates; i++) { err.println("\tat " + currentStack[i]); } if (duplicates > 0) { err.println("\t... " + duplicates + " more"); } parentStack = currentStack; throwable = throwable.getCause(); } } /** * Outputs a printable representation of the receiver's walkback on the * writer specified by the argument. * * @param err PrintWriter The writer to write the walkback on. */ @Override public void printStackTrace(final PrintWriter err) { err.println(toString()); // Don't use getStackTrace() as it calls clone() // Get stackTrace, in case stackTrace is reassigned final StackTraceElement[] stack = getStackTrace(); for (int i = 0; i < stack.length; i++) { err.println("\tat " + stack[i]); } StackTraceElement[] parentStack = stack; Throwable throwable = getCause(); while (throwable != null) { err.print("Caused by: "); err.println(throwable); final StackTraceElement[] currentStack = throwable.getStackTrace(); final int duplicates = countDuplicates(currentStack, parentStack); for (int i = 0; i < currentStack.length - duplicates; i++) { err.println("\tat " + currentStack[i]); } if (duplicates > 0) { err.println("\t... " + duplicates + " more"); } parentStack = currentStack; throwable = throwable.getCause(); } } /** * Answers a string containing a concise, human-readable description of the * receiver. * * @return String a printable representation for the receiver. */ public String toString() { final String msg = getLocalizedMessage(); final String name = getClass().getName(); if (msg == null) { return name; } return name + ": " + msg; } /** * Initialize the cause of the receiver. The cause cannot be reassigned. * * @param throwable The cause of this Throwable * @return the receiver. * @throws IllegalArgumentException when the cause is the receiver * @throws IllegalStateException when the cause has already been initialized */ @Override public synchronized TransactionRolledbackException initCause(final Throwable throwable) { cause = throwable; return this; } /** * Answers the cause of this Throwable, or null if there is no cause. * * @return Throwable The receiver's cause. */ @Override public Throwable getCause() { if (cause == this) { return null; } return cause; } protected Object writeReplace() throws ObjectStreamException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); printStackTrace(new PrintStream(baos)); return new javax.transaction.TransactionRolledbackException(getMessage() + "\n\t" + new String(baos.toByteArray())); } }