/* * 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.ode.bpel.engine.replayer; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.engine.BpelEngineImpl; import org.apache.ode.bpel.engine.BpelProcess; import org.apache.ode.bpel.engine.MyRoleMessageExchangeImpl; import org.apache.ode.bpel.engine.PartnerLinkMyRoleImpl; import org.apache.ode.bpel.engine.PartnerLinkMyRoleImpl.RoutingInfo; import org.apache.ode.bpel.evt.CorrelationMatchEvent; import org.apache.ode.bpel.iapi.BpelEngine; import org.apache.ode.bpel.iapi.MyRoleMessageExchange; import org.apache.ode.bpel.iapi.MessageExchange.Status; import org.apache.ode.bpel.iapi.ProcessConf.CLEANUP_CATEGORY; import org.apache.ode.bpel.iapi.Scheduler.JobDetails; import org.apache.ode.bpel.iapi.Scheduler.JobType; import org.apache.ode.bpel.pmapi.CommunicationType; import org.apache.ode.bpel.pmapi.ExchangeType; import org.apache.ode.bpel.pmapi.FaultType; import org.apache.ode.bpel.pmapi.GetCommunication; import org.apache.ode.bpel.pmapi.GetCommunicationResponse; import org.apache.ode.bpel.pmapi.Replay; import org.apache.ode.bpel.pmapi.CommunicationType.Exchange; import org.apache.ode.bpel.runtime.PROCESS; import org.apache.ode.dao.bpel.BpelDAOConnection; import org.apache.ode.dao.bpel.MessageExchangeDAO; import org.apache.ode.dao.bpel.ProcessDAO; import org.apache.ode.dao.bpel.ProcessInstanceDAO; import org.apache.xmlbeans.XmlCalendar; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; /** * Main class used for replaying. It's invoked from InstanceManagement API. * Receives request and sets up replaying contexts for each instance to replay. * * @author Rafal Rusin * */ public class Replayer { private static final Log __log = LogFactory.getLog(Replayer.class); public static ThreadLocal<Replayer> replayer = new ThreadLocal<Replayer>(); public ReplayerScheduler scheduler = new ReplayerScheduler(); public BpelEngineImpl engine = null; public List<ReplayerContext> contexts = null; public BpelDAOConnection conn = null; public List<Long> replayInstances(Replay request, BpelEngine engine, BpelDAOConnection conn) throws Exception { try { replayer.set(this); this.engine = (BpelEngineImpl) engine; this.conn = conn; Date startDate = Calendar.getInstance().getTime(); contexts = new ArrayList<ReplayerContext>(); { List<Long> toDelete = new ArrayList<Long>(); List<CommunicationType> toRestore = new ArrayList<CommunicationType>(); toDelete.addAll(request.getReplaceInstanceList()); for (Long iid : request.getUpgradeInstanceList()) { toDelete.add(iid); toRestore.add(CommunicationType.Factory.parse(getCommunication(iid, conn).toString())); } toRestore.addAll(request.getRestoreInstanceList()); { Set<CLEANUP_CATEGORY> cleanupCategory = new HashSet<CLEANUP_CATEGORY>(); cleanupCategory.add(CLEANUP_CATEGORY.INSTANCE); cleanupCategory.add(CLEANUP_CATEGORY.MESSAGES); cleanupCategory.add(CLEANUP_CATEGORY.VARIABLES); cleanupCategory.add(CLEANUP_CATEGORY.CORRELATIONS); cleanupCategory.add(CLEANUP_CATEGORY.EVENTS); for (Long l : toDelete) { conn.getInstance(l).delete(cleanupCategory); } } for (CommunicationType r : toRestore) { ReplayerContext context = new ReplayerContext(startDate); context.bpelEngine = (BpelEngineImpl) engine; context.init(r, scheduler); contexts.add(context); } } scheduler.startReplaying(this); { List<Exchange> remainingExchanges = new ArrayList<Exchange>(); for (ReplayerContext c : contexts) { c.answers.remainingExchanges(remainingExchanges); } if (remainingExchanges.size() > 0) { throw new RemainingExchangesException(remainingExchanges); } } List<Long> r = new ArrayList<Long>(); for (ReplayerContext c : contexts) { r.add(c.runtimeContext.getPid()); } return r; } finally { replayer.set(null); } } public GetCommunicationResponse getCommunication(GetCommunication request, BpelDAOConnection conn) throws Exception { GetCommunicationResponse response = GetCommunicationResponse.Factory.newInstance(); for (Long iid : request.getIidList()) { response.addNewRestoreInstance().set(getCommunication(iid, conn)); } return response; } private CommunicationType getCommunication(Long iid, BpelDAOConnection conn) { CommunicationType result = CommunicationType.Factory.newInstance(); List<Exchange> list = new ArrayList<Exchange>(); ProcessInstanceDAO instance = conn.getInstance(iid); if (instance == null) return result; result.setProcessType(instance.getProcess().getType()); for (String mexId : instance.getMessageExchangeIds()) { MessageExchangeDAO mexDao = conn.getMessageExchange(mexId); Exchange e = Exchange.Factory.newInstance(); list.add(e); e.setCreateTime(new XmlCalendar(mexDao.getCreateTime())); e.setOperation(mexDao.getOperation()); try { e.setIn(XmlObject.Factory.parse(mexDao.getRequest().getData())); } catch (XmlException e1) { __log.error("", e1); } try { Status status = Status.valueOf(mexDao.getStatus()); if (status == Status.FAULT) { FaultType f = e.addNewFault(); f.setType(mexDao.getFault()); f.setExplanation(mexDao.getFaultExplanation()); if (mexDao.getResponse() != null) { f.setMessage(XmlObject.Factory.parse(mexDao.getResponse().getData())); } } else if (status == Status.FAILURE) { e.addNewFailure().setExplanation(mexDao.getFaultExplanation()); } else { if (mexDao.getResponse() != null) { e.setOut(XmlObject.Factory.parse(mexDao.getResponse().getData())); } } } catch (XmlException e1) { __log.error("", e1); } e.setType(ExchangeType.Enum.forString("" + mexDao.getDirection())); __log.debug("---"); __log.debug("" + mexDao.getCallee()); __log.debug("" + mexDao.getChannel()); __log.debug("" + mexDao.getCreateTime()); __log.debug("" + mexDao.getEPR()); __log.debug("" + mexDao.getPortType()); if (e.getType() == ExchangeType.P) { e.setService(mexDao.getPortType()); } else { e.setService(mexDao.getCallee()); } } Collections.sort(list, new Comparator<Exchange>() { public int compare(Exchange arg0, Exchange arg1) { return arg0.getCreateTime().compareTo(arg1.getCreateTime()); } }); for (Exchange e : list) { result.addNewExchange().set(e); } return result; } public ReplayerContext findReplayedInstance(long iid) { for (ReplayerContext r : contexts) { if (r.runtimeContext.getPid() == iid) { return r; } } return null; } public void handleJobDetails(JobDetails jobDetail, final Date when) { JobDetails we = jobDetail; __log.debug("handleJobDetails " + jobDetail + " " + when); if (we.getType() == JobType.INVOKE_INTERNAL) { final BpelProcess p = engine._activeProcesses.get(we.getProcessId()); final ProcessDAO processDAO = p.getProcessDAO(); final MyRoleMessageExchangeImpl mex = (MyRoleMessageExchangeImpl) engine.getMessageExchange(we.getMexId()); p.invokeProcess(mex, // time, new BpelProcess.InvokeHandler() { public boolean invoke(PartnerLinkMyRoleImpl target, RoutingInfo routing, boolean createInstance) { if (routing.messageRoute == null && createInstance) { __log.debug("creating new instance via live communication mex:" + mex); ProcessInstanceDAO newInstance = processDAO.createInstance(routing.correlator); ReplayerContext context = new ReplayerContext(null); context.bpelEngine = (BpelEngineImpl) engine; contexts.add(context); ReplayerBpelRuntimeContextImpl runtimeContext = new ReplayerBpelRuntimeContextImpl(p, newInstance, new PROCESS(p.getOProcess()), mex, // time, context); context.runtimeContext = runtimeContext; runtimeContext.setCurrentEventDateTime(when); runtimeContext.updateMyRoleMex(mex); // first receive is matched to provided // mex runtimeContext.execute(); return true; } else if (routing.messageRoute != null) { long iid = routing.messageRoute.getTargetInstance().getInstanceId(); ReplayerContext ctx = findReplayedInstance(iid); if (ctx == null) { throw new IllegalStateException("Trying to hit existing instance via live communication, but there's no such instance mex:" + mex + " iid:" + iid); } __log.debug("hitting existing instance via live communication mex:" + mex + " iid:" + iid); ctx.runtimeContext.inputMsgMatch(routing.messageRoute.getGroupId(), routing.messageRoute.getIndex(), mex); routing.correlator.removeRoutes(routing.messageRoute.getGroupId(), ctx.runtimeContext.getDAO()); mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.MATCHED); mex.getDAO().setInstance(routing.messageRoute.getTargetInstance()); ctx.runtimeContext.execute(); } return false; } }); } else if (we.getType() == JobType.INVOKE_RESPONSE) { __log.debug("reply for live communication"); ReplayerContext ctx = findReplayedInstance(we.getInstanceId()); assert ctx != null; ctx.runtimeContext.invocationResponse(we.getMexId(), we.getChannel()); ctx.runtimeContext.execute(); } } }