blob: 1e8f54d305b5441ea4148a7f7ac522a3c2c05061 [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.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;
}
}