/* * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed 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.jbpm.process.core.timer.impl; import org.drools.core.command.SingleSessionCommandService; import org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession; import org.drools.core.common.InternalKnowledgeRuntime; import org.drools.core.runtime.InternalLocalRunner; import org.drools.core.time.InternalSchedulerService; import org.drools.core.time.Job; import org.drools.core.time.JobContext; import org.drools.core.time.JobHandle; import org.drools.core.time.SelfRemovalJobContext; import org.drools.core.time.TimerService; import org.drools.core.time.Trigger; import org.drools.core.time.impl.CommandServiceTimerJobFactoryManager; import org.drools.core.time.impl.DefaultJobHandle; import org.drools.core.time.impl.TimerJobFactoryManager; import org.drools.core.time.impl.TimerJobInstance; import org.jbpm.process.core.timer.GlobalSchedulerService; import org.jbpm.process.core.timer.NamedJobContext; import org.jbpm.process.instance.timer.TimerManager.ProcessJobContext; import org.kie.api.runtime.Environment; import org.kie.api.runtime.Executable; import org.kie.api.runtime.ExecutableRunner; import org.kie.api.runtime.RequestContext; import org.kie.api.runtime.manager.RuntimeEngine; import org.kie.api.runtime.manager.RuntimeManager; import org.kie.internal.runtime.manager.InternalRuntimeManager; import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; public class GlobalTimerService implements TimerService, InternalSchedulerService { private static final Logger logger = LoggerFactory.getLogger(GlobalTimerService.class); protected TimerJobFactoryManager jobFactoryManager; protected GlobalSchedulerService schedulerService; protected RuntimeManager manager; protected ConcurrentHashMap<Long, List<GlobalJobHandle>> timerJobsPerSession = new ConcurrentHashMap<Long, List<GlobalJobHandle>>(); private String timerServiceId; public GlobalTimerService(RuntimeManager manager, GlobalSchedulerService schedulerService) { this.manager = manager; this.schedulerService = schedulerService; this.schedulerService.initScheduler(this); try { this.jobFactoryManager = (TimerJobFactoryManager) Class.forName("org.jbpm.persistence.timer.GlobalJPATimerJobFactoryManager").newInstance(); } catch (Exception e) { e.printStackTrace(); } } @Override public JobHandle scheduleJob(Job job, JobContext ctx, Trigger trigger) { if (ctx instanceof ProcessJobContext) { ProcessJobContext processCtx = (ProcessJobContext) ctx; List<GlobalJobHandle> jobHandles = timerJobsPerSession.get(processCtx.getSessionId()); if (jobHandles == null) { jobHandles = new CopyOnWriteArrayList<GlobalJobHandle>(); timerJobsPerSession.put(processCtx.getSessionId(), jobHandles); } else { // check if the given job is already scheduled for (GlobalJobHandle handle : jobHandles) { long timerId = handle.getTimerId(); if (timerId == processCtx.getTimer().getId()) { // this timer job is already registered return handle; } } } GlobalJobHandle jobHandle = (GlobalJobHandle) this.schedulerService.scheduleJob(job, ctx, trigger); if (jobHandle != null) { jobHandles.add(jobHandle); } return jobHandle; } GlobalJobHandle jobHandle = (GlobalJobHandle) this.schedulerService.scheduleJob(job, ctx, trigger); return jobHandle; } @Override public boolean removeJob(JobHandle jobHandle) { if (jobHandle == null) { return false; } long sessionId = ((GlobalJobHandle) jobHandle).getSessionId(); List<GlobalJobHandle> handles = timerJobsPerSession.get(sessionId); if (handles == null) { logger.debug("No known job handles for session {}", sessionId); return this.schedulerService.removeJob(jobHandle); } if (handles.contains(jobHandle)) { logger.debug("Found match so removing job handle {} from sessions {} handles", jobHandle, sessionId); handles.remove(jobHandle); if (handles.isEmpty()) { timerJobsPerSession.remove(sessionId); } return this.schedulerService.removeJob(jobHandle); } else { logger.debug("No match for job handle {} within handles of session {}", jobHandle, sessionId); return false; } } @Override public long getCurrentTime() { return System.currentTimeMillis(); } @Override public void shutdown() { //do nothing, this timer service is always active } public void destroy() { Collection<List<GlobalJobHandle>> activeTimers = timerJobsPerSession.values(); for (List<GlobalJobHandle> handles : activeTimers) { for (GlobalJobHandle handle : handles) { this.schedulerService.removeJob(handle); } } } @Override public long getTimeToNextJob() { return 0; } @Override public Collection<TimerJobInstance> getTimerJobInstances(long id) { Collection<TimerJobInstance> timers = new ArrayList<TimerJobInstance>(); List<GlobalJobHandle> jobs = timerJobsPerSession.get(id); { if (jobs != null) { for (GlobalJobHandle job : jobs) { if (job != null && schedulerService.isValid(job)) { timers.add(job.getTimerJobInstance()); } } } } logger.debug("Returning timers {} for session {}", timers, id); return timers; } @Override public void internalSchedule(TimerJobInstance timerJobInstance) { if (this.schedulerService instanceof InternalSchedulerService) { ((InternalSchedulerService) this.schedulerService).internalSchedule(timerJobInstance); } else { throw new UnsupportedOperationException("Unsupported scheduler operation internalSchedule on class " + this.schedulerService.getClass()); } } @Override public void setTimerJobFactoryManager(TimerJobFactoryManager timerJobFactoryManager) { if (jobFactoryManager instanceof CommandServiceTimerJobFactoryManager && timerJobFactoryManager instanceof CommandServiceTimerJobFactoryManager && getRunner() == null) { ( (CommandServiceTimerJobFactoryManager) jobFactoryManager ).setRunner( ( (CommandServiceTimerJobFactoryManager) timerJobFactoryManager ).getRunner() ); } } @Override public TimerJobFactoryManager getTimerJobFactoryManager() { return this.jobFactoryManager; } public ExecutableRunner getRunner( JobContext jobContext ) { JobContext ctxorig = jobContext; if (ctxorig instanceof SelfRemovalJobContext) { ctxorig = ((SelfRemovalJobContext) ctxorig).getJobContext(); } ProcessJobContext ctx = null; if (ctxorig instanceof ProcessJobContext) { ctx = (ProcessJobContext) ctxorig; } else if(ctxorig instanceof NamedJobContext){ return getRunner( ((NamedJobContext)ctxorig).getProcessInstanceId(), ctx ); } else { return getRunner(); } return getRunner( ctx.getProcessInstanceId(), ctx ); } public String getTimerServiceId() { return timerServiceId; } public void setTimerServiceId(String timerServiceId) { this.timerServiceId = timerServiceId; } public JobHandle buildJobHandleForContext(NamedJobContext ctx) { return this.schedulerService.buildJobHandleForContext(ctx); } public InternalRuntimeManager getRuntimeManager() { return (InternalRuntimeManager) manager; } protected ExecutableRunner getRunner( Long processInstanceId, ProcessJobContext ctx ) { RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId)); if (runtime == null) { throw new RuntimeException("No runtime engine found, could not be initialized yet"); } try { if (runtime.getKieSession() instanceof CommandBasedStatefulKnowledgeSession) { CommandBasedStatefulKnowledgeSession cmd = (CommandBasedStatefulKnowledgeSession) runtime.getKieSession(); if (ctx != null) { ctx.setKnowledgeRuntime((InternalKnowledgeRuntime) ( (SingleSessionCommandService) cmd.getRunner() ).getKieSession() ); } return new DisposableCommandService(cmd.getRunner(), manager, runtime, schedulerService.retryEnabled()); } else if (runtime.getKieSession() instanceof InternalKnowledgeRuntime && ctx != null) { ctx.setKnowledgeRuntime((InternalKnowledgeRuntime) runtime.getKieSession()); } return new DisposableCommandService(getRunner(), manager, runtime, schedulerService.retryEnabled()); } catch (Throwable e) { // since the DisposableCommandService was not created dispose runtime engine directly manager.disposeRuntimeEngine(runtime); throw new RuntimeException(e); } } private ExecutableRunner<RequestContext> getRunner() { return jobFactoryManager instanceof CommandServiceTimerJobFactoryManager ? ( (CommandServiceTimerJobFactoryManager) jobFactoryManager ).getRunner() : null; } public static class GlobalJobHandle extends DefaultJobHandle implements JobHandle{ private static final long serialVersionUID = 510l; public GlobalJobHandle(long id) { super(id); } public long getTimerId() { JobContext ctx = this.getTimerJobInstance().getJobContext(); if (ctx instanceof SelfRemovalJobContext) { ctx = ((SelfRemovalJobContext) ctx).getJobContext(); } return ((ProcessJobContext)ctx).getTimer().getId(); } public long getSessionId() { if (this.getTimerJobInstance() != null) { JobContext ctx = this.getTimerJobInstance().getJobContext(); if (ctx instanceof SelfRemovalJobContext) { ctx = ((SelfRemovalJobContext) ctx).getJobContext(); } if (ctx instanceof ProcessJobContext) { return ((ProcessJobContext)ctx).getSessionId(); } } return -1; } } public static class DisposableCommandService implements InternalLocalRunner { private ExecutableRunner<RequestContext> delegate; private RuntimeManager manager; private RuntimeEngine runtime; private boolean retry = false; public DisposableCommandService(ExecutableRunner<RequestContext> delegate, RuntimeManager manager, RuntimeEngine runtime, boolean retry) { this.delegate = delegate; this.manager = manager; this.runtime = runtime; this.retry = retry; } @Override public RequestContext execute( Executable executable, RequestContext ctx ) { try { if (delegate == null) { ExecutableRunner<RequestContext> runner = ExecutableRunner.create(); RequestContext context = runner.createContext().with( runtime.getKieSession() ); return runner.execute( executable, context ); } else { return delegate.execute(executable); } } catch (RuntimeException e) { if (retry) { return delegate.execute(executable, ctx); } else { throw e; } } } @Override public RequestContext createContext() { return delegate.createContext(); } public void dispose() { manager.disposeRuntimeEngine(runtime); } public Environment getEnvironment() { return runtime.getKieSession().getEnvironment(); } } }