diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/TestAudit.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/TestAudit.java index 561c2f061..e7ad8efe0 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/TestAudit.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/TestAudit.java @@ -19,6 +19,8 @@ package org.apache.openjpa.audit; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.util.List; import javax.persistence.EntityManager; @@ -132,6 +134,37 @@ public class TestAudit extends TestCase { 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 latest is determined by a sort on identifier which is assumed to be monotonically ascending. @@ -143,6 +176,21 @@ public class TestAudit extends TestCase { .setMaxResults(1).setParameter("op", op).getResultList(); return entry.get(0); } + + public boolean ensureGarbageCollection() { + ReferenceQueue detector = new ReferenceQueue(); + Object marker = new Object(); + WeakReference ref = new WeakReference(marker, detector); + marker = null; + System.gc(); + try { + return detector.remove() == ref; + } catch (InterruptedException e) { + + } + return false; + } + } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/X.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/X.java index bec2cf52f..0353137a4 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/X.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/audit/X.java @@ -18,6 +18,8 @@ */ package org.apache.openjpa.audit; +import java.util.concurrent.atomic.AtomicLong; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @@ -34,18 +36,25 @@ import javax.persistence.Id; @Auditable public class X { @Id - @GeneratedValue private long id; private String name; private int price; + private static AtomicLong ID_GENERATOR = new AtomicLong(System.currentTimeMillis()); + + public X() { + id = ID_GENERATOR.getAndIncrement(); + } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getPrice() { return price; } diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml index 3ecb58f15..17a9e2f9b 100644 --- a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml @@ -452,6 +452,15 @@ + + org.apache.openjpa.audit.X + org.apache.openjpa.audit.AuditedEntry + + + + + + META-INF/query-result-orm.xml