| /* |
| * 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.audit; |
| |
| |
| import java.lang.ref.ReferenceQueue; |
| import java.lang.ref.WeakReference; |
| import java.util.List; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.Persistence; |
| |
| import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; |
| import org.apache.openjpa.persistence.OpenJPAPersistence; |
| |
| import junit.framework.TestCase; |
| |
| /** |
| * A test for audit facility. |
| * |
| * @author Pinaki Poddar |
| * |
| */ |
| public class TestAudit extends TestCase { |
| private static OpenJPAEntityManagerFactory emf; |
| private static Auditor auditor; |
| private static Object oid; |
| |
| EntityManager em; |
| |
| @Override |
| public void setUp() { |
| if (emf == null) { |
| emf = OpenJPAPersistence.cast(Persistence.createEntityManagerFactory("audit")); |
| assertNotNull(emf); |
| auditor = emf.getConfiguration().getAuditorInstance(); |
| em = emf.createEntityManager(); |
| clearAuditedEntries(); |
| oid = createManagedObject(); |
| } else { |
| em = emf.createEntityManager(); |
| } |
| } |
| |
| private Object createManagedObject() { |
| em.getTransaction().begin(); |
| X x = new X(); |
| x.setName("New Object"); |
| x.setPrice(100); |
| em.persist(x); |
| em.getTransaction().commit(); |
| |
| return emf.getPersistenceUnitUtil().getIdentifier(x); |
| } |
| |
| private void clearAuditedEntries() { |
| em.getTransaction().begin(); |
| em.createQuery("delete from AuditedEntry a").executeUpdate(); |
| em.getTransaction().commit(); |
| } |
| |
| public void testAuditorIsConfigured() { |
| assertNotNull(auditor); |
| } |
| |
| public void testIsEntityAuditable() { |
| assertNotNull(X.class.getAnnotation(Auditable.class)); |
| } |
| |
| public void testNewInstancesAreAudited() { |
| X x = em.find(X.class, oid); |
| assertNotNull(x); |
| |
| AuditedEntry entry = findLastAuditedEntry(AuditableOperation.CREATE); |
| |
| assertNotNull(entry); |
| assertEquals(x, entry.getAudited()); |
| assertEquals(AuditableOperation.CREATE, entry.getOperation()); |
| assertEquals(X.class, entry.getAudited().getClass()); |
| assertTrue(entry.getUpdatedFields().isEmpty()); |
| } |
| |
| public void testUpdateOutsideTransactionAreAudited() { |
| X x = em.find(X.class, oid); |
| assertNotNull(x); |
| |
| x.setName("Updated Object outside transaction"); |
| |
| em.getTransaction().begin(); |
| x = em.merge(x); |
| em.getTransaction().commit(); |
| |
| AuditedEntry entry = findLastAuditedEntry(AuditableOperation.UPDATE); |
| |
| assertNotNull(entry); |
| assertEquals(x, entry.getAudited()); |
| assertEquals(AuditableOperation.UPDATE, entry.getOperation()); |
| assertTrue(entry.getUpdatedFields().contains("name")); |
| assertFalse(entry.getUpdatedFields().contains("price")); |
| } |
| |
| public void testUpdateInsideTransactionAreAudited() { |
| X x = em.find(X.class, oid); |
| assertNotNull(x); |
| |
| |
| em.getTransaction().begin(); |
| x.setPrice(x.getPrice()+100); |
| x = em.merge(x); |
| em.getTransaction().commit(); |
| |
| AuditedEntry entry = findLastAuditedEntry(AuditableOperation.UPDATE); |
| |
| |
| assertNotNull(entry); |
| assertEquals(x, entry.getAudited()); |
| assertEquals(AuditableOperation.UPDATE, entry.getOperation()); |
| assertFalse(entry.getUpdatedFields().contains("name")); |
| assertTrue(entry.getUpdatedFields().contains("price")); |
| } |
| |
| public void testAuditDoesNotLeakMemory() { |
| int N = 1000; |
| EntityManager em = emf.createEntityManager(); |
| long m2 = insert(N, em); |
| em = Persistence.createEntityManagerFactory("no-audit").createEntityManager(); |
| assertNull(OpenJPAPersistence.cast(em).getEntityManagerFactory().getConfiguration().getAuditorInstance()); |
| long m0 = insert(N, em); |
| System.err.println("Memory used with no auditor " + m0); |
| System.err.println("Memory used with auditor " + m2); |
| double pct = 100.0*(m2-m0)/m0; |
| System.err.println("Extra memory with auditor " + pct); |
| assertTrue(pct < 10.0); |
| } |
| |
| private long insert(int N, EntityManager em) { |
| assertTrue(ensureGarbageCollection()); |
| long m1 = Runtime.getRuntime().freeMemory(); |
| em.getTransaction().begin(); |
| for (int i = 0; i < N; i++) { |
| X x = new X(); |
| x.setName("X"+System.currentTimeMillis()); |
| em.persist(x); |
| } |
| em.getTransaction().commit(); |
| assertTrue(ensureGarbageCollection()); |
| long m2 = Runtime.getRuntime().freeMemory(); |
| long mused = m1-m2; |
| |
| return mused; |
| } |
| |
| /** |
| * Finds the latest audit entry of the given operation type. |
| * The <em>latest</em> is determined by a sort on identifier which is assumed to be monotonically ascending. |
| * |
| */ |
| AuditedEntry findLastAuditedEntry(AuditableOperation op) { |
| List<AuditedEntry> entry = |
| em.createQuery("select a from AuditedEntry a where a.operation=:op order by a.id desc", AuditedEntry.class) |
| .setMaxResults(1).setParameter("op", op).getResultList(); |
| return entry.get(0); |
| } |
| |
| public boolean ensureGarbageCollection() { |
| ReferenceQueue<Object> detector = new ReferenceQueue<>(); |
| Object marker = new Object(); |
| WeakReference<Object> ref = new WeakReference<>(marker, detector); |
| marker = null; |
| System.gc(); |
| try { |
| return detector.remove() == ref; |
| } catch (InterruptedException e) { |
| |
| } |
| return false; |
| } |
| |
| |
| |
| } |