2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.persistence;
18 import java.io.Serializable;
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.List;
25 import java.util.concurrent.ConcurrentHashMap;
27 import javax.persistence.Id;
28 import javax.persistence.Version;
30 import org.wamblee.reflection.ReflectionUtils;
33 * Factory which creates a {@link Persistent} object for a given entity for
34 * interfacing with the primary key and version of the entity.
36 * This utility only treats primary keys and fields that are annotated with @Id
37 * and @Version. In case ORM files are used for the definition of primary key
38 * and or version, then those fields are ignored.
40 * @author Erik Brakkee
43 public class PersistentFactory {
46 * Cache of a mapping of class names to entity accessors.
48 private static Map<String, EntityAccessor> CACHE = new ConcurrentHashMap<String, EntityAccessor>();
50 static interface Accessor<T> {
51 void set(Object aEntity, T aValue);
53 T get(Object aEntity);
56 static class FieldAccessor<T> implements Accessor<T> {
59 public FieldAccessor(Field aField) {
61 field.setAccessible(true);
65 public T get(Object aEntity) {
67 return (T) field.get(aEntity);
68 } catch (Exception e) {
69 throw new RuntimeException(e);
74 public void set(Object aEntity, T aValue) {
76 field.set(aEntity, aValue);
77 } catch (IllegalAccessException e) {
78 throw new RuntimeException(e.getMessage(), e);
82 public Field getField() {
87 static class PropertyAccessor<T> implements Accessor<T> {
88 private Method getter;
89 private Method setter;
91 public PropertyAccessor(Method aGetter, Method aSetter) {
94 getter.setAccessible(true);
95 setter.setAccessible(true);
99 public T get(Object aEntity) {
101 return (T) getter.invoke(aEntity);
102 } catch (Exception e) {
103 throw new RuntimeException(e);
108 public void set(Object aEntity, T aValue) {
110 setter.invoke(aEntity, aValue);
111 } catch (Exception e) {
112 throw new RuntimeException(e);
116 public Method getGetter() {
120 public Method getSetter() {
125 static class EntityAccessor {
127 private Accessor version;
129 public EntityAccessor(Accessor<?> aPk, Accessor<?> aVersion) {
134 public Accessor getPk() {
138 public Accessor getVersion() {
143 public static class EntityObjectAccessor implements Persistent {
144 private EntityAccessor accessor;
145 private Object entity;
147 public EntityObjectAccessor(Object aEntity, EntityAccessor aAccessor) {
148 accessor = aAccessor;
152 public EntityAccessor getAccessor() {
157 public Serializable getPrimaryKey() {
158 return (Serializable)accessor.getPk().get(entity);
162 public void setPrimaryKey(Serializable aKey) {
163 accessor.getPk().set(entity, aKey);
167 public Number getPersistedVersion() {
168 return (Number)accessor.getVersion().get(entity);
172 public void setPersistedVersion(Number aVersion) {
173 accessor.getVersion().set(entity, aVersion);
178 * Create the entity accessor for a given class or returns a cached instance
179 * if one already exists.
183 * @return Entity accessor for the given class or null of the given object
186 public static EntityAccessor createEntityAccessor(Class aClass) {
187 EntityAccessor accessor = CACHE.get(aClass.getName());
188 if (accessor == null) {
189 accessor = analyse(aClass);
190 if (accessor != null) {
191 CACHE.put(aClass.getName(), accessor);
197 private static EntityAccessor analyse(Class aClass) {
198 Accessor<Serializable> pk = analyse(aClass, Id.class);
199 Accessor<Integer> version = analyse(aClass, Version.class);
200 if (pk != null || version != null) {
201 return new EntityAccessor(pk, version);
207 * Returns the accessor for a given annotation.
212 * Annotation that must be present.
213 * @return Accessor to use or null if the annotation is not present.
215 private static Accessor analyse(Class aClass,
216 Class<? extends Annotation> aAnnotation) {
217 List<Field> fields = ReflectionUtils.getAllFields(aClass);
218 for (Field field : fields) {
219 if (field.isAnnotationPresent(aAnnotation)) {
220 return new FieldAccessor(field);
223 List<Method> methods = ReflectionUtils.getAllMethods(aClass,
225 for (Method method : methods) {
226 if (method.isAnnotationPresent(aAnnotation)) {
227 String setterName = null;
228 if (method.getName().startsWith("get")) {
229 setterName = method.getName().replaceFirst("get", "set");
230 } else if (method.getName().startsWith("is")) {
231 setterName = method.getName().replaceFirst("is", "set");
234 Class returnType = method.getReturnType();
235 Method setter = method.getDeclaringClass().getDeclaredMethod(setterName, returnType);
236 return new PropertyAccessor(method, setter);
237 } catch (NoSuchMethodException e) {
238 throw new RuntimeException("Error obtaining setter for " +
239 method.getName() + " in class " + aClass.getName(), e);
247 * Creates the {@link Persistent} wrapper for interfacing with primary key and
248 * version of the entity.
249 * @param aEntity Entity to use.
250 * @return Persistent object or null if this is not an entity.
252 public static Persistent create(Object aEntity) {
253 EntityAccessor accessor = createEntityAccessor(aEntity.getClass());
254 if ( accessor == null ) {
257 return new EntityObjectAccessor(aEntity, accessor);