1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.util;
20
21 import java.util.ArrayList;
22 import java.util.BitSet;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.Map;
27
28 import org.apache.openjpa.enhance.PersistenceCapable;
29 import org.apache.openjpa.enhance.PCRegistry;
30 import org.apache.openjpa.enhance.StateManager;
31 import org.apache.openjpa.enhance.ManagedInstanceProvider;
32 import org.apache.openjpa.enhance.ReflectingPersistenceCapable;
33 import org.apache.openjpa.enhance.RuntimeUnenhancedClasssesModes;
34 import org.apache.openjpa.kernel.FetchConfiguration;
35 import org.apache.openjpa.kernel.LockManager;
36 import org.apache.openjpa.kernel.OpenJPAStateManager;
37 import org.apache.openjpa.kernel.PCState;
38 import org.apache.openjpa.kernel.StoreContext;
39 import org.apache.openjpa.kernel.StoreManager;
40 import org.apache.openjpa.lib.util.Closeable;
41 import org.apache.openjpa.lib.util.ReferenceMap;
42 import org.apache.openjpa.lib.util.UUIDGenerator;
43 import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
44 import org.apache.openjpa.meta.ClassMetaData;
45 import org.apache.openjpa.meta.FieldMetaData;
46 import org.apache.openjpa.meta.JavaTypes;
47 import org.apache.openjpa.meta.SequenceMetaData;
48 import org.apache.openjpa.meta.ValueStrategies;
49 import org.apache.openjpa.conf.OpenJPAConfiguration;
50
51 /**
52 * Helper for OpenJPA back-ends.
53 *
54 * @since 0.3.0
55 * @author Abe White
56 * @nojavadoc
57 */
58 public class ImplHelper {
59
60 // Cache for from/to type assignments
61 private static final Map _assignableTypes =
62 new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD);
63
64 // map of all new unenhanced instances active in this classloader
65 public static final Map _unenhancedInstanceMap =
66 new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD) {
67
68 protected boolean eq(Object x, Object y) {
69 // the Entries in ConcurrentReferenceHashMap delegate back to
70 // eq() in their equals() impls
71 if (x instanceof Map.Entry)
72 return super.eq(x, y);
73 else
74 return x == y;
75 }
76
77 protected int hc(Object o) {
78 // the Entries in ConcurrentReferenceHashMap delegate back to
79 // hc() in their hashCode() impls
80 if (o instanceof Map.Entry)
81 return super.hc(o);
82 else
83 return System.identityHashCode(o);
84 }
85 };
86
87 /**
88 * Helper for store manager implementations. This method simply delegates
89 * to the proper singular method for each state manager.
90 *
91 * @see StoreManager#loadAll
92 * @since 0.4.0
93 */
94 public static Collection loadAll(Collection sms, StoreManager store,
95 PCState state, int load, FetchConfiguration fetch, Object context) {
96 Collection failed = null;
97 OpenJPAStateManager sm;
98 LockManager lm;
99 for (Iterator itr = sms.iterator(); itr.hasNext();) {
100 sm = (OpenJPAStateManager) itr.next();
101 if (sm.getManagedInstance() == null) {
102 if (!store.initialize(sm, state, fetch, context))
103 failed = addFailedId(sm, failed);
104 } else if (load != StoreManager.FORCE_LOAD_NONE
105 || sm.getPCState() == PCState.HOLLOW) {
106 lm = sm.getContext().getLockManager();
107 if (!store.load(sm, sm.getUnloaded(fetch), fetch,
108 lm.getLockLevel(sm), context))
109 failed = addFailedId(sm, failed);
110 } else if (!store.exists(sm, context))
111 failed = addFailedId(sm, failed);
112 }
113 return (failed == null) ? Collections.EMPTY_LIST : failed;
114 }
115
116 /**
117 * Add identity of given instance to collection.
118 */
119 private static Collection addFailedId(OpenJPAStateManager sm,
120 Collection failed) {
121 if (failed == null)
122 failed = new ArrayList();
123 failed.add(sm.getId());
124 return failed;
125 }
126
127 /**
128 * Generate a value for the given metadata, or return null. Generates
129 * values for hte following strategies: {@link ValueStrategies#SEQUENCE},
130 * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX}
131 */
132 public static Object generateIdentityValue(StoreContext ctx,
133 ClassMetaData meta, int typeCode) {
134 return generateValue(ctx, meta, null, typeCode);
135 }
136
137 /**
138 * Generate a value for the given metadata, or return null. Generates
139 * values for hte following strategies: {@link ValueStrategies#SEQUENCE},
140 * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX}
141 */
142 public static Object generateFieldValue(StoreContext ctx,
143 FieldMetaData fmd) {
144 return generateValue(ctx, fmd.getDefiningMetaData(), fmd,
145 fmd.getDeclaredTypeCode());
146 }
147
148 /**
149 * Generate a value for the given metadaa.
150 */
151 private static Object generateValue(StoreContext ctx,
152 ClassMetaData meta, FieldMetaData fmd, int typeCode) {
153 int strategy = (fmd == null) ? meta.getIdentityStrategy()
154 : fmd.getValueStrategy();
155 switch (strategy) {
156 case ValueStrategies.SEQUENCE:
157 SequenceMetaData smd = (fmd == null)
158 ? meta.getIdentitySequenceMetaData()
159 : fmd.getValueSequenceMetaData();
160 return JavaTypes.convert(smd.getInstance(ctx.getClassLoader()).
161 next(ctx, meta), typeCode);
162 case ValueStrategies.UUID_STRING:
163 return UUIDGenerator.nextString();
164 case ValueStrategies.UUID_HEX:
165 return UUIDGenerator.nextHex();
166 default:
167 return null;
168 }
169 }
170
171 /**
172 * Returns the fields of the state that require an update.
173 *
174 * @param sm the state to check
175 * @return the BitSet of fields that need update, or null if none
176 */
177 public static BitSet getUpdateFields(OpenJPAStateManager sm) {
178 if ((sm.getPCState() == PCState.PDIRTY
179 && (!sm.isFlushed() || sm.isFlushedDirty()))
180 || (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) {
181 BitSet dirty = sm.getDirty();
182 if (sm.isFlushed()) {
183 dirty = (BitSet) dirty.clone();
184 dirty.andNot(sm.getFlushed());
185 }
186 if (dirty.length() > 0)
187 return dirty;
188 }
189 return null;
190 }
191
192 /**
193 * Close the given resource. The resource can be an extent iterator,
194 * query result, large result set relation, or any closeable OpenJPA
195 * component.
196 */
197 public static void close(Object o) {
198 try {
199 if (o instanceof Closeable)
200 ((Closeable) o).close();
201 } catch (RuntimeException re) {
202 throw re;
203 } catch (Exception e) {
204 throw new GeneralException(e);
205 }
206 }
207
208 /**
209 * Returns true if the specified class is a type that can be managed by
210 * OpenJPA.
211 *
212 * @param type the class to test
213 * @return true if the class is manageable.
214 *
215 * @since 1.0.0
216 */
217 public static boolean isManagedType(OpenJPAConfiguration conf, Class type) {
218 return (PersistenceCapable.class.isAssignableFrom(type)
219 || (type != null
220 && (conf == null || conf.getRuntimeUnenhancedClassesConstant()
221 == RuntimeUnenhancedClasssesModes.SUPPORTED)
222 && PCRegistry.isRegistered(type)));
223 }
224
225 /**
226 * Returns true if the specified instance is manageable.
227 *
228 * @param instance the object to check
229 * @return true if the instance is a persistent type, false otherwise
230 */
231 public static boolean isManageable(Object instance) {
232 return instance instanceof PersistenceCapable
233 || instance != null && PCRegistry.isRegistered(instance.getClass());
234 }
235
236 /**
237 * Returns true if the referenced "to" class is assignable to the "from"
238 * class. This helper method utilizes a cache to help avoid the overhead
239 * of the Class.isAssignableFrom() method.
240 *
241 * @param from target class instance to be checked for assignability
242 * @param to second class instance to be checked for assignability
243 * @return true if the "to" class is assignable to the "from" class
244 */
245 public static boolean isAssignable(Class from, Class to) {
246 if (from == null || to == null)
247 return false;
248
249 Boolean isAssignable = null;
250 Map assignableTo = (Map) _assignableTypes.get(from);
251 if (assignableTo == null) { // "to" cache doesn't exist, so create it...
252 assignableTo = new ConcurrentReferenceHashMap(ReferenceMap.WEAK,
253 ReferenceMap.HARD);
254 _assignableTypes.put(from, assignableTo);
255 } else { // "to" cache exists...
256 isAssignable = (Boolean) assignableTo.get(to);
257 }
258
259 if (isAssignable == null) {// we don't have a record of this pair...
260 isAssignable = Boolean.valueOf(from.isAssignableFrom(to));
261 assignableTo.put(to, isAssignable);
262 }
263
264 return isAssignable.booleanValue();
265 }
266
267 /**
268 * @return the persistence-capable instance responsible for managing
269 * <code>o</code>, or <code>null</code> if <code>o</code> is not manageable.
270 * @since 1.0.0
271 */
272 public static PersistenceCapable toPersistenceCapable(Object o, Object ctx){
273 if (o instanceof PersistenceCapable)
274 return (PersistenceCapable) o;
275
276 OpenJPAConfiguration conf = null;
277 if (ctx instanceof OpenJPAConfiguration)
278 conf = (OpenJPAConfiguration) ctx;
279 else if (ctx instanceof StateManager
280 && ((StateManager) ctx).getGenericContext() instanceof StoreContext)
281 conf = ((StoreContext) ((StateManager) ctx).getGenericContext())
282 .getConfiguration();
283
284 if (!isManageable(o))
285 return null;
286
287 // if we had a putIfAbsent() method, we wouldn't need to sync here
288 synchronized (o) {
289 PersistenceCapable pc = (PersistenceCapable)
290 _unenhancedInstanceMap.get(o);
291
292 if (pc != null)
293 return pc;
294
295 // if we don't have a conf passed in, then we can't create a new
296 // ReflectingPC; this will only be the case when invoked from a
297 // context outside of OpenJPA.
298 if (conf == null)
299 return null;
300
301 pc = new ReflectingPersistenceCapable(o, conf);
302 _unenhancedInstanceMap.put(o, pc);
303 return pc;
304 }
305 }
306
307 public static void registerPersistenceCapable(
308 ReflectingPersistenceCapable pc) {
309 _unenhancedInstanceMap.put(pc.getManagedInstance(), pc);
310 }
311
312 /**
313 * @return the user-visible representation of <code>o</code>.
314 * @since 1.0.0
315 */
316 public static Object getManagedInstance(Object o) {
317 if (o instanceof ManagedInstanceProvider)
318 return ((ManagedInstanceProvider) o).getManagedInstance();
319 else
320 return o;
321 }
322 }