blob: 9d30db4ffe6181b78336de78930b506f0bd77657 [file] [log] [blame]
/*
* 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.Map;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.ManagedInstanceProvider;
import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.ReflectingPersistenceCapable;
import org.apache.openjpa.enhance.RuntimeUnenhancedClassesModes;
import org.apache.openjpa.enhance.StateManager;
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.UUIDGenerator;
import org.apache.openjpa.lib.util.collections.AbstractReferenceMap.ReferenceStrength;
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;
/**
* 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(ReferenceStrength.WEAK, ReferenceStrength.HARD);
// map of all new unenhanced instances active in this classloader
public static final Map _unenhancedInstanceMap =
new ConcurrentReferenceHashMap(ReferenceStrength.WEAK, ReferenceStrength.HARD) {
@Override
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;
}
@Override
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 (Object o : sms) {
sm = (OpenJPAStateManager) o;
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(ReferenceStrength.WEAK,
ReferenceStrength.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 = from.isAssignableFrom(to);
assignableTo.put(to, isAssignable);
}
return isAssignable;
}
/**
* @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;
}
}