From: erik Date: Fri, 30 Apr 2010 10:06:20 +0000 (+0000) Subject: (no commit message) X-Git-Tag: wamblee-utils-0.2.2^2~27 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=0a30e6b23a92b8fd8dffeff6701cc3021d33f3e8;p=utils --- diff --git a/support/general/src/main/java/org/wamblee/persistence/Persistent.java b/support/general/src/main/java/org/wamblee/persistence/Persistent.java index 7b78691c..8eac4565 100644 --- a/support/general/src/main/java/org/wamblee/persistence/Persistent.java +++ b/support/general/src/main/java/org/wamblee/persistence/Persistent.java @@ -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 index 00000000..91fd2b38 --- /dev/null +++ b/support/general/src/main/java/org/wamblee/persistence/PersistentFactory.java @@ -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 CACHE = new ConcurrentHashMap(); + + static interface Accessor { + void set(Object aEntity, T aValue); + + T get(Object aEntity); + } + + static class FieldAccessor implements Accessor { + 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 implements Accessor { + 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 pk = analyse(aClass, Id.class); + Accessor 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 aAnnotation) { + List fields = ReflectionUtils.getAllFields(aClass); + for (Field field : fields) { + if (field.isAnnotationPresent(aAnnotation)) { + return new FieldAccessor(field); + } + } + List 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); + } +}