org.camunda.bpm.application.impl.ServletProcessApplication Maven / Gradle / Ivy
/* 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.camunda.bpm.application.impl;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.camunda.bpm.application.AbstractProcessApplication;
import org.camunda.bpm.application.ProcessApplicationInfo;
import org.camunda.bpm.application.ProcessApplicationReference;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.camunda.bpm.engine.impl.util.ClassLoaderUtil;
/**
* A {@link AbstractProcessApplication} Implementation to be used in a Servlet container environment.
*
* This class implements the {@link ServletContextListener} interface and can thus participate in the
* deployment lifecycle of your web application.
*
* Usage
* In a Servlet 3.0 container it is sufficient adding a custom subclass of
* {@link ServletProcessApplication} annotated with {@literal @}ProcessApplication
to your
* application:
*
* {@literal @}ProcessApplication("Loan Approval App")
* public class LoanApprovalApplication extends ServletProcessApplication {
* // empty implementation
* }
*
* This, in combination with a META-INF/processes.xml
file is sufficient for making sure
* that the process application class is picked up at runtime.
* In a Servlet 2.5 container, the process application can be added as a web listener
* to your project's web.xml
*
* {@literal <}?xml version="1.0" encoding="UTF-8"?{@literal >}
* {@literal <}web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"{@literal >}
*
* {@literal <}listener{@literal >}
* {@literal <}listener-class{@literal >}org.my.project.MyProcessApplication{@literal <}/listener-class{@literal >}
* {@literal <}/listener{@literal >}
*{@literal <}/web-app{@literal >}
*
*
*
* Invocation Semantics
* When the {@link #execute(java.util.concurrent.Callable)} method is invoked, the servlet process
* application modifies the context classloader of the current Thread to the classloader that loaded
* the application-provided subclass of this class. This allows
*
* - the process engine to resolve {@link JavaDelegate} implementations using the classloader
* of the process application
* - In apache tomcat this allows you to resolve Naming Resources (JNDI) form the naming
* context of the process application. JNDI name resolution is based on the TCCL in Apache Tomcat.
*
*
*
*
*
* Set TCCL of Process Application
* |
* | +--------------------+
* | |Process Application |
* invoke v | |
* ProcessEngine -----------------O--|--> Java Delegate |
* | |
* | |
* +--------------------+
*
*
*
* Process Application Reference
* The process engine holds a {@link WeakReference} to the {@link ServletProcessApplication} and does not cache any
* classes loaded using the Process Application classloader.
*
*
* @author Daniel Meyer
* @author Thorben Lindhauer
*
*/
public class ServletProcessApplication extends AbstractProcessApplication implements ServletContextListener {
protected String servletContextName;
protected String servletContextPath;
protected ProcessApplicationReferenceImpl reference;
protected ClassLoader processApplicationClassloader;
protected ServletContext servletContext;
protected String autodetectProcessApplicationName() {
String name = (servletContextName != null && !servletContextName.isEmpty()) ? servletContextName : servletContextPath;
if(name.startsWith("/")) {
name = name.substring(1);
}
return name;
}
public ProcessApplicationReference getReference() {
if(reference == null) {
reference = new ProcessApplicationReferenceImpl(this);
}
return reference;
}
public void contextInitialized(ServletContextEvent sce) {
servletContext = sce.getServletContext();
servletContextPath = servletContext.getContextPath();
servletContextName = sce.getServletContext().getServletContextName();
processApplicationClassloader = initProcessApplicationClassloader(sce);
// perform lifecycle start
deploy();
}
protected ClassLoader initProcessApplicationClassloader(ServletContextEvent sce) {
if (isServlet30ApiPresent(sce) && getClass().equals(ServletProcessApplication.class)) {
return ClassLoaderUtil.getServletContextClassloader(sce);
} else {
return ClassLoaderUtil.getClassloader(getClass());
}
}
private boolean isServlet30ApiPresent(ServletContextEvent sce) {
return sce.getServletContext().getMajorVersion() >= 3;
}
public ClassLoader getProcessApplicationClassloader() {
return processApplicationClassloader;
}
public void contextDestroyed(ServletContextEvent sce) {
// perform lifecycle stop
undeploy();
// clear the reference
if(reference != null) {
reference.clear();
}
reference = null;
}
public Map getProperties() {
Map properties = new HashMap();
// set the servlet context path as property
properties.put(ProcessApplicationInfo.PROP_SERVLET_CONTEXT_PATH, servletContextPath);
return properties;
}
public ServletContext getServletContext() {
return servletContext;
}
}