/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.mim.lib;
import org.objectweb.jorm.api.PClassMapping;
import org.objectweb.jorm.api.PException;
import org.objectweb.perseus.persistence.api.PersistenceException;
import org.objectweb.perseus.persistence.api.RolledBackPersistenceException;
import org.objectweb.perseus.persistence.api.TransactionalPersistenceManager;
import org.objectweb.speedo.api.Debug;
import org.objectweb.speedo.api.ExceptionHelper;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoFetchGroup;
import org.objectweb.speedo.mim.api.DetachedLifeCycle;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.speedo.mim.api.StateItf;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.speedo.pm.api.POManagerItf;
import org.objectweb.speedo.pm.api.POManagerFactoryItf;
import org.objectweb.speedo.query.api.QueryDefinition;
import org.objectweb.speedo.usercache.api.UserCache;
import org.objectweb.speedo.usercache.lib.CompositeUserCache;
import org.objectweb.speedo.usercache.lib.UserCacheImpl;
import org.objectweb.speedo.workingset.api.TransactionItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author S.Chassande-Barrioz
*/
public abstract class AbstractHomeImpl implements HomeItf {
protected TransactionalPersistenceManager tpm = null;
protected POManagerFactoryItf pmf = null;
byte cachePolicy = CACHED;
boolean isShareable = true;
boolean isFieldLockingLevel = false;
boolean prefetchOnQuery = true;
boolean prefetchOnExtent = true;
boolean prefetchOnGenClass = true;
protected Map namedQueries = null;
protected Personality personality;
public AbstractHomeImpl(Personality p) {
personality = p;
}
public AbstractHomeImpl(TransactionalPersistenceManager _tpm,
POManagerFactoryItf _pmf) {
this.tpm = _tpm;
this.pmf = _pmf;
}
/**
* @return false because it corresponds to most of case
* Subclass to change the behavior
*/
protected boolean isAbstract() {
return false;
}
/**
* Create a new instance of a PersistentObjectItf which the clas is given in
* parameter. The default implementation does a
* simple 'clazz.newInstance()'.
*/
protected PersistentObjectItf newSpeedoPOInstance(Class clazz) throws Exception {
return (PersistentObjectItf) clazz.newInstance();
}
// IMPLEMENTATION OF THE HomeItf INTERFACE //
//-------------------------------------------//
public boolean isCacheable() {
return (cachePolicy & CACHED) != 0;
}
public boolean allLoaded() {
return (cachePolicy & ALL) != 0;
}
public boolean hasToFix() {
return (cachePolicy & FIXED) != 0;
}
public void setCachePolicy(byte v) {
cachePolicy = v;
}
public boolean isShareable() {
return isShareable;
}
public void setShareable(boolean v) {
isShareable = v;
}
public boolean isFieldLockingLevel() {
return isFieldLockingLevel;
}
public void setFieldLockingLevel(boolean val) {
isFieldLockingLevel = val;
}
/**
* This default implementation does nothing and returns null.
* Subclass to change the behavior
*/
public Collection fgGetNestedFetchGroups(String fgName) {
return null;
}
/**
* This default implementation returns always false.
* Subclass to change the behavior
*/
public boolean fgIsDefined(String fgName) {
return false;
}
/**
* This default implementation does nothing and returns null.
* Subclass to change the behavior
*/
public Collection fgGetFieldsToLoad(String fgName) {
return null;
}
/**
* @return the PersistenceManagerFactory which represents the data support
* inside which the po is persistent.
*/
public final POManagerFactoryItf getPOManagerFactory() {
return pmf;
}
/**
* It assignes a PersistenceManagerFactory
* @param _pmf is the PersistenceManagerFactory which represents the data
* support inside which the po is persistent.
*/
public final void setPOManagerFactory(POManagerFactoryItf _pmf) {
this.pmf = _pmf;
}
/**
* @return the TransactionalPersistenceManager which manage the
* concurrency, loading, ... of the po.
*/
public final TransactionalPersistenceManager getTransactionalPersistenceManager() {
return tpm;
}
/**
* It assignes a TransactionalPersistenceManager
* @param _tpm is the TransactionalPersistenceManager which manage the
* concurrency, loading, ... of the po.
*/
public final void setTransactionalPersistenceManager(TransactionalPersistenceManager _tpm) {
this.tpm = _tpm;
}
/**
* Notifies the transactional persistency manager of a read intention for
* the given persistent instance.
* @param sp is the PersistentObjectItf which the read access is requested.
* @param fields the ids of the fields that may be accessed by the caller
* of this method. If the i-th bit of 'fields' is set to 1, then the i-th
* field of the given speedo accessor may be accessed by the caller of this
* method.
*/
public final StateItf readIntention(PersistentObjectItf sp, long[] fields) {
if (!sp.speedoIsActive()) {
StateItf sa = sp.speedoGetReferenceState();
if (sa == null) {
sp.speedoSetReferenceState(sp.speedoCreateState());
}
return sa;
}
POManagerItf pm = pmf.lookup();
if (pm == null) {
throw personality.newUserRuntimeException("When a persistent object is used (read), a PersistenceManager is needed");
}
TransactionItf t = pm.getSpeedoTransaction();
try {
StateItf sa = (StateItf) tpm.readIntention(t, sp, null);
sa.loadFields(pm, fields);
return sa;
} catch (RolledBackPersistenceException e) {
throw t.rollBackOnInternalError(e);
} catch (PersistenceException e) {
throw personality.newRuntimeException("Impossible to notify a read intention",
ExceptionHelper.getNested(e));
}
}
public final StateItf writeIntention(PersistentObjectItf sp, long[] fields) {
return writeIntention(sp, fields, null);
}
/**
* Notifies the transactional persistency manager of a write intention for
* the given persistent instance.
*
* @param sp is the PersistentObjectItf which the write access is requested.
* @param fields the ids of the fields that may be accessed by the caller
* of this method. If the i-th bit of 'fields' is set to 1, then the i-th
* field of the given speedo accessor may be accessed by the caller of this
* method.
*/
public StateItf writeIntention(PersistentObjectItf sp, long[] fields, Object thinLock) {
if (!sp.speedoIsActive()) {
StateItf sa = sp.speedoGetReferenceState();
if (sa == null) {
sp.speedoSetReferenceState(sp.speedoCreateState());
}
return sa;
}
POManagerItf pm = pmf.lookup();
if (pm == null) {
throw personality.newUserRuntimeException("When a persistent object is used (read), a PersistenceManager/EntityManager is needed");
}
TransactionItf t = pm.getSpeedoTransaction();
try {
sendEvent(PRE_DIRTY, sp, null);
StateItf sa = (StateItf) tpm.writeIntention(t, sp,
isFieldLockingLevel ? thinLock : null);
sa.loadFields(pm, fields);
sendEvent(POST_DIRTY, sp, null);
return sa;
} catch (RolledBackPersistenceException e) {
throw t.rollBackOnInternalError(e);
} catch (PersistenceException e) {
throw personality.newRuntimeException("Impossible to notify a write intention",
ExceptionHelper.getNested(e));
}
}
/**
* It retrieves the StateItf instance used in the current context.
* If the po is not active then the reference accessor is returned.
* If there is an active then the $classNameFields used in the context is
* returned. Be careul, if the persistent object is not used in the current
* context, then a null value will be returned, because no StateItf
* is registered in the working set.
*/
public final StateItf getState(PersistentObjectItf sp) {
if (!sp.speedoIsActive()) {
return sp.speedoGetReferenceState();
}
POManagerItf pm = pmf.lookup();
if (pm == null) {
throw personality.newUserRuntimeException("When a persistent object is used (read or write), a PersistenceManager is needed");
}
return (StateItf) pm.getSpeedoTransaction().lookup(sp.getPName());
}
public final PersistentObjectItf detachCopy(PersistentObjectItf sp,
POManagerItf pm,
Map map,
Object clone,
Collection fgHints) {
Logger logger = getLogger();
if (Debug.ON && logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "detachCopy()");
}
if (!isAbstract() && clone == null){
try {
//instanciate the PersistentObjectItf clone
clone = newSpeedoPOInstance(sp.getClass());
PersistentObjectItf spClone = (PersistentObjectItf)clone;
spClone.speedoSetEncodedPName(pm.getEncodedPName(sp));
spClone.setCeAge(0);
spClone.speedoIsActive(false);
//instanciate the clone of the fields
StateItf fieldsClone = sp.speedoCreateState();
spClone.speedoSetReferenceState(fieldsClone);
fieldsClone.setSpeedoPO(spClone);
//put the association between the po and its clone into the map
if(map != null)
map.put(sp, clone);
synchronized(fgHints){
readIntention(sp, null).detachCopy(pm, map, fieldsClone, fgHints);
}
//mark the state as detached
fieldsClone.setDetachedStatus(DetachedLifeCycle.DETACHED_CLEAN);
} catch(Exception e){
throw personality.newUserRuntimeException("Detach cannot be performed", new Exception[]{ExceptionHelper.getNested(e)});
}
}
return (PersistentObjectItf)clone;
}
public final void attachCopy(PersistentObjectItf sp,
POManagerItf pm,
Map map,
Object clone,
StateItf sa) {
if(map != null) {
if( (PersistentObjectItf) map.get(clone) == sp)
return;
map.put(clone, sp);
//check the validity version
if (!sa.checkVersion(((PersistentObjectItf)clone).speedoGetReferenceState())) {
throw personality.newRuntimeException("The detached copy is no more valid.");
}
sa.attachCopy(pm, map,
((PersistentObjectItf) clone).speedoGetReferenceState());
}
}
public final Collection fgGetFieldsToLoad(String fgName, boolean onlyDirectRef){
Collection c = fgGetFieldsToLoad(fgName);
if(onlyDirectRef){
Iterator it = c.iterator();
while(it.hasNext()) {
String s = (String) it.next();
if(s.indexOf(SpeedoFetchGroup.FG_DOT) != -1
|| s.indexOf(SpeedoFetchGroup.FG_SHARP) != -1
|| s.indexOf(SpeedoFetchGroup.FG_SLASH) != -1
|| s.indexOf(SpeedoFetchGroup.FG_KEY) != -1
|| s.indexOf(SpeedoFetchGroup.FG_VALUE) != -1) {
it.remove();
}
}
}
return c;
}
public void setPrefetchOnExtent(boolean prefetch) {
this.prefetchOnExtent = prefetch;
}
public boolean getPrefetchOnExtent() {
return prefetchOnExtent;
}
public void setPrefetchOnGenClass(boolean prefetch) {
this.prefetchOnGenClass = prefetch;
}
public boolean getPrefetchOnGenClass() {
return prefetchOnGenClass;
}
public void setPrefetchOnQuery(boolean prefetch) {
this.prefetchOnQuery = prefetch;
}
public boolean getPrefetchOnQuery() {
return prefetchOnQuery;
}
public void initSH() {
}
public Class getClassForQuery(String className, String queryName) {
try {
return getClass().getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
throw personality.newUserRuntimeException("Class '" + className
+ "' used in the query '" + queryName
+ "' defined in by the class '" + getClassName()
+ "' is not available. See the inner class loading problem: ", e);
}
}
public final QueryDefinition addNamedQuery(String name, QueryDefinition query) {
return (QueryDefinition) namedQueries.put(name, query);
}
public final QueryDefinition removeNamedQuery(String name) {
return (QueryDefinition) namedQueries.remove(name);
}
public final QueryDefinition getNamedQuery(String name) {
return (QueryDefinition) namedQueries.get(name);
}
public String getPath() {
return getClassName();
}
//IMPLEMENTATION OF THE UserCacheHelper INTERFACE //
//------------------------------------------------//
protected UserCache[] userCaches = new UserCache[0];
protected synchronized UserCache addUserCache(String userCacheName, String[] fields, int id) {
UserCacheImpl uc = new UserCacheImpl();
uc.setName(userCacheName);
uc.setIndexFieldNames(fields);
uc.setId(id);
uc.setActive(true);
if (id >= userCaches.length) {
UserCache[] newUserCaches = new UserCache[id+1];
System.arraycopy(userCaches, 0, newUserCaches, 0, userCaches.length);
userCaches = newUserCaches;
}
userCaches[id] = uc;
return uc;
}
/**
* This method is implemented by the generated XXXHome class
*/
public boolean activeUserCache(String cacheName) {
return false;
}
public UserCache getUserCache(int cacheId) {
if (cacheId < 0
|| cacheId >= userCaches.length
|| userCaches[cacheId] == null) {
return null;
}
return userCaches[cacheId];
}
public UserCache getUserCache(Collection fieldNames) {
PClassMapping[] pcms = null;
try {
pcms = getSubPCMs();
} catch (PException e) {
throw personality.newRuntimeException("Impossible to fetch home of sub class of "
+ getClassName(), e);
}
if (pcms == null || pcms.length == 0) {
return getUserCacheOfTheClass(fieldNames);
}
ArrayList ucs = null;
//get the user cache of this class
UserCache uc = getUserCacheOfTheClass(fieldNames);
//if it not null, add it to the list
if(uc != null) {
if (ucs == null) {
ucs = new ArrayList();
}
ucs.add(uc);
}
//then do the same for each subclass
for (int i = 0; i < pcms.length; i++) {
uc = ((AbstractHomeImpl) pcms[i])
.getUserCacheOfTheClass(fieldNames);
if (uc != null) {
if (ucs == null) {
ucs = new ArrayList();
}
ucs.add(uc);
}
}
if (ucs != null) {
return new CompositeUserCache((UserCache[])
ucs.toArray(new UserCache[ucs.size()]));
}
return null;
}
private UserCache getUserCacheOfTheClass(Collection fieldNames) {
for(int i=0; i<userCaches.length; i++) {
if (userCaches[i] != null && userCaches[i].isActive()) {
String[] fns = userCaches[i].getIndexFieldNames();
List l = Arrays.asList(fns);
if (fns.length == fieldNames.size()
&& fieldNames.containsAll(l)
&& l.containsAll(fieldNames)
) {
return userCaches[i];
}
}
}
return null;
}
public Collection getActiveUserCache() {
if (userCaches == null || userCaches.length == 0) {
return Collections.EMPTY_LIST;
}
ArrayList al = new ArrayList();
for(int i=0; i<userCaches.length; i++) {
if (userCaches[i] != null && userCaches[i].isActive()) {
al.add(userCaches[i]);
}
}
return al;
}
public void userCacheEntryUnbound(Object oid) {
if (userCaches == null || userCaches.length == 0) {
return;
}
for(int i=0; i<userCaches.length; i++) {
if (userCaches[i] != null && userCaches[i].isActive()) {
userCaches[i].unbindFromOID(oid);
}
}
}
}