001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020package org.crsh.plugin;
021
022import org.crsh.util.ServletContextMap;
023import org.crsh.vfs.FS;
024import org.crsh.vfs.spi.servlet.ServletContextDriver;
025
026import javax.servlet.ServletContext;
027import javax.servlet.ServletContextEvent;
028import javax.servlet.ServletContextListener;
029import java.util.HashMap;
030import java.util.Map;
031
032public class WebPluginLifeCycle extends PluginLifeCycle implements ServletContextListener {
033
034  /** . */
035  private static final Object lock = new Object();
036
037  /** . */
038  private static final Map<String, PluginContext> contextMap = new HashMap<String, PluginContext>();
039
040  /** . */
041  private boolean registered = false;
042
043  /**
044   * Returns a plugin context associated with the servlet context or null if such context does not exist.
045   *
046   * @param contextPath the context path
047   * @return the associated plugin context
048   * @throws NullPointerException if the servlet context argument is null
049   */
050  public static PluginContext getPluginContext(String contextPath) throws NullPointerException {
051    synchronized (lock) {
052      return contextMap.get(contextPath);
053    }
054  }
055
056  /**
057   * Create the service loader discovery, this can be subclassed to provide an implementation, the current
058   * implementation returns a {@link ServiceLoaderDiscovery} instance.
059   *
060   * @param context the servlet context
061   * @param classLoader the class loader
062   * @return the plugin discovery
063   */
064  protected PluginDiscovery createDiscovery(ServletContext context, ClassLoader classLoader) {
065    return new ServiceLoaderDiscovery(classLoader);
066  }
067
068  public void contextInitialized(ServletContextEvent sce) {
069    ServletContext context = sce.getServletContext();
070    String contextPath = context.getContextPath();
071
072    // Use JVM properties as external config
073    setConfig(System.getProperties());
074
075    //
076    synchronized (lock) {
077      if (!contextMap.containsKey(contextPath)) {
078
079        //
080        FS cmdFS = createCommandFS(context);
081        FS confFS = createConfFS(context);
082        ClassLoader webAppLoader = Thread.currentThread().getContextClassLoader();
083        PluginDiscovery discovery = createDiscovery(context, webAppLoader);
084
085        //
086        PluginContext pluginContext = createPluginContext(context, cmdFS, confFS, discovery);
087
088        //
089        contextMap.put(contextPath, pluginContext);
090        registered = true;
091
092        //
093        start(pluginContext);
094      }
095    }
096  }
097
098  /**
099   * Create the plugin context, allowing subclasses to provide a custom configuration.
100   *
101   * @param context the servlet context
102   * @param cmdFS the command file system
103   * @param confFS the conf file system
104   * @param discovery the plugin discovery
105   * @return the plugin context
106   */
107  protected PluginContext createPluginContext(ServletContext context, FS cmdFS, FS confFS, PluginDiscovery discovery) {
108    return new PluginContext(discovery, new ServletContextMap(context), cmdFS, confFS, context.getClassLoader());
109  }
110
111  /**
112   * Create the command file system, this method binds the <code>/WEB-INF/crash/commands/</code> path of the
113   * servlet context.
114   *
115   * @param context the servlet context
116   * @return the command file system
117   */
118  protected FS createCommandFS(ServletContext context) {
119    return new FS().mount(new ServletContextDriver(context, "/WEB-INF/crash/commands/"));
120  }
121
122  /**
123   * Create the conf file system, this method binds the <code>/WEB-INF/crash/</code> path of the
124   * servlet context.
125   *
126   * @param context the servlet context
127   * @return the conf file system
128   */
129  protected FS createConfFS(ServletContext context) {
130    return new FS().mount(new ServletContextDriver(context, "/WEB-INF/crash/"));
131  }
132
133  public void contextDestroyed(ServletContextEvent sce) {
134    if (registered) {
135
136      //
137      ServletContext sc = sce.getServletContext();
138      String contextPath = sc.getContextPath();
139
140      //
141      synchronized (lock) {
142
143        //
144        contextMap.remove(contextPath);
145        registered = false;
146
147        //
148        stop();
149      }
150    }
151  }
152}