Author: nfilotto
Date: 2009-10-05 06:19:24 -0400 (Mon, 05 Oct 2009)
New Revision: 207
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerClassLoader.java
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerContext.java
kernel/trunk/container/src/main/java/org/exoplatform/container/UnifiedClassLoader.java
kernel/trunk/container/src/main/java/org/exoplatform/container/WebAppInitContext.java
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerConfig.java
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinition.java
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinitionPlugin.java
kernel/trunk/container/src/main/java/org/exoplatform/container/web/
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractFilter.java
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpServlet.java
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpSessionListener.java
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerConfigOwner.java
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java
kernel/trunk/container/src/test/java/org/exoplatform/container/TestPortalContainerInitTaskContextComparator.java
kernel/trunk/container/src/test/java/org/exoplatform/container/TestUnifiedClassLoader.java
kernel/trunk/container/src/test/java/org/exoplatform/container/TestWebAppInitContextComparator.java
Modified:
kernel/trunk/container/src/main/java/conf/configuration.xml
kernel/trunk/container/src/main/java/org/exoplatform/container/ExoContainerContext.java
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainer.java
kernel/trunk/container/src/main/java/org/exoplatform/container/RootContainer.java
kernel/trunk/container/src/main/java/org/exoplatform/container/configuration/ConfigurationManagerImpl.java
kernel/trunk/container/src/main/java/org/exoplatform/container/util/ContainerUtil.java
Log:
EXOJCR-166: Support separated ear delivery
Modified: kernel/trunk/container/src/main/java/conf/configuration.xml
===================================================================
--- kernel/trunk/container/src/main/java/conf/configuration.xml 2009-10-04 19:39:27 UTC
(rev 206)
+++ kernel/trunk/container/src/main/java/conf/configuration.xml 2009-10-05 10:19:24 UTC
(rev 207)
@@ -43,4 +43,8 @@
</properties-param>
</init-params>
</component>
+
+ <component>
+ <type>org.exoplatform.container.definition.PortalContainerConfig</type>
+ </component>
</configuration>
Modified:
kernel/trunk/container/src/main/java/org/exoplatform/container/ExoContainerContext.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/ExoContainerContext.java 2009-10-04
19:39:27 UTC (rev 206)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/ExoContainerContext.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -55,6 +55,45 @@
return container;
}
+ /**
+ * @return if the embedded container is a {@link PortalContainer}, it will return the
name the
+ * portal container otherwise it will return <code>null</code>
+ */
+ public String getPortalContainerName()
+ {
+ if (container instanceof PortalContainer)
+ {
+ return ((PortalContainer)container).getName();
+ }
+ return null;
+ }
+
+ /**
+ * @return if the embedded container is a {@link PortalContainer}, it will return the
name
+ * of the rest context name related to the portal container otherwise it will return
the default name
+ */
+ public String getRestContextName()
+ {
+ if (container instanceof PortalContainer)
+ {
+ return ((PortalContainer)container).getRestContextName();
+ }
+ return PortalContainer.DEFAULT_REST_CONTEXT_NAME;
+ }
+
+ /**
+ * @return if the embedded container is a {@link PortalContainer}, it will return the
name
+ * of the realm name related to the portal container otherwise it will return the
default name
+ */
+ public String getRealmName()
+ {
+ if (container instanceof PortalContainer)
+ {
+ return ((PortalContainer)container).getRealmName();
+ }
+ return PortalContainer.DEFAULT_REALM_NAME;
+ }
+
public String getName()
{
return name;
Modified:
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainer.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainer.java 2009-10-04
19:39:27 UTC (rev 206)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainer.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -18,6 +18,8 @@
*/
package org.exoplatform.container;
+import org.exoplatform.container.RootContainer.PortalContainerInitTask;
+import org.exoplatform.container.definition.PortalContainerConfig;
import org.exoplatform.container.jmx.MX4JComponentAdapterFactory;
import org.exoplatform.container.xml.PortalContainerInfo;
import org.exoplatform.management.annotations.Managed;
@@ -25,12 +27,17 @@
import org.exoplatform.management.jmx.annotations.NameTemplate;
import org.exoplatform.management.jmx.annotations.NamingContext;
import org.exoplatform.management.jmx.annotations.Property;
-import org.picocontainer.ComponentAdapter;
-import org.picocontainer.PicoContainer;
-import org.picocontainer.PicoException;
-import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import javax.servlet.ServletContext;
@@ -45,21 +52,94 @@
{
/**
+ * Logger
+ */
+ private static final Log log = ExoLogger.getLogger(PortalContainer.class);
+
+ /**
* The default name of the portal container
*/
- private static final String DEFAULT_PORTAL_CONTAINER_NAME = "portal";
+ public static final String DEFAULT_PORTAL_CONTAINER_NAME;
- private static ThreadLocal currentContainer_ = new ThreadLocal();
+ /**
+ * The default name of a the {@link ServletContext} of the rest web application
+ */
+ public static final String DEFAULT_REST_CONTEXT_NAME;
- private boolean started_ = false;
+ /**
+ * The default name of a the realm
+ */
+ public static final String DEFAULT_REALM_NAME;
+ /**
+ * The configuration of the portal containers
+ */
+ private static final PortalContainerConfig CONFIG;
+ static
+ {
+ ExoContainer top = ExoContainerContext.getTopContainer();
+ CONFIG = top instanceof RootContainer ?
((RootContainer)top).getPortalContainerConfig() : null;
+ if (CONFIG == null)
+ {
+ DEFAULT_PORTAL_CONTAINER_NAME =
PortalContainerConfig.DEFAULT_PORTAL_CONTAINER_NAME;
+ DEFAULT_REST_CONTEXT_NAME = PortalContainerConfig.DEFAULT_REST_CONTEXT_NAME;
+ DEFAULT_REALM_NAME = PortalContainerConfig.DEFAULT_REALM_NAME;
+ }
+ else
+ {
+ DEFAULT_PORTAL_CONTAINER_NAME = CONFIG.getDefaultPortalContainer();
+ DEFAULT_REST_CONTEXT_NAME = CONFIG.getDefaultRestContext();
+ DEFAULT_REALM_NAME = CONFIG.getDefaultRealmName();
+ }
+ }
+
+ private static ThreadLocal<PortalContainer> currentContainer_ = new
ThreadLocal<PortalContainer>();
+
+ private volatile boolean started_;
+
private PortalContainerInfo pinfo_;
private SessionManager smanager_;
+ /**
+ * The name of the portal container
+ */
private final String name;
- public PortalContainer(PicoContainer parent, ServletContext portalContext)
+ /**
+ * The comparator used to sort the web applications by priorities
+ */
+ private final Comparator<WebAppInitContext> webAppComparator;
+
+ /**
+ * The full {@link ServletContext} of the portal container after merging all the
+ * {@link ServletContext} that have been registered
+ */
+ private final ServletContext portalMergedContext;
+
+ /**
+ * The full {@link ClassLoader} of the portal container after merging all the {@link
ClassLoader}
+ * of the {@link ServletContext} that have been registered
+ */
+ private final ClassLoader portalMergedClassLoader;
+
+ /**
+ * The {@link Set} of all the web application context that share configuration
+ */
+ private volatile Set<WebAppInitContext> webAppContexts;
+
+ /**
+ * To allow overriding we need to have a custom {@link ClassLoader} by web
applications by portal
+ * containers
+ */
+ private volatile Map<String, ClassLoader> webAppClassLoaders;
+
+ /**
+ * The {@link ServletContext} of the current portal container
+ */
+ final ServletContext portalContext;
+
+ public PortalContainer(RootContainer parent, ServletContext portalContext)
{
super(new MX4JComponentAdapterFactory(), parent);
registerComponentInstance(ServletContext.class, portalContext);
@@ -67,8 +147,131 @@
pinfo_ = new PortalContainerInfo(portalContext);
registerComponentInstance(PortalContainerInfo.class, pinfo_);
this.name = portalContext.getServletContextName();
+ final List<String> dependencies =
parent.getPortalContainerConfig().getDependencies(name);
+ if (dependencies == null || dependencies.isEmpty())
+ {
+ // No order is required
+ this.webAppComparator = null;
+ }
+ else
+ {
+ this.webAppComparator = new WebAppInitContextComparator(dependencies);
+ }
+ this.webAppContexts = Collections.singleton(new WebAppInitContext(portalContext));
+ this.portalContext = portalContext;
+ this.portalMergedContext = new PortalContainerContext(this);
+ this.portalMergedClassLoader = new PortalContainerClassLoader(this);
+ this.webAppClassLoaders =
Collections.unmodifiableMap(Collections.singletonMap(name, portalMergedClassLoader));
}
+ /**
+ * @return a {@link Set} ordered by priority of all the {@link WebAppInitContext} that
represents
+ * the full portal context
+ */
+ Set<WebAppInitContext> getWebAppInitContexts()
+ {
+ return webAppContexts;
+ }
+
+ /**
+ * This gives the merged {@link ClassLoader} between the {@link
PortalContainerClassLoader} and the
+ * {@link ClassLoader} of the web application.
+ *
+ * @param context the {@link ServletContext} of the web application
+ * @return the merged {@link ClassLoader} between the {@link
PortalContainerClassLoader} and
+ * the {@link ClassLoader} of the web application that allows us to override resources
contained
+ * into the {@link ClassLoader} of the web application
+ */
+ public ClassLoader getWebAppClassLoader(ServletContext context)
+ {
+ final String contextName = context.getServletContextName();
+ ClassLoader cl = webAppClassLoaders.get(contextName);
+ if (cl == null)
+ {
+ synchronized (this)
+ {
+ cl = webAppClassLoaders.get(contextName);
+ if (cl == null)
+ {
+ cl =
+ new UnifiedClassLoader(new
ClassLoader[]{Thread.currentThread().getContextClassLoader(),
+ portalMergedClassLoader});
+ Map<String, ClassLoader> cls = new HashMap<String,
ClassLoader>(webAppClassLoaders);
+ cls.put(contextName, cl);
+ this.webAppClassLoaders = Collections.unmodifiableMap(cls);
+ }
+ }
+ }
+ return cl;
+ }
+
+ /**
+ * @return the full {@link ClassLoader} of the portal container after merging all the
+ * {@link ClassLoader} of all {@link ServletContext} that have been registered
+ */
+ public ClassLoader getPortalClassLoader()
+ {
+ return portalMergedClassLoader;
+ }
+
+ /**
+ * @return the full {@link ServletContext} of the portal container after merging all
the
+ * {@link ServletContext} that have been registered
+ */
+ public ServletContext getPortalContext()
+ {
+ return portalMergedContext;
+ }
+
+ /**
+ * Register a new servlet context that contains configuration files and potentially
resource files
+ * We assume that this method is called within the initialization context of the
related web application
+ * @param context the {@link ServletContext} of the web application to register
+ */
+ public synchronized void registerContext(ServletContext context)
+ {
+ final WebAppInitContext webappCtx = new WebAppInitContext(context);
+ if (!webAppContexts.contains(webappCtx))
+ {
+ final Set<WebAppInitContext> contexts;
+ if (webAppComparator == null)
+ {
+ contexts = new HashSet<WebAppInitContext>(webAppContexts);
+ }
+ else
+ {
+ contexts = new TreeSet<WebAppInitContext>(webAppComparator);
+ contexts.addAll(webAppContexts);
+ }
+ contexts.add(webappCtx);
+ this.webAppContexts = Collections.unmodifiableSet(contexts);
+ }
+ }
+
+ /**
+ * Unregister a servlet context that contains configuration files and potentially
resource files
+ * @param context the {@link ServletContext} of the web application to unregister
+ */
+ public synchronized void unregisterContext(ServletContext context)
+ {
+ final WebAppInitContext webappCtx = new WebAppInitContext(context);
+ if (webAppContexts.contains(webappCtx))
+ {
+ final Set<WebAppInitContext> contexts;
+ if (webAppComparator == null)
+ {
+ contexts = new HashSet<WebAppInitContext>(webAppContexts);
+ }
+ else
+ {
+ contexts = new TreeSet<WebAppInitContext>(webAppComparator);
+ contexts.addAll(webAppContexts);
+ }
+ contexts.remove(webappCtx);
+ this.webAppContexts = Collections.unmodifiableSet(contexts);
+ }
+ }
+
@Managed
@ManagedDescription("The portal container name")
public String getName()
@@ -110,6 +313,10 @@
return pinfo_;
}
+ /**
+ * @return the current instance of {@link PortalContainer} that has been stored into
the related
+ * {@link ThreadLocal}. If no value has been set the default portal container will be
returned
+ */
public static PortalContainer getInstance()
{
PortalContainer container = (PortalContainer)currentContainer_.get();
@@ -121,50 +328,230 @@
return container;
}
- @Managed
- public boolean isStarted()
+ /**
+ * @return the current instance of {@link PortalContainer} that has been stored into
the related
+ * {@link ThreadLocal}. If no value has been set, it will return
<code>null</code>
+ */
+ public static PortalContainer getInstanceIfPresent()
{
- return started_;
+ return currentContainer_.get();
}
- public void start()
+ /**
+ * @see the method isPortalContainerName of {@link PortalContainerConfig}
+ */
+ public static boolean isPortalContainerName(String name)
{
- super.start();
- started_ = true;
+ if (CONFIG == null)
+ {
+ return DEFAULT_PORTAL_CONTAINER_NAME.equals(name);
+ }
+ else
+ {
+ return CONFIG.isPortalContainerName(name);
+ }
}
- public void stop()
+ /**
+ * Add an init-task to all the portal container instances related to the given
ServletContext
+ *
+ * @param context the context from which we extract the context name
+ * @param task the task to execute
+ */
+ public static void addInitTask(ServletContext context, PortalContainerInitTask task)
{
- super.stop();
- started_ = false;
+ addInitTask(context, task, null);
}
- synchronized public ComponentAdapter getComponentAdapterOfType(Class componentType)
+ /**
+ * Add an init-task to all the portal container instances related to the given
ServletContext if the
+ * given portal container name is <code>null</code> other it will execute
the task only of this
+ * portal container if the {@link ServletContext} is on of its dependencies
+ *
+ * @param context the context from which we extract the context name
+ * @param task the task to execute
+ * @param portalContainerName the name of the portal container for which we want to
execute the task
+ */
+ public static void addInitTask(ServletContext context, PortalContainerInitTask task,
String portalContainerName)
{
- return super.getComponentAdapterOfType(componentType);
+ if (context == null || CONFIG == null)
+ {
+ return;
+ }
+ String contextName = context.getServletContextName();
+ List<String> portalContainerNames =
CONFIG.getPortalContainerNames(contextName);
+ RootContainer root = RootContainer.getInstance();
+ // We assume that we have at list one portal container otherwise there is a bug in
PortalContainerConfig
+ for (String name : portalContainerNames)
+ {
+ if (portalContainerName == null || portalContainerName.equals(name))
+ {
+ root.addInitTask(context, task, name);
+ }
+ }
}
- synchronized public List getComponentAdaptersOfType(Class componentType)
+ /**
+ * Gives the first portal container instance related to the given ServletContext
+ *
+ * @param context the context from which we extract the context name
+ */
+ public static PortalContainer getInstance(ServletContext context)
{
- return super.getComponentAdaptersOfType(componentType);
+ if (context == null || CONFIG == null)
+ {
+ return null;
+ }
+ List<String> portalContainerNames =
CONFIG.getPortalContainerNames(context.getServletContextName());
+ RootContainer root = RootContainer.getInstance();
+ return root.getPortalContainer(portalContainerNames.get(0));
}
- synchronized public ComponentAdapter unregisterComponent(Object componentKey)
+ /**
+ * We first try to get the ExoContainer that has been stored in the ThreadLocal
+ * if the value is of type PortalContainer, we return it otherwise we get the
+ * portal container corresponding the given servlet context
+ *
+ * @param context the context from which we extract the portal container name
+ */
+ public static PortalContainer getCurrentInstance(ServletContext context)
{
- return super.unregisterComponent(componentKey);
+ final ExoContainer container = ExoContainerContext.getCurrentContainer();
+ if (container instanceof PortalContainer)
+ {
+ if (log.isDebugEnabled())
+ log.debug("A portal container has been set in the ThreadLocal of
ExoContainerContext");
+ return (PortalContainer)container;
+ }
+ else
+ {
+ PortalContainer pContainer = PortalContainer.getInstanceIfPresent();
+ if (pContainer == null)
+ {
+ if (log.isDebugEnabled())
+ log.debug("No portal container has been set in the ThreadLoal of
PortalContainer");
+ pContainer = PortalContainer.getInstance(context);
+ }
+ return pContainer;
+ }
}
- synchronized public ComponentAdapter registerComponent(ComponentAdapter
componentAdapter)
- throws DuplicateComponentKeyRegistrationException
+ /**
+ * Returns the name of the current portal container that has been stored in the
ThreadLocal. If no
+ * value can be found the value of PortalContainer.DEFAULT_PORTAL_CONTAINER_NAME will
be used
+ */
+ public static String getCurrentPortalContainerName()
{
- return super.registerComponent(componentAdapter);
+ final PortalContainer container = getInstanceIfPresent();
+ if (container == null)
+ {
+ return DEFAULT_PORTAL_CONTAINER_NAME;
+ }
+ else
+ {
+ return container.getName();
+ }
}
- synchronized public List getComponentInstancesOfType(Class componentType) throws
PicoException
+ /**
+ * Returns the name of the current rest context corresponding to the portal container
+ * that has been stored in the ThreadLocal. If no value can be found the value of
+ * PortalContainer.DEFAULT_REST_CONTEXT_NAME will be used
+ */
+ public static String getCurrentRestContextName()
{
- return super.getComponentInstancesOfType(componentType);
+ final String containerName = getCurrentPortalContainerName();
+ return getRestContextName(containerName);
}
+ /**
+ * Returns the name of the rest context corresponding to the given portal container
name
+ * @param portalContainerName the name of the portal container for which we want the
+ * name of the rest {@link ServletContext}
+ */
+ public static String getRestContextName(String portalContainerName)
+ {
+ if (CONFIG == null)
+ {
+ return DEFAULT_REST_CONTEXT_NAME;
+ }
+ return CONFIG.getRestContextName(portalContainerName);
+ }
+
+ /**
+ * Returns the name of the rest context corresponding to the current portal container
+ */
+ public String getRestContextName()
+ {
+ return getRestContextName(getName());
+ }
+
+ /**
+ * Returns the name of the current realm corresponding to the portal container
+ * that has been stored in the ThreadLocal. If no value can be found the value of
+ * PortalContainer.DEFAULT_REALM_NAME will be used
+ */
+ public static String getCurrentRealmName()
+ {
+ final String containerName = getCurrentPortalContainerName();
+ return getRealmName(containerName);
+ }
+
+ /**
+ * Returns the name of the realm corresponding to the given portal container name
+ * @param portalContainerName the name of the portal container for which we want the
+ * name of the realm
+ */
+ public static String getRealmName(String portalContainerName)
+ {
+ if (CONFIG == null)
+ {
+ return DEFAULT_REALM_NAME;
+ }
+ return CONFIG.getRealmName(portalContainerName);
+ }
+
+ /**
+ * Returns the name of the realm corresponding to the current portal container
+ */
+ public String getRealmName()
+ {
+ return getRealmName(getName());
+ }
+
+ /**
+ * Indicates if the given servlet context is a dependency of the given portal
container
+ * @param container the portal container
+ * @param context the {@link ServletContext}
+ * @return <code>true</code> if the dependencies matches,
<code>false</code> otherwise;
+ */
+ public static boolean isScopeValid(PortalContainer container, ServletContext context)
+ {
+ if (CONFIG == null)
+ {
+ return true;
+ }
+ return CONFIG.isScopeValid(container.getName(), context.getServletContextName());
+ }
+
+ @Managed
+ public boolean isStarted()
+ {
+ return started_;
+ }
+
+ public void start()
+ {
+ super.start();
+ started_ = true;
+ }
+
+ public void stop()
+ {
+ super.stop();
+ started_ = false;
+ }
+
public static void setInstance(PortalContainer instance)
{
currentContainer_.set(instance);
@@ -176,4 +563,47 @@
PortalContainer pcontainer = (PortalContainer)currentContainer_.get();
return pcontainer.getComponentInstanceOfType(key);
}
+
+ /**
+ * This class is used to compare the {@link WebAppInitContext}
+ */
+ static class WebAppInitContextComparator implements
Comparator<WebAppInitContext>
+ {
+
+ private final List<String> dependencies;
+
+ WebAppInitContextComparator(List<String> dependencies)
+ {
+ this.dependencies = dependencies;
+ }
+
+ /**
+ * This will sort all the {@link WebAppInitContext} such that we will first have
+ * all the web applications defined in the list of dependencies of the
+ * related portal container (see {@link PortalContainerConfig} for more details
+ * about the dependencies) ordered in the same order as the dependencies, then
+ * we will have all the web applications undefined ordered by context name
+ */
+ public int compare(WebAppInitContext ctx1, WebAppInitContext ctx2)
+ {
+ int idx1 = dependencies.indexOf(ctx1.getServletContextName());
+ int idx2 = dependencies.indexOf(ctx2.getServletContextName());
+ if (idx1 == -1 && idx2 != -1)
+ {
+ return 1;
+ }
+ else if (idx1 != -1 && idx2 == -1)
+ {
+ return -1;
+ }
+ else if (idx1 == -1 && idx2 == -1)
+ {
+ return ctx1.getServletContextName().compareTo(ctx2.getServletContextName());
+ }
+ else
+ {
+ return idx1 - idx2;
+ }
+ }
+ }
}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerClassLoader.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerClassLoader.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerClassLoader.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import java.util.Set;
+
+/**
+ * This class defined the {@link ClassLoader} of a portal container
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 15 sept. 2009
+ */
+class PortalContainerClassLoader extends UnifiedClassLoader
+{
+
+ /**
+ * The related portal container
+ */
+ private final PortalContainer container;
+
+ PortalContainerClassLoader(PortalContainer container)
+ {
+ super(getClassLoaders(container));
+ this.container = container;
+ }
+
+ /**
+ * Retrieves the list of all the {@link ClassLoader} that are associated to the given
+ * portal container
+ */
+ private static ClassLoader[] getClassLoaders(PortalContainer container)
+ {
+ final Set<WebAppInitContext> contexts = container.getWebAppInitContexts();
+ final ClassLoader[] cls = new ClassLoader[contexts.size()];
+ int i = 0;
+ for (WebAppInitContext ctx : contexts)
+ {
+ cls[i++] = ctx.getWebappClassLoader();
+ }
+ return cls;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected ClassLoader[] getClassLoaders()
+ {
+ return getClassLoaders(container);
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerContext.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerContext.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/PortalContainerContext.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * This class is used to merge all the {@link ServletContext} related to a given portal
container.
+ * It will used the {@link WebAppInitContext} that have been defined in the related
portal container.
+ * It will always consider that the {@link WebAppInitContext}
+ * with the highest priority has always right, in other words for example in the method
+ * getInitParameter, it will try to get the init parameter in the {@link
WebAppInitContext}
+ * of the highest priority, if it cans not find it, it will try the {@link
WebAppInitContext}
+ * with the second highest priority and so on. The priority of the {@link
WebAppInitContext} is
+ * the order given by the method PortalContainer.getWebAppInitContexts(),
+ * the last {@link WebAppInitContext} is the one with the highest priority.
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 14 sept. 2009
+ */
+class PortalContainerContext implements ServletContext
+{
+
+ /**
+ * The related portal container
+ */
+ private final PortalContainer container;
+
+ PortalContainerContext(PortalContainer container)
+ {
+ this.container = container;
+ }
+
+ private WebAppInitContext[] getWebAppInitContexts()
+ {
+ final Set<WebAppInitContext> contexts = container.getWebAppInitContexts();
+ final WebAppInitContext[] aContexts = new WebAppInitContext[contexts.size()];
+ return (WebAppInitContext[])contexts.toArray(aContexts);
+ }
+
+ private ServletContext getPortalContext()
+ {
+ return container.portalContext;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getAttribute(String name)
+ {
+ return getPortalContext().getAttribute(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public Enumeration getAttributeNames()
+ {
+ return getPortalContext().getAttributeNames();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ServletContext getContext(String uripath)
+ {
+ return getPortalContext().getContext(uripath);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getInitParameter(String name)
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ String param = context.getInitParameter(name);
+ if (param != null)
+ {
+ return param;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public Enumeration<String> getInitParameterNames()
+ {
+ final Set<WebAppInitContext> contexts = container.getWebAppInitContexts();
+ Set<String> names = null;
+ for (WebAppInitContext context : contexts)
+ {
+ Enumeration<String> eNames =
context.getServletContext().getAttributeNames();
+ if (eNames != null)
+ {
+ if (names == null)
+ {
+ names = new HashSet<String>();
+ }
+ names.addAll(Collections.list(eNames));
+ }
+ }
+ if (names == null)
+ {
+ return null;
+ }
+ return Collections.enumeration(names);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getMajorVersion()
+ {
+ return getPortalContext().getMajorVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getMimeType(String file)
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ String mimeType = context.getMimeType(file);
+ if (mimeType != null)
+ {
+ return mimeType;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getMinorVersion()
+ {
+ return getPortalContext().getMinorVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public RequestDispatcher getNamedDispatcher(String name)
+ {
+ return getPortalContext().getNamedDispatcher(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getRealPath(String path)
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ final InputStream is = context.getResourceAsStream(path);
+ if (is != null)
+ {
+ // The resource exists within this servlet context
+ return context.getRealPath(path);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public RequestDispatcher getRequestDispatcher(String path)
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ final InputStream is = context.getResourceAsStream(path);
+ if (is != null)
+ {
+ // The resource exists within this servlet context
+ return context.getRequestDispatcher(path);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URL getResource(String path) throws MalformedURLException
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ final URL url = context.getResource(path);
+ if (url != null)
+ {
+ return url;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InputStream getResourceAsStream(String path)
+ {
+ final WebAppInitContext[] contexts = getWebAppInitContexts();
+ for (int i = contexts.length - 1; i >= 0; i--)
+ {
+ final ServletContext context = contexts[i].getServletContext();
+ final InputStream is = context.getResourceAsStream(path);
+ if (is != null)
+ {
+ return is;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public Set<String> getResourcePaths(String path)
+ {
+ final Set<WebAppInitContext> contexts = container.getWebAppInitContexts();
+ Set<String> paths = null;
+ for (WebAppInitContext context : contexts)
+ {
+ Set<String> sPaths = context.getServletContext().getResourcePaths(path);
+ if (sPaths != null)
+ {
+ if (paths == null)
+ {
+ paths = new LinkedHashSet<String>();
+ }
+ paths.addAll(sPaths);
+ }
+ }
+ return paths;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getServerInfo()
+ {
+ return getPortalContext().getServerInfo();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("deprecation")
+ public Servlet getServlet(String name) throws ServletException
+ {
+ return getPortalContext().getServlet(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getServletContextName()
+ {
+ return getPortalContext().getServletContextName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings({"deprecation", "unchecked"})
+ public Enumeration getServletNames()
+ {
+ return getPortalContext().getServletNames();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings({"deprecation", "unchecked"})
+ public Enumeration getServlets()
+ {
+ return getPortalContext().getServlets();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void log(String message)
+ {
+ getPortalContext().log(message);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("deprecation")
+ public void log(Exception exception, String message)
+ {
+ getPortalContext().log(exception, message);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void log(String message, Throwable throwable)
+ {
+ getPortalContext().log(message, throwable);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeAttribute(String name)
+ {
+ getPortalContext().removeAttribute(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setAttribute(String name, Object object)
+ {
+ getPortalContext().setAttribute(name, object);
+ }
+}
Modified:
kernel/trunk/container/src/main/java/org/exoplatform/container/RootContainer.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/RootContainer.java 2009-10-04
19:39:27 UTC (rev 206)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/RootContainer.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -21,6 +21,8 @@
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.container.configuration.ConfigurationManagerImpl;
import org.exoplatform.container.configuration.MockConfigurationManagerImpl;
+import org.exoplatform.container.definition.PortalContainerConfig;
+import org.exoplatform.container.definition.PortalContainerDefinition;
import org.exoplatform.container.jmx.ManagementContextImpl;
import org.exoplatform.container.monitor.jvm.J2EEServerInfo;
import org.exoplatform.container.monitor.jvm.OperatingSystemInfo;
@@ -36,7 +38,15 @@
import java.io.File;
import java.lang.management.ManagementFactory;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.PriorityBlockingQueue;
import javax.management.MBeanServer;
import javax.servlet.ServletContext;
@@ -66,12 +76,25 @@
private OperatingSystemInfo osenv_;
+ private PortalContainerConfig config_;
+
private static final Log log = ExoLogger.getLogger(RootContainer.class);
private static volatile boolean booting = false;
private final J2EEServerInfo serverenv_ = new J2EEServerInfo();
+ /**
+ * The list of all the tasks to execute while initializing the corresponding portal
containers
+ */
+ private final ConcurrentMap<String, ConcurrentMap<String,
Queue<PortalContainerInitTaskContext>>> initTasks =
+ new ConcurrentHashMap<String, ConcurrentMap<String,
Queue<PortalContainerInitTaskContext>>>();
+
+ /**
+ * The list of the web application contexts corresponding to all the portal
containers
+ */
+ private final Queue<WebAppInitContext> portalContexts = new
ConcurrentLinkedQueue<WebAppInitContext>();
+
public RootContainer()
{
super(new ManagementContextImpl(findMBeanServer(), new HashMap<String,
String>()));
@@ -88,6 +111,28 @@
return osenv_;
}
+ /**
+ * @return the {@link PortalContainerConfig} corresponding to the {@link
RootContainer}
+ */
+ PortalContainerConfig getPortalContainerConfig()
+ {
+ if (config_ == null)
+ {
+ config_ =
(PortalContainerConfig)this.getComponentInstanceOfType(PortalContainerConfig.class);
+ }
+ return config_;
+ }
+
+ /**
+ * Indicates if the current instance is aware of the {@link PortalContainerConfig}
+ * @return <code>true</code> if we are using the old way to configure the
portal containers,
+ * <code>false</code> otherwise
+ */
+ public boolean isPortalContainerConfigAware()
+ {
+ return getPortalContainerConfig().hasDefinition();
+ }
+
public J2EEServerInfo getServerEnvironment()
{
return serverenv_;
@@ -125,14 +170,96 @@
return pcontainer;
}
- synchronized public PortalContainer createPortalContainer(ServletContext context)
+ /**
+ * Register a new portal container. It will try to detect if {@link
PortalContainerDefinition} has
+ * been defined, if so it will create the portal container later otherwise we assume
that we
+ * expect the old behavior, thus the portal container will be initialized
synchronously
+ * @param context the context of the portal container
+ */
+ public void registerPortalContainer(ServletContext context)
{
+ PortalContainerConfig config = getPortalContainerConfig();
+ // Ensure that the portal container has been registered
+ config.registerPortalContainerName(context.getServletContextName());
+ if (config.hasDefinition())
+ {
+ // The new behavior has been detected thus, the creation will be done at the end
asynchronously
+ portalContexts.add(new WebAppInitContext(context));
+ // We assume that a ServletContext of a portal container owns configuration
files
+ final PortalContainerPreInitTask task = new PortalContainerPreInitTask()
+ {
+
+ public void execute(ServletContext context, PortalContainer portalContainer)
+ {
+ portalContainer.registerContext(context);
+ }
+ };
+ PortalContainer.addInitTask(context, task);
+ }
+ else
+ {
+ // The old behavior has been detected thus, the creation will be done
synchronously
+ createPortalContainer(context);
+ }
+ }
+
+ /**
+ * Creates all the portal containers that have been registered thanks to the method
+ * <code>registerPortalContainer</code>
+ */
+ public synchronized void createPortalContainers()
+ {
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ WebAppInitContext context;
+ boolean hasChanged = false;
try
{
+ while ((context = portalContexts.poll()) != null)
+ {
+ // Set the context classloader of the related web application
+
Thread.currentThread().setContextClassLoader(context.getWebappClassLoader());
+ hasChanged = true;
+ createPortalContainer(context.getServletContext());
+ }
+ }
+ finally
+ {
+ if (hasChanged)
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ }
+ PortalContainerConfig config = getPortalContainerConfig();
+ for (String portalContainerName : initTasks.keySet())
+ {
+ // Unregister name of portal container that doesn't exist
+ log.warn("The portal container '" + portalContainerName +
"' doesn't not exist or"
+ + " it has not yet been registered, please check your
PortalContainerDefinitions and "
+ + "the loading order.");
+ config.unregisterPortalContainerName(portalContainerName);
+ }
+ // remove all the unneeded tasks
+ initTasks.clear();
+ }
+
+ public synchronized void createPortalContainer(ServletContext context)
+ {
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ boolean hasChanged = false;
+ final String portalContainerName = context.getServletContextName();
+ try
+ {
+ log.info("Trying to create the portal container '" +
portalContainerName + "'");
PortalContainer pcontainer = new PortalContainer(this, context);
PortalContainer.setInstance(pcontainer);
- ExoContainerContext.setCurrentContainer(pcontainer);
- ConfigurationManagerImpl cService = new ConfigurationManagerImpl(context);
+ executeInitTasks(pcontainer, PortalContainerPreInitTask.TYPE);
+ // Set the full classloader of the portal container
+
Thread.currentThread().setContextClassLoader(pcontainer.getPortalClassLoader());
+ hasChanged = true;
+ ConfigurationManagerImpl cService = new
ConfigurationManagerImpl(pcontainer.getPortalContext());
// add configs from services
try
@@ -141,9 +268,7 @@
}
catch (Exception ex)
{
- System.err.println("ERROR: cannot add configuration
conf/portal/configuration.xml. ServletContext: "
- + context);
- ex.printStackTrace();
+ log.error("Cannot add configuration conf/portal/configuration.xml.
ServletContext: " + context, ex);
}
// Add configuration that depends on the environment
@@ -163,27 +288,29 @@
}
catch (Exception ex)
{
- System.err.println("ERROR: cannot add configuration " + uri +
". ServletContext: " + context);
- ex.printStackTrace();
+ log.error("Cannot add configuration " + uri + ".
ServletContext: " + context, ex);
}
// add configs from web apps
- try
+ Set<WebAppInitContext> contexts = pcontainer.getWebAppInitContexts();
+ for (WebAppInitContext webappctx : contexts)
{
- cService.addConfiguration("war:/conf/configuration.xml");
+ ServletContext ctx = webappctx.getServletContext();
+ try
+ {
+ cService.addConfiguration(ctx, "war:/conf/configuration.xml");
+ }
+ catch (Exception ex)
+ {
+ log.error("Cannot add configuration war:/conf/configuration.xml.
ServletContext: " + ctx, ex);
+ }
}
- catch (Exception ex)
- {
- System.err.println("ERROR: cannot add configuration
war:/conf/configuration.xml. ServletContext: "
- + context);
- ex.printStackTrace();
- }
// add config from application server,
// $AH_HOME/exo-conf/portal/configuration.xml
String overrideConfig =
- singleton_.getServerEnvironment().getExoConfigurationDirectory() +
"/portal/"
- + pcontainer.getPortalContainerInfo().getContainerName() +
"/configuration.xml";
+ singleton_.getServerEnvironment().getExoConfigurationDirectory() +
"/portal/" + portalContainerName
+ + "/configuration.xml";
try
{
File file = new File(overrideConfig);
@@ -192,30 +319,43 @@
}
catch (Exception ex)
{
- System.err.println("ERROR: cannot add configuration " +
overrideConfig + ". ServletContext: " + context);
- ex.printStackTrace();
+ log.error("Cannot add configuration " + overrideConfig + ".
ServletContext: " + context, ex);
}
cService.processRemoveConfiguration();
ComponentAdapter adapter =
pcontainer.registerComponentInstance(ConfigurationManager.class, cService);
pcontainer.initContainer();
- registerComponentInstance(context.getServletContextName(), pcontainer);
- PortalContainer.setInstance(pcontainer);
- ExoContainerContext.setCurrentContainer(pcontainer);
+ registerComponentInstance(portalContainerName, pcontainer);
pcontainer.start();
// Register the portal as an mbean
managementContext.register(pcontainer);
//
- return pcontainer;
+ executeInitTasks(pcontainer, PortalContainerPostInitTask.TYPE);
+ executeInitTasks(pcontainer, PortalContainerPostCreateTask.TYPE);
+ log.info("The portal container '" + portalContainerName +
"' has been created successfully");
}
catch (Exception ex)
{
- System.err.println("ERROR: cannot create portal container. ServletContext:
" + context);
- ex.printStackTrace();
+ log.error("Cannot create the portal container '" +
portalContainerName + "' . ServletContext: " + context, ex);
}
- return null;
+ finally
+ {
+ if (hasChanged)
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ try
+ {
+ PortalContainer.setInstance(null);
+ }
+ catch (Exception e)
+ {
+ log.warn("An error occured while cleaning the ThreadLocal", e);
+ }
+ }
}
synchronized public void removePortalContainer(ServletContext servletContext)
@@ -316,6 +456,147 @@
singleton_ = rcontainer;
}
+ /**
+ * Calls the other method <code>addInitTask</code> with
<code>ServletContext.getServletContextName()</code>
+ * as portal container name
+ *
+ * @param context the servlet context from which the task comes from
+ * @param task the task to add
+ */
+ public void addInitTask(ServletContext context, PortalContainerInitTask task)
+ {
+ addInitTask(context, task, context.getServletContextName());
+ }
+
+ /**
+ * First check if the related portal container has already been initialized. If so
+ * it will call the method onAlreadyExists on the given task otherwise the task will
+ * be added to the task list to execute during the related portal container
initialization
+ *
+ * @param context the servlet context from which the task comes from
+ * @param task the task to add
+ * @param portalContainer the name of the portal container on which the task must be
executed
+ */
+ public void addInitTask(ServletContext context, PortalContainerInitTask task, String
portalContainer)
+ {
+ final PortalContainer container = getPortalContainer(portalContainer);
+ if (!task.alreadyExists(container))
+ {
+ if (log.isDebugEnabled())
+ log.debug("The portal container '" + portalContainer
+ + "' has not yet been initialized, thus the task can be
added");
+ ConcurrentMap<String, Queue<PortalContainerInitTaskContext>> queues
= initTasks.get(portalContainer);
+ if (queues == null)
+ {
+ queues = new ConcurrentHashMap<String,
Queue<PortalContainerInitTaskContext>>();
+ final ConcurrentMap<String,
Queue<PortalContainerInitTaskContext>> q =
+ initTasks.putIfAbsent(portalContainer, queues);
+ if (q != null)
+ {
+ queues = q;
+ }
+ }
+ final String type = task.getType();
+ Queue<PortalContainerInitTaskContext> queue = queues.get(type);
+ if (queue == null)
+ {
+ final List<String> dependencies =
getPortalContainerConfig().getDependencies(portalContainer);
+ if (dependencies == null || dependencies.isEmpty())
+ {
+ // No order is required
+ queue = new
ConcurrentLinkedQueue<PortalContainerInitTaskContext>();
+ }
+ else
+ {
+ queue =
+ new PriorityBlockingQueue<PortalContainerInitTaskContext>(10,
+ new PortalContainerInitTaskContextComparator(dependencies));
+ }
+ final Queue<PortalContainerInitTaskContext> q =
queues.putIfAbsent(type, queue);
+ if (q != null)
+ {
+ queue = q;
+ }
+ }
+ queue.add(new PortalContainerInitTaskContext(context, task));
+ }
+ else
+ {
+ if (log.isDebugEnabled())
+ log.debug("The portal container '" + portalContainer
+ + "' has already been initialized, thus we call
onAlreadyExists");
+ PortalContainer oldPortalContainer = PortalContainer.getInstanceIfPresent();
+ try
+ {
+ PortalContainer.setInstance(container);
+ task.onAlreadyExists(context, container);
+ }
+ finally
+ {
+ PortalContainer.setInstance(oldPortalContainer);
+ }
+ }
+ }
+
+ /**
+ * Executes all the tasks of the given type related to the given portal container
+ * @param portalContainer the portal container on which we want to execute the tasks
+ * @param type the type of the task to execute
+ */
+ private void executeInitTasks(PortalContainer portalContainer, String type)
+ {
+ final String portalContainerName = portalContainer.getName();
+ final ConcurrentMap<String, Queue<PortalContainerInitTaskContext>>
queues = initTasks.get(portalContainerName);
+ if (queues == null)
+ {
+ return;
+ }
+ final Queue<PortalContainerInitTaskContext> queue = queues.get(type);
+ if (queue == null)
+ {
+ return;
+ }
+ if (log.isDebugEnabled())
+ log.debug("Start launching the " + type + " tasks of the portal
container '" + portalContainer + "'");
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ PortalContainerInitTaskContext context;
+ boolean hasChanged = false;
+ try
+ {
+ while ((context = queue.poll()) != null)
+ {
+ if (context.getServletContextName().equals(portalContainer.getName()))
+ {
+ context.getTask().execute(portalContainer.getPortalContext(),
portalContainer);
+ }
+ else
+ {
+ // The current servlet context is not the context of the portal
+ // Set the context classloader of the related web application
+
Thread.currentThread().setContextClassLoader(context.getWebappClassLoader());
+ hasChanged = true;
+ context.getTask().execute(context.getServletContext(), portalContainer);
+ }
+ }
+ }
+ finally
+ {
+ if (hasChanged)
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ }
+ queues.remove(type);
+ if (queues.isEmpty())
+ {
+ initTasks.remove(portalContainerName);
+ }
+ if (log.isDebugEnabled())
+ log.debug("End launching the " + type + " tasks of the portal
container '" + portalContainer + "'");
+ }
+
static class ShutdownThread extends Thread
{
RootContainer container_;
@@ -336,4 +617,222 @@
super.stop();
ExoContainerContext.setTopContainer(null);
}
+
+ /**
+ * This interface is used to define a task that needs to be launched at a given state
during the
+ * initialization of a portal container
+ */
+ public static interface PortalContainerInitTask
+ {
+
+ /**
+ * This method allows the implementation to define what the state "already
exists"
+ * means for a portal container
+ *
+ * @param portalContainer the value of the current portal container
+ * @return <code>true</code> if the portal container exists according
to the task
+ * requirements, <code>false</code> otherwise
+ */
+ public boolean alreadyExists(PortalContainer portalContainer);
+
+ /**
+ * This method is called if the related portal container has already been
registered
+ *
+ * @param context the servlet context of the web application
+ * @param portalContainer the value of the current portal container
+ */
+ public void onAlreadyExists(ServletContext context, PortalContainer
portalContainer);
+
+ /**
+ * Executes the task
+ *
+ * @param context the servlet context of the web application
+ * @param container The portal container on which we would like to execute the
task
+ */
+ public void execute(ServletContext context, PortalContainer portalContainer);
+
+ /**
+ * @return the type of the task
+ */
+ public String getType();
+ }
+
+ /**
+ * This class is used to define a task that needs to be launched after the
initialization of a
+ * portal container
+ */
+ public static abstract class PortalContainerPostInitTask implements
PortalContainerInitTask
+ {
+
+ /**
+ * The name of the type of task
+ */
+ public static final String TYPE = "post-init";
+
+ /**
+ * {@inheritDoc}
+ */
+ public final boolean alreadyExists(PortalContainer portalContainer)
+ {
+ return portalContainer != null && portalContainer.isStarted();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void onAlreadyExists(ServletContext context, PortalContainer
portalContainer)
+ {
+ execute(context, portalContainer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getType()
+ {
+ return TYPE;
+ }
+ }
+
+ /**
+ * This class is used to define a task that needs to be launched before the
initialization of a
+ * portal container
+ */
+ public static abstract class PortalContainerPreInitTask implements
PortalContainerInitTask
+ {
+
+ /**
+ * The name of the type of task
+ */
+ public static final String TYPE = "pre-init";
+
+ /**
+ * {@inheritDoc}
+ */
+ public final boolean alreadyExists(PortalContainer portalContainer)
+ {
+ return portalContainer != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void onAlreadyExists(ServletContext context, PortalContainer
portalContainer)
+ {
+ throw new IllegalStateException("No pre init tasks can be added to the
portal container '"
+ + portalContainer.getName() + "', because it has already been "
+ "initialized. Check the webapp '"
+ + context.getServletContextName() + "'");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getType()
+ {
+ return TYPE;
+ }
+ }
+
+ /**
+ * This class is used to define a task that needs to be launched after creating a
portal container
+ * Those type of tasks must be launched after all the "post-init" tasks.
+ */
+ public static abstract class PortalContainerPostCreateTask implements
PortalContainerInitTask
+ {
+
+ /**
+ * The name of the type of task
+ */
+ public static final String TYPE = "post-create";
+
+ /**
+ * {@inheritDoc}
+ */
+ public final boolean alreadyExists(PortalContainer portalContainer)
+ {
+ return portalContainer != null && portalContainer.isStarted();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void onAlreadyExists(ServletContext context, PortalContainer
portalContainer)
+ {
+ execute(context, portalContainer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getType()
+ {
+ return TYPE;
+ }
+ }
+
+ /**
+ * This class is used to defined the context of the embedded {@link
PortalContainerInitTask}
+ */
+ static class PortalContainerInitTaskContext extends WebAppInitContext
+ {
+
+ /**
+ * The task to execute
+ */
+ private final PortalContainerInitTask task;
+
+ PortalContainerInitTaskContext(ServletContext context, PortalContainerInitTask
task)
+ {
+ super(context);
+ this.task = task;
+ }
+
+ public PortalContainerInitTask getTask()
+ {
+ return task;
+ }
+ }
+
+ /**
+ * This class is used to compare the {@link PortalContainerInitTaskContext}
+ */
+ static class PortalContainerInitTaskContextComparator implements
Comparator<PortalContainerInitTaskContext>
+ {
+
+ private final List<String> dependencies;
+
+ PortalContainerInitTaskContextComparator(List<String> dependencies)
+ {
+ this.dependencies = dependencies;
+ }
+
+ /**
+ * This will sort all the {@link PortalContainerInitTaskContext} such that we will
first have
+ * all the web applications defined in the list of dependencies of the
+ * related portal container (see {@link PortalContainerConfig} for more details
+ * about the dependencies) ordered in the same order as the dependencies, then
+ * we will have all the web applications undefined ordered by context name
+ */
+ public int compare(PortalContainerInitTaskContext ctx1,
PortalContainerInitTaskContext ctx2)
+ {
+ int idx1 = dependencies.indexOf(ctx1.getServletContextName());
+ int idx2 = dependencies.indexOf(ctx2.getServletContextName());
+ if (idx1 == -1 && idx2 != -1)
+ {
+ return 1;
+ }
+ else if (idx1 != -1 && idx2 == -1)
+ {
+ return -1;
+ }
+ else if (idx1 == -1 && idx2 == -1)
+ {
+ return ctx1.getServletContextName().compareTo(ctx2.getServletContextName());
+ }
+ else
+ {
+ return idx1 - idx2;
+ }
+ }
+ }
}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/UnifiedClassLoader.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/UnifiedClassLoader.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/UnifiedClassLoader.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * This class is used to merge different {@link ClassLoader} to create one single one.
+ * This ClassLoader is used only for the resources the class will be loaded from the
+ * ContextClassLoader. For each resources, it will always consider that the {@link
ClassLoader}
+ * with the highest priority has always right, in other words for example in the method
+ * getResource, it will try to get the resource in the {@link ClassLoader} of the
highest
+ * priority, if it cans not find it, it will try the {@link ClassLoader} with the second
highest
+ * priority and so on. The priority of the {@link ClassLoader} is the order given in the
+ * constructor, the last {@link ClassLoader} is the one with the highest priority.
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 18 sept. 2009
+ */
+class UnifiedClassLoader extends ClassLoader
+{
+
+ /**
+ * The list of all the {@link ClassLoader} to merge together
+ */
+ private final ClassLoader[] cls;
+
+ /**
+ * @param cls the list of all the {@link ClassLoader} to merge ordered by priority.
The last
+ * {@link ClassLoader} has highest priority.
+ */
+ UnifiedClassLoader(ClassLoader... cls)
+ {
+ super(Thread.currentThread().getContextClassLoader());
+ if (cls == null || cls.length == 0)
+ {
+ throw new IllegalArgumentException("The array of ClassLoader cannot be
empty");
+ }
+ this.cls = cls;
+ }
+
+ /**
+ * Allows to override the list of {@link ClassLoader} if it is a dynamic list
+ * @return the list of the {@link ClassLoader} to merge
+ */
+ protected ClassLoader[] getClassLoaders()
+ {
+ return cls;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getResource(String name)
+ {
+ final ClassLoader[] cls = getClassLoaders();
+ for (int i = cls.length - 1; i >= 0; i--)
+ {
+ final ClassLoader cl = cls[i];
+ URL url = cl.getResource(name);
+ if (url != null)
+ {
+ return url;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException
+ {
+ final ClassLoader[] cls = getClassLoaders();
+ final Set<URL> urls = new LinkedHashSet<URL>();
+ for (int i = 0, length = cls.length; i < length; i++)
+ {
+ Enumeration<URL> eUrls = cls[i].getResources(name);
+ if (eUrls != null && eUrls.hasMoreElements())
+ {
+ // Prevent duplicates
+ urls.addAll(Collections.list(eUrls));
+ }
+ }
+ return Collections.enumeration(urls);
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/WebAppInitContext.java
===================================================================
--- kernel/trunk/container/src/main/java/org/exoplatform/container/WebAppInitContext.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/WebAppInitContext.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import javax.servlet.ServletContext;
+
+/**
+ * This class is used to define the initialization context of a web application
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 11 sept. 2009
+ */
+public class WebAppInitContext
+{
+
+ /**
+ * The servlet context of the web application
+ */
+ private final ServletContext servletContext;
+
+ /**
+ * The class loader of the web application;
+ */
+ private final ClassLoader webappClassLoader;
+
+ public WebAppInitContext(ServletContext servletContext)
+ {
+ this.servletContext = servletContext;
+ this.webappClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ public ServletContext getServletContext()
+ {
+ return servletContext;
+ }
+
+ public String getServletContextName()
+ {
+ return servletContext.getServletContextName();
+ }
+
+ public ClassLoader getWebappClassLoader()
+ {
+ return webappClassLoader;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o instanceof WebAppInitContext)
+ {
+ return
getServletContextName().equals(((WebAppInitContext)o).getServletContextName());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return getServletContextName().hashCode();
+ }
+}
Modified:
kernel/trunk/container/src/main/java/org/exoplatform/container/configuration/ConfigurationManagerImpl.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/configuration/ConfigurationManagerImpl.java 2009-10-04
19:39:27 UTC (rev 206)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/configuration/ConfigurationManagerImpl.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -18,8 +18,14 @@
*/
package org.exoplatform.container.configuration;
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer;
import org.exoplatform.container.xml.Component;
import org.exoplatform.container.xml.Configuration;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
import java.io.File;
import java.io.IOException;
@@ -47,6 +53,10 @@
final static public boolean LOG_DEBUG = System.getProperty(LOG_DEBUG_PROPERTY) !=
null;
+ private static final String EXO_CONTAINER_PROP_NAME =
"container.name.suffix";
+
+ private static final Log log = ExoLogger.getLogger(ConfigurationManagerImpl.class);
+
protected Configuration configurations_;
private ServletContext scontext_;
@@ -88,6 +98,13 @@
return configurations_;
}
+ public void addConfiguration(ServletContext context, String url) throws Exception
+ {
+ if (url == null)
+ return;
+ addConfiguration(context, getURL(context, url));
+ }
+
public void addConfiguration(String url) throws Exception
{
if (url == null)
@@ -107,9 +124,13 @@
public void addConfiguration(URL url) throws Exception
{
+ addConfiguration(scontext_, url);
+ }
+ private void addConfiguration(ServletContext context, URL url) throws Exception
+ {
if (LOG_DEBUG)
- System.out.println("Add configuration " + url);
+ log.info("Add configuration " + url);
if (url == null)
return;
try
@@ -154,20 +175,18 @@
conf = unmarshaller.unmarshall(urlObject);
configurations_.mergeConfiguration(conf);
if (LOG_DEBUG)
- System.out.println("\timport " + urlObject);
+ log.info("\timport " + urlObject);
}
else
{
- System.err.println("WARNING: Couldn't process the URL for
" + uri + " configuration file ignored ");
+ log.warn("Couldn't process the URL for " + uri + "
configuration file ignored ");
}
}
}
}
catch (Exception ex)
{
- // System .err.println("Error: " + ex.getMessage());
- System.err.println("ERROR: cannot process the configuration " + url);
- ex.printStackTrace();
+ log.error("Cannot process the configuration " + url, ex);
}
finally
{
@@ -247,6 +266,11 @@
public URL getURL(String url) throws Exception
{
+ return getURL(scontext_, url);
+ }
+
+ private URL getURL(ServletContext context, String url) throws Exception
+ {
if (url.startsWith("jar:"))
{
String path = removePrefix("jar:/", url);
@@ -262,9 +286,9 @@
else if (url.startsWith("war:"))
{
String path = removePrefix("war:", url);
- if (scontext_ != null)
+ if (context != null)
{
- return scontext_.getResource(WAR_CONF_LOCATION + path);
+ return context.getResource(WAR_CONF_LOCATION + path);
}
if (scontextClassLoader_ != null)
{
@@ -324,7 +348,21 @@
{
String value = null;
String key = input.substring(start + 2, i);
- value = System.getProperty(key);
+ if (key.equals(EXO_CONTAINER_PROP_NAME))
+ {
+ // The requested key is the name of current container
+ ExoContainer container =
ExoContainerContext.getCurrentContainerIfPresent();
+ if (container instanceof PortalContainer)
+ {
+ // The current container is a portal container
+ RootContainer rootContainer =
(RootContainer)ExoContainerContext.getTopContainer();
+ value = rootContainer.isPortalContainerConfigAware() ? "_"
+ container.getContext().getName() : "";
+ }
+ }
+ else
+ {
+ value = System.getProperty(key);
+ }
if (value != null)
{
properties = true;
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerConfig.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerConfig.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerConfig.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.definition;
+
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer;
+import org.exoplatform.container.xml.InitParams;
+import org.exoplatform.container.xml.ValueParam;
+import org.picocontainer.Startable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+/**
+ * This class is used to define the configuration related to the portal containers
themselves.
+ * It is mainly dedicated to the {@link RootContainer} to allows to understand how to
manage and
+ * deploy all the portal containers
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 26 ao�t 2009
+ */
+public class PortalContainerConfig implements Startable
+{
+
+ /**
+ * The default name of a portal container
+ */
+ public static final String DEFAULT_PORTAL_CONTAINER_NAME = "portal";
+
+ /**
+ * The default name of a the {@link ServletContext} of the rest web application
+ */
+ public static final String DEFAULT_REST_CONTEXT_NAME = "rest";
+
+ /**
+ * The default realm name
+ */
+ public static final String DEFAULT_REALM_NAME = "exo-domain";
+
+ /**
+ * The name of the default portal container
+ */
+ private String defaultPortalContainerName;
+
+ /**
+ * The name of the default rest {@link ServletContext}
+ */
+ private String defaultRestContextName;
+
+ /**
+ * The name of the default realm
+ */
+ private String defaultRealmName;
+
+ /**
+ * Indicates if the component has already been initialized
+ */
+ private volatile boolean initialized;
+
+ /**
+ * The list of all the portal containers
+ */
+ private List<String> portalContainerNames;
+
+ /**
+ * The list of all the web application scopes
+ */
+ private Map<String, List<String>> scopes;
+
+ /**
+ * The list of all the {@link PortalContainerDefinition} that have been registered
+ */
+ private Map<String, PortalContainerDefinition> definitions =
+ Collections.unmodifiableMap(new HashMap<String,
PortalContainerDefinition>());
+
+ public PortalContainerConfig()
+ {
+ this(null);
+ }
+
+ public PortalContainerConfig(InitParams params)
+ {
+ if (params == null)
+ {
+ return;
+ }
+ final ValueParam vDpc =
params.getValueParam("default.portal.container");
+ if (vDpc != null && vDpc.getValue().trim().length() > 0)
+ {
+ this.defaultPortalContainerName = vDpc.getValue().trim();
+ }
+ final ValueParam vRc = params.getValueParam("default.rest.context");
+ if (vRc != null && vRc.getValue().trim().length() > 0)
+ {
+ this.defaultRestContextName = vRc.getValue().trim();
+ }
+ final ValueParam vRn = params.getValueParam("default.realm.name");
+ if (vRn != null && vRn.getValue().trim().length() > 0)
+ {
+ this.defaultRealmName = vRn.getValue().trim();
+ }
+ }
+
+ /**
+ * @return the default name of the portal container
+ */
+ public String getDefaultPortalContainer()
+ {
+ return defaultPortalContainerName;
+ }
+
+ /**
+ * @return the default name of the rest {@link ServletContext}
+ */
+ public String getDefaultRestContext()
+ {
+ return defaultRestContextName;
+ }
+
+ /**
+ * @return the default name of the realm
+ */
+ public String getDefaultRealmName()
+ {
+ return defaultRealmName;
+ }
+
+ /**
+ * Indicates if at least one portal container definition has been defined. If no
portal definition
+ * has been defined, we assume that we want the old behavior
+ * @return <code>true</code> if at least one definition has been set,
<code>false</code> otherwise
+ */
+ public boolean hasDefinition()
+ {
+ return !definitions.isEmpty();
+ }
+
+ /**
+ * Registers a name of a portal container if it has not yet been registered
+ * @param name the name of the portal container to register
+ */
+ public synchronized void registerPortalContainerName(String name)
+ {
+ if (!portalContainerNames.contains(name))
+ {
+ final List<String> lPortalContainerNames = new
ArrayList<String>(portalContainerNames.size() + 1);
+ lPortalContainerNames.add(name);
+ lPortalContainerNames.addAll(portalContainerNames);
+ this.portalContainerNames =
Collections.unmodifiableList(lPortalContainerNames);
+ }
+ }
+
+ /**
+ * Unregisters a name of a portal container if it has not yet been unregistered
+ * @param name the name of the portal container to register
+ */
+ public synchronized void unregisterPortalContainerName(String name)
+ {
+ if (portalContainerNames.contains(name))
+ {
+ final List<String> lPortalContainerNames = new
ArrayList<String>(portalContainerNames);
+ lPortalContainerNames.remove(name);
+ this.portalContainerNames =
Collections.unmodifiableList(lPortalContainerNames);
+ }
+ }
+
+ /**
+ * Indicates if the given name is the name of a registered portal container
+ * @param name the name to check
+ * @return <code>true</code> if the name is a name of a portal container,
<code>false</code>
+ * otherwise.
+ */
+ public boolean isPortalContainerName(String name)
+ {
+ return name == null ? false : portalContainerNames.contains(name);
+ }
+
+ /**
+ * Gives the list of all the portal container names for which the web application is
available
+ * @param contextName the context name of the web application
+ * @return the list of all the portal container names for which the web application is
available
+ */
+ public List<String> getPortalContainerNames(String contextName)
+ {
+ if (contextName == null)
+ {
+ throw new IllegalArgumentException("The context name cannot be
null");
+ }
+ final List<String> result = scopes.get(contextName);
+ if (result == null || result.isEmpty())
+ {
+ // we assume the old behavior is expected
+ final String portalContainerName =
+ portalContainerNames.contains(contextName) ? contextName :
defaultPortalContainerName;
+ return Collections.singletonList(portalContainerName);
+ }
+ return result;
+ }
+
+ /**
+ * Gives all the dependencies related to the given portal container
+ * @param portalContainerName the name of the portal container for which we want the
dependencies
+ * @return a list of sorted context names
+ */
+ public List<String> getDependencies(String portalContainerName)
+ {
+ final PortalContainerDefinition definition = definitions.get(portalContainerName);
+ return definition == null ? null : definition.getDependencies();
+ }
+
+ /**
+ * Gives the name of the rest {@link ServletContext} related to the given portal
container
+ * @param portalContainerName the name of the portal container for which we want the
rest context name
+ * @return the name of the related rest context name. It tries to get it from the
{@link PortalContainerDefinition}
+ * if it has not been set it will return
<code>defaultRestContextName</code>
+ */
+ public String getRestContextName(String portalContainerName)
+ {
+ final PortalContainerDefinition definition = definitions.get(portalContainerName);
+ if (definition == null)
+ {
+ return defaultRestContextName;
+ }
+ else
+ {
+ String contextName = definition.getRestContextName();
+ return contextName == null ? defaultRestContextName : contextName;
+ }
+ }
+
+ /**
+ * Gives the name of the realm related to the given portal container
+ * @param portalContainerName the name of the portal container for which we want the
realm name
+ * @return the name of the related realm name. It tries to get it from the {@link
PortalContainerDefinition}
+ * if it has not been set it will return <code>defaultRealmName</code>
+ */
+ public String getRealmName(String portalContainerName)
+ {
+ final PortalContainerDefinition definition = definitions.get(portalContainerName);
+ if (definition == null)
+ {
+ return defaultRealmName;
+ }
+ else
+ {
+ String realmName = definition.getRealmName();
+ return realmName == null ? defaultRealmName : realmName;
+ }
+ }
+
+ /**
+ * Indicates if the given servlet context is a dependency of the given portal
container
+ * @param portalContainerName the name of the portal container
+ * @param contextName the name of the {@link ServletContext}
+ * @return <code>true</code> if the dependencies matches,
<code>false</code> otherwise;
+ */
+ public boolean isScopeValid(String portalContainerName, String contextName)
+ {
+ final List<String> result = scopes.get(contextName);
+ if (result == null || result.isEmpty())
+ {
+ // we assume the old behavior is expected
+ return true;
+ }
+ else
+ {
+ return result.contains(portalContainerName);
+ }
+ }
+
+ /**
+ * Allow to define a set of {@link PortalContainerDefinition}
+ * @param plugin the plugin that contains all the {@link PortalContainerDefinition} to
define
+ */
+ public void registerPlugin(PortalContainerDefinitionPlugin plugin)
+ {
+ final List<PortalContainerDefinition> lDefs =
plugin.getPortalContainerDefinitions();
+ if (lDefs != null && !lDefs.isEmpty())
+ {
+ synchronized (this)
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("The PortalContainerConfig has
already been initialized");
+ }
+ final Map<String, PortalContainerDefinition> tempDefinitions =
+ new HashMap<String, PortalContainerDefinition>(definitions);
+ for (PortalContainerDefinition def : lDefs)
+ {
+ String name = def.getName();
+ if (name == null || (name = name.trim()).length() == 0)
+ {
+ continue;
+ }
+ else
+ {
+ // Ensure that the name doesn't contain any useless characters
+ def.setName(name);
+ }
+ tempDefinitions.put(name, def);
+ }
+ this.definitions = Collections.unmodifiableMap(tempDefinitions);
+ }
+ }
+ }
+
+ /**
+ * Construct the scopes of all the web applications from the given {@link
PortalContainerDefinition}
+ * @param definition the definition of a {@link PortalContainer} that contains the
dependencies with
+ * the web application
+ * @param scopes the map in which the scope must be defined
+ */
+ private void registerDependencies(PortalContainerDefinition definition, Map<String,
List<String>> scopes)
+ {
+ final List<String> dependencies = definition.getDependencies();
+ if (definition == null || dependencies.isEmpty())
+ {
+ return;
+ }
+ for (String context : dependencies)
+ {
+ if (context == null || (context = context.trim()).length() == 0)
+ {
+ continue;
+ }
+ List<String> lPortalContainerNames = scopes.get(context);
+ if (lPortalContainerNames == null)
+ {
+ // There is no
+ lPortalContainerNames = new ArrayList<String>();
+ }
+ else
+ {
+ // The existing collection is unmodifiable thus we need to create a new one
+ lPortalContainerNames = new ArrayList<String>(lPortalContainerNames);
+ }
+ lPortalContainerNames.add(definition.getName());
+ scopes.put(context, Collections.unmodifiableList(lPortalContainerNames));
+ }
+ }
+
+ /**
+ * Initialize the current component
+ * @param mDefinitions the list of all the portal container definition to treat
+ */
+ private void initialize(Map<String, PortalContainerDefinition> mDefinitions)
+ {
+ if (mDefinitions.isEmpty())
+ {
+ // No definitions have been found, the default values will be set
+ if (defaultPortalContainerName == null)
+ {
+ this.defaultPortalContainerName = DEFAULT_PORTAL_CONTAINER_NAME;
+ }
+ }
+ final List<String> lPortalContainerNames = new
ArrayList<String>(mDefinitions.size() + 1);
+ // Add the default portal container name
+ if (defaultPortalContainerName != null)
+ {
+ lPortalContainerNames.add(defaultPortalContainerName);
+ }
+ final Map<String, List<String>> mScopes = new HashMap<String,
List<String>>();
+ for (Map.Entry<String, PortalContainerDefinition> entry :
mDefinitions.entrySet())
+ {
+ PortalContainerDefinition definition = entry.getValue();
+ String name = definition.getName();
+ if (!name.equals(defaultPortalContainerName))
+ {
+ if (defaultPortalContainerName == null)
+ {
+ this.defaultPortalContainerName = name;
+ }
+ lPortalContainerNames.add(name);
+ }
+ if (defaultRestContextName == null)
+ {
+ this.defaultRestContextName = definition.getRestContextName();
+ }
+ if (defaultRealmName == null)
+ {
+ this.defaultRealmName = definition.getRealmName();
+ }
+ registerDependencies(definition, mScopes);
+ }
+ this.portalContainerNames = Collections.unmodifiableList(lPortalContainerNames);
+ this.scopes = Collections.unmodifiableMap(mScopes);
+ if (defaultRestContextName == null)
+ {
+ this.defaultRestContextName = DEFAULT_REST_CONTEXT_NAME;
+ }
+ if (defaultRealmName == null)
+ {
+ this.defaultRealmName = DEFAULT_REALM_NAME;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start()
+ {
+ if (!initialized)
+ {
+ synchronized (this)
+ {
+ if (!initialized)
+ {
+ // Prevent to add new definitions after initializing the
PortalContainerConfig
+ this.initialized = true;
+ }
+ else
+ {
+ // The PortalContainerConfig has already been initialized
+ return;
+ }
+ }
+ }
+ else
+ {
+ // The PortalContainerConfig has already been initialized
+ return;
+ }
+ initialize(definitions);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop()
+ {
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinition.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinition.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinition.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.definition;
+
+import org.exoplatform.container.PortalContainer;
+
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+/**
+ * This class is used to define a {@link PortalContainer} and its dependencies. The
dependencies
+ * are in fact all the web applications that the {@link PortalContainer} needs to be
properly
+ * initialized. Be aware that the dependency order is used to define the initialization
order.
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 8 sept. 2009
+ */
+public class PortalContainerDefinition
+{
+
+ /**
+ * The name of the related {@link PortalContainer}
+ */
+ private String name;
+
+ /**
+ * The realm name of the related {@link PortalContainer}
+ */
+ private String realmName;
+
+ /**
+ * The name of the {@link ServletContext} of the rest web application
+ */
+ private String restContextName;
+
+ /**
+ * The list of all the context names that are needed to initialized properly the
+ * {@link PortalContainer}. The order of all the dependencies will define the
initialization order
+ */
+ private List<String> dependencies;
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public List<String> getDependencies()
+ {
+ return dependencies;
+ }
+
+ public void setDependencies(List<String> dependencies)
+ {
+ this.dependencies = dependencies;
+ }
+
+ public String getRealmName()
+ {
+ return realmName;
+ }
+
+ public void setRealmName(String realmName)
+ {
+ this.realmName = realmName;
+ }
+
+ public String getRestContextName()
+ {
+ return restContextName;
+ }
+
+ public void setRestContextName(String restContextName)
+ {
+ this.restContextName = restContextName;
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinitionPlugin.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinitionPlugin.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/definition/PortalContainerDefinitionPlugin.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.definition;
+
+import org.exoplatform.container.component.BaseComponentPlugin;
+import org.exoplatform.container.xml.InitParams;
+
+import java.util.List;
+
+/**
+ * This class allows you to dynamically define a new portal container with all its
dependencies
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 8 sept. 2009
+ */
+public class PortalContainerDefinitionPlugin extends BaseComponentPlugin
+{
+
+ /**
+ * The initial parameter of this plugin
+ */
+ private final InitParams params;
+
+ public PortalContainerDefinitionPlugin(InitParams params)
+ {
+ this.params = params;
+ }
+
+ /**
+ * @return all the {@link PortalContainerDefinition} related to this plugin
+ */
+ public List<PortalContainerDefinition> getPortalContainerDefinitions()
+ {
+ return params.getObjectParamValues(PortalContainerDefinition.class);
+ }
+}
Modified:
kernel/trunk/container/src/main/java/org/exoplatform/container/util/ContainerUtil.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/util/ContainerUtil.java 2009-10-04
19:39:27 UTC (rev 206)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/util/ContainerUtil.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -74,7 +74,9 @@
// deploy them to a temp dir and include both jars, the one in sar and tmp
// dir,
// in the class path. It cause the configuration run twice
- int index = key.lastIndexOf("exo-");
+ int index1 = key.lastIndexOf("exo-");
+ int index2 = key.lastIndexOf("exo.");
+ int index = index1 < index2 ? index2 : index1;
if (index >= 0)
key = key.substring(index);
map.put(key, url);
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractFilter.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractFilter.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractFilter.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.web;
+
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * This class is the root class of all the Filters that require an ExoContainer
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 21 ao�t 2009
+ */
+public abstract class AbstractFilter implements Filter
+{
+
+ /**
+ * The filter configuration
+ */
+ protected FilterConfig config;
+
+ /**
+ * The Servlet context name
+ */
+ protected String servletContextName;
+
+ /**
+ * Indicates if we need a portal environment.
+ */
+ private volatile Boolean requirePortalEnvironment;
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void init(FilterConfig config) throws ServletException
+ {
+ this.config = config;
+ this.servletContextName = config.getServletContext().getServletContextName();
+ afterInit(config);
+ }
+
+ /**
+ * Allows sub-classes to initialize
+ * @param config the current filter configuration
+ */
+ protected void afterInit(FilterConfig config) throws ServletException
+ {
+ }
+
+ /**
+ * @return Gives the {@link ExoContainer} that fits best with the current context
+ */
+ protected final ExoContainer getContainer()
+ {
+ ExoContainer container = ExoContainerContext.getCurrentContainer();
+ if (container instanceof RootContainer)
+ {
+ // The top container is a RootContainer, thus we assume that we are in a portal
mode
+ container = PortalContainer.getCurrentInstance(config.getServletContext());
+ if (container == null)
+ {
+ container = ExoContainerContext.getTopContainer();
+ }
+ }
+ // The container is a PortalContainer or a StandaloneContainer
+ return container;
+ }
+
+ /**
+ * Indicates if it requires that a full portal environment must be set
+ * @return <code>true</code> if it requires the portal environment
<code>false</code> otherwise.
+ */
+ protected boolean requirePortalEnvironment()
+ {
+ if (requirePortalEnvironment == null)
+ {
+ synchronized (this)
+ {
+ if (requirePortalEnvironment == null)
+ {
+ this.requirePortalEnvironment =
PortalContainer.isPortalContainerName(servletContextName);
+ }
+ }
+ }
+ return requirePortalEnvironment.booleanValue();
+ }
+
+ /**
+ * @return the current {@link ServletContext}
+ */
+ protected ServletContext getServletContext()
+ {
+ if (requirePortalEnvironment())
+ {
+ ExoContainer container = getContainer();
+ if (container instanceof PortalContainer)
+ {
+ return ((PortalContainer)container).getPortalContext();
+ }
+ }
+ return config.getServletContext();
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpServlet.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpServlet.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpServlet.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.web;
+
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 29 sept. 2009
+ */
+public abstract class AbstractHttpServlet extends HttpServlet
+{
+
+ /**
+ * Serial Version ID.
+ */
+ private static final long serialVersionUID = -3302886470677004895L;
+
+ /**
+ * The filter configuration
+ */
+ protected ServletConfig config;
+
+ /**
+ * The Servlet context name
+ */
+ protected String servletContextName;
+
+ /**
+ * Indicates if we need a portal environment.
+ */
+ private volatile Boolean requirePortalEnvironment;
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+ this.config = config;
+ this.servletContextName = config.getServletContext().getServletContextName();
+ afterInit(config);
+ }
+
+ /**
+ * Allows sub-classes to initialize
+ * @param config the current servlet configuration
+ */
+ protected void afterInit(ServletConfig config) throws ServletException
+ {
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse)
+ */
+ public final void service(HttpServletRequest req, HttpServletResponse res) throws
ServletException, IOException
+ {
+ final ExoContainer oldContainer = ExoContainerContext.getCurrentContainer();
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ ExoContainer container = null;
+ boolean hasBeenSet = false;
+ try
+ {
+ container = getContainer();
+ if (!container.equals(oldContainer))
+ {
+ if (container instanceof PortalContainer)
+ {
+ PortalContainer.setInstance((PortalContainer)container);
+ }
+ ExoContainerContext.setCurrentContainer(container);
+ hasBeenSet = true;
+ }
+ if (requirePortalEnvironment() && container instanceof PortalContainer)
+ {
+ if (PortalContainer.getInstanceIfPresent() == null)
+ {
+ // The portal container has not been set
+ PortalContainer.setInstance((PortalContainer)container);
+ hasBeenSet = true;
+ }
+ // Set the full classloader of the portal container
+
Thread.currentThread().setContextClassLoader(((PortalContainer)container).getPortalClassLoader());
+ }
+ onService(container, req, res);
+ }
+ finally
+ {
+ if (hasBeenSet)
+ {
+ if (container instanceof PortalContainer)
+ {
+ // Remove the current Portal Container and the current ExoContainer
+ PortalContainer.setInstance(null);
+ }
+ // Re-set the old container
+ ExoContainerContext.setCurrentContainer(oldContainer);
+ }
+ if (requirePortalEnvironment())
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ }
+ }
+
+ /**
+ * Indicates if it requires that a full portal environment must be set
+ * @return <code>true</code> if it requires the portal environment
<code>false</code> otherwise.
+ */
+ protected boolean requirePortalEnvironment()
+ {
+ if (requirePortalEnvironment == null)
+ {
+ synchronized (this)
+ {
+ if (requirePortalEnvironment == null)
+ {
+ this.requirePortalEnvironment =
PortalContainer.isPortalContainerName(servletContextName);
+ }
+ }
+ }
+ return requirePortalEnvironment.booleanValue();
+ }
+
+ /**
+ * Allow the sub classes to execute a task when the method
<code>service</code> is called
+ * @param container the eXo container
+ * @param req the {@link HttpServletRequest}
+ * @param res the {@link HttpServletResponse}
+ */
+ protected void onService(ExoContainer container, HttpServletRequest req,
HttpServletResponse res)
+ throws ServletException, IOException
+ {
+ // Dispatches to the right HTTP method
+ super.service(req, res);
+ }
+
+ /**
+ * @return Gives the {@link ExoContainer} that fits best with the current context
+ */
+ protected final ExoContainer getContainer()
+ {
+ ExoContainer container = ExoContainerContext.getCurrentContainer();
+ if (container instanceof RootContainer)
+ {
+ // The top container is a RootContainer, thus we assume that we are in a portal
mode
+ container = PortalContainer.getCurrentInstance(config.getServletContext());
+ if (container == null)
+ {
+ container = ExoContainerContext.getTopContainer();
+ }
+ }
+ // The container is a PortalContainer or a StandaloneContainer
+ return container;
+ }
+
+ /**
+ * @return the current {@link ServletContext}
+ */
+ @Override
+ public ServletContext getServletContext()
+ {
+ if (requirePortalEnvironment())
+ {
+ ExoContainer container = getContainer();
+ if (container instanceof PortalContainer)
+ {
+ return ((PortalContainer)container).getPortalContext();
+ }
+ }
+ return super.getServletContext();
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpSessionListener.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpSessionListener.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/web/AbstractHttpSessionListener.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.web;
+
+import org.exoplatform.container.ExoContainer;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer;
+
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 29 sept. 2009
+ */
+public abstract class AbstractHttpSessionListener implements HttpSessionListener
+{
+
+ /**
+ * @see
javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)
+ */
+ public final void sessionCreated(HttpSessionEvent event)
+ {
+ final ExoContainer oldContainer = ExoContainerContext.getCurrentContainer();
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ ExoContainer container = null;
+ boolean hasBeenSet = false;
+ try
+ {
+ container = getContainer(event);
+ if (!container.equals(oldContainer))
+ {
+ if (container instanceof PortalContainer)
+ {
+ PortalContainer.setInstance((PortalContainer)container);
+ }
+ ExoContainerContext.setCurrentContainer(container);
+ hasBeenSet = true;
+ }
+ if (requirePortalEnvironment() && container instanceof PortalContainer)
+ {
+ if (PortalContainer.getInstanceIfPresent() == null)
+ {
+ // The portal container has not been set
+ PortalContainer.setInstance((PortalContainer)container);
+ hasBeenSet = true;
+ }
+ // Set the full classloader of the portal container
+
Thread.currentThread().setContextClassLoader(((PortalContainer)container).getPortalClassLoader());
+ }
+ onSessionCreated(container, event);
+ }
+ finally
+ {
+ if (hasBeenSet)
+ {
+ if (container instanceof PortalContainer)
+ {
+ // Remove the current Portal Container and the current ExoContainer
+ PortalContainer.setInstance(null);
+ }
+ // Re-set the old container
+ ExoContainerContext.setCurrentContainer(oldContainer);
+ }
+ if (requirePortalEnvironment())
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ }
+ }
+
+ /**
+ * @see
javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
+ */
+ public final void sessionDestroyed(HttpSessionEvent event)
+ {
+ final ExoContainer oldContainer = ExoContainerContext.getCurrentContainer();
+ // Keep the old ClassLoader
+ final ClassLoader currentClassLoader =
Thread.currentThread().getContextClassLoader();
+ ExoContainer container = null;
+ boolean hasBeenSet = false;
+ try
+ {
+ container = getContainer(event);
+ if (!container.equals(oldContainer))
+ {
+ if (container instanceof PortalContainer)
+ {
+ PortalContainer.setInstance((PortalContainer)container);
+ }
+ ExoContainerContext.setCurrentContainer(container);
+ hasBeenSet = true;
+ }
+ if (requirePortalEnvironment() && container instanceof PortalContainer)
+ {
+ if (PortalContainer.getInstanceIfPresent() == null)
+ {
+ // The portal container has not been set
+ PortalContainer.setInstance((PortalContainer)container);
+ hasBeenSet = true;
+ }
+ // Set the full classloader of the portal container
+
Thread.currentThread().setContextClassLoader(((PortalContainer)container).getPortalClassLoader());
+ }
+ onSessionDestroyed(container, event);
+ }
+ finally
+ {
+ if (hasBeenSet)
+ {
+ if (container instanceof PortalContainer)
+ {
+ // Remove the current Portal Container and the current ExoContainer
+ PortalContainer.setInstance(null);
+ }
+ // Re-set the old container
+ ExoContainerContext.setCurrentContainer(oldContainer);
+ }
+ if (requirePortalEnvironment())
+ {
+ // Re-set the old classloader
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ }
+ }
+ }
+
+ /**
+ * Indicates if it requires that a full portal environment must be set
+ * @return <code>true</code> if it requires the portal environment
<code>false</code> otherwise.
+ */
+ protected abstract boolean requirePortalEnvironment();
+
+ /**
+ * Allow sub-classes to execute an action when a session is created
+ * @param container the eXo container
+ * @param event the {@link HttpSessionEvent}
+ */
+ protected abstract void onSessionCreated(ExoContainer container, HttpSessionEvent
event);
+
+ /**
+ * Allow sub-classes to execute an action when a session is destroyed
+ * @param container the eXo container
+ * @param event the {@link HttpSessionEvent}
+ */
+ protected abstract void onSessionDestroyed(ExoContainer container, HttpSessionEvent
event);
+
+ /**
+ * @return Gives the {@link ExoContainer} that fits best with the current context
+ */
+ protected final ExoContainer getContainer(HttpSessionEvent event)
+ {
+ ExoContainer container = ExoContainerContext.getCurrentContainer();
+ if (container instanceof RootContainer)
+ {
+ // The top container is a RootContainer, thus we assume that we are in a portal
mode
+ container =
PortalContainer.getCurrentInstance(event.getSession().getServletContext());
+ if (container == null)
+ {
+ container = ExoContainerContext.getTopContainer();
+ }
+ }
+ // The container is a PortalContainer or a StandaloneContainer
+ return container;
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerConfigOwner.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerConfigOwner.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerConfigOwner.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.web;
+
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer.PortalContainerPostInitTask;
+import org.exoplatform.container.RootContainer.PortalContainerPreInitTask;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * This class is used to indicated that this servlet context provides resources and/or
configuration
+ * files to the associated portal containers
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 14 sept. 2009
+ */
+public class PortalContainerConfigOwner implements ServletContextListener
+{
+
+ public void contextInitialized(ServletContextEvent event)
+ {
+ final PortalContainerPreInitTask task = new PortalContainerPreInitTask()
+ {
+
+ public void execute(ServletContext context, PortalContainer portalContainer)
+ {
+ portalContainer.registerContext(context);
+ }
+ };
+ PortalContainer.addInitTask(event.getServletContext(), task);
+ }
+
+ public void contextDestroyed(ServletContextEvent event)
+ {
+ final PortalContainerPostInitTask task = new PortalContainerPostInitTask()
+ {
+
+ public void execute(ServletContext context, PortalContainer portalContainer)
+ {
+ portalContainer.unregisterContext(context);
+ }
+ };
+ PortalContainer.addInitTask(event.getServletContext(), task);
+ }
+}
Added:
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java
===================================================================
---
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java
(rev 0)
+++
kernel/trunk/container/src/main/java/org/exoplatform/container/web/PortalContainerCreator.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container.web;
+
+import org.exoplatform.container.RootContainer;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * This class is used to create and initialize all the portal containers that have been
+ * registered previously
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 9 sept. 2009
+ */
+public class PortalContainerCreator implements ServletContextListener
+{
+
+ /**
+ * {@inheritDoc}
+ */
+ public void contextDestroyed(ServletContextEvent event)
+ {
+ }
+
+ /**
+ * Initializes and creates all the portal container that have been registered
previously
+ */
+ public void contextInitialized(ServletContextEvent event)
+ {
+ RootContainer rootContainer = RootContainer.getInstance();
+ rootContainer.createPortalContainers();
+ }
+}
Added:
kernel/trunk/container/src/test/java/org/exoplatform/container/TestPortalContainerInitTaskContextComparator.java
===================================================================
---
kernel/trunk/container/src/test/java/org/exoplatform/container/TestPortalContainerInitTaskContextComparator.java
(rev 0)
+++
kernel/trunk/container/src/test/java/org/exoplatform/container/TestPortalContainerInitTaskContextComparator.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import junit.framework.TestCase;
+
+import org.exoplatform.container.RootContainer.PortalContainerInitTaskContext;
+import org.exoplatform.container.RootContainer.PortalContainerInitTaskContextComparator;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 24 sept. 2009
+ */
+@SuppressWarnings("unchecked")
+public class TestPortalContainerInitTaskContextComparator extends TestCase
+{
+
+ public void testCompare()
+ {
+ PortalContainerInitTaskContextComparator comparator = new
PortalContainerInitTaskContextComparator(Arrays.asList("d", "c"));
+ PortalContainerInitTaskContext[] contexts =
+ {createPortalContainerInitTaskContext("b"),
createPortalContainerInitTaskContext("d"),
createPortalContainerInitTaskContext("a"),
+ createPortalContainerInitTaskContext("c")};
+ Arrays.sort(contexts, comparator);
+ assertEquals("d", contexts[0].getServletContextName());
+ assertEquals("c", contexts[1].getServletContextName());
+ assertEquals("a", contexts[2].getServletContextName());
+ assertEquals("b", contexts[3].getServletContextName());
+ }
+
+
+ private PortalContainerInitTaskContext createPortalContainerInitTaskContext(String
name)
+ {
+ return new PortalContainerInitTaskContext(new MockServletContext(name), null);
+ }
+
+ private static class MockServletContext implements ServletContext
+ {
+
+ private final String name;
+
+ private MockServletContext(String name)
+ {
+ this.name = name;
+ }
+
+ public Object getAttribute(String name)
+ {
+
+ return null;
+ }
+
+ public Enumeration getAttributeNames()
+ {
+
+ return null;
+ }
+
+ public ServletContext getContext(String uripath)
+ {
+
+ return null;
+ }
+
+ public String getInitParameter(String name)
+ {
+
+ return null;
+ }
+
+ public Enumeration getInitParameterNames()
+ {
+
+ return null;
+ }
+
+ public int getMajorVersion()
+ {
+
+ return 0;
+ }
+
+ public String getMimeType(String file)
+ {
+
+ return null;
+ }
+
+ public int getMinorVersion()
+ {
+
+ return 0;
+ }
+
+ public RequestDispatcher getNamedDispatcher(String name)
+ {
+
+ return null;
+ }
+
+ public String getRealPath(String path)
+ {
+
+ return null;
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path)
+ {
+
+ return null;
+ }
+
+ public URL getResource(String path) throws MalformedURLException
+ {
+
+ return null;
+ }
+
+ public InputStream getResourceAsStream(String path)
+ {
+
+ return null;
+ }
+
+ public Set getResourcePaths(String path)
+ {
+
+ return null;
+ }
+
+ public String getServerInfo()
+ {
+
+ return null;
+ }
+
+ public Servlet getServlet(String name) throws ServletException
+ {
+
+ return null;
+ }
+
+ public String getServletContextName()
+ {
+ return name;
+ }
+
+ public Enumeration getServletNames()
+ {
+
+ return null;
+ }
+
+ public Enumeration getServlets()
+ {
+
+ return null;
+ }
+
+ public void log(String msg)
+ {
+
+ }
+
+ public void log(Exception exception, String msg)
+ {
+
+ }
+
+ public void log(String message, Throwable throwable)
+ {
+
+ }
+
+ public void removeAttribute(String name)
+ {
+
+ }
+
+ public void setAttribute(String name, Object object)
+ {
+
+ }
+
+ }
+}
Added:
kernel/trunk/container/src/test/java/org/exoplatform/container/TestUnifiedClassLoader.java
===================================================================
---
kernel/trunk/container/src/test/java/org/exoplatform/container/TestUnifiedClassLoader.java
(rev 0)
+++
kernel/trunk/container/src/test/java/org/exoplatform/container/TestUnifiedClassLoader.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import junit.framework.TestCase;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 24 sept. 2009
+ */
+public class TestUnifiedClassLoader extends TestCase
+{
+
+ public void testConstructor()
+ {
+ try
+ {
+ new UnifiedClassLoader();
+ fail("An IllegalArgumentException is expected");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ try
+ {
+ new UnifiedClassLoader(new ClassLoader[0]);
+ fail("An IllegalArgumentException is expected");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ public void testGetResource() throws Exception
+ {
+ UnifiedClassLoader mcl = new UnifiedClassLoader(new ClassLoader[]{new
MockClassLoader(null, null), new MockClassLoader(null, null)});
+ assertNull(mcl.getResource(null));
+ URL result = new URL("file:///foo");
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null, null), new
MockClassLoader(result, null)});
+ assertEquals(result, mcl.getResource(null));
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(result, null),
new MockClassLoader(null, null)});
+ assertEquals(result, mcl.getResource(null));
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(new
URL("file:///foo2"), null), new MockClassLoader(result, null)});
+ assertEquals(result, mcl.getResource(null));
+ }
+
+ public void testGetResources() throws Exception
+ {
+ UnifiedClassLoader mcl = new UnifiedClassLoader(new ClassLoader[]{new
MockClassLoader(null, Collections.enumeration(new ArrayList<URL>())), new
MockClassLoader(null, Collections.enumeration(new ArrayList<URL>()))});
+ Enumeration<URL> eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertFalse(eResult.hasMoreElements());
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null, null), new
MockClassLoader(null, null)});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertFalse(eResult.hasMoreElements());
+ URL result = new URL("file:///foo");
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result))), new MockClassLoader(null,
Collections.enumeration(new ArrayList<URL>()))});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result))), new MockClassLoader(null, null)});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null,
Collections.enumeration(new ArrayList<URL>())), new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result)))});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null, null), new
MockClassLoader(null, Collections.enumeration(Arrays.asList(result)))});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ URL result1 = new URL("file:///foo");
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result))), new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result1)))});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ URL result2 = new URL("file:///foo2");
+ mcl = new UnifiedClassLoader(new ClassLoader[]{new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result))), new MockClassLoader(null,
Collections.enumeration(Arrays.asList(result2)))});
+ eResult = mcl.getResources(null);
+ assertNotNull(eResult);
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result, eResult.nextElement());
+ assertTrue(eResult.hasMoreElements());
+ assertEquals(result2, eResult.nextElement());
+ assertFalse(eResult.hasMoreElements());
+ }
+
+ private static class MockClassLoader extends ClassLoader
+ {
+
+ private final URL getResourceResult;
+
+ private final Enumeration<URL> getResourcesResult;
+
+ private MockClassLoader(URL getResourceResult, Enumeration<URL>
getResourcesResult)
+ {
+ this.getResourceResult = getResourceResult;
+ this.getResourcesResult = getResourcesResult;
+ }
+
+ public URL getResource(String name)
+ {
+ return getResourceResult;
+ }
+
+ public Enumeration<URL> getResources(String name)
+ {
+ return getResourcesResult;
+ }
+ }
+}
Added:
kernel/trunk/container/src/test/java/org/exoplatform/container/TestWebAppInitContextComparator.java
===================================================================
---
kernel/trunk/container/src/test/java/org/exoplatform/container/TestWebAppInitContextComparator.java
(rev 0)
+++
kernel/trunk/container/src/test/java/org/exoplatform/container/TestWebAppInitContextComparator.java 2009-10-05
10:19:24 UTC (rev 207)
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2003-2009 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.container;
+
+import junit.framework.TestCase;
+
+import org.exoplatform.container.PortalContainer.WebAppInitContextComparator;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 24 sept. 2009
+ */
+@SuppressWarnings("unchecked")
+public class TestWebAppInitContextComparator extends TestCase
+{
+
+ public void testCompare()
+ {
+ WebAppInitContextComparator comparator = new
WebAppInitContextComparator(Arrays.asList("d", "c"));
+ WebAppInitContext[] contexts =
+ {createWebAppInitContext("b"), createWebAppInitContext("d"),
createWebAppInitContext("a"),
+ createWebAppInitContext("c")};
+ Arrays.sort(contexts, comparator);
+ assertEquals("d", contexts[0].getServletContextName());
+ assertEquals("c", contexts[1].getServletContextName());
+ assertEquals("a", contexts[2].getServletContextName());
+ assertEquals("b", contexts[3].getServletContextName());
+ }
+
+ private WebAppInitContext createWebAppInitContext(String name)
+ {
+ return new WebAppInitContext(new MockServletContext(name));
+ }
+
+ private static class MockServletContext implements ServletContext
+ {
+
+ private final String name;
+
+ private MockServletContext(String name)
+ {
+ this.name = name;
+ }
+
+ public Object getAttribute(String name)
+ {
+
+ return null;
+ }
+
+ public Enumeration getAttributeNames()
+ {
+
+ return null;
+ }
+
+ public ServletContext getContext(String uripath)
+ {
+
+ return null;
+ }
+
+ public String getInitParameter(String name)
+ {
+
+ return null;
+ }
+
+ public Enumeration getInitParameterNames()
+ {
+
+ return null;
+ }
+
+ public int getMajorVersion()
+ {
+
+ return 0;
+ }
+
+ public String getMimeType(String file)
+ {
+
+ return null;
+ }
+
+ public int getMinorVersion()
+ {
+
+ return 0;
+ }
+
+ public RequestDispatcher getNamedDispatcher(String name)
+ {
+
+ return null;
+ }
+
+ public String getRealPath(String path)
+ {
+
+ return null;
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path)
+ {
+
+ return null;
+ }
+
+ public URL getResource(String path) throws MalformedURLException
+ {
+
+ return null;
+ }
+
+ public InputStream getResourceAsStream(String path)
+ {
+
+ return null;
+ }
+
+ public Set getResourcePaths(String path)
+ {
+
+ return null;
+ }
+
+ public String getServerInfo()
+ {
+
+ return null;
+ }
+
+ public Servlet getServlet(String name) throws ServletException
+ {
+
+ return null;
+ }
+
+ public String getServletContextName()
+ {
+ return name;
+ }
+
+ public Enumeration getServletNames()
+ {
+
+ return null;
+ }
+
+ public Enumeration getServlets()
+ {
+
+ return null;
+ }
+
+ public void log(String msg)
+ {
+
+ }
+
+ public void log(Exception exception, String msg)
+ {
+
+ }
+
+ public void log(String message, Throwable throwable)
+ {
+
+ }
+
+ public void removeAttribute(String name)
+ {
+
+ }
+
+ public void setAttribute(String name, Object object)
+ {
+
+ }
+
+ }
+}