(no commit message)
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Fri, 30 Apr 2010 10:06:20 +0000 (10:06 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Fri, 30 Apr 2010 10:06:20 +0000 (10:06 +0000)
support/general/src/main/java/org/wamblee/persistence/Persistent.java
support/general/src/main/java/org/wamblee/persistence/PersistentFactory.java [new file with mode: 0644]

index 7b78691c2a4b00611ebe9086cf599dfe76440a02..8eac4565275dffeed78b319735af6596109f4958 100644 (file)
@@ -33,7 +33,7 @@ public interface Persistent {
      * 
      * @see #setPrimaryKey(Serializable)
      */
-    Long getPrimaryKey();
+    Serializable getPrimaryKey();
 
     /**
      * Sets the primary key.
@@ -43,7 +43,7 @@ public interface Persistent {
      * 
      * @see #getPrimaryKey()
      */
-    void setPrimaryKey(Long aKey);
+    void setPrimaryKey(Serializable aKey);
 
     /**
      * Gets the version.
@@ -52,7 +52,7 @@ public interface Persistent {
      * 
      * @see #setPersistedVersion(int)
      */
-    int getPersistedVersion();
+    Number getPersistedVersion();
 
     /**
      * Sets the version.
@@ -62,5 +62,5 @@ public interface Persistent {
      * 
      * @see #getPersistedVersion()
      */
-    void setPersistedVersion(int aVersion);
+    void setPersistedVersion(Number aVersion);
 }
diff --git a/support/general/src/main/java/org/wamblee/persistence/PersistentFactory.java b/support/general/src/main/java/org/wamblee/persistence/PersistentFactory.java
new file mode 100644 (file)
index 0000000..91fd2b3
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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 java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.persistence.Id;
+import javax.persistence.Version;
+
+import org.wamblee.reflection.ReflectionUtils;
+
+/**
+ * Factory which creates a {@link Persistent} object for a given entity for
+ * interfacing with the primary key and version of the entity.
+ * 
+ * This utility only treats primary keys and fields that are annotated with @Id
+ * and @Version. In case ORM files are used for the definition of primary key
+ * and or version, then those fields are ignored.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
+public class PersistentFactory {
+
+    /**
+     * Cache of a mapping of class names to entity accessors.
+     */
+    private static Map<String, EntityAccessor> CACHE = new ConcurrentHashMap<String, EntityAccessor>();
+
+    static interface Accessor<T> {
+        void set(Object aEntity, T aValue);
+
+        T get(Object aEntity);
+    }
+
+    static class FieldAccessor<T> implements Accessor<T> {
+        private Field field;
+
+        public FieldAccessor(Field aField) {
+            field = aField;
+            field.setAccessible(true);
+        }
+
+        @Override
+        public T get(Object aEntity) {
+            try {
+                return (T) field.get(aEntity);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            } 
+        }
+
+        @Override
+        public void set(Object aEntity, T aValue) {
+            try {
+                field.set(aEntity, aValue);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+        }
+
+        public Field getField() {
+            return field;
+        }
+    }
+
+    static class PropertyAccessor<T> implements Accessor<T> {
+        private Method getter;
+        private Method setter;
+
+        public PropertyAccessor(Method aGetter, Method aSetter) {
+            getter = aGetter;
+            setter = aSetter;
+            getter.setAccessible(true);
+            setter.setAccessible(true);
+        }
+
+        @Override
+        public T get(Object aEntity) {
+            try {
+                return (T) getter.invoke(aEntity);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void set(Object aEntity, T aValue) {
+            try {
+                setter.invoke(aEntity, aValue);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            } 
+        }
+
+        public Method getGetter() {
+            return getter;
+        }
+
+        public Method getSetter() {
+            return setter;
+        }
+    }
+
+    static class EntityAccessor {
+        private Accessor pk;
+        private Accessor version;
+
+        public EntityAccessor(Accessor<?> aPk, Accessor<?> aVersion) {
+            pk = aPk;
+            version = aVersion;
+        }
+
+        public Accessor getPk() {
+            return pk;
+        }
+
+        public Accessor getVersion() {
+            return version;
+        }
+    }
+
+    public static class EntityObjectAccessor implements Persistent {
+        private EntityAccessor accessor;
+        private Object entity;
+
+        public EntityObjectAccessor(Object aEntity, EntityAccessor aAccessor) {
+            accessor = aAccessor;
+            entity = aEntity;
+        }
+
+        public EntityAccessor getAccessor() {
+            return accessor;
+        };
+
+        @Override
+        public Serializable getPrimaryKey() {
+            return (Serializable)accessor.getPk().get(entity);
+        }
+
+        @Override
+        public void setPrimaryKey(Serializable aKey) {
+            accessor.getPk().set(entity, aKey);
+        } 
+
+        @Override
+        public Number getPersistedVersion() {
+            return (Number)accessor.getVersion().get(entity);
+        }
+
+        @Override
+        public void setPersistedVersion(Number aVersion) {
+            accessor.getVersion().set(entity, aVersion);
+        }
+    }
+
+    /**
+     * Create the entity accessor for a given class or returns a cached instance
+     * if one already exists.
+     * 
+     * @param aClass
+     *            Class.
+     * @return Entity accessor for the given class or null of the given object
+     *         is not an entity.
+     */
+    public static EntityAccessor createEntityAccessor(Class aClass) {
+        EntityAccessor accessor = CACHE.get(aClass.getName());
+        if (accessor == null) {
+            accessor = analyse(aClass);
+            if (accessor != null) {
+                CACHE.put(aClass.getName(), accessor);
+            }
+        }
+        return accessor;
+    }
+
+    private static EntityAccessor analyse(Class aClass) {
+        Accessor<Serializable> pk = analyse(aClass, Id.class);
+        Accessor<Integer> version = analyse(aClass, Version.class);
+        if (pk != null || version != null) {
+            return new EntityAccessor(pk, version);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the accessor for a given annotation.
+     * 
+     * @param aClass
+     *            Class to analyse.
+     * @param aAnnotation
+     *            Annotation that must be present.
+     * @return Accessor to use or null if the annotation is not present.
+     */
+    private static Accessor analyse(Class aClass,
+        Class<? extends Annotation> aAnnotation) {
+        List<Field> fields = ReflectionUtils.getAllFields(aClass);
+        for (Field field : fields) {
+            if (field.isAnnotationPresent(aAnnotation)) {
+                return new FieldAccessor(field);
+            }
+        }
+        List<Method> methods = ReflectionUtils.getAllMethods(aClass,
+            Object.class);
+        for (Method method : methods) {
+            if (method.isAnnotationPresent(aAnnotation)) {
+                String setterName = null;
+                if (method.getName().startsWith("get")) {
+                    setterName = method.getName().replaceFirst("get", "set");
+                } else if (method.getName().startsWith("is")) {
+                    setterName = method.getName().replaceFirst("is", "set");
+                }
+                try {
+                    Class returnType = method.getReturnType();
+                    Method setter = method.getDeclaringClass().getDeclaredMethod(setterName, returnType);
+                    return new PropertyAccessor(method, setter);
+                } catch (NoSuchMethodException e) {
+                    throw new RuntimeException("Error obtaining setter for " +
+                        method.getName() + " in class " + aClass.getName(), e);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates the {@link Persistent} wrapper for interfacing with primary key and 
+     * version of the entity. 
+     * @param aEntity Entity to use. 
+     * @return Persistent object or null if this is not an entity. 
+     */
+    public static Persistent create(Object aEntity) { 
+        EntityAccessor accessor = createEntityAccessor(aEntity.getClass());
+        if ( accessor == null ) { 
+            return null; 
+        }
+        return new EntityObjectAccessor(aEntity, accessor);
+    }
+}