blob: 9f942a8b0b8aecefd816dc1a8d78d2553c81633d [file] [log] [blame]
/*
* 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.openejb.web;
import org.apache.openejb.AppContext;
import org.apache.openejb.BeanContext;
import org.apache.openejb.Injection;
import org.apache.openejb.OpenEJBRuntimeException;
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.ClassListInfo;
import org.apache.openejb.assembler.classic.FilterInfo;
import org.apache.openejb.assembler.classic.InjectionBuilder;
import org.apache.openejb.assembler.classic.JndiEncBuilder;
import org.apache.openejb.assembler.classic.ListenerInfo;
import org.apache.openejb.assembler.classic.ParamValueInfo;
import org.apache.openejb.assembler.classic.ServletInfo;
import org.apache.openejb.assembler.classic.WebAppBuilder;
import org.apache.openejb.assembler.classic.WebAppInfo;
import org.apache.openejb.cdi.CdiBuilder;
import org.apache.openejb.core.CoreContainerSystem;
import org.apache.openejb.core.WebContext;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.ContainerSystem;
import org.apache.openejb.util.ArrayEnumeration;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.webbeans.web.lifecycle.test.MockServletContextEvent;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.ws.rs.core.Application;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class LightweightWebAppBuilder implements WebAppBuilder {
private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB, LightweightWebAppBuilder.class);
private static Method addServletMethod = null;
private static Method removeServletMethod = null;
private static Method addFilterMethod = null;
private static Method removeFilterMethod = null;
static {
try {
final Class<?> utilClass = LightweightWebAppBuilder.class.getClassLoader().loadClass("org.apache.openejb.server.httpd.util.HttpUtil");
addServletMethod = utilClass.getMethod("addServlet", String.class, WebContext.class, String.class);
removeServletMethod = utilClass.getMethod("removeServlet", String.class, WebContext.class);
addFilterMethod = utilClass.getMethod("addFilter", String.class, WebContext.class, String.class, FilterConfig.class);
removeFilterMethod = utilClass.getMethod("removeFilter", String.class, WebContext.class);
} catch (Exception e) {
LOGGER.info("Web features will not be available, add openejb-http if you need them");
}
}
private final Map<WebAppInfo, DeployedWebObjects> servletDeploymentInfo = new HashMap<WebAppInfo, DeployedWebObjects>();
private final Map<WebAppInfo, List<Object>> listeners = new HashMap<WebAppInfo, List<Object>>();
private final Map<WebAppInfo, ServletContextEvent> servletContextEvents = new HashMap<WebAppInfo, ServletContextEvent>();
@Override
public void deployWebApps(final AppInfo appInfo, final ClassLoader classLoader) throws Exception {
final CoreContainerSystem cs = (CoreContainerSystem) SystemInstance.get().getComponent(ContainerSystem.class);
final AppContext appContext = cs.getAppContext(appInfo.appId);
if (appContext == null) {
throw new OpenEJBRuntimeException("Can't find app context for " + appInfo.appId);
}
for (WebAppInfo webAppInfo : appInfo.webApps) {
final Set<Injection> injections = new HashSet<Injection>(appContext.getInjections());
injections.addAll(new InjectionBuilder(classLoader).buildInjections(webAppInfo.jndiEnc));
final Map<String, Object> bindings = new HashMap<String, Object>();
bindings.putAll(appContext.getBindings());
bindings.putAll(new JndiEncBuilder(webAppInfo.jndiEnc, injections, webAppInfo.moduleId, "Bean", null, webAppInfo.uniqueId, classLoader).buildBindings(JndiEncBuilder.JndiScope.comp));
final WebContext webContext = new WebContext(appContext);
webContext.setBindings(bindings);
webContext.getBindings().putAll(new JndiEncBuilder(webAppInfo.jndiEnc, injections, webAppInfo.moduleId, "Bean", null, webAppInfo.uniqueId, classLoader).buildBindings(JndiEncBuilder.JndiScope.comp));
webContext.setJndiEnc(WebInitialContext.create(bindings, appContext.getGlobalJndiContext()));
webContext.setClassLoader(classLoader);
webContext.setId(webAppInfo.moduleId);
webContext.setContextRoot(webAppInfo.contextRoot);
webContext.getInjections().addAll(injections);
webContext.setInitialContext(new EmbeddedInitialContext(webContext.getJndiEnc(), webContext.getBindings()));
appContext.getWebContexts().add(webContext);
cs.addWebContext(webContext);
if (!appInfo.webAppAlone) {
final Assembler assembler = SystemInstance.get().getComponent(Assembler.class);
final List<BeanContext> beanContexts = assembler.initEjbs(classLoader, appInfo, appContext, injections, new ArrayList<BeanContext>(), webAppInfo.moduleId);
appContext.getBeanContexts().addAll(beanContexts);
new CdiBuilder().build(appInfo, appContext, appContext.getBeanContexts(), webContext);
assembler.startEjbs(true, beanContexts);
}
final ServletContextEvent sce = new MockServletContextEvent();
servletContextEvents.put(webAppInfo, sce);
// listeners
for (ListenerInfo listener : webAppInfo.listeners) {
final Class<?> clazz = webContext.getClassLoader().loadClass(listener.classname);
final Object instance = webContext.newInstance(clazz);
if (ServletContextListener.class.isInstance(instance)) {
((ServletContextListener) instance).contextInitialized(sce);
}
List<Object> list = listeners.get(webAppInfo);
if (list == null) {
list = new ArrayList<Object>();
listeners.put(webAppInfo, list);
}
list.add(instance);
}
final DeployedWebObjects deployedWebObjects = new DeployedWebObjects();
deployedWebObjects.webContext = webContext;
servletDeploymentInfo.put(webAppInfo, deployedWebObjects);
if (addServletMethod == null) { // can't manage filter/servlets
continue;
}
// register filters
for (FilterInfo info : webAppInfo.filters) {
for (String mapping : info.mappings) {
final FilterConfig config = new SimpleFilterConfig(sce.getServletContext(), info.name, info.initParams);
try {
addFilterMethod.invoke(null, info.classname, webContext, mapping, config);
deployedWebObjects.filterMappings.add(mapping);
} catch (Exception e) {
LOGGER.warning(e.getMessage(), e);
}
}
}
for (ClassListInfo info : webAppInfo.webAnnotatedClasses) {
final String url = info.name;
for (String filterPath : info.list) {
String classname = nameFromUrls(url, filterPath);
final Class<?> clazz = webContext.getClassLoader().loadClass(classname);
final WebFilter annotation = clazz.getAnnotation(WebFilter.class);
if (annotation != null) {
final Properties initParams = new Properties();
for (WebInitParam param : annotation.initParams()) {
initParams.put(param.name(), param.value());
}
final FilterConfig config = new SimpleFilterConfig(sce.getServletContext(), info.name, initParams);
for (String mapping: annotation.urlPatterns()) {
try {
addFilterMethod.invoke(null, classname, webContext, mapping, config);
deployedWebObjects.filterMappings.add(mapping);
} catch (Exception e) {
LOGGER.warning(e.getMessage(), e);
}
}
}
}
}
// register servlets
for (ServletInfo info : webAppInfo.servlets) {
if ("true".equalsIgnoreCase(appInfo.properties.getProperty("openejb.jaxrs.on", "true"))) {
// skip jaxrs servlets
boolean skip = false;
for (ParamValueInfo pvi : info.initParams) {
if ("javax.ws.rs.Application".equals(pvi.name) || Application.class.getName().equals(pvi.name)) {
skip = true;
}
}
if (skip) {
continue;
}
if (info.servletClass == null) {
try {
if (Application.class.isAssignableFrom(classLoader.loadClass(info.servletName))) {
continue;
}
} catch (Exception e) {
// no-op
}
}
} // else let the user manage itself a rest servlet etc...
// deploy
for (String mapping : info.mappings) {
try {
addServletMethod.invoke(null, info.servletClass, webContext, mapping);
deployedWebObjects.mappings.add(mapping);
} catch (Exception e) {
LOGGER.warning(e.getMessage(), e);
}
}
}
for (ClassListInfo info : webAppInfo.webAnnotatedClasses) {
final String url = info.name;
for (String servletPath : info.list) {
String classname = nameFromUrls(url, servletPath);
final Class<?> clazz = webContext.getClassLoader().loadClass(classname);
final WebServlet annotation = clazz.getAnnotation(WebServlet.class);
if (annotation != null) {
for (String mapping: annotation.urlPatterns()) {
try {
addServletMethod.invoke(null, classname, webContext, mapping);
deployedWebObjects.mappings.add(mapping);
} catch (Exception e) {
LOGGER.warning(e.getMessage(), e);
}
}
}
}
}
}
}
private static String nameFromUrls(final String url, final String path) {
final String value = path.substring(url.length()).replace(File.separatorChar, '/').replace('/', '.');
return value.substring(0, value.length() - ".class".length());
}
@Override
public void undeployWebApps(final AppInfo appInfo) throws Exception {
for (WebAppInfo webAppInfo : appInfo.webApps) {
final DeployedWebObjects context = servletDeploymentInfo.remove(webAppInfo);
final ServletContextEvent sce = servletContextEvents.remove(webAppInfo);
final List<Object> listenerInstances = listeners.remove(webAppInfo);
if (addServletMethod != null) {
for (String mapping : context.mappings) {
try {
removeServletMethod.invoke(null, mapping, context.webContext);
} catch (Exception e) {
// no-op
}
}
for (String mapping : context.filterMappings) {
try {
removeFilterMethod.invoke(null, mapping, context.webContext);
} catch (Exception e) {
// no-op
}
}
}
if (listenerInstances != null) {
for (Object instance : listenerInstances) {
if (ServletContextListener.class.isInstance(instance)) {
((ServletContextListener) instance).contextDestroyed(sce);
}
}
}
}
}
@Override
public Map<ClassLoader, Map<String, Set<String>>> getJsfClasses() {
return Collections.emptyMap(); // while we don't manage servlet in embedded mode we don't need it
}
private static class DeployedWebObjects {
public List<String> mappings = new ArrayList<String>();
public List<String> filterMappings = new ArrayList<String>();
public WebContext webContext;
}
private static class EmbeddedInitialContext implements Context {
private final Context delegate;
private final Map<String, Object> bindings;
public EmbeddedInitialContext(final Context jndiEnc, final Map<String, Object> bindings) {
this.delegate = jndiEnc;
this.bindings = bindings;
}
@Override
public Object lookup(final Name name) throws NamingException {
return lookup(name.toString());
}
@Override
public Object lookup(final String name) throws NamingException {
try {
return delegate.lookup(name);
} catch (NameNotFoundException nnfe) {
return bindings.get(name);
}
}
@Override
public void bind(final Name name, final Object obj) throws NamingException {
// no-op
}
@Override
public void bind(final String name, final Object obj) throws NamingException {
// no-op
}
@Override
public void rebind(final Name name, final Object obj) throws NamingException {
// no-op
}
@Override
public void rebind(final String name, final Object obj) throws NamingException {
// no-op
}
@Override
public void unbind(final Name name) throws NamingException {
// no-op
}
@Override
public void unbind(final String name) throws NamingException {
// no-op
}
@Override
public void rename(final Name oldName, final Name newName) throws NamingException {
// no-op
}
@Override
public void rename(final String oldName, final String newName) throws NamingException {
// no-op
}
@Override
public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException {
return null;
}
@Override
public NamingEnumeration<NameClassPair> list(final String name) throws NamingException {
return null;
}
@Override
public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException {
return null;
}
@Override
public NamingEnumeration<Binding> listBindings(final String name) throws NamingException {
return null;
}
@Override
public void destroySubcontext(final Name name) throws NamingException {
// no-op
}
@Override
public void destroySubcontext(final String name) throws NamingException {
// no-op
}
@Override
public Context createSubcontext(final Name name) throws NamingException {
return null;
}
@Override
public Context createSubcontext(final String name) throws NamingException {
return null;
}
@Override
public Object lookupLink(final Name name) throws NamingException {
return null;
}
@Override
public Object lookupLink(final String name) throws NamingException {
return null;
}
@Override
public NameParser getNameParser(final Name name) throws NamingException {
return null;
}
@Override
public NameParser getNameParser(final String name) throws NamingException {
return null;
}
@Override
public Name composeName(final Name name, final Name prefix) throws NamingException {
return null;
}
@Override
public String composeName(final String name, final String prefix) throws NamingException {
return null;
}
@Override
public Object addToEnvironment(final String propName, final Object propVal) throws NamingException {
return null;
}
@Override
public Object removeFromEnvironment(final String propName) throws NamingException {
return null;
}
@Override
public Hashtable<?, ?> getEnvironment() throws NamingException {
return null;
}
@Override
public void close() throws NamingException {
// no-op
}
@Override
public String getNameInNamespace() throws NamingException {
return null;
}
}
private static class SimpleFilterConfig implements FilterConfig {
private final Properties params;
private final String name;
private final ServletContext servletContext;
public SimpleFilterConfig(final ServletContext sc, final String name, final Properties initParams) {
this.name = name;
params = initParams;
servletContext = sc;
}
@Override
public String getFilterName() {
return name;
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public String getInitParameter(final String name) {
return params.getProperty(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return new ArrayEnumeration(params.keySet());
}
}
}