| /* |
| * 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.persistence.exception; |
| |
| import java.io.InputStream; |
| import java.sql.SQLException; |
| import java.util.List; |
| |
| import javax.persistence.EntityExistsException; |
| import javax.persistence.EntityManager; |
| import javax.persistence.EntityNotFoundException; |
| import javax.persistence.OptimisticLockException; |
| import javax.persistence.Query; |
| |
| import org.apache.openjpa.jdbc.conf.JDBCConfiguration; |
| import org.apache.openjpa.jdbc.sql.DBDictionary; |
| import org.apache.openjpa.jdbc.sql.SQLErrorCodeReader; |
| import org.apache.openjpa.persistence.test.SingleEMFTestCase; |
| |
| /** |
| * Tests proper JPA exceptions are raised by the implementation. |
| * Actual runtime type of the raised exception is a subclass of JPA-defined |
| * exception. |
| * The raised exception may nest the expected exception. |
| * |
| * @author Pinaki Poddar |
| */ |
| public class TestException extends SingleEMFTestCase { |
| private static long ID_COUNTER = System.currentTimeMillis(); |
| |
| @Override |
| public void setUp() { |
| super.setUp(PObject.class, CLEAR_TABLES); |
| } |
| |
| /** |
| * Tests that when Optimistic transaction consistency is violated, the |
| * exception thrown is an instance of javax.persistence.OptimisticException. |
| */ |
| // TODO: Re-enable this test once OPENJPA-991 issue is corrected. |
| public void disabledTestThrowsOptimisticException() { |
| |
| boolean supportsQueryTimeout = ((JDBCConfiguration) emf |
| .getConfiguration()).getDBDictionaryInstance().supportsQueryTimeout; |
| |
| EntityManager em1 = emf.createEntityManager(); |
| EntityManager em2 = emf.createEntityManager(); |
| assertNotEquals(em1, em2); |
| |
| em1.getTransaction().begin(); |
| PObject pc = new PObject(); |
| long id = ++ID_COUNTER; |
| pc.setId(id); |
| em1.persist(pc); |
| em1.getTransaction().commit(); |
| em1.clear(); |
| |
| em1.getTransaction().begin(); |
| em2.getTransaction().begin(); |
| |
| PObject pc1 = em1.find(PObject.class, id); |
| PObject pc2 = em2.find(PObject.class, id); |
| |
| assertTrue(pc1 != pc2); |
| |
| pc1.setName("Modified in TXN1"); |
| if (supportsQueryTimeout) { |
| em1.flush(); |
| try { |
| pc2.setName("Modified in TXN2"); |
| em2.flush(); |
| fail("Expected " + OptimisticLockException.class); |
| } catch (Throwable t) { |
| assertException(t, OptimisticLockException.class); |
| } |
| } else { |
| pc2.setName("Modified in TXN2"); |
| } |
| |
| em1.getTransaction().commit(); |
| try { |
| em2.getTransaction().commit(); |
| fail("Expected " + OptimisticLockException.class); |
| } catch (Throwable t) { |
| assertException(t, OptimisticLockException.class); |
| } |
| } |
| |
| public void testThrowsEntityExistsException() { |
| EntityManager em = emf.createEntityManager(); |
| |
| em.getTransaction().begin(); |
| PObject pc = new PObject(); |
| long id = ++ID_COUNTER; |
| pc.setId(id); |
| em.persist(pc); |
| em.getTransaction().commit(); |
| em.clear(); |
| |
| em.getTransaction().begin(); |
| PObject pc2 = new PObject(); |
| pc2.setId(id); |
| em.persist(pc2); |
| try { |
| em.getTransaction().commit(); |
| fail("Expected " + EntityExistsException.class); |
| } catch (Throwable t) { |
| assertException(t, EntityExistsException.class); |
| } |
| em.close(); |
| } |
| |
| public void testThrowsEntityNotFoundException() { |
| EntityManager em = emf.createEntityManager(); |
| |
| em.getTransaction().begin(); |
| PObject pc = new PObject(); |
| long id = ++ID_COUNTER; |
| pc.setId(id); |
| em.persist(pc); |
| em.getTransaction().commit(); |
| |
| EntityManager em2 = emf.createEntityManager(); |
| em2.getTransaction().begin(); |
| PObject pc2 = em2.find(PObject.class, id); |
| assertNotNull(pc2); |
| em2.remove(pc2); |
| em2.getTransaction().commit(); |
| |
| try { |
| em.refresh(pc); |
| fail("Expected " + EntityNotFoundException.class); |
| } catch (Throwable t) { |
| assertException(t, EntityNotFoundException.class); |
| } |
| em.close(); |
| } |
| |
| public void testErrorCodeConfigurationHasAllKnownDictionaries() { |
| SQLErrorCodeReader reader = new SQLErrorCodeReader(); |
| InputStream in = DBDictionary.class.getResourceAsStream |
| ("sql-error-state-codes.xml"); |
| assertNotNull(in); |
| List<String> names = reader.getDictionaries(in); |
| assertTrue(names.size()>=18); |
| for (String name:names) { |
| try { |
| Class.forName(name, false, Thread.currentThread() |
| .getContextClassLoader()); |
| } catch (Throwable t) { |
| fail("DB dictionary " + name + " can not be loaded"); |
| t.printStackTrace(); |
| } |
| } |
| } |
| |
| /** |
| * Invalid query throws IllegalArgumentException on construction |
| * as per JPA spec. |
| */ |
| public void testIllegalArgumennExceptionOnInvalidQuery() { |
| EntityManager em = emf.createEntityManager(); |
| try { |
| em.createQuery("This is not a valid JPQL query"); |
| fail("Did not throw IllegalArgumentException for invalid query."); |
| } catch (Throwable t) { |
| assertException(t, IllegalArgumentException.class); |
| } |
| em.close(); |
| } |
| |
| /** |
| * Invalid named query fails as per spec on factory based construction. |
| */ |
| public void testIllegalArgumennExceptionOnInvalidNamedQuery() { |
| EntityManager em = emf.createEntityManager(); |
| try { |
| Query query = em.createNamedQuery("This is invalid Named query"); |
| fail("Did not throw IllegalArgumentException for invalid query."); |
| } catch (Throwable t) { |
| assertException(t, IllegalArgumentException.class); |
| } |
| em.close(); |
| } |
| |
| /** |
| * Asserts that the given expected type of the exception is equal to or a |
| * subclass of the given throwable or any of its nested exception. |
| * Otherwise fails assertion and prints the given throwable and its nested |
| * exception on the console. |
| */ |
| @Override |
| public void assertException(Throwable t, Class expectedType) { |
| if (!isExpectedException(t, expectedType)) { |
| getLog().error("TestException.assertException() - unexpected exception type", t); |
| //t.printStackTrace(); |
| print(t, 0); |
| fail(t + " or its cause is not instanceof " + expectedType); |
| } else { |
| if (getLog().isTraceEnabled()) { |
| getLog().trace("TestException.assertException() - caught expected exception type=" + |
| expectedType, t); |
| } |
| } |
| } |
| |
| /** |
| * Affirms if the given expected type of the exception is equal to or a |
| * subclass of the given throwable or any of its nested exception. |
| */ |
| boolean isExpectedException(Throwable t, Class expectedType) { |
| if (t == null) |
| return false; |
| if (expectedType.isAssignableFrom(t.getClass())) |
| return true; |
| return isExpectedException(t.getCause(), expectedType); |
| } |
| |
| void print(Throwable t, int tab) { |
| if (t == null) return; |
| StringBuilder str = new StringBuilder(80); |
| for (int i=0; i<tab*4;i++) |
| str.append(" "); |
| String sqlState = (t instanceof SQLException) ? |
| "(SQLState=" + ((SQLException)t).getSQLState() + ":" |
| + t.getMessage() + ")" : ""; |
| str.append(t.getClass().getName() + sqlState); |
| getLog().error(str); |
| if (t.getCause() == t) |
| return; |
| print(t.getCause(), tab+1); |
| } |
| } |