org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. Camunda licenses this file to you under the Apache License,
* Version 2.0; 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.camunda.bpm.engine.impl.interceptor;
import org.camunda.bpm.engine.delegate.ProcessEngineServicesAware;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.cmd.CommandLogger;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.context.ProcessEngineContextImpl;
/**
* Interceptor used for opening the {@link CommandContext} and {@link CommandInvocationContext}.
*
* Since 7.1, this interceptor will not always open a new command context but instead reuse an existing
* command context if possible. This is required for supporting process engine public API access from
* delegation code (see {@link ProcessEngineServicesAware}.). However, for every command, a new
* command invocation context is created. While a command context holds resources that are
* shared between multiple commands, such as database sessions, a command invocation context holds
* resources specific for a single command.
*
* The interceptor will check whether an open command context exists. If true, it will reuse the
* command context. If false, it will open a new one. We will always push the context to the
* {@link Context} stack. So ins some situations, you will see the same context being pushed to the sack
* multiple times. The rationale is that the size of the stack should allow you to determine whether
* you are currently running an 'inner' command or an 'outer' command as well as your current stack size.
* Existing code may rely on this behavior.
*
* The interceptor can be configured using the property {@link #alwaysOpenNew}.
* If this property is set to true, we will always open a new context regardless whether there already
* exists an active context or not. This is required for properly supporting REQUIRES_NEW semantics for
* commands run through the {@link ProcessEngineConfigurationImpl#getCommandInterceptorsTxRequiresNew()}
* chain. In that context the 'inner' command must be able to succeed / fail independently from the
* 'outer' command.
*
*
* @author Tom Baeyens
* @author Daniel Meyer
* @author Thorben Lindhauer
*/
public class CommandContextInterceptor extends CommandInterceptor {
private final static CommandLogger LOG = CommandLogger.CMD_LOGGER;
protected CommandContextFactory commandContextFactory;
protected ProcessEngineConfigurationImpl processEngineConfiguration;
/** if true, we will always open a new command context */
protected boolean alwaysOpenNew;
public CommandContextInterceptor() {
}
public CommandContextInterceptor(CommandContextFactory commandContextFactory, ProcessEngineConfigurationImpl processEngineConfiguration) {
this.commandContextFactory = commandContextFactory;
this.processEngineConfiguration = processEngineConfiguration;
}
public CommandContextInterceptor(CommandContextFactory commandContextFactory, ProcessEngineConfigurationImpl processEngineConfiguration, boolean alwaysOpenNew) {
this(commandContextFactory, processEngineConfiguration);
this.alwaysOpenNew = alwaysOpenNew;
}
public T execute(Command command) {
CommandContext context = null;
if(!alwaysOpenNew) {
// check whether we can reuse the command context
CommandContext existingCommandContext = Context.getCommandContext();
if(existingCommandContext != null && isFromSameEngine(existingCommandContext)) {
context = existingCommandContext;
}
}
// only create a new command context on the current command level (CAM-10002)
boolean isNew = ProcessEngineContextImpl.consume();
boolean openNew = (context == null || isNew);
CommandInvocationContext commandInvocationContext = new CommandInvocationContext(command, processEngineConfiguration);
Context.setCommandInvocationContext(commandInvocationContext);
try {
if(openNew) {
LOG.debugOpeningNewCommandContext();
context = commandContextFactory.createCommandContext();
} else {
LOG.debugReusingExistingCommandContext();
}
Context.setCommandContext(context);
Context.setProcessEngineConfiguration(processEngineConfiguration);
// delegate to next interceptor in chain
return next.execute(command);
} catch (Throwable t) {
commandInvocationContext.trySetThrowable(t);
} finally {
try {
if (openNew) {
LOG.closingCommandContext();
context.close(commandInvocationContext);
} else {
commandInvocationContext.rethrow();
}
} finally {
Context.removeCommandInvocationContext();
Context.removeCommandContext();
Context.removeProcessEngineConfiguration();
// restore the new command context flag
ProcessEngineContextImpl.set(isNew);
}
}
return null;
}
protected boolean isFromSameEngine(CommandContext existingCommandContext) {
return processEngineConfiguration == existingCommandContext.getProcessEngineConfiguration();
}
public CommandContextFactory getCommandContextFactory() {
return commandContextFactory;
}
public void setCommandContextFactory(CommandContextFactory commandContextFactory) {
this.commandContextFactory = commandContextFactory;
}
public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
return processEngineConfiguration;
}
public void setProcessEngineContext(ProcessEngineConfigurationImpl processEngineContext) {
this.processEngineConfiguration = processEngineContext;
}
}