001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. 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,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence;
020:
021: import java.lang.reflect.InvocationTargetException;
022:
023: import org.apache.openjpa.kernel.Broker;
024: import org.apache.openjpa.util.Exceptions;
025: import org.apache.openjpa.util.OpenJPAException;
026: import org.apache.openjpa.util.RuntimeExceptionTranslator;
027: import org.apache.openjpa.util.StoreException;
028: import org.apache.openjpa.util.UserException;
029:
030: /**
031: * Converts from OpenJPA to persistence exception types.
032: *
033: * @author Abe White
034: * @author Marc Prud'hommeaux
035: * @nojavadoc
036: */
037: public class PersistenceExceptions extends Exceptions {
038:
039: public static final RuntimeExceptionTranslator TRANSLATOR = new RuntimeExceptionTranslator() {
040: public RuntimeException translate(RuntimeException re) {
041: return PersistenceExceptions.toPersistenceException(re);
042: }
043: };
044:
045: /**
046: * Returns a {@link RuntimeExceptionTranslator} that will perform
047: * the correct exception translation as well as roll back the
048: * current transaction when for all but {@link NoResultException}
049: * and {@link NonUniqueResultException} in accordance with
050: * section 3.7 of the EJB 3.0 specification.
051: */
052: public static RuntimeExceptionTranslator getRollbackTranslator(
053: final OpenJPAEntityManager em) {
054: return new RuntimeExceptionTranslator() {
055: private boolean throwing = false;
056:
057: public RuntimeException translate(RuntimeException re) {
058: RuntimeException ex = toPersistenceException(re);
059: if (!(ex instanceof NonUniqueResultException)
060: && !(ex instanceof NoResultException)
061: && !throwing) {
062: try {
063: throwing = true;
064: if (em.isOpen()
065: && ((EntityManagerImpl) em).isActive())
066: ((EntityManagerImpl) em)
067: .setRollbackOnly(re);
068: } finally {
069: // handle re-entrancy
070: throwing = false;
071: }
072: }
073:
074: return ex;
075: }
076: };
077: }
078:
079: /**
080: * Convert the given throwable to the proper persistence exception.
081: */
082: public static RuntimeException toPersistenceException(Throwable t) {
083: return (RuntimeException) translateException(t, true);
084: }
085:
086: /**
087: * Translate the given exception.
088: *
089: * @param checked whether to translate checked exceptions
090: */
091: private static Throwable translateException(Throwable t,
092: boolean checked) {
093: if (isPersistenceException(t))
094: return t;
095:
096: // immediately throw errors
097: if (t instanceof Error)
098: throw (Error) t;
099:
100: OpenJPAException ke;
101: if (!(t instanceof OpenJPAException)) {
102: if (!checked || t instanceof RuntimeException)
103: return t;
104: ke = new org.apache.openjpa.util.GeneralException(t
105: .getMessage());
106: ke.setStackTrace(t.getStackTrace());
107: return ke;
108: }
109:
110: // if only nested exception is a persistence one, return it directly
111: ke = (OpenJPAException) t;
112: if (ke.getNestedThrowables().length == 1
113: && isPersistenceException(ke.getCause()))
114: return ke.getCause();
115:
116: // RuntimeExceptions thrown from callbacks should be thrown directly
117: if (ke.getType() == OpenJPAException.USER
118: && ke.getSubtype() == UserException.CALLBACK
119: && ke.getNestedThrowables().length == 1) {
120: Throwable e = ke.getCause();
121: if (e instanceof InvocationTargetException)
122: e = e.getCause();
123:
124: if (e instanceof RuntimeException)
125: return e;
126: }
127:
128: // perform intelligent translation of openjpa exceptions
129: switch (ke.getType()) {
130: case OpenJPAException.STORE:
131: return translateStoreException(ke);
132: case OpenJPAException.USER:
133: return translateUserException(ke);
134: default:
135: return translateGeneralException(ke);
136: }
137: }
138:
139: /**
140: * Translate the given store exception.
141: */
142: private static Throwable translateStoreException(OpenJPAException ke) {
143: Exception e;
144: switch (ke.getSubtype()) {
145: case StoreException.OBJECT_NOT_FOUND:
146: e = new org.apache.openjpa.persistence.EntityNotFoundException(
147: ke.getMessage(), getNestedThrowables(ke),
148: getFailedObject(ke), ke.isFatal());
149: break;
150: case StoreException.OPTIMISTIC:
151: case StoreException.LOCK:
152: e = new org.apache.openjpa.persistence.OptimisticLockException(
153: ke.getMessage(), getNestedThrowables(ke),
154: getFailedObject(ke), ke.isFatal());
155: break;
156: case StoreException.OBJECT_EXISTS:
157: e = new org.apache.openjpa.persistence.EntityExistsException(
158: ke.getMessage(), getNestedThrowables(ke),
159: getFailedObject(ke), ke.isFatal());
160: break;
161: default:
162: e = new org.apache.openjpa.persistence.PersistenceException(
163: ke.getMessage(), getNestedThrowables(ke),
164: getFailedObject(ke), ke.isFatal());
165: }
166: e.setStackTrace(ke.getStackTrace());
167: return e;
168: }
169:
170: /**
171: * Translate the given user exception.
172: */
173: private static Exception translateUserException(OpenJPAException ke) {
174: Exception e;
175: switch (ke.getSubtype()) {
176: case UserException.NO_TRANSACTION:
177: e = new org.apache.openjpa.persistence.TransactionRequiredException(
178: ke.getMessage(), getNestedThrowables(ke),
179: getFailedObject(ke), ke.isFatal());
180: break;
181: case UserException.NO_RESULT:
182: e = new org.apache.openjpa.persistence.NoResultException(ke
183: .getMessage(), getNestedThrowables(ke),
184: getFailedObject(ke), ke.isFatal());
185: break;
186: case UserException.NON_UNIQUE_RESULT:
187: e = new org.apache.openjpa.persistence.NonUniqueResultException(
188: ke.getMessage(), getNestedThrowables(ke),
189: getFailedObject(ke), ke.isFatal());
190: break;
191: case UserException.INVALID_STATE:
192: e = new org.apache.openjpa.persistence.InvalidStateException(
193: ke.getMessage(), getNestedThrowables(ke),
194: getFailedObject(ke), ke.isFatal());
195: break;
196: default:
197: e = new org.apache.openjpa.persistence.ArgumentException(ke
198: .getMessage(), getNestedThrowables(ke),
199: getFailedObject(ke), ke.isFatal());
200: }
201: e.setStackTrace(ke.getStackTrace());
202: return e;
203: }
204:
205: /**
206: * Translate the given general exception.
207: */
208: private static Throwable translateGeneralException(
209: OpenJPAException ke) {
210: Exception e = new org.apache.openjpa.persistence.PersistenceException(
211: ke.getMessage(), getNestedThrowables(ke),
212: getFailedObject(ke), ke.isFatal());
213: e.setStackTrace(ke.getStackTrace());
214: return e;
215: }
216:
217: /**
218: * Return true if the given exception is a persistence exception.
219: */
220: private static boolean isPersistenceException(Throwable t) {
221: return t.getClass().getName().startsWith(
222: "org.apache.openjpa.persistence.");
223: }
224:
225: /**
226: * Translate the nested throwables of the given openjpa exception into
227: * nested throwables for a persistence exception.
228: */
229: private static Throwable[] getNestedThrowables(OpenJPAException ke) {
230: Throwable[] nested = ke.getNestedThrowables();
231: if (nested.length == 0)
232: return nested;
233:
234: Throwable[] trans = new Throwable[nested.length];
235: for (int i = 0; i < nested.length; i++)
236: trans[i] = translateException(nested[i], false);
237: return trans;
238: }
239:
240: /**
241: * Return the failed object for the given exception, performing any
242: * necessary conversions.
243: */
244: private static Object getFailedObject(OpenJPAException ke) {
245: Object o = ke.getFailedObject();
246: if (o == null)
247: return null;
248: if (o instanceof Broker)
249: return JPAFacadeHelper.toEntityManager((Broker) o);
250: return JPAFacadeHelper.fromOpenJPAObjectId(o);
251: }
252:
253: /**
254: * Helper method to extract a nested exception from an internal nested
255: * array in a safe way.
256: */
257: static Throwable getCause(Throwable[] nested) {
258: if (nested == null || nested.length == 0)
259: return null;
260: return nested[0];
261: }
262: }
|