/**
* Copyright (C) 2001-2005 France Telecom R&D
*/
package org.objectweb.speedo.pm.lib;
import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.util.Fractal;
import org.objectweb.jorm.api.PClassMapping;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.naming.api.PBinder;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.jorm.naming.api.PNameCoder;
import org.objectweb.jorm.util.api.Loggable;
import org.objectweb.perseus.cache.api.CacheEntry;
import org.objectweb.perseus.concurrency.lib.Semaphore;
import org.objectweb.perseus.persistence.api.PersistenceException;
import org.objectweb.perseus.persistence.api.RolledBackPersistenceException;
import org.objectweb.perseus.persistence.api.State;
import org.objectweb.perseus.persistence.api.StateFilter;
import org.objectweb.perseus.persistence.api.TransactionalPersistenceManager;
import org.objectweb.perseus.persistence.api.TransactionalWorkingSet;
import org.objectweb.perseus.persistence.api.VirtualState;
import org.objectweb.speedo.api.ExceptionHelper;
import org.objectweb.speedo.api.SpeedoRuntimeException;
import org.objectweb.speedo.genclass.api.SpeedoGenClassPO;
import org.objectweb.speedo.mapper.api.JormFactory;
import org.objectweb.speedo.mim.api.FetchPlanItf;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.speedo.mim.api.StateItf;
import org.objectweb.speedo.pm.api.POManagerFactoryItf;
import org.objectweb.speedo.pm.api.POManagerItf;
import org.objectweb.speedo.query.api.QueryManager;
import org.objectweb.speedo.query.api.QueryManagerAttribute;
import org.objectweb.speedo.workingset.api.TransactionItf;
import org.objectweb.speedo.workingset.jdo.api.JDOTransactionItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.LoggerFactory;
import java.util.Collection;
import java.util.Map;
import javax.jdo.listener.InstanceLifecycleEvent;
import javax.transaction.Status;
public abstract class AbstractPOManager implements POManagerItf {
public final static String PO_MANAGER_FACTORY_BINDING = "po-manager-factory";
public final static String TRANSACTIONAL_PERSISTENCE_MANAGER_BINDING = "transactional-persistence-manager";
public final static String QUERY_MANAGER_BINDING = "query-manager";
public final static String TRANSACTION_BINDING = "transaction";
public final static String JORM_FACTORY_BINDING = "jorm-factory";
public final static String PNAME_CODER_BINDING = "pname-coder";
public final static String COMPONENT_BINDING = "component";
protected Logger logger;
protected LoggerFactory loggerFactory;
/**
* The POManagedFactory managing this POManagerItf
*/
protected POManagerFactoryItf pmf = null;
/**
* The transaction associated to this po manager.
*/
protected TransactionItf tx;
/**
* The manager of Query. It contains the optimized queries already
* used.
*/
protected QueryManager queryManager = null;
/**
* The TransactionalPersistenceManager (Perseus)
*/
protected TransactionalPersistenceManager tpm = null;
/**
* The JormFactory able to initialize the Persistent of classes
*/
protected JormFactory jf = null;
/**
* The PNameCoder able to encode/decode all PName
*/
protected PNameCoder pnc = null;
/**
* is the information permiting the access to the data store
*/
protected Object connectionSpec = null;
/**
* Indicates if number of po manager user. O means the POManagerItf
* is closed.
*/
protected short nbUse = 0;
/**
* The fractal reference to this
*/
protected POManagerItf thisPM = null;
/**
* The lastest thread associated to the current POManagerItf
*/
protected Thread currentThread = null;
/**
* A semaphore object used to support the multithread mode
*/
protected Semaphore semaphore;
protected boolean prefetchOnExtent = true;
protected boolean prefetchOnQuery = true;
protected FetchPlanItf fetchPlan;
/**
*
*/
public AbstractPOManager() {
semaphore = new Semaphore(false);
}
// IMPLEMENTATION OF THE UserBindingController INTERFACE //
//-------------------------------------------------------//
public String[] listFc() {
return new String[] {
PO_MANAGER_FACTORY_BINDING,
QUERY_MANAGER_BINDING,
TRANSACTIONAL_PERSISTENCE_MANAGER_BINDING,
TRANSACTION_BINDING,
JORM_FACTORY_BINDING,
PNAME_CODER_BINDING
};
}
public Object lookupFc(String s) {
if (PO_MANAGER_FACTORY_BINDING.equals(s)) {
return pmf;
} else if (QUERY_MANAGER_BINDING.equals(s)) {
return queryManager;
} else if (TRANSACTIONAL_PERSISTENCE_MANAGER_BINDING.equals(s)) {
return tpm;
} else if (TRANSACTION_BINDING.equals(s)) {
return tx;
} else if (PNAME_CODER_BINDING.equals(s)) {
return pnc;
} else if (JORM_FACTORY_BINDING.equals(s)) {
return jf;
} else {
return null;
}
}
public void bindFc(String s, Object o) {
if ("monolog-factory".equals(s)) {
loggerFactory = (LoggerFactory) o;
} else if ("logger".equals(s)) {
logger = (Logger) o;
} else if (PO_MANAGER_FACTORY_BINDING.equals(s)) {
pmf = (POManagerFactoryItf) o;
} else if (QUERY_MANAGER_BINDING.equals(s)) {
queryManager = (QueryManager) o;
if (queryManager != null) {
try {
QueryManagerAttribute qma = (QueryManagerAttribute)
Fractal.getAttributeController(
((Interface) queryManager).getFcItfOwner()) ;
prefetchOnExtent = qma.getPrefetchActivatedOnExtent();
prefetchOnQuery = qma.getPrefetchActivatedOnQuery();
} catch (Exception e) {
logger.log(BasicLevel.WARN,
"impossible to fetch the attribute prefetchActivatedOnExtent: ", e);
}
}
} else if (TRANSACTIONAL_PERSISTENCE_MANAGER_BINDING.equals(s)) {
tpm = (TransactionalPersistenceManager) o;
} else if (TRANSACTION_BINDING.equals(s)) {
tx = (TransactionItf) o;
} else if (PNAME_CODER_BINDING.equals(s)) {
pnc = (PNameCoder) o;
} else if (JORM_FACTORY_BINDING.equals(s)) {
jf = (JormFactory) o;
} else if (COMPONENT_BINDING.equals(s)) {
try {
thisPM = (POManagerItf) ((Component) o).getFcInterface("po-manager");
} catch (NoSuchInterfaceException e) {
}
}
}
public void unbindFc(String s) {
if (PO_MANAGER_FACTORY_BINDING.equals(s))
pmf = null;
else if (TRANSACTIONAL_PERSISTENCE_MANAGER_BINDING.equals(s))
tpm = null;
else if (TRANSACTION_BINDING.equals(s))
tx = null;
else if (QUERY_MANAGER_BINDING.equals(s))
queryManager = null;
else if (PNAME_CODER_BINDING.equals(s))
pnc = null;
else if (JORM_FACTORY_BINDING.equals(s))
jf = null;
}
// IMPLEMENTATION OF THE POManagerItf INTERFACE //
//----------------------------------------------//
public abstract FetchPlanItf speedoGetFetchPlan();
public TransactionalPersistenceManager getTransactionalPersistenceManager() {
return tpm;
}
public void open(Object connectionspec) {
resetPMOnOpen(connectionspec);
try {
tx.activate();
} catch (PersistenceException e) {
throw new SpeedoRuntimeException(
"Impossible to open this persistence manager", e);
}
}
public Object getConnectionSpec() {
return connectionSpec;
}
public void addUse() {
nbUse++;
logger.log(BasicLevel.DEBUG, "POManagerItf used: " + nbUse);
}
/**
* A POManager instance can be used until it is closed.
* @return true if this POManager has been closed
* @see #closePOManager()
*/
public boolean isPOMClosed() {
bindPMThread();
return nbUse == 0;
}
public void closePOManager() {
if (semaphore.on) {
semaphore.P();
}
if (nbUse == 0) {
return;
} else if (nbUse > 1) {
nbUse--;
logger.log(BasicLevel.DEBUG, "Imbricated POManager closed ("
+ nbUse + ")");
return;
}
bindPMThread();
try {
tpm.close(tx);
} catch (PersistenceException e) {
throw new SpeedoRuntimeException(
"Impossible to close the persistence manager",
ExceptionHelper.getNested(e));
} finally {
nbUse--;
//Forget the information to access to the data store.
connectionSpec = null;
currentThread = null;
try {
pmf.poManagerClosed(thisPM);
} finally {
if (semaphore.on) {
semaphore.V();
}
if (logger.isLoggable(BasicLevel.INFO))
logger.log(BasicLevel.INFO, "Persistence Manager closed");
}
}
}
public TransactionItf getSpeedoTransaction() {
return tx;
}
public POManagerFactoryItf getPOManagerFactory() {
return pmf;
}
public Object getEncodedPName(PersistentObjectItf o) {
try {
assertIsPO(o, "");
} catch (Exception e) {
return null;
}
PersistentObjectItf po = (PersistentObjectItf) o;
assertPOManager(po);
if (!po.speedoIsActive()) {
throw new SpeedoRuntimeException("Non persistent object does not have identifier: " + o);
}
try {
return pnc.encodeAbstract(po.getPName());
} catch (PException e) {
throw new SpeedoRuntimeException(
"Problem while encoding persistent name.",
new Exception[]{ExceptionHelper.getNested(e)});
}
}
public PName decodeIdentifier(Class aClass, Object s) {
assertIsOpen();
bindPMThread();
try {
return pnc.decodeAbstract(s, aClass);
} catch (PException e) {
throw new SpeedoRuntimeException("Invalid persistent object identifier "
+ s + " for the class " + aClass, new Exception[]{e});
}
}
public void speedoDeletePersistentAll(Object[] o) {
if (o == null || o.length == 0) {
return;
}
for (int i = 0; i < o.length; i++) {
speedoDeletePersistent(o[i]);
}
}
// IMPLEMENTATION OF THE Synchronization INTERFACE //
//-------------------------------------------------//
public void beforeCompletion() {
byte stat = tx.getStatus();
if (stat == TransactionalWorkingSet.CTX_PREPARED_FAIL
|| stat == TransactionalWorkingSet.CTX_PREPARED_OK) {
/**
* The PersistenceManager has been registered several times as a
* Synchronization and the beforeCompletion method is call several
* times.
*/
return;
}
bindPMThread();
logger.log(BasicLevel.INFO, "beforeCompletion of the transaction: ");
try {
tpm.prepare(tx);
} catch (PersistenceException e) {
Exception ie = ExceptionHelper.getNested(e);
logger.log(BasicLevel.ERROR,
"Error during the preparation of the transaction:", ie);
throw new SpeedoRuntimeException("", ie);
}
}
public void afterCompletion(int i) {
if (nbUse == 0) {
/**
* The PersistenceManager has been registered several times as a
* Synchronization and the afterCompletion method is call several
* times.
*/
return;
}
bindPMThread();
boolean commit = (i == Status.STATUS_COMMITTED);
try {
if (commit) {
logger.log(BasicLevel.DEBUG, "afterCompletion(STATUS_COMMITTED) of the transaction: ");
tpm.commit(tx);
} else {
logger.log(BasicLevel.DEBUG, "afterCompletion(STATUS_ROLLEDBACK) of the transaction: ");
tpm.rollback(tx);
}
} catch (PersistenceException e) {
Exception ie = ExceptionHelper.getNested(e);
logger.log(BasicLevel.ERROR, "Error during the "
+ (commit ? "commit" : "rollback")
+ " of the transaction:", ie);
throw new SpeedoRuntimeException("", ie);
} finally {
closePOManager();
}
}
// OTHER METHODS //
//---------------//
public Object speedoGetObject(PName pn, boolean validate) {
if (pn.isNull()) {
return null;
}
try {
if (!(pn.getPNameManager() instanceof PBinder)) {
pn = pnc.decodeAbstract(pn, tx.getConnectionHolder());
}
if (validate) {
State s = tx.lookup(pn);
if (s != null && s != VirtualState.instance) {
return s.getCacheEntry();
} else {
//Fetch an instance (new or from the cache)
CacheEntry ce = tpm.getObjectById(tx, pn);
//clear the state
tpm.refresh(tx, ce);
//if it exists then object loading else exception
tpm.readIntention(tx, ce, null);
return ce;
}
} else {
//do not reload if present in the cache
return tpm.readIntention(tx, pn, null).getCacheEntry();
}
} catch (PersistenceException e) {
throw new SpeedoRuntimeException(e);
} catch (PException e) {
throw new SpeedoRuntimeException(e);
}
}
public Object speedoGetObject(Object id, Class poc, boolean validate)
throws PException, PersistenceException {
Object oid = id;
if (poc != null) {
oid = pnc.decodeAbstract(oid, poc);
}
PName pn = pnc.decodeAbstract(oid, tx.getConnectionHolder());
return speedoGetObject(pn, validate);
}
protected Object speedoPersist(PersistentObjectItf po, Map map)
throws PException, PersistenceException {
//initialize the PBinding of the persistent object
PClassMapping pcm;
if (po instanceof SpeedoGenClassPO) {
pcm = jf.getGenClassMapping(
((SpeedoGenClassPO) po).speedoGetGenClassId());
//Assign the PBinding
((SpeedoGenClassPO) po).speedoSetPBinding(pcm.createPBinding());
if (po instanceof Loggable) {
((Loggable) po).setLogger(logger);
}
} else {
pcm = jf.getPClassMapping(po.getClass());
}
po.init(pcm);
po.speedoGetHome().sendEvent(HomeItf.PRE_NEW, po, null);
Object hints = po.speedoGetPNameHints();
final StateItf state;
if (hints == null) {
state = (StateItf) tpm.export(tx, po);
} else {
state = (StateItf) tpm.export(tx, po, hints);
}
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG,
"New persistent instance, identifier=" + po.getPName());
}
if (map == null) {
//call the po to make persistent the reference field
state.makePersistent(this);
} else {
//call the po to make persistent the reference field, with the map context
state.makePersistentOnAttach(this, map);
}
po.speedoGetHome().sendEvent(HomeItf.POST_NEW, po, null);
return po;
}
public void speedoFlush() throws PersistenceException {
tpm.flush(tx, (StateFilter) null);
}
protected void resetPMOnOpen(Object connectionspec) {
connectionSpec = connectionspec;
nbUse = 0;
//??? init the transaction mode Optimistic or not ?
//??? init the ignoreCache property or not ?
//??? init the multithread property or not ?
}
public Semaphore getSemaphore() {
return semaphore;
}
protected void bindPMThread() {
if (currentThread != Thread.currentThread()) {
currentThread = Thread.currentThread();
pmf.bindPM2Thread(thisPM);
currentThread = null;
}
}
/**
* Verify the persistence manager is open.
* @exception SpeedoRuntimeException if the pomanager is closed.
*/
protected void assertIsOpen() {
if (nbUse == 0)
throw new SpeedoRuntimeException("The persistent manager is closed.");
}
/**
* Verify that an instance is persistence object.
* @param pc the object to test.
* @exception SpeedoRuntimeException if the object is not persistence capable.
*/
protected void assertIsPO(Object pc, String cmd) {
if (!(pc instanceof PersistentObjectItf)) {
if (pc == null) {
throw new SpeedoRuntimeException("Null persistent object instance specified");
} else if (pc.getClass().isArray()
|| pc instanceof Collection) {
throw new SpeedoRuntimeException("You must use the " + cmd
+ "All method with a multivalued parameter: " + pc);
} else {
throw new SpeedoRuntimeException("The object is not a PersistentObjectItf, beware to : " + pc);
}
}
}
/**
* Verify the instance is managed by this persistence manager.
* @param sp a speedo po instance.
* @exception SpeedoRuntimeException if the instance is managed by
* another persistence manager.
*/
protected void assertPOManager(PersistentObjectItf sp) {
POManagerItf pm = (POManagerItf) sp.speedoGetPOManager();
if (!thisPM.equals(pm)) {
if (pm == null) {
try {
tpm.readIntention(tx, sp, null);
} catch (RolledBackPersistenceException e) {
throw ((JDOTransactionItf) tx).rollBackOnInternalError(e);
} catch (PersistenceException e) {
throw new SpeedoRuntimeException("Impossible to add the " +
"persistent object into the working set", e);
}
} else {
throw new SpeedoRuntimeException(
"Object not managed by this persistence manager, object: "
+ sp);
}
}
}
}