001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.util;
020:
021: import java.util.ArrayList;
022: import java.util.BitSet;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.Iterator;
026: import java.util.Map;
027:
028: import org.apache.openjpa.enhance.PersistenceCapable;
029: import org.apache.openjpa.enhance.PCRegistry;
030: import org.apache.openjpa.enhance.StateManager;
031: import org.apache.openjpa.enhance.ManagedInstanceProvider;
032: import org.apache.openjpa.enhance.ReflectingPersistenceCapable;
033: import org.apache.openjpa.enhance.RuntimeUnenhancedClasssesModes;
034: import org.apache.openjpa.kernel.FetchConfiguration;
035: import org.apache.openjpa.kernel.LockManager;
036: import org.apache.openjpa.kernel.OpenJPAStateManager;
037: import org.apache.openjpa.kernel.PCState;
038: import org.apache.openjpa.kernel.StoreContext;
039: import org.apache.openjpa.kernel.StoreManager;
040: import org.apache.openjpa.lib.util.Closeable;
041: import org.apache.openjpa.lib.util.ReferenceMap;
042: import org.apache.openjpa.lib.util.UUIDGenerator;
043: import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
044: import org.apache.openjpa.meta.ClassMetaData;
045: import org.apache.openjpa.meta.FieldMetaData;
046: import org.apache.openjpa.meta.JavaTypes;
047: import org.apache.openjpa.meta.SequenceMetaData;
048: import org.apache.openjpa.meta.ValueStrategies;
049: import org.apache.openjpa.conf.OpenJPAConfiguration;
050:
051: /**
052: * Helper for OpenJPA back-ends.
053: *
054: * @since 0.3.0
055: * @author Abe White
056: * @nojavadoc
057: */
058: public class ImplHelper {
059:
060: // Cache for from/to type assignments
061: private static final Map _assignableTypes = new ConcurrentReferenceHashMap(
062: ReferenceMap.WEAK, ReferenceMap.HARD);
063:
064: // map of all new unenhanced instances active in this classloader
065: public static final Map _unenhancedInstanceMap = new ConcurrentReferenceHashMap(
066: ReferenceMap.WEAK, ReferenceMap.HARD) {
067:
068: protected boolean eq(Object x, Object y) {
069: // the Entries in ConcurrentReferenceHashMap delegate back to
070: // eq() in their equals() impls
071: if (x instanceof Map.Entry)
072: return super .eq(x, y);
073: else
074: return x == y;
075: }
076:
077: protected int hc(Object o) {
078: // the Entries in ConcurrentReferenceHashMap delegate back to
079: // hc() in their hashCode() impls
080: if (o instanceof Map.Entry)
081: return super .hc(o);
082: else
083: return System.identityHashCode(o);
084: }
085: };
086:
087: /**
088: * Helper for store manager implementations. This method simply delegates
089: * to the proper singular method for each state manager.
090: *
091: * @see StoreManager#loadAll
092: * @since 0.4.0
093: */
094: public static Collection loadAll(Collection sms,
095: StoreManager store, PCState state, int load,
096: FetchConfiguration fetch, Object context) {
097: Collection failed = null;
098: OpenJPAStateManager sm;
099: LockManager lm;
100: for (Iterator itr = sms.iterator(); itr.hasNext();) {
101: sm = (OpenJPAStateManager) itr.next();
102: if (sm.getManagedInstance() == null) {
103: if (!store.initialize(sm, state, fetch, context))
104: failed = addFailedId(sm, failed);
105: } else if (load != StoreManager.FORCE_LOAD_NONE
106: || sm.getPCState() == PCState.HOLLOW) {
107: lm = sm.getContext().getLockManager();
108: if (!store.load(sm, sm.getUnloaded(fetch), fetch, lm
109: .getLockLevel(sm), context))
110: failed = addFailedId(sm, failed);
111: } else if (!store.exists(sm, context))
112: failed = addFailedId(sm, failed);
113: }
114: return (failed == null) ? Collections.EMPTY_LIST : failed;
115: }
116:
117: /**
118: * Add identity of given instance to collection.
119: */
120: private static Collection addFailedId(OpenJPAStateManager sm,
121: Collection failed) {
122: if (failed == null)
123: failed = new ArrayList();
124: failed.add(sm.getId());
125: return failed;
126: }
127:
128: /**
129: * Generate a value for the given metadata, or return null. Generates
130: * values for hte following strategies: {@link ValueStrategies#SEQUENCE},
131: * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX}
132: */
133: public static Object generateIdentityValue(StoreContext ctx,
134: ClassMetaData meta, int typeCode) {
135: return generateValue(ctx, meta, null, typeCode);
136: }
137:
138: /**
139: * Generate a value for the given metadata, or return null. Generates
140: * values for hte following strategies: {@link ValueStrategies#SEQUENCE},
141: * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX}
142: */
143: public static Object generateFieldValue(StoreContext ctx,
144: FieldMetaData fmd) {
145: return generateValue(ctx, fmd.getDefiningMetaData(), fmd, fmd
146: .getDeclaredTypeCode());
147: }
148:
149: /**
150: * Generate a value for the given metadaa.
151: */
152: private static Object generateValue(StoreContext ctx,
153: ClassMetaData meta, FieldMetaData fmd, int typeCode) {
154: int strategy = (fmd == null) ? meta.getIdentityStrategy() : fmd
155: .getValueStrategy();
156: switch (strategy) {
157: case ValueStrategies.SEQUENCE:
158: SequenceMetaData smd = (fmd == null) ? meta
159: .getIdentitySequenceMetaData() : fmd
160: .getValueSequenceMetaData();
161: return JavaTypes.convert(smd.getInstance(
162: ctx.getClassLoader()).next(ctx, meta), typeCode);
163: case ValueStrategies.UUID_STRING:
164: return UUIDGenerator.nextString();
165: case ValueStrategies.UUID_HEX:
166: return UUIDGenerator.nextHex();
167: default:
168: return null;
169: }
170: }
171:
172: /**
173: * Returns the fields of the state that require an update.
174: *
175: * @param sm the state to check
176: * @return the BitSet of fields that need update, or null if none
177: */
178: public static BitSet getUpdateFields(OpenJPAStateManager sm) {
179: if ((sm.getPCState() == PCState.PDIRTY && (!sm.isFlushed() || sm
180: .isFlushedDirty()))
181: || (sm.getPCState() == PCState.PNEW && sm
182: .isFlushedDirty())) {
183: BitSet dirty = sm.getDirty();
184: if (sm.isFlushed()) {
185: dirty = (BitSet) dirty.clone();
186: dirty.andNot(sm.getFlushed());
187: }
188: if (dirty.length() > 0)
189: return dirty;
190: }
191: return null;
192: }
193:
194: /**
195: * Close the given resource. The resource can be an extent iterator,
196: * query result, large result set relation, or any closeable OpenJPA
197: * component.
198: */
199: public static void close(Object o) {
200: try {
201: if (o instanceof Closeable)
202: ((Closeable) o).close();
203: } catch (RuntimeException re) {
204: throw re;
205: } catch (Exception e) {
206: throw new GeneralException(e);
207: }
208: }
209:
210: /**
211: * Returns true if the specified class is a type that can be managed by
212: * OpenJPA.
213: *
214: * @param type the class to test
215: * @return true if the class is manageable.
216: *
217: * @since 1.0.0
218: */
219: public static boolean isManagedType(OpenJPAConfiguration conf,
220: Class type) {
221: return (PersistenceCapable.class.isAssignableFrom(type) || (type != null
222: && (conf == null || conf
223: .getRuntimeUnenhancedClassesConstant() == RuntimeUnenhancedClasssesModes.SUPPORTED) && PCRegistry
224: .isRegistered(type)));
225: }
226:
227: /**
228: * Returns true if the specified instance is manageable.
229: *
230: * @param instance the object to check
231: * @return true if the instance is a persistent type, false otherwise
232: */
233: public static boolean isManageable(Object instance) {
234: return instance instanceof PersistenceCapable
235: || instance != null
236: && PCRegistry.isRegistered(instance.getClass());
237: }
238:
239: /**
240: * Returns true if the referenced "to" class is assignable to the "from"
241: * class. This helper method utilizes a cache to help avoid the overhead
242: * of the Class.isAssignableFrom() method.
243: *
244: * @param from target class instance to be checked for assignability
245: * @param to second class instance to be checked for assignability
246: * @return true if the "to" class is assignable to the "from" class
247: */
248: public static boolean isAssignable(Class from, Class to) {
249: if (from == null || to == null)
250: return false;
251:
252: Boolean isAssignable = null;
253: Map assignableTo = (Map) _assignableTypes.get(from);
254: if (assignableTo == null) { // "to" cache doesn't exist, so create it...
255: assignableTo = new ConcurrentReferenceHashMap(
256: ReferenceMap.WEAK, ReferenceMap.HARD);
257: _assignableTypes.put(from, assignableTo);
258: } else { // "to" cache exists...
259: isAssignable = (Boolean) assignableTo.get(to);
260: }
261:
262: if (isAssignable == null) {// we don't have a record of this pair...
263: isAssignable = Boolean.valueOf(from.isAssignableFrom(to));
264: assignableTo.put(to, isAssignable);
265: }
266:
267: return isAssignable.booleanValue();
268: }
269:
270: /**
271: * @return the persistence-capable instance responsible for managing
272: * <code>o</code>, or <code>null</code> if <code>o</code> is not manageable.
273: * @since 1.0.0
274: */
275: public static PersistenceCapable toPersistenceCapable(Object o,
276: Object ctx) {
277: if (o instanceof PersistenceCapable)
278: return (PersistenceCapable) o;
279:
280: OpenJPAConfiguration conf = null;
281: if (ctx instanceof OpenJPAConfiguration)
282: conf = (OpenJPAConfiguration) ctx;
283: else if (ctx instanceof StateManager
284: && ((StateManager) ctx).getGenericContext() instanceof StoreContext)
285: conf = ((StoreContext) ((StateManager) ctx)
286: .getGenericContext()).getConfiguration();
287:
288: if (!isManageable(o))
289: return null;
290:
291: // if we had a putIfAbsent() method, we wouldn't need to sync here
292: synchronized (o) {
293: PersistenceCapable pc = (PersistenceCapable) _unenhancedInstanceMap
294: .get(o);
295:
296: if (pc != null)
297: return pc;
298:
299: // if we don't have a conf passed in, then we can't create a new
300: // ReflectingPC; this will only be the case when invoked from a
301: // context outside of OpenJPA.
302: if (conf == null)
303: return null;
304:
305: pc = new ReflectingPersistenceCapable(o, conf);
306: _unenhancedInstanceMap.put(o, pc);
307: return pc;
308: }
309: }
310:
311: public static void registerPersistenceCapable(
312: ReflectingPersistenceCapable pc) {
313: _unenhancedInstanceMap.put(pc.getManagedInstance(), pc);
314: }
315:
316: /**
317: * @return the user-visible representation of <code>o</code>.
318: * @since 1.0.0
319: */
320: public static Object getManagedInstance(Object o) {
321: if (o instanceof ManagedInstanceProvider)
322: return ((ManagedInstanceProvider) o).getManagedInstance();
323: else
324: return o;
325: }
326: }
|