blob: e63f3afa21334a993e8c99c043a60b3d3873b499 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.snaps.core.internal.webapp;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.eclipse.gemini.web.core.WebContainer;
import org.eclipse.gemini.web.tomcat.spi.WebBundleClassLoaderFactory;
import org.eclipse.virgo.medic.eventlog.EventLogger;
import org.eclipse.virgo.snaps.core.RequestRouter;
import org.eclipse.virgo.snaps.core.internal.Snap;
import org.eclipse.virgo.snaps.core.internal.Host;
import org.eclipse.virgo.snaps.core.internal.SnapException;
import org.eclipse.virgo.snaps.core.internal.SnapUtils;
import org.eclipse.virgo.snaps.core.internal.SnapsLogEvents;
import org.eclipse.virgo.snaps.core.internal.webapp.config.BundleWebXmlLoader;
import org.eclipse.virgo.snaps.core.internal.webapp.config.WebXml;
import org.eclipse.virgo.snaps.core.internal.webapp.container.VirtualContainer;
import org.eclipse.virgo.snaps.core.internal.webapp.container.VirtualContainerFactory;
import org.eclipse.virgo.util.io.IOUtils;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TODO Document WebAppSnap
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* TODO Document concurrent semantics of WebAppSnap
*
*/
class WebAppSnap implements Snap {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Host host;
private final Bundle snapBundle;
private volatile VirtualContainer virtualContainer;
private volatile ClassLoader snapClassLoader;
private final WebBundleClassLoaderFactory classLoaderFactory;
private final String contextPath;
private final EventLogger eventLogger;
/**
* @param host
* @param snapBundle
* @param webBundleClassLoaderFactory
*/
public WebAppSnap(Host host, Bundle snapBundle, WebBundleClassLoaderFactory webBundleClassLoaderFactory, EventLogger eventLogger) {
this.host = host;
this.snapBundle = snapBundle;
this.contextPath = SnapUtils.getSnapContextPath(snapBundle);
this.classLoaderFactory = webBundleClassLoaderFactory;
this.eventLogger = eventLogger;
}
/**
* {@inheritDoc}
*
* @throws ServletException
*/
public synchronized final void init() throws ServletException {
logger.info("Initializing snap '{}'", this.contextPath);
WebXml webXml = BundleWebXmlLoader.loadWebXml(this.snapBundle);
SnapServletContext servletContext = new SnapServletContext(this.host.getServletContext(), this.snapBundle, this.contextPath);
servletContext.setAttribute(WebContainer.ATTRIBUTE_BUNDLE_CONTEXT, this.snapBundle.getBundleContext());
this.snapClassLoader = this.classLoaderFactory.createWebBundleClassLoader(this.snapBundle);
try {
((Lifecycle) snapClassLoader).start();
} catch (LifecycleException e) {
logger.error("Failed to start snap's class loader", e);
throw new ServletException("Failed to start web bundle's class loader", e);
}
VirtualContainer virtualContainer = createVirtualContainer(webXml, servletContext, this.snapClassLoader, host.getRouter());
virtualContainer.init();
this.virtualContainer = virtualContainer;
this.eventLogger.log(SnapsLogEvents.SNAP_BOUND, SnapUtils.boundContextPath(this.host.getServletContext().getContextPath(), this.contextPath));
}
/**
* {@inheritDoc}
*/
public final void destroy() {
VirtualContainer virtualContainer = this.virtualContainer;
if (virtualContainer != null) {
virtualContainer.destroy();
} else {
// TODO Log warning that dispatcher was null during destroy
}
ClassLoader snapClassLoader = this.snapClassLoader;
if (snapClassLoader != null) {
try {
((Lifecycle) snapClassLoader).stop();
} catch (LifecycleException e) {
logger.error("Failed to stop snap's class loader", e);
throw new SnapException("Failed to stop web bundle class loader", e);
}
} else {
// TODO Log warning that class loader was null during destroy
}
this.eventLogger.log(SnapsLogEvents.SNAP_UNBOUND, SnapUtils.boundContextPath(this.host.getServletContext().getContextPath(), this.contextPath));
}
/**
* {@inheritDoc}
*/
public final String getContextPath() {
return this.contextPath;
}
/**
* {@inheritDoc}
*/
public final void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
VirtualContainer virtualContainer = this.virtualContainer;
if (virtualContainer != null) {
virtualContainer.createDispatcher().service(request, response);
} else {
// TODO Log warning that dispatcher is not present
throw new ServletException("handleRequest invoked when virtual container was null");
}
}
protected VirtualContainer createVirtualContainer(WebXml webXml, SnapServletContext servletContext, ClassLoader snapClassLoader, RequestRouter requestRouter) {
return VirtualContainerFactory.fromWebXml(webXml, servletContext, snapClassLoader, requestRouter);
}
/**
* {@inheritDoc}
*/
public Properties getSnapProperties() {
Properties properties = new Properties();
URL url = this.snapBundle.getEntry("META-INF/snap.properties");
if (url != null) {
InputStream is = null;
try {
is = url.openStream();
properties.load(is);
} catch (IOException ioe) {
} finally {
IOUtils.closeQuietly(is);
}
}
return properties;
}
@Override
public synchronized void addHost(Host host) {
logger.info("Adding host '{}' to snap '{}'", host.getBundle(), this.contextPath);
SnapServletContext servletContext = new SnapServletContext(host.getServletContext(), this.snapBundle, this.contextPath);
servletContext.setAttribute(WebContainer.ATTRIBUTE_BUNDLE_CONTEXT, this.snapBundle.getBundleContext());
this.virtualContainer.addHost(servletContext);
this.eventLogger.log(SnapsLogEvents.SNAP_BOUND, SnapUtils.boundContextPath(host.getServletContext().getContextPath(), this.contextPath));
}
}