| /* |
| * 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(); |
| } |
| } |