/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.openjpa.util; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import org.apache.openjpa.enhance.PersistenceCapable; import org.apache.openjpa.enhance.PCRegistry; import org.apache.openjpa.enhance.StateManager; import org.apache.openjpa.enhance.ManagedInstanceProvider; import org.apache.openjpa.enhance.ReflectingPersistenceCapable; import org.apache.openjpa.enhance.RuntimeUnenhancedClassesModes; import org.apache.openjpa.kernel.FetchConfiguration; import org.apache.openjpa.kernel.LockManager; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.PCState; import org.apache.openjpa.kernel.StoreContext; import org.apache.openjpa.kernel.StoreManager; import org.apache.openjpa.lib.util.Closeable; import org.apache.openjpa.lib.util.ReferenceMap; import org.apache.openjpa.lib.util.UUIDGenerator; import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.meta.SequenceMetaData; import org.apache.openjpa.meta.ValueStrategies; import org.apache.openjpa.conf.OpenJPAConfiguration; /** * Helper for OpenJPA back-ends. * * @since 0.3.0 * @author Abe White */ public class ImplHelper { // Cache for from/to type assignments private static final Map _assignableTypes = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); // map of all new unenhanced instances active in this classloader public static final Map _unenhancedInstanceMap = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD) { protected boolean eq(Object x, Object y) { // the Entries in ConcurrentReferenceHashMap delegate back to // eq() in their equals() impls if (x instanceof Map.Entry) return super.eq(x, y); else return x == y; } protected int hc(Object o) { // the Entries in ConcurrentReferenceHashMap delegate back to // hc() in their hashCode() impls if (o instanceof Map.Entry) return super.hc(o); else return System.identityHashCode(o); } }; /** * Helper for store manager implementations. This method simply delegates * to the proper singular method for each state manager. * * @see StoreManager#loadAll * @since 0.4.0 */ public static Collection loadAll(Collection sms, StoreManager store, PCState state, int load, FetchConfiguration fetch, Object context) { Collection failed = null; OpenJPAStateManager sm; LockManager lm; for (Iterator itr = sms.iterator(); itr.hasNext();) { sm = (OpenJPAStateManager) itr.next(); if (sm.getManagedInstance() == null) { if (!store.initialize(sm, state, fetch, context)) failed = addFailedId(sm, failed); } else if (load != StoreManager.FORCE_LOAD_NONE || sm.getPCState() == PCState.HOLLOW) { lm = sm.getContext().getLockManager(); if (!store.load(sm, sm.getUnloaded(fetch), fetch, lm.getLockLevel(sm), context)) failed = addFailedId(sm, failed); } else if (!store.exists(sm, context)) failed = addFailedId(sm, failed); } return (failed == null) ? Collections.EMPTY_LIST : failed; } /** * Add identity of given instance to collection. */ private static Collection addFailedId(OpenJPAStateManager sm, Collection failed) { if (failed == null) failed = new ArrayList(); failed.add(sm.getId()); return failed; } /** * Generate a value for the given metadata, or return null. Generates * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} */ public static Object generateIdentityValue(StoreContext ctx, ClassMetaData meta, int typeCode) { return generateValue(ctx, meta, null, typeCode); } /** * Generate a value for the given metadata, or return null. Generates * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} */ public static Object generateFieldValue(StoreContext ctx, FieldMetaData fmd) { return generateValue(ctx, fmd.getDefiningMetaData(), fmd, fmd.getDeclaredTypeCode()); } /** * Generate a value for the given metadaa. */ private static Object generateValue(StoreContext ctx, ClassMetaData meta, FieldMetaData fmd, int typeCode) { int strategy = (fmd == null) ? meta.getIdentityStrategy() : fmd.getValueStrategy(); switch (strategy) { case ValueStrategies.SEQUENCE: SequenceMetaData smd = (fmd == null) ? meta.getIdentitySequenceMetaData() : fmd.getValueSequenceMetaData(); return JavaTypes.convert(smd.getInstance(ctx.getClassLoader()). next(ctx, meta), typeCode); case ValueStrategies.UUID_STRING: return UUIDGenerator.nextString(UUIDGenerator.TYPE1); case ValueStrategies.UUID_HEX: return UUIDGenerator.nextHex(UUIDGenerator.TYPE1); case ValueStrategies.UUID_TYPE4_STRING: return UUIDGenerator.nextString(UUIDGenerator.TYPE4); case ValueStrategies.UUID_TYPE4_HEX: return UUIDGenerator.nextHex(UUIDGenerator.TYPE4); default: return null; } } /** * Returns the fields of the state that require an update. * * @param sm the state to check * @return the BitSet of fields that need update, or null if none */ public static BitSet getUpdateFields(OpenJPAStateManager sm) { if ((sm.getPCState() == PCState.PDIRTY && (!sm.isFlushed() || sm.isFlushedDirty())) || (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) { BitSet dirty = sm.getDirty(); if (sm.isFlushed()) { dirty = (BitSet) dirty.clone(); dirty.andNot(sm.getFlushed()); } if (dirty.length() > 0) return dirty; } return null; } /** * Close the given resource. The resource can be an extent iterator, * query result, large result set relation, or any closeable OpenJPA * component. */ public static void close(Object o) { try { if (o instanceof Closeable) ((Closeable) o).close(); } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new GeneralException(e); } } /** * Returns true if the specified class is a type that can be managed by * OpenJPA. * * @param type the class to test * @return true if the class is manageable. * * @since 1.0.0 */ public static boolean isManagedType(OpenJPAConfiguration conf, Class type) { return (PersistenceCapable.class.isAssignableFrom(type) || (type != null && (conf == null || conf.getRuntimeUnenhancedClassesConstant() == RuntimeUnenhancedClassesModes.SUPPORTED) && PCRegistry.isRegistered(type))); } /** * Returns true if the specified instance is manageable. * * @param instance the object to check * @return true if the instance is a persistent type, false otherwise */ public static boolean isManageable(Object instance) { return instance instanceof PersistenceCapable || instance != null && PCRegistry.isRegistered(instance.getClass()); } /** * Returns true if the referenced "to" class is assignable to the "from" * class. This helper method utilizes a cache to help avoid the overhead * of the Class.isAssignableFrom() method. * * @param from target class instance to be checked for assignability * @param to second class instance to be checked for assignability * @return true if the "to" class is assignable to the "from" class */ public static boolean isAssignable(Class from, Class to) { if (from == null || to == null) return false; Boolean isAssignable = null; Map assignableTo = (Map) _assignableTypes.get(from); if (assignableTo == null) { // "to" cache doesn't exist, so create it... assignableTo = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); _assignableTypes.put(from, assignableTo); } else { // "to" cache exists... isAssignable = (Boolean) assignableTo.get(to); } if (isAssignable == null) {// we don't have a record of this pair... isAssignable = Boolean.valueOf(from.isAssignableFrom(to)); assignableTo.put(to, isAssignable); } return isAssignable.booleanValue(); } /** * @return the persistence-capable instance responsible for managing * <code>o</code>, or <code>null</code> if <code>o</code> is not manageable. * @since 1.0.0 */ public static PersistenceCapable toPersistenceCapable(Object o, Object ctx){ if (o instanceof PersistenceCapable) return (PersistenceCapable) o; OpenJPAConfiguration conf = null; if (ctx instanceof OpenJPAConfiguration) conf = (OpenJPAConfiguration) ctx; else if (ctx instanceof StateManager && ((StateManager) ctx).getGenericContext() instanceof StoreContext) conf = ((StoreContext) ((StateManager) ctx).getGenericContext()) .getConfiguration(); if (!isManageable(o)) return null; // if we had a putIfAbsent() method, we wouldn't need to sync here synchronized (o) { PersistenceCapable pc = (PersistenceCapable) _unenhancedInstanceMap.get(o); if (pc != null) return pc; // if we don't have a conf passed in, then we can't create a new // ReflectingPC; this will only be the case when invoked from a // context outside of OpenJPA. if (conf == null) return null; pc = new ReflectingPersistenceCapable(o, conf); _unenhancedInstanceMap.put(o, pc); return pc; } } public static void registerPersistenceCapable( ReflectingPersistenceCapable pc) { _unenhancedInstanceMap.put(pc.getManagedInstance(), pc); } /** * @return the user-visible representation of <code>o</code>. * @since 1.0.0 */ public static Object getManagedInstance(Object o) { if (o instanceof ManagedInstanceProvider) return ((ManagedInstanceProvider) o).getManagedInstance(); else return o; } }