(no commit message)
authorErik Brakkee <erik@brakkee.org>
Wed, 21 Jul 2010 18:48:03 +0000 (18:48 +0000)
committerErik Brakkee <erik@brakkee.org>
Wed, 21 Jul 2010 18:48:03 +0000 (18:48 +0000)
support/general/pom.xml
support/general/src/main/java/org/wamblee/persistence/AbstractDetachable.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/persistence/Detachable.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/persistence/InMemoryDetachable.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/persistence/JpaDetachable.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/persistence/PersistentFactory.java
support/general/src/test/java/org/wamblee/persistence/AbstractDetachableTest.java [new file with mode: 0644]
support/general/src/test/java/org/wamblee/persistence/InMemoryDetachableTest.java [new file with mode: 0644]
support/general/src/test/java/org/wamblee/persistence/JpaDetachableTest.java [new file with mode: 0644]

index f76dd170f9c427d258a063c5f8160832ee8ef855..da1ff2a5d5ee856a694f43bfd359cc3829c3aa75 100644 (file)
   <name>/support/general</name>
   <url>http://wamblee.org</url>
   <dependencies>
+    <dependency>
+      <groupId>javax.persistence</groupId>
+      <artifactId>persistence-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
     <dependency>
       <groupId>javax</groupId>
       <artifactId>javaee-api</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>oro</groupId>
diff --git a/support/general/src/main/java/org/wamblee/persistence/AbstractDetachable.java b/support/general/src/main/java/org/wamblee/persistence/AbstractDetachable.java
new file mode 100644 (file)
index 0000000..1e7461b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+/**
+ * Detachable implementation that takes care of the basic logic for detachable
+ * objects. All that needs to be done is to implement {@link #load()}.
+ * 
+ * @param <T>
+ *            The type of the object to be attached/detached
+ * @param <Ref>
+ *            The type of the reference to store when the object is detached.
+ */
+public abstract class AbstractDetachable<T, Ref> implements Detachable<T> {
+
+    private T object;
+    private Ref reference;
+
+    /**
+     * Constructs the detachable.
+     * 
+     * @param aObject
+     *            Object.
+     * @throws IllegalArgumentException
+     *             When the object passed in is null.
+     */
+    protected AbstractDetachable(T aObject) {
+        if (aObject == null) {
+            throw new IllegalArgumentException("Object '" + aObject +
+                "' is null");
+        }
+        object = aObject;
+        reference = null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.wamblee.persistence.Detachable#detach()
+     */
+    public void detach() {
+        if (object == null) {
+            return; // Nothing to do.
+        }
+        reference = getReference(object);
+        if (reference == null) {
+            throw new IllegalStateException("Object '" + object +
+                "' not persisted yet'");
+        }
+        object = null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.wamblee.persistence.Detachable#get()
+     */
+    public T get() {
+        if (object == null) {
+            object = load(reference);
+            reference = null;
+        }
+        return object;
+    }
+
+    /**
+     * For testing.
+     * 
+     * @return current object.
+     */
+    T getObject() {
+        return object;
+    }
+
+    /**
+     * For testing.
+     * 
+     * @return The reference.
+     */
+    Ref getReference() {
+        return reference;
+    }
+
+    /**
+     * Loads the object based on a reference.
+     * 
+     * @param aReference
+     *            Reference.
+     * @return Object (may be null ).
+     */
+    protected abstract T load(Ref aReference);
+
+    /**
+     * Obtains the reference for a given object.
+     * 
+     * @param aObject
+     *            Object.
+     * @return Reference.
+     */
+    protected abstract Ref getReference(T aObject);
+
+}
diff --git a/support/general/src/main/java/org/wamblee/persistence/Detachable.java b/support/general/src/main/java/org/wamblee/persistence/Detachable.java
new file mode 100644 (file)
index 0000000..0d681d0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+import java.io.Serializable;
+
+/**
+ * Represents a detachable object. It represent a reference to a persistent object. 
+ * The object is detachable in that the memory footprint can be reduced by not keeping 
+ * a reference to the complete java object, but only to its identify in persistent 
+ * storage. This is typically the primary key in a relational database. 
+ */
+public interface Detachable<T> extends Serializable {
+       
+       /** 
+        * Detaches the object. 
+        */
+       void detach(); 
+       
+       /**
+        * Gets the object, attaching it if needed.
+        * @return
+        */
+       T get(); 
+
+}
diff --git a/support/general/src/main/java/org/wamblee/persistence/InMemoryDetachable.java b/support/general/src/main/java/org/wamblee/persistence/InMemoryDetachable.java
new file mode 100644 (file)
index 0000000..f8ba0c3
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+/**
+ * Inmemory detachable that simply returns the object passed in. Typiocally used for testing. 
+ * It does nothing at detach. 
+ * 
+ * @param <T>
+ *            Type of the detachable.
+ */
+public class InMemoryDetachable<T> implements Detachable<T> {
+
+    private T object;
+
+    /**
+     * Constructs the detachable.
+     * @param aObject
+     */
+    public InMemoryDetachable(T aObject) {
+        object = aObject;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.wamblee.persistence.Detachable#detach()
+     */
+    public void detach() {
+        // No op.
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.wamblee.persistence.Detachable#get()
+     */
+    public T get() {
+        return object;
+    }
+
+}
diff --git a/support/general/src/main/java/org/wamblee/persistence/JpaDetachable.java b/support/general/src/main/java/org/wamblee/persistence/JpaDetachable.java
new file mode 100644 (file)
index 0000000..ac0912f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+import java.io.Serializable;
+
+import javax.persistence.EntityManager;
+
+/**
+ * Detachable JPA entity. 
+ * 
+ * @author Erik Brakkee
+ *
+ * @param <T> Entity type. 
+ */
+public class JpaDetachable<T> extends AbstractDetachable<T, Serializable> {
+    
+    private EntityManager entityManager; 
+    private Class<T> entityType; 
+    
+    /**
+     * Constructs the detachable. 
+     * @param aEntity Entity. 
+     */
+    public JpaDetachable(EntityManager aEntityManager, T aEntity) {
+        super(aEntity);
+        entityManager = aEntityManager;
+        entityType = (Class<T>)aEntity.getClass();
+    }
+    
+    /*
+     * (non-Javadoc)
+     * @see org.wamblee.persistence.AbstractDetachable#getReference(java.lang.Object)
+     */
+    protected Serializable getReference(T aObject) {
+        Persistent persistent = PersistentFactory.create(aObject);
+        return persistent.getPrimaryKey(); 
+    }
+    
+    @Override
+    protected T load(Serializable aReference) {
+        return entityManager.find(entityType, aReference);
+    }
+}
index 6b24e3f06e3f32a8b8f1391e09961d12d2e5ce9c..2ad1526599adf13e29df8a6afe21b9671b02e948 100644 (file)
@@ -132,15 +132,15 @@ public class PersistentFactory {
         List<Accessor> pkAccessors = AnnotationUtils.analyse(aClass, Id.class);
         List<Accessor> versionAccessors = AnnotationUtils.analyse(aClass,
             Version.class);
-        Accessor<Serializable> pk = null; 
-        if ( pkAccessors.size() > 0 ) { 
+        Accessor<Serializable> pk = null;
+        if (pkAccessors.size() > 0) {
             pk = pkAccessors.get(0);
         }
-        Accessor<Integer> version = null; 
-        if ( versionAccessors.size() > 0 ) { 
+        Accessor<Integer> version = null;
+        if (versionAccessors.size() > 0) {
             version = versionAccessors.get(0);
         }
-        if (pk != null|| version != null) {
+        if (pk != null || version != null) {
             return new EntityAccessor(pk, version);
         }
         return null;
diff --git a/support/general/src/test/java/org/wamblee/persistence/AbstractDetachableTest.java b/support/general/src/test/java/org/wamblee/persistence/AbstractDetachableTest.java
new file mode 100644 (file)
index 0000000..0a342e7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+import static org.mockito.Mockito.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.TestCase.*;
+
+public class AbstractDetachableTest {
+
+    public static class X {
+
+    }
+
+    public static interface DetachableMethods {
+        Integer getReference(X aObject);
+
+        X load(Integer aReference);
+    }
+
+    public class MyDetachable extends AbstractDetachable<X, Integer> {
+
+        public MyDetachable(X aObj) {
+            super(aObj);
+        }
+
+        @Override
+        protected Integer getReference(X aObject) {
+            return methods.getReference(aObject);
+        }
+
+        @Override
+        protected X load(Integer aReference) {
+            return methods.load(aReference);
+        }
+
+    }
+
+    private DetachableMethods methods;
+    private AbstractDetachable<X, Integer> detachable;
+    private X x; 
+
+    @Before
+    public void setUp() {
+        methods = mock(DetachableMethods.class);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullPassedIn() {
+        detachable = new MyDetachable(null);
+    }
+
+    @Test
+    public void testDetach() {
+        x = new X();
+        Integer ref = 100;
+        when(methods.getReference(any(X.class))).thenReturn(ref);
+        detachable = new MyDetachable(x);
+        verifyNoMoreInteractions(methods);
+        detachable.detach();
+        verify(methods).getReference(same(x));
+        assertNull(detachable.getObject());
+        assertEquals(ref, detachable.getReference());
+    }
+    
+    @Test
+    public void testDetachTwice() { 
+        reset(methods); 
+        testDetach(); 
+        detachable.detach(); 
+        detachable.detach(); 
+        verifyNoMoreInteractions(methods);
+    }
+    
+    @Test(expected = IllegalStateException.class) 
+    public void testDetachWithNullReference() { 
+        x = new X();
+        Integer ref = 100;
+        when(methods.getReference(any(X.class))).thenReturn(null);
+        detachable = new MyDetachable(x);
+        verifyNoMoreInteractions(methods);
+        detachable.detach();
+    }
+    
+    @Test
+    public void testReattach() {
+        testDetach(); 
+        reset(methods);
+        
+        X x2 = new X(); // the new X loaded from persistent storage.
+        when(methods.load(detachable.getReference())).thenReturn(x2); 
+        
+        X x3 = detachable.get();
+        verify(methods).load(100); 
+        assertSame(x2, x3); 
+        
+        assertSame(x2, detachable.getObject());
+    }
+    
+    @Test
+    public void testGetInvokedTwiceNoLoads() { 
+        testReattach(); 
+        reset(methods); 
+        detachable.get(); 
+        detachable.get(); 
+        verifyNoMoreInteractions(methods);
+    }
+    
+
+}
diff --git a/support/general/src/test/java/org/wamblee/persistence/InMemoryDetachableTest.java b/support/general/src/test/java/org/wamblee/persistence/InMemoryDetachableTest.java
new file mode 100644 (file)
index 0000000..b7d0a01
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+import static junit.framework.Assert.*;
+
+import org.junit.Test;
+
+public class InMemoryDetachableTest {
+
+    public static class X {
+
+    }
+
+    @Test
+    public void testXxx() {
+        X x = new X();
+        InMemoryDetachable<X> detachable = new InMemoryDetachable<X>(x);
+        assertSame(x, detachable.get());
+    }
+}
diff --git a/support/general/src/test/java/org/wamblee/persistence/JpaDetachableTest.java b/support/general/src/test/java/org/wamblee/persistence/JpaDetachableTest.java
new file mode 100644 (file)
index 0000000..0fee2db
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed 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.wamblee.persistence;
+
+import static org.mockito.Mockito.*;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Id;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.TestCase.*;
+
+public class JpaDetachableTest {
+    
+    public class X { 
+        @Id
+        private int id; 
+        
+    }
+    
+    private EntityManager em; 
+    
+    @Before 
+    public void setUp() { 
+        em = mock(EntityManager.class);
+    }
+    
+    @Test
+    public void testDetachAttach() { 
+        X x = new X(); 
+        x.id = 100; 
+        
+        JpaDetachable<X> d = new JpaDetachable<X>(em, x);
+        assertNull(d.getReference());
+        d.detach();
+        assertEquals(100, d.getReference());
+        
+        X x2 = new X();
+        x2.id = 100;
+        when(em.find(X.class, 100)).thenReturn(x2);
+        X x3 = d.get();
+        assertSame(x2, x3); 
+        verify(em).find(X.class, 100);
+        
+    }
+}