blob: 3acdcf3715eeda5954a3b1c2834e032ccccbf7b4 [file] [log] [blame]
/*
* 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.openjpa.util;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.apache.openjpa.conf.OpenJPAVersion;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.JavaVersions;
/**
* Utility methods for externalizing and handling exceptions.
*
* @author Marc Prud'hommeaux
* @since 0.2.5
*/
public class Exceptions {
public static final Throwable[] EMPTY_THROWABLES = new Throwable[0];
static final String SEP = J2DoPrivHelper.getLineSeparator();
private static final OutputStream DEV_NULL = new OutputStream() {
@Override
public void write(int b) {
}
};
/**
* Test to see if the specified object will be able to be serialized. This
* will check if the object implements {@link Serializable}, and if so,
* will try to perform an actual serialization. This is in case the object
* has fields which, in turn, are not serializable.
*
* @param ob the object to test
* @return true if the object will be able to be serialized
*/
private static boolean isSerializable(Object ob) {
if (!(ob instanceof Serializable))
return false;
// don't serialize persistent objects exceptions to prevent
// reading in all the state
if (!ImplHelper.isManagedType(null, ob.getClass()))
return false;
// now do an actual test to see if we will be
// able to perform the serialization
try {
new ObjectOutputStream(DEV_NULL).writeObject(ob);
return true;
} catch (Throwable t) {
return false;
}
}
/**
* Safely stringify the given object.
*/
public static String toString(Object ob) {
if (ob == null)
return "null";
// don't take oid of new objects since it can cause a flush if auto-inc
// and the id is meaningless anyway
Object oid = getObjectId(ob);
if (oid != null) {
if (oid instanceof Id)
return oid.toString();
String oidString = oid.toString();
// some oids stringify their class names. Some do not.
if (oidString.indexOf(ob.getClass().getName()) == -1) {
return ob.getClass().getName() + "-" + oidString;
} else {
return oidString;
}
}
if (ImplHelper.isManagedType(null, ob.getClass())) {
// never call toString() on a PersistenceCapable, since
// it may access persistent fields; fall-back to using
// the standard object stringification mechanism. New
// instances that use proxying (property-access instances,
// for example) that were created with the 'new' keyword
// will not end up in this code, which is ok since they
// don't do lazy loading anyways, so they will stringify
// safely.
return ob.getClass().getName() + "@"
+ Integer.toHexString(System.identityHashCode(ob));
}
try {
String s = ob.toString();
if (s.indexOf(ob.getClass().getName()) == -1)
s += " [" + ob.getClass().getName() + "]";
return s;
} catch (Throwable t) {
return ob.getClass().getName();
}
}
/**
* Safely stringify the given objects.
*/
public static String toString(Collection failed) {
StringBuilder buf = new StringBuilder();
buf.append("[");
for (Iterator itr = failed.iterator(); itr.hasNext();) {
buf.append(Exceptions.toString(itr.next()));
if (itr.hasNext())
buf.append(", ");
}
buf.append("]");
return buf.toString();
}
/**
* Stringify the given exception.
*/
public static String toString(ExceptionInfo e) {
int type = e.getType();
StringBuilder buf = new StringBuilder();
buf.append("<").
append(OpenJPAVersion.VERSION_ID).
append(' ').
append(e.isFatal() ? "fatal " : "nonfatal ").
append (type == ExceptionInfo.GENERAL ? "general error" :
type == ExceptionInfo.INTERNAL ? "internal error" :
type == ExceptionInfo.STORE ? "store error" :
type == ExceptionInfo.UNSUPPORTED ? "unsupported error" :
type == ExceptionInfo.USER ? "user error" :
(type + " error")).
append("> ");
buf.append(e.getClass().getName()).append(": ").
append(e.getMessage());
Object failed = e.getFailedObject();
if (failed != null)
buf.append(SEP).append("FailedObject: ").
append(toString(failed));
return buf.toString();
}
/**
* Print the stack trace of the exception's nested throwables.
*/
public static void printNestedThrowables(ExceptionInfo e, PrintStream out) {
// if this is Java 1.4 and there is exactly a single
// exception, then defer to 1.4's behavior of printing
// out the result of getCause(). This deferral happens in
// the calling code.
Throwable[] nested = e.getNestedThrowables();
int i = (JavaVersions.VERSION >= 4) ? 1 : 0;
if (i < nested.length) {
out.println("NestedThrowables:");
for (; i < nested.length; i++)
// guard against a nasty null in the array
if (nested[i] != null)
nested[i].printStackTrace(out);
}
}
/**
* Print the stack trace of the exception's nested throwables.
*/
public static void printNestedThrowables(ExceptionInfo e, PrintWriter out) {
// if this is Java 1.4 and there is exactly a single
// exception, then defer to 1.4's behavior of printing
// out the result of getCause(). This deferral happens in
// the calling code.
Throwable[] nested = e.getNestedThrowables();
int i = (JavaVersions.VERSION >= 4) ? 1 : 0;
if (i < nested.length) {
out.println("NestedThrowables:");
for (; i < nested.length; i++)
// guard against a nasty null in the array
if (nested[i] != null)
nested[i].printStackTrace(out);
}
}
/**
* Convert the specified failed object into a serializable
* object for when we are serializing an Exception. It will
* try the following:
* <ul>
* <li>if the object can be serialized, return the object itself</li>
* <li>if the object has a serializable oid, return the oid</li>
* <li>if the object has a non-serializable oid, return the oid's
* toString and the object class</li>
* <li>return the object's toString</li>
* </ul>
*
* @param ob the object to convert
* @return some serialized representation of the object
*/
public static Object replaceFailedObject(Object ob) {
if (ob == null)
return null;
if (isSerializable(ob))
return ob;
// don't take oid of new objects since it can cause a flush if auto-inc
// and the id is meaningless anyway
Object oid = getObjectId(ob);
if (oid != null && isSerializable(oid))
return oid;
// last ditch: stringify the object
return toString(ob);
}
/**
* Convert the specified throwables into a serialzable array. If
* any of the nested throwables cannot be serialized, they will
* be converted into a Exception with the original message.
*/
public static Throwable[] replaceNestedThrowables(Throwable[] nested) {
if (nested == null || nested.length == 0)
return nested;
if (isSerializable(nested))
return nested;
Throwable[] newNested = new Throwable[nested.length];
for (int i = 0; i < nested.length; i++) {
if (isSerializable(nested[i]))
newNested[i] = nested[i];
else
// guard against a nasty null in the array by using valueOf
// instead of toString to prevent throwing yet another
// exception
newNested[i] = new Exception(String.valueOf(nested[i]));
}
return newNested;
}
/**
* Return the object id for <code>ob</code> if it has one, or
* <code>null</code> otherwise.
*/
private static Object getObjectId(Object ob) {
if (!ImplHelper.isManageable(ob))
return null;
PersistenceCapable pc = ImplHelper.toPersistenceCapable(ob, null);
if (pc == null || pc.pcIsNew())
return null;
else
return pc.pcFetchObjectId();
}
public static String toClassName(Class<?> cls) {
if (cls == null) return "";
if (cls.isArray())
return toClassName(cls.getComponentType())+"[]";
return cls.getName();
}
public static String toClassNames(Collection<? extends Class<?>> classes) {
if (classes == null) return "";
StringBuilder buffer = new StringBuilder();
for (Class<?> cls : classes) {
buffer.append("\r\n").append(toClassName(cls));
}
return buffer.toString();
}
}