Jpa merge support is now available.
[utils] / support / general / src / main / java / org / wamblee / persistence / PersistentFactory.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
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
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
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.
15  */
16 package org.wamblee.persistence;
17
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;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26
27 import javax.persistence.Id;
28 import javax.persistence.Version;
29
30 import org.wamblee.reflection.ReflectionUtils;
31
32 /**
33  * Factory which creates a {@link Persistent} object for a given entity for
34  * interfacing with the primary key and version of the entity.
35  * 
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.
39  * 
40  * @author Erik Brakkee
41  * 
42  */
43 public class PersistentFactory {
44
45     /**
46      * Cache of a mapping of class names to entity accessors.
47      */
48     private static Map<String, EntityAccessor> CACHE = new ConcurrentHashMap<String, EntityAccessor>();
49
50     static interface Accessor<T> {
51         void set(Object aEntity, T aValue);
52
53         T get(Object aEntity);
54     }
55
56     static class FieldAccessor<T> implements Accessor<T> {
57         private Field field;
58
59         public FieldAccessor(Field aField) {
60             field = aField;
61             field.setAccessible(true);
62         }
63
64         @Override
65         public T get(Object aEntity) {
66             try {
67                 T value = (T) field.get(aEntity);
68                 return value;
69             } catch (Exception e) {
70                 throw new RuntimeException(e);
71             }
72         }
73
74         @Override
75         public void set(Object aEntity, T aValue) {
76             try {
77                 field.set(aEntity, aValue);
78             } catch (IllegalAccessException e) {
79                 throw new RuntimeException(e.getMessage(), e);
80             }
81         }
82
83         public Field getField() {
84             return field;
85         }
86     }
87
88     static class PropertyAccessor<T> implements Accessor<T> {
89         private Method getter;
90         private Method setter;
91
92         public PropertyAccessor(Method aGetter, Method aSetter) {
93             getter = aGetter;
94             setter = aSetter;
95             getter.setAccessible(true);
96             setter.setAccessible(true);
97         }
98
99         @Override
100         public T get(Object aEntity) {
101             try {
102                 return (T) getter.invoke(aEntity);
103             } catch (Exception e) {
104                 throw new RuntimeException(e);
105             }
106         }
107
108         @Override
109         public void set(Object aEntity, T aValue) {
110             try {
111                 setter.invoke(aEntity, aValue);
112             } catch (Exception e) {
113                 throw new RuntimeException(e);
114             }
115         }
116
117         public Method getGetter() {
118             return getter;
119         }
120
121         public Method getSetter() {
122             return setter;
123         }
124     }
125
126     static class EntityAccessor {
127         private Accessor pk;
128         private Accessor version;
129
130         public EntityAccessor(Accessor<?> aPk, Accessor<?> aVersion) {
131             pk = aPk;
132             version = aVersion;
133         }
134
135         public Accessor getPk() {
136             return pk;
137         }
138
139         public Accessor getVersion() {
140             return version;
141         }
142     }
143
144     public static class EntityObjectAccessor implements Persistent {
145         private EntityAccessor accessor;
146         private Object entity;
147
148         public EntityObjectAccessor(Object aEntity, EntityAccessor aAccessor) {
149             accessor = aAccessor;
150             entity = aEntity;
151         }
152
153         public EntityAccessor getAccessor() {
154             return accessor;
155         };
156
157         @Override
158         public Serializable getPrimaryKey() {
159             if (accessor == null || accessor.getPk() == null) {
160                 return null;
161             }
162             return (Serializable) accessor.getPk().get(entity);
163         }
164
165         @Override
166         public void setPrimaryKey(Serializable aKey) {
167             if (accessor == null || accessor.getPk() == null) {
168                 return;
169             }
170             accessor.getPk().set(entity, aKey);
171         }
172
173         @Override
174         public Number getPersistedVersion() {
175             if ( accessor == null || accessor.getVersion() == null) { 
176                 return null; 
177             }
178             return (Number) accessor.getVersion().get(entity);
179         }
180
181         @Override
182         public void setPersistedVersion(Number aVersion) {
183             if ( accessor == null || accessor.getVersion() == null) { 
184                 return; 
185             }
186             accessor.getVersion().set(entity, aVersion);
187         }
188     }
189
190     /**
191      * Create the entity accessor for a given class or returns a cached instance
192      * if one already exists.
193      * 
194      * @param aClass
195      *            Class.
196      * @return Entity accessor for the given class or null of the given object
197      *         is not an entity.
198      */
199     public static EntityAccessor createEntityAccessor(Class aClass) {
200         EntityAccessor accessor = CACHE.get(aClass.getName());
201         if (accessor == null) {
202             accessor = analyse(aClass);
203             if (accessor != null) {
204                 CACHE.put(aClass.getName(), accessor);
205             }
206         }
207         return accessor;
208     }
209
210     private static EntityAccessor analyse(Class aClass) {
211         Accessor<Serializable> pk = analyse(aClass, Id.class);
212         Accessor<Integer> version = analyse(aClass, Version.class);
213         if (pk != null || version != null) {
214             return new EntityAccessor(pk, version);
215         }
216         return null;
217     }
218
219     /**
220      * Returns the accessor for a given annotation.
221      * 
222      * @param aClass
223      *            Class to analyse.
224      * @param aAnnotation
225      *            Annotation that must be present.
226      * @return Accessor to use or null if the annotation is not present.
227      */
228     private static Accessor analyse(Class aClass,
229         Class<? extends Annotation> aAnnotation) {
230         List<Field> fields = ReflectionUtils.getAllFields(aClass);
231         for (Field field : fields) {
232             if (field.isAnnotationPresent(aAnnotation)) {
233                 return new FieldAccessor(field);
234             }
235         }
236         List<Method> methods = ReflectionUtils.getAllMethods(aClass,
237             Object.class);
238         for (Method method : methods) {
239             if (method.isAnnotationPresent(aAnnotation)) {
240                 String setterName = null;
241                 if (method.getName().startsWith("get")) {
242                     setterName = method.getName().replaceFirst("get", "set");
243                 } else if (method.getName().startsWith("is")) {
244                     setterName = method.getName().replaceFirst("is", "set");
245                 }
246                 try {
247                     Class returnType = method.getReturnType();
248                     Method setter = method.getDeclaringClass()
249                         .getDeclaredMethod(setterName, returnType);
250                     return new PropertyAccessor(method, setter);
251                 } catch (NoSuchMethodException e) {
252                     throw new RuntimeException("Error obtaining setter for " +
253                         method.getName() + " in class " + aClass.getName(), e);
254                 }
255             }
256         }
257         return null;
258     }
259
260     /**
261      * Creates the {@link Persistent} wrapper for interfacing with primary key
262      * and version of the entity.
263      * 
264      * @param aEntity
265      *            Entity to use.
266      * @return Persistent object or null if this is not an entity.
267      */
268     public static Persistent create(Object aEntity) {
269         EntityAccessor accessor = createEntityAccessor(aEntity.getClass());
270         if (accessor == null) {
271             return null;
272         }
273         return new EntityObjectAccessor(aEntity, accessor);
274     }
275 }