/**
* 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.fractal.api.control.BindingController;
import org.objectweb.jorm.api.PClassMapping;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.lib.JormPathHelper;
import org.objectweb.jorm.naming.api.PBinder;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.jorm.naming.api.PNameManager;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.jorm.util.api.Loggable;
import org.objectweb.perseus.cache.api.CacheEntry;
import org.objectweb.perseus.cache.api.CacheEntryFactory;
import org.objectweb.perseus.cache.api.CacheEvent;
import org.objectweb.perseus.cache.api.FixableCacheEntry;
import org.objectweb.perseus.persistence.api.ConnectionHolder;
import org.objectweb.perseus.persistence.api.MemoryInstanceManager;
import org.objectweb.perseus.persistence.api.PersistenceException;
import org.objectweb.perseus.persistence.api.State;
import org.objectweb.perseus.persistence.api.StateManager;
import org.objectweb.speedo.api.ExceptionHelper;
import org.objectweb.speedo.api.SpeedoRuntimeException;
import org.objectweb.speedo.genclass.AbstractGenClassHome;
import org.objectweb.speedo.genclass.SupportedGenClass;
import org.objectweb.speedo.genclass.api.SpeedoGenClassPO;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.mapper.api.JormFactory;
import org.objectweb.speedo.mim.api.LifeCycle;
import org.objectweb.speedo.mim.api.MemoryInstanceManagerAttribute;
import org.objectweb.speedo.mim.api.StateItf;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import javax.jdo.listener.InstanceLifecycleEvent;
/**
* This class is an implementation of the MemoryInstanceManager provided by the
* persistence framwork availlable in perseus. This implementation depends on
* the identifier used, and the way to instanciate user objects. This
* implementation supports only the jorm object identifier: PName.
* This implementation supports also the instanciation of the Jorm generic
* class. To do this it is needed to specify for each jorm generic class used
* the name of the class which must be instanciated. This configuration is done
* via the GenClassNames attribute.
* This implementation does not used a pool, then the instance are created at
* each newInstance call.
*
* @author S.Chassande-Barrioz
*/
public class MemoryInstanceManagerImpl
implements MemoryInstanceManager,
MemoryInstanceManagerAttribute,
StateManager,
CacheEntryFactory,
BindingController {
public final static String JORM_FACTORY_BINDING = "jorm-factory";
/**
* This fields maintains the association between the name of the Jorm
* generic class and the class name which must be implemented.
*/
private Map name2gcimpl = null;
protected JormFactory jf = null;
protected Personality personality;
protected Logger logger = null;
public MemoryInstanceManagerImpl() {
name2gcimpl = Collections.EMPTY_MAP;
}
private String getGCClassName(String cn) {
String res = (String) name2gcimpl.get(cn);
if (res != null) {
return res;
}
for (int i = 0; i < SupportedGenClass.GC_IMPL.length; i++) {
if (SupportedGenClass.GC_IMPL[i][0].equals(cn)) {
return personality.getGenClassName(
SupportedGenClass.GC_IMPL[i][1]);
}
}
return null;
}
/**
* Active a persistent object :
* - assign the PClassMapping
* - assign a Pbinding for the SpeedoGenClassPO
*
* @param sp is PersistentObjectItf or a SpeedoGenClassPO instance, the
* persistent object
* @param pn the identifier of the object
*/
private void activePO(PersistentObjectItf sp, PName pn) {
//fetch the PClassMapping
PClassMapping pcm = null;
if (pn != null && (pn.getPNameManager() instanceof PBinder)) {
pcm = ((PBinder) pn.getPNameManager()).getBinderClassMapping();
}
if (sp instanceof SpeedoGenClassPO) {
if (((SpeedoGenClassPO) sp).speedoGetPBinding() == null) {
if (pcm == null) {
pcm = jf.getGenClassMapping(
((SpeedoGenClassPO) sp).speedoGetGenClassId());
}
//Assign the PBinding
try {
((SpeedoGenClassPO) sp).speedoSetPBinding(pcm.createPBinding());
} catch (PException e) {
throw personality.newRuntimeException("Impossible to initialize the delegate PBinding of the genclass", e);
}
if (sp instanceof Loggable) {
((Loggable) sp).setLogger(logger);
}
}
} else {
if (pcm == null) {
try {
pcm = jf.getPClassMapping(sp.getClass());
} catch (PException e) {
throw personality.newRuntimeException("Impossible to initialize a class", e);
}
}
if (sp.getPClassMapping() == null) {
//Assign the PClassMapping
try {
sp.init(pcm);
} catch (PException e) {
Exception ie = ExceptionHelper.getNested(e);
if (logger != null)
logger.log(BasicLevel.ERROR,
"Impossible to init the persistent class: "
+ sp.getClass().getName(), ie);
throw personality.newRuntimeException("Impossible to init the persistent class:" +
ie.getMessage());
}
}
}
//Mark as active
sp.speedoIsActive(true);
if (logger != null && logger.isLoggable(BasicLevel.DEBUG))
logger.log(BasicLevel.DEBUG, "PO activated: " + sp.getClass().getName());
}
private HomeItf getHome(Object o) {
if (o == null) {
logger.log(BasicLevel.WARN, "Impossible to find the home: null object");
return null;
} else if (o instanceof PersistentObjectItf) {
PersistentObjectItf po =(PersistentObjectItf) o;
HomeItf h = po.speedoGetHome();
if (h == null) {
if (o instanceof SpeedoGenClassPO) {
PName pn = po.getPName();
if (pn == null) {
h = (HomeItf) ((PBinder) pn.getPNameManager()).getBinderClassMapping();
} else {
h = (HomeItf) jf.getGenClassMapping(
((SpeedoGenClassPO) po).speedoGetGenClassId());
}
} else {
try {
h = (HomeItf) jf.getPClassMapping(o.getClass());
} catch (PException e) {
throw new SpeedoRuntimeException(e);
}
}
}
if (h == null) {
logger.log(BasicLevel.WARN, "Impossible to find the home");
}
return h;
} else if (o instanceof PName) {
PName pn = (PName) o;
PNameManager pnm = pn.getPNameManager();
while (!(pnm instanceof PBinder)) {
PName npn;
try {
npn = pn.resolve(null);
} catch (PException e) {
logger.log(BasicLevel.WARN, "Impossible to find the home from the PName " + pn, e);
return null;
}
if (npn == pn) {
logger.log(BasicLevel.WARN, "Impossible to find the home from the PName " + pn +" : cycle");
return null;
} else {
pnm = pn.getPNameManager();
}
}
return (HomeItf) ((PBinder) pnm).getBinderClassMapping();
} else {
logger.log(BasicLevel.WARN, "No home found with this object: " + o.getClass());
return null;
}
}
// IMPLEMENTATION OF THE MemoryInstanceManagerAttribute INTERFACE //
//----------------------------------------------------------------------//
public String[] listFc() {
return new String[]{JORM_FACTORY_BINDING};
}
public Object lookupFc(String s) {
if (JORM_FACTORY_BINDING.equals(s)) {
return jf;
} else {
return null;
}
}
public void bindFc(String s, Object o) {
if (JORM_FACTORY_BINDING.equals(s)) {
jf = (JormFactory) o;
} else if ("logger".equals(s)) {
logger = (Logger) o;
}
}
public void unbindFc(String s) {
if (JORM_FACTORY_BINDING.equals(s)) {
jf = null;
}
}
// IMPLEMENTATION OF THE MemoryInstanceManagerAttribute INTERFACE //
//----------------------------------------------------------------------//
/**
* @return a String describing the genclass names with the following format:
* "(jorm_name,java_name),(jorm_name,java_name),(jorm_name,java_name)}"
*/
public String getGenClassNames() {
if (name2gcimpl == null || name2gcimpl.size() == 0)
return "{}";
StringBuffer res = new StringBuffer("");
boolean first = true;
for (Iterator it = name2gcimpl.entrySet().iterator(); it.hasNext();) {
if (first) {
res.append("{");
first = false;
} else
res.append(",");
res.append("(");
Map.Entry me = (Map.Entry) it.next();
res.append(me.getKey());
res.append(",");
res.append(me.getValue());
res.append(")");
}
res.append("}");
return res.toString();
}
/**
* It assignes a description of the gen class names.
* @param gcname is the desciption which must follow this format:
* "(jorm_name,java_name),(jorm_name,java_name),(jorm_name,java_name)}"
*/
public void setGenClassNames(String gcname) {
if (gcname == null || gcname.length() == 0)
name2gcimpl = Collections.EMPTY_MAP;
name2gcimpl = new HashMap(5);
StringTokenizer st = new StringTokenizer(gcname, "{,()}:;[]", false);
String key = null;
while (st.hasMoreTokens()) {
if (key == null) {
key = st.nextToken().trim();
} else {
name2gcimpl.put(key, st.nextToken().trim());
}
}
}
public Personality getPersonality() {
return personality;
}
public void setPersonality(Personality p) {
this.personality = p;
}
// IMPLEMENTATION OF THE MemoryInstanceManager INTERFACE //
//-------------------------------------------------------//
/**
* It creates an instance since an identifier
* @param oid is the identifier of the futur object
* @return a memory instance
*/
public Object newInstance(Object oid, ConnectionHolder context) throws PersistenceException {
if (!(oid instanceof PName))
throw new PersistenceException("Unmanaged object identifier: " + oid);
PName pn = (PName) oid;
String className = null;
try {
pn = pn.resolve(context);
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "newInstance(" + oid + "): pn.resolve=" + pn);
logger.log(BasicLevel.DEBUG, "newInstance(" + oid + "): pn.type=" + pn.getPType());
}
PType type = pn.getPType();
if (type == null) {
//Error detected by Roland Hedayat
throw new PersistenceException("newInstance() "
+ "\n\toid=" + oid
+ "\n\tpn=" + pn
+ "\n\tpn.pnc=" + (pn.getPNameManager() != null ? pn.getPNameManager() : null));
}
className = type.getJormName();
int idx = className.indexOf(JormPathHelper.SEP);
PClassMapping pcm = ((PBinder) pn.getPNameManager())
.getBinderClassMapping();
PersistentObjectItf sp = null;
if ((pcm instanceof AbstractGenClassHome) && idx != -1) {
// This is a Generic class
className = getGCClassName(className.substring(0, idx));
sp = (PersistentObjectItf) Class.forName(className).newInstance();
} else {
if (pcm != null) {
className = pcm.getClassName();
}
ClassLoader cl = jf.getClassLoader(className);
if (cl == null) {
cl = oid.getClass().getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
}
sp = (PersistentObjectItf)
cl.loadClass(className).newInstance();
}
sp.speedoSetReferenceState(null);
return sp;
} catch (PException e) {
throw new PersistenceException(
"Impossible to analyze the object identifier" + oid, e);
} catch (Exception e) {
throw new PersistenceException(
"Impossible to instanciate the class " + className
+ "Be careful to the generic class (Collection, ...), oid class:"
+ oid.getClass(), e);
}
}
public boolean isObjectSharing(Object o) {
HomeItf home = getHome(o);
return home == null || home.isShareable();
}
// IMPLEMENTATION OF THE StateManager INTERFACE //
//----------------------------------------------//
public State createState(CacheEntry ce) {
return ((PersistentObjectItf) ce).speedoCreateState();
}
public State createState(State s) {
PersistentObjectItf sp = (PersistentObjectItf) s.getCacheEntry();
StateItf sa = sp.speedoCreateState();
sp.speedoCopyState(((StateItf) s), sa);
return sa;
}
public State getReferenceState(CacheEntry ce) {
return ((PersistentObjectItf) ce).speedoGetReferenceState();
}
public void destroyState(State state) {
//nothing to do
}
public void makeUnexported(State state) {
((StateItf) state).speedoChangeStatus(LifeCycle.ACTION_DELETEPERSISTENT);
}
public boolean isUnexported(State state) {
return LifeCycle.isDeleted(((StateItf) state).speedoGetStatus());
}
public void makeExported(State state) {
((StateItf) state).speedoChangeStatus(LifeCycle.ACTION_MAKEPERSISTENT);
}
public boolean isExported(State state) {
return LifeCycle.isNew(((StateItf) state).speedoGetStatus());
}
public void makeDirty(State state) {
((StateItf) state).speedoChangeStatus(LifeCycle.ACTION_WRITEFIELD_ACTIVEDATASTORETRANSACTION);
((StateItf) state).setFlushed(false);
}
public boolean isDirty(State state) {
return LifeCycle.isDirty(((StateItf) state).speedoGetStatus());
}
public void setReferenceState(CacheEntry ce, State state) {
if (state == ((PersistentObjectItf) ce).speedoGetReferenceState()) {
return;
}
PersistentObjectItf sp = (PersistentObjectItf) ce;
if (sp.speedoIsActive() && state == null) {
sp.speedoGetHome().sendEvent(HomeItf.PRE_CLEAR, sp, null);
}
sp.speedoSetReferenceState((StateItf) state);
if (sp.speedoIsActive() && state == null) {
sp.speedoGetHome().sendEvent(HomeItf.POST_CLEAR, sp, null);
}
}
public void makeClean(State state) {
StateItf sa = (StateItf) state;
sa.speedoSetStatus(LifeCycle.PERSISTENT_CLEAN);
}
public void makeFlushed(State state) {
((StateItf) state).setFlushed(true);
}
public boolean isFlushed(State state) {
return ((StateItf) state).hasBeenFlush();
}
public void makeUnbound(CacheEntry ce) {
PersistentObjectItf sp = (PersistentObjectItf) ce;
sp.speedoIsActive(false);
try {
sp.init(null);
} catch (PException e) {
logger.log(BasicLevel.WARN, "Error during the unbinding: ", e);
}
}
public void makeBound(CacheEntry ce, Object oid) {
PName pname = (PName) oid;
PersistentObjectItf sp = (PersistentObjectItf) ce;
try {
sp.bind(pname);
} catch (PException e) {
logger.log(BasicLevel.WARN, "Error during the binding: ", e);
}
}
public boolean isBound(CacheEntry ce) {
return ((PersistentObjectItf) ce).speedoIsActive();
}
public boolean isToMerge(State state) {
return ((StateItf) state).isToMerge();
}
public void makeToMerge(State state, Object thinLock) {
((StateItf) state).makeToMerge(thinLock);
}
public State merge(State oldState, State newState) {
return ((StateItf) newState).merge(oldState);
}
public void stateNoMoreUsed(State state) {
((StateItf) state).unSwizzle();
}
// IMPLEMENTATION OF THE CacheEventListener INTERFACE //
//----------------------------------------------------//
/**
* @param event
*/
public void entryBound(CacheEvent event) {
}
/**
* @param event
*/
public void entryUnbound(CacheEvent event) {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Entry unbound from the cache: \n\tid: "
+ event.getCeIdentifier());
}
PName pn = (PName) event.getCeIdentifier();
HomeItf sh = (HomeItf)
((PBinder) pn.getPNameManager()).getBinderClassMapping();
sh.userCacheEntryUnbound(pn);
}
// IMPLEMENTATION OF THE CacheEntryFactory INTERFACE //
//---------------------------------------------------//
public int getCachingStrategy(Object o) {
HomeItf home = getHome(o);
if (home == null) {
return CACHING_STRATEGY_CACHED;
} else {
return home.isCacheable()
? CACHING_STRATEGY_CACHED
: CACHING_STRATEGY_NO_CACHE;
}
}
/**
* binds the PersistentObjectItf to its identifier (PName) if it does not have
* already one.
* @param id is the PName of the PersistentObjectItf
* @param obj is the PersistentObjectItf instance added into the cache
* @return the PersistentObjectItf instance
*/
public FixableCacheEntry create(Object id, Object obj) {
PersistentObjectItf sp = (PersistentObjectItf) obj;
PName pn = (PName) id;
activePO(sp, pn);
if (sp.getPName() == null || sp.getPName().isNull()) {
try {
pn.resolve(null);
sp.bind(pn);
} catch (PException e) {
throw personality.newRuntimeException(
"Impossible to bind the po to its persistent name: ", e);
}
}
/**
* else the entry is a new persistent object, the export created an
* identifier
*/
return (FixableCacheEntry) obj;
}
}