blob: 3144789d0e63867054f52d205a1a3a2428f58e41 [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.persistence;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import javax.persistence.spi.LoadState;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.StateManager;
import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.util.ImplHelper;
public class OpenJPAPersistenceUtil {
/**
* Returns the identifier of the persistent entity.
* @param entity
* @return The identifier of the entity or null if the entity
* is not persistent.
*/
public static Object getIdentifier(Object entity) {
return getIdentifier(null, entity);
}
/**
* Get the object identifier for a persistent entity managed by one
* of the entity managers of the specified entity manager factory.
* @return The identifier of the entity or null if the entity does
* not have an identifier assigned or is not managed by any of the
* entity managers of the entity manager factory.
*/
public static Object getIdentifier(OpenJPAEntityManagerFactory emf,
Object entity) {
if (entity instanceof PersistenceCapable) {
PersistenceCapable pc = (PersistenceCapable)entity;
// Per contract, if not managed by the owning emf, return null.
if (emf != null) {
if (!isManagedBy(emf, pc)) {
return null;
}
}
StateManager sm = pc.pcGetStateManager();
if (sm != null && sm instanceof OpenJPAStateManager) {
OpenJPAStateManager osm = (OpenJPAStateManager)sm;
return osm.getObjectId();
}
}
return null;
}
/**
* Determines whether the specified state manager is managed by an open
* broker within the persistence unit of the provided EMF instance.
* @param emf OpenJPAEntityManagerFactory
* @param sm StateManager
* @return true if this state manager is managed by a broker within
* this persistence unit.
*/
public static boolean isManagedBy(OpenJPAEntityManagerFactory emf, Object entity) {
// Assert a valid emf was provided, it is open, and the entity is PC
if (emf == null || !emf.isOpen() || !ImplHelper.isManageable(entity)) {
return false;
}
// Assert the context is a broker
PersistenceCapable pc = (PersistenceCapable)entity;
if (!(pc.pcGetGenericContext() instanceof Broker)) {
return false;
}
// Assert the broker is available and open
Broker broker = (Broker)pc.pcGetGenericContext();
if (broker == null || broker.isClosed()) {
return false;
}
// Assert the emf associated with the PC is the same as the provided emf
OpenJPAEntityManagerFactory eemf = JPAFacadeHelper.toEntityManagerFactory(broker.getBrokerFactory());
if (eemf == emf && eemf.isOpen()) {
return true;
}
return false;
}
/**
* Determines whether the attribute on the specified object is loaded and
* is managed by one of the entity managers. Use isManagedBy() to
* determine if an object is managed by a specific entity manager
* factory.
*
* @return LoadState.LOADED - if the attribute is loaded.
* LoadState.NOT_LOADED - if the attribute is not loaded or any
* EAGER fetch attributes of the entity are not loaded.
* LoadState.UNKNOWN - if the entity is not managed by this
* provider or if it does not contain the persistent
* attribute.
*/
public static LoadState isLoaded(Object obj, String attr) {
if (obj == null) {
return LoadState.UNKNOWN;
}
// If the object has a state manager, call it directly.
if (obj instanceof PersistenceCapable) {
PersistenceCapable pc = (PersistenceCapable)obj;
StateManager sm = pc.pcGetStateManager();
if (sm != null && sm instanceof OpenJPAStateManager) {
return isLoaded((OpenJPAStateManager)sm, attr, null);
}
}
return LoadState.UNKNOWN;
}
private static LoadState isLoaded(OpenJPAStateManager sm, String attr,
HashSet<OpenJPAStateManager> pcs) {
boolean isLoaded = true;
try {
BitSet loadSet = sm.getLoaded();
if (attr != null) {
FieldMetaData fmd = sm.getMetaData().getField(attr);
// Could not find field metadata for the specified attribute.
if (fmd == null) {
return LoadState.UNKNOWN;
}
// Otherwise, return the load state
if(!loadSet.get(fmd.getIndex())) {
return LoadState.NOT_LOADED;
}
}
if (pcs != null && pcs.contains(sm)) {
return LoadState.LOADED;
}
FieldMetaData[] fmds = sm.getMetaData().getFields();
// Check load state of all persistent eager fetch attributes
if (fmds != null && fmds.length > 0) {
pcs = addToLoadSet(pcs, sm);
for (FieldMetaData fmd : fmds) {
if (requiresFetch(sm, fmd)) {
if (!isLoadedField(sm, fmd, pcs)) {
isLoaded = false;
break;
}
}
}
pcs.remove(sm);
}
} catch (RuntimeException e) {
// treat any exceptions, like UnsupportedOperationException
// for detached entities, as LoadState.UNKNOWN
return LoadState.UNKNOWN;
}
return isLoaded ? LoadState.LOADED : LoadState.NOT_LOADED;
}
private static boolean requiresFetch(OpenJPAStateManager sm, FieldMetaData fmd) {
if (sm instanceof StateManagerImpl)
return ((StateManagerImpl)sm).requiresFetch(fmd);
return fmd.isInDefaultFetchGroup();
}
private static HashSet<OpenJPAStateManager> addToLoadSet(
HashSet<OpenJPAStateManager> pcs, OpenJPAStateManager sm) {
if (pcs == null) {
pcs = new HashSet<OpenJPAStateManager>();
}
pcs.add(sm);
return pcs;
}
private static boolean isLoadedField(OpenJPAStateManager sm,
FieldMetaData fmd, HashSet<OpenJPAStateManager> pcs) {
BitSet loadSet = sm.getLoaded();
// Simple load state check for the field
if (!loadSet.get(fmd.getIndex()))
return false;
Object field = sm.fetchField(fmd.getIndex(), false);
// Get the state manager for the field, if it is a PC
OpenJPAStateManager ofsm = getStateManager(field);
// Prevent circular load state evaluation for this sm.
if (ofsm != null && pcs.contains(ofsm))
return true;
// If a collection type, determine if it is loaded
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.COLLECTION:
return isLoadedCollection(sm, fmd.getElement(),
(Collection<?>)field, pcs);
case JavaTypes.MAP:
return isLoadedMap(sm, fmd,
(Map<?,?>)field, pcs);
case JavaTypes.ARRAY:
return isLoadedArray(sm, fmd.getElement(),
(Object[])field, pcs);
}
// If other PC type, determine if it is loaded
if (ofsm != null && fmd.isDeclaredTypePC()) {
return isLoaded(ofsm, null, pcs) ==
LoadState.LOADED;
}
return true;
}
private static boolean isLoadedCollection(OpenJPAStateManager sm,
ValueMetaData vmd, Collection<?> coll, HashSet<OpenJPAStateManager> pcs) {
// This field passed the load state check in isLoadedField, so
// if any of these conditions are true the collection is loaded.
if (sm == null || coll == null || coll.size() == 0) {
return true;
}
// Convert to array to prevent concurrency issues
Object[] arr = coll.toArray();
return isLoadedArray(sm, vmd, arr, pcs);
}
private static boolean isLoadedArray(OpenJPAStateManager sm,
ValueMetaData vmd, Object[] arr,
HashSet<OpenJPAStateManager> pcs) {
// This field passed the load state check in isLoadedField, so
// if any of these conditions are true the array is loaded.
if (sm == null || arr == null || arr.length == 0) {
return true;
}
// Not a collection of PC's
if (!vmd.isDeclaredTypePC()) {
return true;
}
for (Object pc : arr) {
OpenJPAStateManager esm = getStateManager(pc);
if (esm == null) {
return true;
}
if (!(isLoaded(esm, null, pcs) == LoadState.LOADED))
return false;
}
return true;
}
private static boolean isLoadedMap(OpenJPAStateManager sm,
FieldMetaData fmd, Map<?,?> map, HashSet<OpenJPAStateManager> pcs) {
// This field passed the load state check in isLoadedField, so
// if any of these conditions are true the map is loaded.
if (sm == null || map == null || map.size() == 0) {
return true;
}
boolean keyIsPC = fmd.getKey().isDeclaredTypePC();
boolean valIsPC = fmd.getElement().isDeclaredTypePC();
// Map is does not contain PCs in either keys or values
if (!(keyIsPC || valIsPC)) {
return true;
}
Object[] arr = map.keySet().toArray();
for (Object key : arr) {
if (keyIsPC) {
OpenJPAStateManager ksm = getStateManager(key);
if (ksm == null) {
return true;
}
if (!(isLoaded(ksm, null, pcs) == LoadState.LOADED))
return false;
}
if (valIsPC) {
Object value = map.get(key);
OpenJPAStateManager vsm = getStateManager(value);
if (vsm == null) {
return true;
}
if (!(isLoaded(vsm, null, pcs) == LoadState.LOADED))
return false;
}
}
return true;
}
private static OpenJPAStateManager getStateManager(Object obj) {
if (obj == null || !(obj instanceof PersistenceCapable)) {
return null;
}
PersistenceCapable pc = (PersistenceCapable)obj;
StateManager sm = pc.pcGetStateManager();
if (sm == null || !(sm instanceof OpenJPAStateManager)) {
return null;
}
return (OpenJPAStateManager)sm;
}
}