/* * JBoss, Home of Professional Open Source * Copyright 2009-11, Red Hat Middleware LLC, and others contributors as indicated * by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.riftsaw.engine.internal; import java.io.File; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.common.evt.DebugBpelEventListener; import org.apache.ode.bpel.engine.BpelManagementFacadeImpl; import org.apache.ode.bpel.engine.BpelServerImpl; import org.apache.ode.bpel.engine.CountLRUDehydrationPolicy; import org.apache.ode.bpel.engine.cron.CronScheduler; import org.apache.ode.bpel.evt.BpelEvent; import org.apache.ode.bpel.extvar.jdbc.JdbcExternalVariableModule; import org.apache.ode.bpel.iapi.BpelEventListener; import org.apache.ode.bpel.iapi.CacheProvider; import org.apache.ode.bpel.iapi.EndpointReferenceContext; import org.apache.ode.bpel.iapi.Message; import org.apache.ode.bpel.iapi.MyRoleMessageExchange; import org.apache.ode.bpel.iapi.ProcessConf; import org.apache.ode.bpel.iapi.ProcessStoreEvent; import org.apache.ode.bpel.iapi.ProcessStoreListener; import org.apache.ode.bpel.iapi.Scheduler; import org.apache.ode.bpel.intercept.MessageExchangeInterceptor; import org.apache.ode.bpel.memdao.BpelDAOConnectionFactoryImpl; import org.apache.ode.bpel.pmapi.BpelManagementFacade; import org.apache.ode.dao.bpel.BpelDAOConnectionFactory; import org.apache.ode.dao.scheduler.SchedulerDAOConnectionFactory; import org.apache.ode.dao.store.ConfStoreDAOConnectionFactory; import org.apache.ode.il.cache.CacheProviderFactory; import org.apache.ode.il.config.OdeConfigProperties; import org.apache.ode.il.dbutil.Database; import org.apache.ode.scheduler.simple.SimpleScheduler; import org.apache.ode.store.RiftSawProcessStore; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.GUID; import org.apache.ode.utils.Properties; import org.infinispan.manager.EmbeddedCacheManager; import org.riftsaw.engine.BPELEngine; import org.riftsaw.engine.BPELEngineListener; import org.riftsaw.engine.DeploymentRef; import org.riftsaw.engine.DeploymentUnit; import org.riftsaw.engine.Fault; import org.riftsaw.engine.ServiceLocator; import org.riftsaw.engine.jboss.JndiRegistry; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This class provides an ODE based implementation of the BPEL engine interface. * */ public class BPELEngineImpl implements BPELEngine { private static final Log LOG=LogFactory.getLog(BPELEngineImpl.class); private static final String _jndiName = "java:jboss/BPELEngine"; private static final String _emfJndiName = "java:jboss/BPELEMFactory"; private static final String DEPLOYMENT_FOLDER_ENV_VAR = "jboss.server.temp.dir"; private BpelServerImpl _bpelServer; private RiftSawProcessStore _store; private OdeConfigProperties _odeConfig; private TransactionManager _txMgr; private BpelDAOConnectionFactory _daoCF; private ConfStoreDAOConnectionFactory _storeCF; private SchedulerDAOConnectionFactory _schedulerDaoCF; private Scheduler _scheduler; private Database _db; private ExecutorService _executorService; private CronScheduler _cronScheduler; private CacheProvider _cacheProvider; private ServiceLocator _serviceLocator; private DeploymentManager _deploymentManager; private java.util.Map<BPELEngineListener,ProxyBpelEventListener> _listeners= new java.util.HashMap<BPELEngineListener, BPELEngineImpl.ProxyBpelEventListener>(); /** * {@inheritDoc} */ public void init(ServiceLocator locator, java.util.Properties props) throws Exception { if (_odeConfig == null) { if (props == null) { props = new java.util.Properties(); } LOG.info("ODE PROPS="+props); _odeConfig = new OdeConfigProperties(props, "bpel."); } _serviceLocator = locator; LOG.info("Initializing transaction manager"); initTxMgr(); LOG.info("Creating data source."); initDataSource(); populateDatabaseSchema(); LOG.info("Starting DAO."); initDAO(); LOG.info("Initializing deployment manager"); initDeploymentManager(); EndpointReferenceContextImpl eprContext = new EndpointReferenceContextImpl(this); initCacheProvider(); LOG.info("Initializing BPEL process store."); initProcessStore(eprContext); LOG.info("Initializing UDDI registration"); //initUDDIRegistration(); LOG.info("Initializing BPEL server."); initBpelServer(eprContext, locator); _store.loadAll(); // Register BPEL event listeners configured in axis2.properties file. registerEventListeners(); registerMexInterceptors(); //registerExtensionActivityBundles(); registerExternalVariableModules(); try { _bpelServer.start(); } catch (Exception ex) { String errmsg = "SERVER START FAILED"; LOG.error(errmsg, ex); } LOG.info("Starting scheduler"); _scheduler.start(); registerServicesIntoJNDI(); } /** * Set the ode config. * * @param odeConfig The ode config to set. */ public void setOdeConfig(OdeConfigProperties odeConfig) { _odeConfig = odeConfig; } /** * Set the deployment manager. * * @param deploymentManager The deployment manager to set. */ public void setDeploymentManager(DeploymentManager deploymentManager) { _deploymentManager = deploymentManager; } /** * Set the transaction manager. * * @param transactionManager The transaction manager to set. */ public void setTransactionManager(TransactionManager transactionManager) { _txMgr = transactionManager; } /** * Set the daoCF. * * @param daoCF The daoCF to set. */ public void setDaoCF(BpelDAOConnectionFactory daoCF) { _daoCF = daoCF; } /** * Set the storeCF. * * @param storeCF The storeCF to set. */ public void setStoreCF(ConfStoreDAOConnectionFactory storeCF) { _storeCF = storeCF; } /** * Set the schedulerDaoCF. * * @param schedulerDaoCF The schedulerDaoCF to set. */ public void setSchedulerDaoCF(SchedulerDAOConnectionFactory schedulerDaoCF) { _schedulerDaoCF = schedulerDaoCF; } /** * Set the executor service. * * @param executorService The executor service to set. */ public void setExecutorService(ExecutorService executorService) { _executorService = executorService; } /** * Set the cache provider. * * @param cacheProvider The cache provider to set. */ public void setCacheProvider(CacheProvider cacheProvider) { _cacheProvider = cacheProvider; } private void populateDatabaseSchema() throws Exception { //populate the schema only if the db.emb.create=false in the bpel.properties if (!Boolean.valueOf(_odeConfig.getProperty(OdeConfigProperties.PROP_DB_EMBEDDED_CREATE, "false"))) { String dialect = _odeConfig.getProperties().getProperty("hibernate.dialect"); DatabaseInitialiser dbInitialiser = new DatabaseInitialiser(_db.getDataSource(), _txMgr, dialect); dbInitialiser.initDatabase(); } } private void registerServicesIntoJNDI() { LOG.info("Register BPEL engine, EntityManagerFactory into JNDI."); try { final JndiRegistry registry = new JndiRegistry(); registry.bindToJndi(_jndiName, this); // hack to expose the EntityManagerFactory. // See org.apache.ode.dao.jpa.hibernate.BpelDAOConnectionFactoryImpl as well. Object emf = _odeConfig.getProperties().get("ode.emf"); if (emf != null) { registry.bindToJndi(_emfJndiName, emf); } } catch (Throwable t) { LOG.error("Failed to bind engine to JNDI registry", t); } } private void unregisterServicesFromJNDI() { LOG.info("Unbind the services from JNDI."); try { final JndiRegistry registry = new JndiRegistry(); registry.unbindFromJndi(_jndiName); registry.unbindFromJndi(_emfJndiName); } catch (Throwable t) { LOG.debug("Failed to unbind services on engine close", t); } } /** * This method returns the service locator * associated with the BPEL engine. * * @return The service locator */ public ServiceLocator getServiceLocator() { return (_serviceLocator); } /** * Initialise the process store. * * @param eprContext The endpoint reference context */ protected void initProcessStore(EndpointReferenceContext eprContext) { _store = createProcessStore(eprContext, _txMgr, _storeCF); _store.registerListener(new ProcessStoreListenerImpl()); } /** * Create the process store. * * @param eprContext The endpoint reference context * @param txm The transaction manager * @param cf The connection factory * @return The process store */ protected RiftSawProcessStore createProcessStore(EndpointReferenceContext eprContext, TransactionManager txm, ConfStoreDAOConnectionFactory cf) { return new RiftSawProcessStore(eprContext, txm, cf, _cacheProvider); } /** * This method initializes the cache provider. */ private void initCacheProvider() { if (_cacheProvider == null) { _cacheProvider = CacheProviderFactory.getCacheProvider(_odeConfig); } try { _cacheProvider.start(_odeConfig.getProperties()); } catch (Exception e) { LOG.error("Error in starting cache provider", e); throw new RuntimeException("Error in initCacheProvider.", e); } } private void initDeploymentManager() { if (_deploymentManager == null) { _deploymentManager = new DeploymentManager(); String folder=_odeConfig.getProperty("deployment.folder"); if (folder == null) { folder = System.getProperty(DEPLOYMENT_FOLDER_ENV_VAR); } if (folder != null) { _deploymentManager.setDeploymentFolder(folder); } } } /** * This method initializes the data store. * * @throws Exception Failed to initialize */ private void initDataSource() throws Exception { _db = new Database(_odeConfig); _db.setTransactionManager(_txMgr); //_db.setWorkRoot(new java.io.File("/tmp/h2")); try { _db.start(); } catch (Exception ex) { String errmsg = "FAILED TO INITIALISE DATA SOURCE"; LOG.error(errmsg, ex); throw new Exception(errmsg, ex); } } /** * This method initializes the transaction manager. * * @throws Exception Failed to initialize */ private void initTxMgr() throws Exception { if (_txMgr == null) { String txFactoryName = _odeConfig.getTxFactoryClass(); LOG.info("Initializing transaction manager using " + txFactoryName); try { Class<?> txFactClass = this.getClass().getClassLoader().loadClass(txFactoryName); Object txFact = txFactClass.newInstance(); _txMgr = (TransactionManager) txFactClass.getMethod("getTransactionManager", (Class[]) null).invoke(txFact); //if (__logTx.isDebugEnabled() && System.getProperty("ode.debug.tx") != null) // _txMgr = new DebugTxMgr(_txMgr); //_axisConfig.addParameter("ode.transaction.manager", _txMgr); } catch (Exception e) { LOG.error("Couldn't initialize a transaction manager with factory: " + txFactoryName, e); throw new Exception("Couldn't initialize a transaction manager with factory: " + txFactoryName, e); } } } /** * This method initializes DAO. * * @throws Exception Failed to initialize */ protected void initDAO() throws Exception { if (_daoCF == null) { LOG.info("USING DAO: "+_odeConfig.getDAOConnectionFactory() + ", " + _odeConfig.getDAOConfStoreConnectionFactory() + ", " + _odeConfig.getDAOConfScheduleConnectionFactory()); try { _daoCF = _db.createDaoCF(); _storeCF = _db.createDaoStoreCF(); _schedulerDaoCF = _db.createDaoSchedulerCF(); } catch (Exception ex) { String errmsg = "DAO INSTANTIATION FAILED: "+_odeConfig.getDAOConnectionFactory() + " , " + _odeConfig.getDAOConfStoreConnectionFactory() + " and " + _odeConfig.getDAOConfScheduleConnectionFactory(); LOG.error(errmsg, ex); throw new Exception(errmsg, ex); } } else if (_storeCF == null) { String errmsg = "DAO INSTANTIATION FAILED: DAO initialized using setters, but ConfStoreDAOConnectionFactory was not set."; LOG.error(errmsg); throw new Exception(errmsg); } else if (_schedulerDaoCF == null) { String errmsg = "DAO INSTANTIATION FAILED: DAO initialized using setters, but SchedulerDAOConnectionFactory was not set."; LOG.error(errmsg); throw new Exception(errmsg); } } /** * This method creates the scheduler. * * @return The scheduler */ protected Scheduler createScheduler() { String clusterNodeName = _odeConfig.getProperty(OdeConfigProperties.RIFTSAW_NODE_NAME, "non-cluster-node"); String cacheName = _odeConfig.getProperty(OdeConfigProperties.CACHE_NAME_PROPERTY, "cluster"); try { EmbeddedCacheManager ecm = (EmbeddedCacheManager) new InitialContext().lookup(OdeConfigProperties.CACHE_CONTAINER_ROOT + cacheName); clusterNodeName = ecm.getAddress().toString(); org.infinispan.Cache<String, String> cache = ecm.getCache(); cache.start(); ecm.addListener(new MemberDropListener(_schedulerDaoCF, _txMgr)); } catch (NamingException e) { //Ignore for now, it means that it is not in the clustering environment. LOG.info("RiftSaw (BPEL Component) will be started in the non cluster environment."); } LOG.info("The schduler node name is: " + clusterNodeName); SimpleScheduler scheduler = new SimpleScheduler(clusterNodeName, _schedulerDaoCF, _txMgr, _odeConfig.getProperties()); scheduler.setExecutorService(_executorService); scheduler.setTransactionManager(_txMgr); return scheduler; } /** * Initialize the BPEL server. * * @param eprContext The endpoint reference context * @param locator The service locator */ private void initBpelServer(EndpointReferenceContextImpl eprContext, ServiceLocator locator) { if (LOG.isDebugEnabled()) { LOG.debug("ODE initializing"); } ThreadFactory threadFactory = new ThreadFactory() { private int _threadNumber = 0; public Thread newThread(Runnable r) { _threadNumber += 1; Thread t = new Thread(r, "ODEServer-"+_threadNumber); t.setDaemon(true); return t; } }; if (_executorService == null) { if (_odeConfig.getThreadPoolMaxSize() == 0) { _executorService = Executors.newCachedThreadPool(threadFactory); } else { _executorService = Executors.newFixedThreadPool(_odeConfig.getThreadPoolMaxSize(), threadFactory); } } _bpelServer = new BpelServerImpl(); _scheduler = createScheduler(); _scheduler.setJobProcessor(_bpelServer); BpelServerImpl.PolledRunnableProcessor polledRunnableProcessor = new BpelServerImpl.PolledRunnableProcessor(); polledRunnableProcessor.setPolledRunnableExecutorService(_executorService); polledRunnableProcessor.setContexts(_bpelServer.getContexts()); _scheduler.setPolledRunnableProcesser(polledRunnableProcessor); _cronScheduler = new CronScheduler(); _cronScheduler.setScheduledTaskExec(_executorService); _cronScheduler.setContexts(_bpelServer.getContexts()); _bpelServer.setCronScheduler(_cronScheduler); _bpelServer.setDaoConnectionFactory(_daoCF); _bpelServer.setInMemDaoConnectionFactory(new BpelDAOConnectionFactoryImpl(_scheduler, _odeConfig.getInMemMexTtl())); _bpelServer.setEndpointReferenceContext(eprContext); _bpelServer.setMessageExchangeContext(new MessageExchangeContextImpl(locator)); _bpelServer.setBindingContext(new RiftsawBindingContext()); _bpelServer.setScheduler(_scheduler); if (_odeConfig.isDehydrationEnabled()) { CountLRUDehydrationPolicy dehy = new CountLRUDehydrationPolicy(); dehy.setProcessMaxAge(_odeConfig.getDehydrationMaximumAge()); dehy.setProcessMaxCount(_odeConfig.getDehydrationMaximumCount()); _bpelServer.setDehydrationPolicy(dehy); } _bpelServer.setConfigProperties(_odeConfig.getProperties()); _bpelServer.init(); _bpelServer.setInstanceThrottledMaximumCount(_odeConfig.getInstanceThrottledMaximumCount()); _bpelServer.setProcessThrottledMaximumCount(_odeConfig.getProcessThrottledMaximumCount()); _bpelServer.setProcessThrottledMaximumSize(_odeConfig.getProcessThrottledMaximumSize()); _bpelServer.setHydrationLazy(_odeConfig.isHydrationLazy()); _bpelServer.setHydrationLazyMinimumSize(_odeConfig.getHydrationLazyMinimumSize()); _bpelServer.setXTSEnable(_odeConfig.isXTSEnable()); } /** * {@inheritDoc} */ public void deploy(DeploymentUnit bdu) throws Exception { _store.deploy(bdu); } /** * {@inheritDoc} */ public void undeploy(DeploymentUnit bdu) throws Exception { _store.undeploy(bdu); } /** * {@inheritDoc} */ public DeploymentRef deploy(File deployment) throws Exception { return(deploy(deployment.getName(), deployment)); } /** * {@inheritDoc} */ public DeploymentRef deploy(String name, File deployment) throws Exception { DeploymentRef ret=null; if (LOG.isDebugEnabled()) { LOG.debug("Deploying: "+name+" at: "+deployment); } java.util.List<DeploymentUnit> dus=null; try { dus=_deploymentManager.getDeploymentUnits(name, deployment); } catch(Exception e) { LOG.error("Failed to get deployment units from '"+deployment+"'", e); throw e; } for (DeploymentUnit du : dus) { try { _store.deploy(du); } catch(Exception e) { LOG.error("Failed to deploy '"+du.getName()+"'", e); // Need to undeploy any successful deployments for (DeploymentUnit du2 : dus) { if (du == du2) { break; } else { if (LOG.isDebugEnabled()) { LOG.debug("Undeploying '"+du2.getName() +"' after failure when deploying '"+du.getName()+"'"); } _store.undeploy(du2); } } throw e; } } ret = new DeploymentRefImpl(dus); if (LOG.isDebugEnabled()) { LOG.debug("Deployed: "+deployment+" ref="+ret); } return(ret); } /** * {@inheritDoc} */ public void undeploy(DeploymentRef ref) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Undeploying ref: "+ref); } if (ref instanceof DeploymentRefImpl) { for (int i=0; i < ((DeploymentRefImpl)ref).getDeploymentUnits().size(); i++) { DeploymentUnit du=((DeploymentRefImpl)ref).getDeploymentUnits().get(i); if (_store != null) { _store.undeploy(du); } } } } /** * {@inheritDoc} */ public void close() throws Exception { ClassLoader old = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); try { if (_bpelServer != null) { try { LOG.debug("shutting down BPEL server."); _bpelServer.shutdown(); _bpelServer = null; } catch (Throwable ex) { LOG.debug("Error stopping services.", ex); } if (_cronScheduler != null) { try { LOG.debug("shutting down cron scheduler."); _cronScheduler.shutdown(); _cronScheduler = null; } catch (Exception ex) { LOG.debug("Cron scheduler couldn't be shutdown.", ex); } } if (_scheduler != null) { try { LOG.debug("shutting down scheduler."); _scheduler.shutdown(); _scheduler = null; } catch (Exception ex) { LOG.debug("Scheduler couldn't be shutdown.", ex); } } if (_store != null) { try { _store.shutdown(); _store = null; } catch (Throwable t) { LOG.debug("Store could not be shutdown.", t); } } //Stop the cache _cacheProvider.stop(); if (_daoCF != null) { try { _daoCF.shutdown(); } catch (Throwable ex) { LOG.debug("Bpel DAO shutdown failed.", ex); } finally { _daoCF = null; } } if (_storeCF != null) { try { _storeCF.shutdown(); } catch (Throwable ex) { LOG.debug("Store DAO shutdown failed.", ex); } finally { _storeCF = null; } } if (_schedulerDaoCF != null) { try { _schedulerDaoCF.shutdown(); } catch (Throwable ex) { LOG.debug("Scheduler DAO shutdown failed.", ex); } finally { _schedulerDaoCF = null; } } if (_db != null) { try { _db.shutdown(); } catch (Throwable ex) { LOG.debug("DB shutdown failed.", ex); } finally { _db = null; } } if (_txMgr != null) { LOG.debug("shutting down transaction manager."); _txMgr = null; } unregisterServicesFromJNDI(); } } finally { Thread.currentThread().setContextClassLoader(old); } } /** * Register the event listeners. */ private void registerEventListeners() { // let's always register the debugging listener.... _bpelServer.registerBpelEventListener(new DebugBpelEventListener()); // then, whatever else they want. String listenersStr = _odeConfig.getEventListeners(); if (listenersStr != null) { for (StringTokenizer tokenizer = new StringTokenizer(listenersStr, ",;"); tokenizer.hasMoreTokens();) { String listenerCN = tokenizer.nextToken(); try { _bpelServer.registerBpelEventListener((BpelEventListener) Class.forName(listenerCN).newInstance()); LOG.debug("REGISTERED EVENT LISTENER: "+listenerCN); } catch (Exception e) { LOG.warn("Couldn't register the event listener " + listenerCN + ", the class couldn't be " + "loaded properly: " + e); } } } } /** * Register the message exchange interceptors. */ private void registerMexInterceptors() { String listenersStr = _odeConfig.getMessageExchangeInterceptors(); if (listenersStr != null) { for (StringTokenizer tokenizer = new StringTokenizer(listenersStr, ",;"); tokenizer.hasMoreTokens();) { String interceptorCN = tokenizer.nextToken(); try { _bpelServer.registerMessageExchangeInterceptor((MessageExchangeInterceptor) Class.forName(interceptorCN).newInstance()); LOG.debug("MESSAGE EXCHANGE INTERCEPTOR REGISTERED: "+interceptorCN); } catch (Exception e) { LOG.warn("Couldn't register the event listener " + interceptorCN + ", the class couldn't be " + "loaded properly: " + e); } } } } /** * Register the external variable modules. */ private void registerExternalVariableModules() { JdbcExternalVariableModule jdbcext; jdbcext = new JdbcExternalVariableModule(); jdbcext.registerDataSource("ode", _db.getDataSource()); _bpelServer.registerExternalVariableEngine(jdbcext); } /** * Handle the process store event. * * @param pse The process store event */ private void handleEvent(ProcessStoreEvent pse) { LOG.debug("Process store event: " + pse); ProcessConf pconf = _store.getProcessConfiguration(pse.pid); switch (pse.type) { case DEPLOYED: if (pconf != null) { /* * If and only if an old process exists with the same pid, the old process is cleaned up. * The following line is IMPORTANT and used for the case when the deployment and store * do not have the process while the process itself exists in the BPEL_PROCESS table. * Notice that the new process is actually created on the 'ACTIVATED' event. */ _bpelServer.cleanupProcess(pconf); } break; case ACTVIATED: // bounce the process _bpelServer.unregister(pse.pid); if (pconf != null) { _bpelServer.register(pconf); } else { LOG.debug("slighly odd: recevied event " + pse + " for process not in store!"); } break; case RETIRED: // are there are instances of this process running? boolean instantiated = _bpelServer.hasActiveInstances(pse.pid); // remove the process _bpelServer.unregister(pse.pid); // bounce the process if necessary if (instantiated) { if (pconf != null) { _bpelServer.register(pconf); } else { LOG.debug("slighly odd: recevied event " + pse + " for process not in store!"); } } else { // we may have potentially created a lot of garbage, so, // let's hope the garbage collector is configured properly. if (pconf != null) { _bpelServer.cleanupProcess(pconf); } } break; case DISABLED: case UNDEPLOYED: _bpelServer.unregister(pse.pid); if (pconf != null) { _bpelServer.cleanupProcess(pconf); } String retiredProcess = _store.getLatestPackageVersion(pse.deploymentUnit); if (retiredProcess != null) { _store.setRetiredPackage(retiredProcess, false); _store.setRetiredPackage(retiredProcess, true); } break; default: LOG.debug("Ignoring store event: " + pse); } if (pconf != null) { if (pse.type == ProcessStoreEvent.Type.UNDEPLOYED) { LOG.debug("Cancelling all cron scheduled jobs on store event: " + pse); _bpelServer.getContexts().cronScheduler.cancelProcessCronJobs(pse.pid, true); } // Except for undeploy event, we need to re-schedule process dependent jobs LOG.debug("(Re)scheduling cron scheduled jobs on store event: " + pse); if (pse.type != ProcessStoreEvent.Type.UNDEPLOYED) { _bpelServer.getContexts().cronScheduler.scheduleProcessCronJobs(pse.pid, pconf); } } } /** * {@inheritDoc} */ public Element invoke(QName serviceName, String portName, String operationName, Element mesg, java.util.Map<String, Object> headers) throws Exception { Element ret=null; boolean success = true; MyRoleMessageExchange odeMex = null; Future<?> responseFuture = null; Transaction current=null; boolean immediate=_odeConfig.getProperty("invoke.immediate", Boolean.FALSE.toString()).equalsIgnoreCase(Boolean.TRUE.toString()); try { current =_txMgr.getTransaction(); if (current == null) { _txMgr.begin(); if (LOG.isDebugEnabled()) { LOG.debug("Starting transaction."); } } else { if (LOG.isDebugEnabled()) { LOG.debug("Using existing transaction."); } immediate = true; } if (LOG.isDebugEnabled()) { LOG.debug("Immediate invocation mode: "+immediate); } odeMex = createMessageExchange(serviceName, portName, operationName); odeMex.setProperty("isTwoWay", Boolean.toString(odeMex.getOperation().getOutput() != null)); if (LOG.isDebugEnabled()) { LOG.debug("Is two way operation? "+odeMex.getProperty("isTwoWay")); } if (odeMex.getOperation() != null) { // Preparing message to send to ODE Message odeRequest = odeMex.createMessage(odeMex.getOperation().getInput().getMessage().getQName()); odeRequest.setMessage(mesg); if (headers != null) { Set<String> keys = headers.keySet(); for (String key : keys) { Object headerPart = headers.get(key); if (headerPart instanceof Element) { Element element = (Element)headerPart; QName name=QName.valueOf(key); odeRequest.setHeaderPart(name.getLocalPart(), element); // put the element to header } } } if (LOG.isDebugEnabled()) { LOG.debug("Invoking ODE using MEX " + odeMex); LOG.debug("Message content: " + DOMUtils.domToString(odeRequest.getMessage())); } // Invoke ODE // NOTE: 'immediate' parameter could be either true, so that requests invoked immediately? // or 'current != null', so only true if request being performed in an outer transaction responseFuture = odeMex.invoke(odeRequest, immediate); LOG.debug("Commiting ODE MEX " + odeMex); if (current == null) { try { if (LOG.isDebugEnabled()) { LOG.debug("Commiting transaction."); } _txMgr.commit(); } catch (Exception e) { LOG.error("Commit failed", e); success = false; } } } else { success = false; } } catch (Exception e) { LOG.error("Exception occured while invoking ODE", e); success = false; String errmesg = e.getMessage(); if (errmesg == null) { errmesg = "An exception occured while invoking ODE."; } throw new Exception(errmesg, e); } finally { if (!success) { if (odeMex != null) { odeMex.release(success); } if (current == null) { try { _txMgr.rollback(); } catch (Exception e) { throw new Exception("Rollback failed", e); } } } } if (odeMex.getOperation().getOutput() != null) { if (!immediate) { // Waits for the response to arrive try { responseFuture.get(resolveTimeout(serviceName, portName, odeMex), TimeUnit.MILLISECONDS); } catch (Exception e) { String errorMsg = "Timeout or execution error when waiting for response to MEX " + odeMex + " " + e.toString(); LOG.error(errorMsg, e); throw new Exception(errorMsg); } } // Hopefully we have a response LOG.debug("Handling response for MEX " + odeMex); boolean commit = false; if (current == null) { try { if (LOG.isDebugEnabled()) { LOG.debug("Starting transaction."); } _txMgr.begin(); } catch (Exception ex) { throw new Exception("Error starting transaction!", ex); } } try { // Refreshing the message exchange odeMex = (MyRoleMessageExchange) _bpelServer.getEngine().getMessageExchange(odeMex.getMessageExchangeId()); Message msg = onResponse(odeMex); if (msg != null) { // Set header parts to the headers Map<String, Node> headerParts = msg.getHeaderParts(); if (headers != null) { headers.clear(); Set<String> keys = headerParts.keySet(); for (String key : keys) { Element e = (Element)msg.getHeaderPart(key).getFirstChild(); String k; if (e.getNamespaceURI() == null || e.getNamespaceURI().isEmpty()) { k = e.getLocalName(); } else { k = "{"+e.getNamespaceURI()+"}" + e.getLocalName(); } headers.put(k, e); } } ret = msg.getMessage(); } LOG.debug("Returning: "+ret); commit = true; } catch (Fault f) { commit = true; throw f; } catch (Exception e) { LOG.error("Error processing response for MEX " + odeMex, e); throw new Exception("An exception occured when invoking ODE.", e); } finally { odeMex.release(commit); if (current == null) { if (commit) { try { if (LOG.isDebugEnabled()) { LOG.debug("Comitting transaction."); } _txMgr.commit(); } catch (Exception e) { throw new Exception("Commit failed!", e); } } else { try { _txMgr.rollback(); } catch (Exception ex) { throw new Exception("Rollback failed!", ex); } } } } if (!success) { throw new Exception("Message was either unroutable or timed out!"); } } else { // One ways cleanup odeMex.release(true); } return ret; } /** * Create a message exchange. * * @param serviceName The service name * @param portName The port name * @param operationName The operation name * @return The message exchange */ private MyRoleMessageExchange createMessageExchange(QName serviceName, String portName, String operationName) { // Creating message exchange String messageId = new GUID().toString(); MyRoleMessageExchange odeMex = _bpelServer.getEngine() .createMessageExchange(messageId, serviceName, operationName); if (LOG.isDebugEnabled()) { LOG.debug("ODE routed to operation " + odeMex.getOperation() + " from service " + serviceName); } return odeMex; } /** * This method processes the supplied message exchange to return the response. * * @param mex The message exchange * @return The contents * @throws Exception Failed */ private Message onResponse(MyRoleMessageExchange mex) throws Exception { Message ret=null; switch (mex.getStatus()) { case FAULT: if (LOG.isDebugEnabled()) { LOG.debug("Fault response message: " + mex.getFault()); } throw new Fault(mex.getFault(), mex.getFaultResponse().getMessage()); //break; case ASYNC: case RESPONSE: ret = mex.getResponse(); if (LOG.isDebugEnabled()) { LOG.debug("Response message " + ret); } break; case FAILURE: if (LOG.isDebugEnabled()) { LOG.debug("Failure response message: " + mex.getFault()); } LOG.error("Failure details: "+mex.getFaultResponse()); throw new Exception("Failure response message: "+mex.getFault()+" : "+mex.getFaultExplanation()); default: throw new Exception("Received ODE message exchange in unexpected state: " + mex.getStatus()); } return (ret); } /** * This method returns the timeout period. * * @param serviceName The service name * @param portName The port name * @param odeMex The message exchange * @return The timeout period */ private long resolveTimeout(QName serviceName, String portName, MyRoleMessageExchange odeMex) { ProcessConf conf=odeMex.getProcessConf(); String timeout = conf.getEndpointProperties(serviceName, portName).get(Properties.PROP_MEX_TIMEOUT); if (timeout != null) { try { return Long.parseLong(timeout); } catch (NumberFormatException e) { if (LOG.isWarnEnabled()) { LOG.warn("Mal-formatted Property: ["+ Properties.PROP_MEX_TIMEOUT+"=" +timeout+"] Default value ("+Properties.DEFAULT_MEX_TIMEOUT+") will be used"); } } } return Properties.DEFAULT_MEX_TIMEOUT; } /** * This class provides the process store listener implementation. * */ private class ProcessStoreListenerImpl implements ProcessStoreListener { /** * {@inheritDoc} */ public void onProcessStoreEvent(ProcessStoreEvent event) { handleEvent(event); } } public BpelManagementFacade getManagementInterface() { return new BpelManagementFacadeImpl(_bpelServer, _store); } public RiftSawProcessStore getStore() { return _store; } /** * {@inheritDoc} */ public void register(BPELEngineListener l) { synchronized (_listeners) { if (!_listeners.containsKey(l)) { ProxyBpelEventListener bel=new ProxyBpelEventListener(l); _listeners.put(l, bel); _bpelServer.registerBpelEventListener(bel); } } } /** * {@inheritDoc} */ public void unregister(BPELEngineListener l) { synchronized (_listeners) { if (_listeners.containsKey(l)) { ProxyBpelEventListener bel=_listeners.remove(l); _bpelServer.unregisterBpelEventListener(bel); } } } protected class ProxyBpelEventListener implements BpelEventListener { private BPELEngineListener _listener=null; public ProxyBpelEventListener(BPELEngineListener l) { _listener = l; } public void onEvent(BpelEvent bpelEvent) { _listener.onEvent(bpelEvent); } public void startup(java.util.Properties configProperties) { } public void shutdown() { } } }