[jboss-cvs] JBossAS SVN: r100726 - in trunk: server/src/etc/conf/all/bootstrap and 4 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Mon Feb 8 18:24:58 EST 2010


Author: bstansberry at jboss.com
Date: 2010-02-08 18:24:57 -0500 (Mon, 08 Feb 2010)
New Revision: 100726

Added:
   trunk/server/src/etc/deploy/admin-console-activator-jboss-beans.xml
   trunk/server/src/etc/deploy/jbossws-console-activator-jboss-beans.xml
   trunk/server/src/etc/deploy/jmx-console-activator-jboss-beans.xml
   trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/
   trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/ContextDemandListener.java
   trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextIntegrator.java
   trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextProfileManager.java
Modified:
   trunk/build/build.xml
   trunk/server/src/etc/conf/all/bootstrap/vfs.xml
   trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/deployers/TomcatService.java
Log:
[JBAS-7713] Activation of profiles upon receipt of first request

Modified: trunk/build/build.xml
===================================================================
--- trunk/build/build.xml	2010-02-08 22:46:23 UTC (rev 100725)
+++ trunk/build/build.xml	2010-02-08 23:24:57 UTC (rev 100726)
@@ -148,6 +148,7 @@
     <!-- libs shared by the server configurations -->
     <property name="install.common" value="${install.root}/common"/>
     <property name="install.common.lib" value="${install.common}/lib"/>
+    <property name="install.common.deploy" value="${install.common}/deploy"/>
 
     <!-- Configuration for the nightly build and test job -->
     <property name="run.nightly.sleep" value="1"/>
@@ -332,7 +333,7 @@
         <include name="jboss-local-jdbc.rar"/>
         <include name="jbossweb.sar/**"/>
         <include name="jboss-xa-jdbc.rar"/>
-        <include name="jmx-console.war/**"/>
+        <include name="jmx-console-activator-jboss-beans.xml"/>
         <include name="http-invoker.sar/**"/>
         <include name="ROOT.war/**"/>
         <include name="security/**"/>
@@ -360,7 +361,7 @@
         <exclude name="conf/jndi.properties"/>
         <!-- ORBInitialContextFactory -->
         <include name="deploy/**"/>
-        <exclude name="deploy/admin-console.war/**"/>
+        <!--<exclude name="deploy/admin-console-activator-jboss-beans.xml"/>-->
         <exclude name="deploy/http-invoker.sar/**"/>
         <exclude name="deploy/jmx-remoting.sar/**"/>
         <exclude name="deploy/management/**"/>
@@ -1435,8 +1436,9 @@
     </unjar>
 
     <!-- Include the JMX console war unpacked -->
-    <mkdir dir="${install.server}/all/deploy/jmx-console.war"/>
-    <unjar src="${varia.module.output}/jboss-as-varia-jmx-console.war" dest="${install.server}/all/deploy/jmx-console.war"/>
+    <mkdir dir="${install.common.deploy}"/>
+    <mkdir dir="${install.common.deploy}/jmx-console.war"/>
+    <unjar src="${varia.module.output}/jboss-as-varia-jmx-console.war" dest="${install.common.deploy}/jmx-console.war"/>
 
 
     <!-- Include the HTTP invoker service unpacked -->
@@ -1821,9 +1823,10 @@
   </target>
 
   <target name="install-jbossws-to-deploy">
-    <!-- Install JBossWS web console to deploy directory -->
+  	 <!-- Install JBossWS web console to deploy directory -->
+  	 <mkdir dir="${install.common.deploy}"/>
     <unzip src="${org.jboss.ws.native:jbossws-native-management:war}"
-        dest="${install.all.deploy}/jbossws-console.war"
+        dest="${install.common.deploy}/jbossws-console.war"
       />
   </target>
 
@@ -2312,13 +2315,9 @@
     </unzip>
 
     <!-- Install Admin Console -->
-    <mkdir dir="${install.server}/all/deploy/admin-console.war"/>
+    <mkdir dir="${install.common.deploy}/admin-console.war"/>
     <unjar src="${org.jboss.jopr:jopr-embedded-jbas5:war}"
-          dest="${install.server}/all/deploy/admin-console.war"/>
- 
-     <mkdir dir="${install.server}/default/deploy/admin-console.war"/>
-     <unjar src="${org.jboss.jopr:jopr-embedded-jbas5:war}"
-          dest="${install.server}/default/deploy/admin-console.war"/>
+          dest="${install.common.deploy}/admin-console.war"/>
 
     <!-- Copy varia stuff -->
 

Modified: trunk/server/src/etc/conf/all/bootstrap/vfs.xml
===================================================================
--- trunk/server/src/etc/conf/all/bootstrap/vfs.xml	2010-02-08 22:46:23 UTC (rev 100725)
+++ trunk/server/src/etc/conf/all/bootstrap/vfs.xml	2010-02-08 23:24:57 UTC (rev 100726)
@@ -21,6 +21,10 @@
           <value><inject bean="VfsNamesExceptionHandler"/></value>
         </entry>
         <entry>
+          <key>${jboss.common.base.url}/deploy</key>
+          <value><inject bean="VfsNamesExceptionHandler"/></value>
+        </entry>
+        <entry>
           <key>${jboss.server.lib.url}</key>
           <value><inject bean="VfsNamesExceptionHandler"/></value>
         </entry>

Added: trunk/server/src/etc/deploy/admin-console-activator-jboss-beans.xml
===================================================================
--- trunk/server/src/etc/deploy/admin-console-activator-jboss-beans.xml	                        (rev 0)
+++ trunk/server/src/etc/deploy/admin-console-activator-jboss-beans.xml	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+    <bean name="AdminConsoleActivator" class="org.jboss.web.tomcat.service.ondemand.OnDemandContextProfileManager">
+      
+      <property name="profileService"><inject bean="ProfileService"/></property>
+      <property name="profileFactory"><inject bean="ProfileFactory"/></property>
+      <property name="onDemandContextIntegrator"><inject bean="WebServer"/></property>
+
+      <property name="contextName">admin-console</property>
+
+      <!-- Build a profile from the contents of this single URI -->
+      <property name="singleURI">${jboss.common.base.url}deploy${/}admin-console.war</property>
+      
+      <!-- 
+        Whether activation of the profile (i.e. deployment of the URI contents)
+        should be deferred until the first request (true) or done as part of
+        the start of this bean (false).
+        
+        WARNING: This configuration option may be altered in a subsequent AS 6.0
+        milestone release.
+        
+        FIXME: https://jira.jboss.org/jira/browse/JBAS-7714        
+        -->
+      <property name="activateOnDemand">${jboss.as.deployment.ondemand:true}</property>
+
+   </bean>
+    
+</deployment>


Property changes on: trunk/server/src/etc/deploy/admin-console-activator-jboss-beans.xml
___________________________________________________________________
Name: svn:keywords
   + Id Revision

Added: trunk/server/src/etc/deploy/jbossws-console-activator-jboss-beans.xml
===================================================================
--- trunk/server/src/etc/deploy/jbossws-console-activator-jboss-beans.xml	                        (rev 0)
+++ trunk/server/src/etc/deploy/jbossws-console-activator-jboss-beans.xml	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+    <bean name="JBossWSConsoleActivator" class="org.jboss.web.tomcat.service.ondemand.OnDemandContextProfileManager">
+      
+      <property name="profileService"><inject bean="ProfileService"/></property>
+      <property name="profileFactory"><inject bean="ProfileFactory"/></property>
+      <property name="onDemandContextIntegrator"><inject bean="WebServer"/></property>
+
+      <property name="contextName">jbossws</property>
+
+      <!-- Build a profile from the contents of this single URI -->
+      <property name="singleURI">${jboss.common.base.url}deploy${/}jbossws-console.war</property>
+      
+      <!-- 
+        Whether activation of the profile (i.e. deployment of the URI contents)
+        should be deferred until the first request (true) or done as part of
+        the start of this bean (false).
+        
+        WARNING: This configuration option may be altered in a subsequent AS 6.0
+        milestone release.
+        
+        FIXME: https://jira.jboss.org/jira/browse/JBAS-7714        
+        -->
+      <property name="activateOnDemand">${jboss.as.deployment.ondemand:true}</property>
+
+   </bean>
+    
+</deployment>


Property changes on: trunk/server/src/etc/deploy/jbossws-console-activator-jboss-beans.xml
___________________________________________________________________
Name: svn:keywords
   + Id Revision

Added: trunk/server/src/etc/deploy/jmx-console-activator-jboss-beans.xml
===================================================================
--- trunk/server/src/etc/deploy/jmx-console-activator-jboss-beans.xml	                        (rev 0)
+++ trunk/server/src/etc/deploy/jmx-console-activator-jboss-beans.xml	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+    <bean name="JmxConsoleActivator" class="org.jboss.web.tomcat.service.ondemand.OnDemandContextProfileManager">
+      
+      <property name="profileService"><inject bean="ProfileService"/></property>
+      <property name="profileFactory"><inject bean="ProfileFactory"/></property>
+      <property name="onDemandContextIntegrator"><inject bean="WebServer"/></property>
+
+      <property name="contextName">jmx-console</property>
+
+      <!-- Build a profile from the contents of this single URI -->
+      <property name="singleURI">${jboss.common.base.url}deploy${/}jmx-console.war</property>
+      
+      <!-- 
+        Whether activation of the profile (i.e. deployment of the URI contents)
+        should be deferred until the first request (true) or done as part of
+        the start of this bean (false).
+        
+        WARNING: This configuration option may be altered in a subsequent AS 6.0
+        milestone release.
+        
+        FIXME: https://jira.jboss.org/jira/browse/JBAS-7714        
+        -->
+      <property name="activateOnDemand">${jboss.as.deployment.ondemand:true}</property>
+
+   </bean>
+    
+</deployment>


Property changes on: trunk/server/src/etc/deploy/jmx-console-activator-jboss-beans.xml
___________________________________________________________________
Name: svn:keywords
   + Id Revision

Modified: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/deployers/TomcatService.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/deployers/TomcatService.java	2010-02-08 22:46:23 UTC (rev 100725)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/deployers/TomcatService.java	2010-02-08 23:24:57 UTC (rev 100726)
@@ -26,8 +26,13 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 import javax.management.MBeanServer;
@@ -45,8 +50,9 @@
 import org.apache.catalina.startup.Catalina;
 import org.apache.catalina.startup.CatalinaProperties;
 import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.http.mapper.Mapper;
+import org.apache.tomcat.util.http.mapper.OnDemandContextMappingListener;
 import org.apache.tomcat.util.modeler.Registry;
-import org.jboss.bootstrap.api.as.server.JBossASServer;
 import org.jboss.bootstrap.api.lifecycle.LifecycleState;
 import org.jboss.bootstrap.spi.as.server.JBossASServerProvider;
 import org.jboss.kernel.spi.dependency.KernelController;
@@ -65,6 +71,8 @@
 import org.jboss.web.tomcat.metadata.ServiceMetaData;
 import org.jboss.web.tomcat.metadata.ValveMetaData;
 import org.jboss.web.tomcat.security.HttpServletRequestPolicyContextHandler;
+import org.jboss.web.tomcat.service.ondemand.ContextDemandListener;
+import org.jboss.web.tomcat.service.ondemand.OnDemandContextIntegrator;
 import org.jboss.web.tomcat.service.session.SessionIDGenerator;
 import org.jboss.xb.binding.Unmarshaller;
 import org.jboss.xb.binding.UnmarshallerFactory;
@@ -86,7 +94,7 @@
  * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
  * @version $Revision$
  */
-public class TomcatService extends ServiceMBeanSupport implements NotificationListener, TomcatServiceMBean
+public class TomcatService extends ServiceMBeanSupport implements NotificationListener, TomcatServiceMBean, OnDemandContextIntegrator
 {
    
    /** The associated Tomcat deployer * */
@@ -97,7 +105,13 @@
    
    // Use a flag because isInShutdown doesn't appear to be correct
    private boolean connectorsRunning = false;
-
+   
+   private final Set<ContextDemandAdapter> contextDemandAdapters = 
+      Collections.synchronizedSet(new HashSet<ContextDemandAdapter>());
+   
+   private final Map<String, Mapper> mappers = 
+      Collections.synchronizedMap(new HashMap<String, Mapper>());
+   
    // Dependency inject the Executor pojo
 
    public Executor getExecutor()
@@ -218,7 +232,15 @@
          service.setName(serviceMetaData.getName());
          service.setServer(catalinaServer);
          catalinaServer.addService(service);
+         
+         // Integrate support for on-demand contexts
+         Mapper mapper = service.getMapper();
+         mappers.put(service.getName(), mapper);
+         ContextDemandAdapter adapter = new ContextDemandAdapter(service.getName());
+         mapper.registerOnDemandContextMappingListener(adapter);
+         contextDemandAdapters.add(adapter);
 
+
          // Server/Service/Executor
          // Executor is useless in JBoss: the Executor will get injected in the executor field
          // and used directly
@@ -766,4 +788,87 @@
       super.unsetKernelControllerContext(controllerContext);
       JBossWebMicrocontainerBeanLocator.setKernelController(null);
    }
+   
+   public void registerContextDemandListener(ContextDemandListener listener)
+   {
+      for (ContextDemandAdapter adapter : contextDemandAdapters)
+      {
+         adapter.registerContextDemandListener(listener);
+      }
+   }
+   
+   public void removeContextDemandListener(ContextDemandListener listener)
+   {
+      for (ContextDemandAdapter adapter : contextDemandAdapters)
+      {
+         adapter.removeContextDemandListener(listener);
+      }
+   }
+   
+   public void registerOnDemandContext(String serviceName, String hostName, String contextName)
+   {
+      Mapper mapper = mappers.get(serviceName);
+      if (mapper != null)
+      {
+         // ensure the path starts w/ slash or Mapper won't match it correctly
+         if (contextName.length() > 0 && ('/' != contextName.charAt(0)))
+         {
+            contextName = "/" + contextName;
+         }
+         mapper.addOnDemandContext(hostName, contextName);
+      }
+      else
+      {
+         log.warn("Cannot register on-demand context for unknown engine " + serviceName);
+      }
+   }
+   
+   public void removeOnDemandContext(String serviceName, String hostName, String contextName)
+   {
+      Mapper mapper = mappers.get(serviceName);
+      if (mapper != null)
+      {
+         mapper.removeContext(hostName, contextName);
+      }
+      else
+      {
+         log.warn("Cannot remove on-demand context for unknown engine " + serviceName);
+      }
+   }
+   
+   private class ContextDemandAdapter implements OnDemandContextMappingListener
+   {
+      private final String engineName;
+      private final Set<ContextDemandListener> contextDemandListeners = 
+          Collections.synchronizedSet(new HashSet<ContextDemandListener>());
+
+      private ContextDemandAdapter(String engineName)
+      {
+         this.engineName = engineName;
+      }
+
+      public void onDemandContextMapped(String hostName, String contextName)
+      {
+         for (ContextDemandListener listener : contextDemandListeners)
+         {
+            listener.contextDemanded(this.engineName, hostName, contextName);
+         }         
+      }
+      
+      private void registerContextDemandListener(ContextDemandListener listener)
+      {
+         if (listener != null )
+         {
+            contextDemandListeners.add(listener);
+         }
+      }
+      
+      private void removeContextDemandListener(ContextDemandListener listener)
+      {
+         if (listener != null )
+         {
+            contextDemandListeners.remove(listener);
+         }
+      }
+   }
 }

Added: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/ContextDemandListener.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/ContextDemandListener.java	                        (rev 0)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/ContextDemandListener.java	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.web.tomcat.service.ondemand;
+
+/**
+ * Interface implemented by listeners that wish to be notified of requests
+ * for as-yet-undeployed "on-demand" web applications.  It is expected that the
+ * typical implementation of this interface would trigger deployment of the web
+ * application.
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision$
+ */
+public interface ContextDemandListener
+{
+   /** 
+    * Notification that a request has been received for a web application
+    * previously {@link OnDemandContextIntegrator#registerContextDemandListener(ContextDemandListener)
+    * registered with JBoss Web} as eligible for on-demand deployment.
+    * <p>
+    * Implementors of this interface should assume that the notification will be
+    * received before the web server has handled the request and that any handling
+    * of the request is either being done by the calling thread or will block 
+    * until the <code>contextDemanded</code> call has returned.
+    * </p>
+    *
+    * 
+    * @param serviceName the name of the JBoss Web <code>Service</code> that 
+    *                    includes the connector that received the request.
+    *                    Will not be <code>null</code>.
+    * @param hostName the name of the JBoss Web <code>Host</code> that is the
+    *                 virtual host associated with the request.
+    *                 Will not be <code>null</code>.
+    * @param contextName the name of the on-demand web application context, i.e.
+    *                    the context path portion of the request URL, with
+    *                    any leading and trailing / removed.
+    *                    Will not be <code>null</code>.
+    */
+   void contextDemanded(String serviceName, String hostName, String contextName);
+}


Property changes on: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/ContextDemandListener.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision

Added: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextIntegrator.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextIntegrator.java	                        (rev 0)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextIntegrator.java	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat, Inc, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.ondemand;
+
+/**
+ * Mediates between any {@link ContextDemandListener}s and JBoss Web, allowing 
+ * JBoss Web to be made aware of the presence of on-demand applications and
+ * for the listeners to be notified when a user request for an on-demand
+ * application has arrived. An implementation of this interface is responsible
+ * for finding and interacting with the relevant JBoss Web components, freeing
+ * the listeners from that responsibility.
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision$
+ */
+public interface OnDemandContextIntegrator
+{
+   /**
+    * Registers a listener to receive callbacks when a request is received
+    * for an on-demand web application.
+    * 
+    * @param listener the listener
+    */
+   void registerContextDemandListener(ContextDemandListener listener);
+
+   /**
+    * Removes a listener from the set of listeners receiving callbacks when a
+    * request is received for an on-demand web application.
+    * 
+    * @param listener the listener
+    */
+   void removeContextDemandListener(ContextDemandListener listener);
+
+   /**
+    * Configures JBoss Web to recognize requests for the given connectors, virtual
+    * host and web application context as being for an as-yet-undeployed 
+    * "on-demand" web application.
+    * 
+    * @param serviceName the name of the JBoss Web <code>Service</code> that 
+    *                    includes the connectors that will receive requests for 
+    *                    the on-demand web application. Cannot be <code>null</code>.
+    * @param hostName the name of the JBoss Web <code>Host</code> that is the
+    *                 virtual host for on-demand web application. 
+    *                 Cannot be <code>null</code>.
+    * @param contextName the name of the on-demand web application context, i.e.
+    *                    the context path portion of URLs that target it, with
+    *                    any leading and trailing / removed
+    */
+   void registerOnDemandContext(String serviceName, String hostName, String contextName);
+
+   /**
+    * Configures JBoss Web to no longer recognize requests for the given 
+    * connectors, virtual host and web application context.
+    * 
+    * @param serviceName the name of the JBoss Web <code>Service</code> that 
+    *                    includes the connectors that will receive requests for 
+    *                    the on-demand web application.  Cannot be <code>null</code>.
+    * @param hostName the name of the JBoss Web <code>Host</code> that is the
+    *                 virtual host for on-demand web application.  Cannot be <code>null</code>.
+    * @param contextName the name of the on-demand web application context, i.e.
+    *                    the context path portion of URLs that target it, with
+    *                    any leading and trailing / removed
+    */
+   void removeOnDemandContext(String serviceName, String hostName, String contextName);
+
+}
\ No newline at end of file


Property changes on: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextIntegrator.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision

Added: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextProfileManager.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextProfileManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextProfileManager.java	2010-02-08 23:24:57 UTC (rev 100726)
@@ -0,0 +1,668 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat, Inc, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.ondemand;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.jboss.logging.Logger;
+import org.jboss.profileservice.spi.NoSuchProfileException;
+import org.jboss.profileservice.spi.Profile;
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.profileservice.spi.ProfileService;
+import org.jboss.profileservice.spi.metadata.ProfileMetaData;
+import org.jboss.profileservice.spi.metadata.ProfileSourceMetaData;
+import org.jboss.profileservice.spi.metadata.SubProfileMetaData;
+import org.jboss.system.server.profile.repository.metadata.AbstractProfileSourceMetaData;
+import org.jboss.system.server.profile.repository.metadata.BasicProfileMetaData;
+import org.jboss.system.server.profile.repository.metadata.BasicSubProfileMetaData;
+import org.jboss.system.server.profile.repository.metadata.HotDeploymentProfileSourceMetaData;
+import org.jboss.system.server.profile.repository.metadata.ImmutableProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.AbstractProfileFactory;
+
+/**
+ * {@link ContextDemandListener} that creates and registers a {@link ProfileService}
+ * {@link Profile} and then {@link #activateProfile() activates it} when
+ * it receives a notification that a web request wishes to access a targetted
+ * application.
+ * 
+ * TODO: deal with host name aliases and multiple contexts
+ * 
+ * TODO: The ProfileService integration aspect of this class duplicates 
+ * equivalent functionality used for clustering's deploy-hasingleton directory
+ * deployment. Both solve the same conceptual problem of an external event 
+ * triggering activation of a profile. Abstract this out and put it in a
+ * shared location.
+ * 
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class OnDemandContextProfileManager implements ContextDemandListener
+{   
+   public static final String DEFAULT_SERVICE_NAME = "jboss.web";
+   public static final String DEFAULT_HOST_NAME = "localhost";
+   public static final String DEFAULT_ROOT_WAR_PROFILE_NAME = "ROOT.war";
+   
+   /** The profile service */
+   private ProfileService profileService;
+   /** The factory for creating profiles */
+   private AbstractProfileFactory profileFactory;
+   /** Integration hook into JBoss Web */
+   private OnDemandContextIntegrator contextIntegrator;
+   
+   /** The list of URIs to scan */
+   private List<URI> uriList = new CopyOnWriteArrayList<URI>();
+   
+   protected final Logger log = Logger.getLogger(getClass());
+   
+   /** Whether this node has activated its profile */
+   private boolean activated;
+   
+   /** The profile service key domain */
+   private String profileDomain;
+   
+   /** The profile service key server */
+   private String profileServer;
+   
+   /** The profile service key name */
+   private String profileName;
+   
+   /** The profile service key */
+   private ProfileKey profileKey;
+
+   /** Name of the JBoss Web service we associate with */
+   private String serviceName = DEFAULT_SERVICE_NAME;
+   /** Name of the JBoss Web virtual host we associate with */
+   private String hostName = DEFAULT_HOST_NAME;
+   /** Name of the JBoss Web context we associate with */
+   private String contextName;
+   /** contextName with a preceding / (if not root context) */
+   private String contextPath; // TODO do something smarter
+   
+   private boolean activateOnDemand = true;
+
+   // ----------------------------------------------------------- Constructors
+   
+   /**
+    * Create a new OnDemandContextProfileManager.
+    */
+   public OnDemandContextProfileManager()
+   {
+      super();
+   }
+   
+   // ----------------------------------------------------------  Properties
+
+   /**
+    * Gets the list of URIs that will be included as the content of the
+    * profile when it is activated.
+    * 
+    * @return the list. Will not be <code>null</code>
+    */
+   public List<URI> getURIList()
+   {
+      return new ArrayList<URI>(uriList);
+   }
+
+   /**
+    * Sets the list of URIs that will be included as the content of the
+    * profile when it is activated.
+    * 
+    * @param list the list. Cannot be <code>null</code>
+    * @throws IOException
+    * @throws IllegalArgumentException if <code>list</code> is <code>null</code>
+    */
+   public void setURIList(final List<URI> list) throws IOException
+   {
+      if (list == null)
+      {
+         throw new IllegalArgumentException("list argument cannot be null");
+      }
+   
+      // start out with a fresh list
+      uriList.clear();
+   
+      for(int n = 0; n < list.size(); n ++)
+      {
+         URI uri = list.get(n);
+         if (uri == null)
+         {
+            throw new IllegalArgumentException("list element["+n+"] is null");
+         }
+
+         if( uriList.add(uri) == true )
+         {
+            log.debug("Added URI: " + uri);
+         }  
+      }
+      log.debug("URI list: " + uriList);
+   }
+   
+   /**
+    * Returns the single URI that is to be associated with the profile, or
+    * <code>null</code> if there is less than or more than one such URI.
+    * 
+    * @return the URI or <code>null</code>
+    */
+   public URI getSingleURI()
+   {
+      List<URI> list = getURIList();
+      return list.size() == 1 ? list.get(0) : null;
+   }
+   
+   /**
+    * Convenience method for {@link #setURIList(List) setting the list of URIs}
+    * to include in the profile to a single URI.
+    * 
+    * @param deploymentURI the URI. Cannot be <code>null</code>
+    * @throws IOException
+    * @throws IllegalArgumentException if deploymentURI is <code>null</code>
+    */
+   public void setSingleURI(URI deploymentURI) throws IOException
+   {
+      if (deploymentURI == null)
+      {
+         throw new IllegalArgumentException("deploymentURI argument cannot be null");
+      }
+      List<URI> list = Arrays.asList(deploymentURI);
+      setURIList(list);
+   }
+
+   /**
+    * Gets the value that should be used for the 
+    * {@link ProfileKey#getDomain() domain} portion of
+    * the on-demand @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @return the domain, or <code>null</code> if not set
+    */
+   public String getProfileDomain()
+   {
+      return profileDomain;
+   }
+
+   /**
+    * Sets the value that should be used for the 
+    * {@link ProfileKey#getDomain() domain} portion of
+    * the singleton @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @param profileDomain the domain, or <code>null</code>
+    */
+   public void setProfileDomain(String profileDomain)
+   {
+      this.profileDomain = profileDomain;
+   }
+
+   /**
+    * Gets the value that should be used for the 
+    * {@link ProfileKey#getServer() server} portion of
+    * the on-demand @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @return the server, or <code>null</code> if not set
+    */
+   public String getProfileServer()
+   {
+      return profileServer;
+   }
+
+   /**
+    * Sets the value that should be used for the 
+    * {@link ProfileKey#getServer() server} portion of
+    * the on-demand @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @param profileServer the server, or <code>null</code>
+    */
+   public void setProfileServer(String profileServer)
+   {
+      this.profileServer = profileServer;
+   }
+
+   /**
+    * Gets the value that should be used for the 
+    * {@link ProfileKey#getName() name} portion of
+    * the on-demand @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @return the name, or <code>null</code> if not set
+    */
+   public String getProfileName()
+   {
+      if (profileName == null)
+      {
+         if ("".equals(contextName))
+         {
+            profileName = DEFAULT_ROOT_WAR_PROFILE_NAME;
+         }
+         else
+         {
+            profileName = contextName + ".war";
+         }
+      }
+      return profileName;
+   }
+
+   /**
+    * Sets the value that should be used for the 
+    * {@link ProfileKey#getName() name} portion of
+    * the singleton @{link Profile}'s {@link #getProfileKey() ProfileKey}.
+    * 
+    * @param profileName the name, or <code>null</code>
+    */
+   public void setProfileName(String profileName)
+   {
+      this.profileName = profileName;
+   }
+   
+   /**
+    * Gets whether this object has activated its profile.
+    * 
+    * @return <code>true</code> if {@link #activateProfile()} has successfully
+    *         completed and {@link #releaseProfile()} has not been called;
+    *         <code>false</code> otherwise.
+    */
+   public boolean isActivated()
+   {
+      return activated;
+   }
+   
+   
+
+   public String getServiceName()
+   {
+      return serviceName;
+   }
+
+   public void setServiceName(String serviceName)
+   {
+      if (serviceName == null)
+      {
+         throw new IllegalArgumentException("serviceName is null");
+      }
+      this.serviceName = serviceName;
+   }
+
+   public String getHostName()
+   {
+      return hostName;
+   }
+
+   public void setHostName(String hostName)
+   {
+      if (hostName == null)
+      {
+         throw new IllegalArgumentException("hostName is null");
+      }
+      this.hostName = hostName;
+   }
+
+   public String getContextName()
+   {
+      return contextName;
+   }
+
+   public void setContextName(String contextName)
+   {
+      if (contextName == null)
+      {
+         throw new IllegalArgumentException("contextName is null");
+      }
+      if ("ROOT".equals(contextName) || "/ROOT".equals(contextName))
+      {
+         contextName = "";
+      }
+      this.contextName = contextName;
+   }
+
+   /**
+    * Sets the ProfileService reference.
+    * 
+    * @param profileService the profileService. Cannot be <code>null</code>
+    * 
+    * @throws IllegalArgumentException if <code>profileService</code> is <code>null</code>
+    */
+   public void setProfileService(ProfileService profileService)
+   {
+      if (profileService == null)
+      {
+         throw new IllegalArgumentException("profileService is null");
+      }
+      
+      this.profileService = profileService;
+   }
+
+   /**
+    * Sets the factory that should be used to create the on-demand Profile.
+    * 
+    * @param profileFactory the factory Cannot be <code>null</code>
+    * 
+    * @throws IllegalArgumentException if <code>profileFactory</code> is <code>null</code>
+    */
+   public void setProfileFactory(AbstractProfileFactory profileFactory)
+   {
+      if (profileFactory == null)
+      {
+         throw new IllegalArgumentException("profileFactory is null");
+      }
+      this.profileFactory = profileFactory;
+   }
+
+   /**
+    * Sets the {@link OnDemandContextIntegrator} used to integrate with the
+    * web server
+    * 
+    * @param contextManager the manager. Cannot be <code>null</code>
+    * 
+    * @throws IllegalArgumentException if <code>contextManager</code> is <code>null</code>
+    */
+   public void setOnDemandContextIntegrator(OnDemandContextIntegrator contextManager)
+   {
+      if (contextManager == null)
+      {
+         throw new IllegalArgumentException("contextManager is null");
+      }
+      this.contextIntegrator = contextManager;
+   } 
+
+   /**
+    * Gets whether the profile should be activated on during the {@link #start()}
+    * phase of this bean's deployment rather than on receipt of an HTTP request. 
+    * This property allows a simple configuration to turn off the "on-demand"
+    * behavior for environments (e.g. production servers) where a more
+    * deterministic startup is appropriate.
+    * 
+    * @return <code>false</code> if the profile should be activated as part of
+    *         startup of this bean; <code>true</code> if activation should
+    *         be deferred until an HTTP request is received. Default is
+    *         <code>true</code>
+    *         
+    * @deprecated This is a temporary API for AS 6.0.0.M2; something else
+    *             may replace it in later releases
+    */
+   public boolean isActivateOnDemand()
+   {
+      return activateOnDemand;
+   }
+
+   /**
+    * Sets whether the profile should be activated on during the {@link #start()}
+    * phase of this bean's deployment rather than on receipt of an HTTP request. 
+    * This property allows a simple configuration to turn off the "on-demand"
+    * behavior for environments (e.g. production servers) where a more
+    * deterministic startup is appropriate.
+    * 
+    * @param activateOnDemand <code>false</code> if the profile should be 
+    *                          activated as part of startup of this bean; 
+    *                          <code>true</code> if activation should be 
+    *                          deferred until an HTTP request is received.
+    *         
+    * @deprecated This is a temporary API for AS 6.0.0.M2; something else
+    *             may replace it in later releases
+    */
+   public void setActivateOnDemand(boolean activateOnDemand)
+   {
+      this.activateOnDemand = activateOnDemand;
+   }   
+
+   // --------------------------------------------------  ContextDemandListener
+
+   public void contextDemanded(String serviceName, String hostName, String contextName)
+   {
+      if (this.contextPath.equals(contextName) 
+            && this.hostName.equals(hostName) 
+            && this.serviceName.equals(serviceName))
+      {
+         try
+         {
+            activateProfile();
+         }
+         catch (Exception e)
+         {
+            log.error("Unable to activate profile " + getProfileKey(), e);
+         }
+      }
+   }
+
+   // -----------------------------------------------------------------  Public
+
+   /**
+    * Builds a profile from the {@link #getURIList() URI list} and registers
+    * it under the configured {@link #getProfileKey()}.
+    */
+   public void start() throws Exception
+   {    
+      if (this.profileFactory == null)
+      {
+         throw new IllegalStateException("Must configure ProfileFactory");
+      } 
+      
+      if (profileService == null)
+      {
+         throw new IllegalStateException("Must configure ProfileService");
+      }
+      
+      if (contextIntegrator == null)
+      {
+         throw new IllegalStateException("Must configure OnDemandContextManager");
+      }
+      
+      if (serviceName == null)
+      {
+         throw new IllegalStateException("Must configure serviceName");
+      }
+      
+      if (hostName == null)
+      {
+         throw new IllegalStateException("Must configure hostName");
+      }
+      
+      if (contextName == null)
+      {
+         throw new IllegalStateException("Must configure contextName");
+      }
+      
+      URI[] rootURIs = uriList.toArray(new URI[uriList.size()]);
+      // TODO add dependencies on bootstrap profiles 
+      String[] rootSubProfiles = new String[0];
+      // Create a hotdeployment profile
+      ProfileMetaData metadata = createProfileMetaData(true, rootURIs, rootSubProfiles);
+      
+      Profile profile = profileFactory.createProfile(getProfileKey(), metadata);
+      profileService.registerProfile(profile);      
+      
+      if (this.activateOnDemand)
+      {
+         contextIntegrator.registerContextDemandListener(this);
+         contextPath = (contextName.length() == 0 || '/' == contextName.charAt(0) ? contextName : "/" + contextName);
+         contextIntegrator.registerOnDemandContext(serviceName, hostName, contextPath);  
+      }
+      else
+      {    
+         // FIXME we don't validate as we expect the PS to do it at the end
+         // of startup; need to check if this is correct 
+         activateProfile(false);
+      }
+   }
+   
+   /**
+    * Unregisters the profile registered in {@link #start()}.
+    */
+   public void stop() throws Exception
+   {      
+      ProfileKey profKey = getProfileKey();
+      if (profileService != null &&  profKey != null)
+      {
+         try
+         {
+            // Inactivate first if needed
+            if (profileService.getActiveProfileKeys().contains(profKey))
+            {
+               releaseProfile();
+            }
+            
+            profileService.unregisterProfile(profKey);
+         }
+         catch (NoSuchProfileException e)
+         {
+            log.warn("Could not unregister unknown profile " + profKey);
+         }
+      }
+   }
+
+   /**
+    * Tells the ProfileService to 
+    * {@link ProfileService#activateProfile(ProfileKey) activate the on-demand profile}.
+    */
+   public void activateProfile() throws Exception
+   {
+      activateProfile(true);
+   }
+   
+   /**
+    * Gets the key for the {@link Profile} that we activate and release.
+    * 
+    * @return the key. Will not return <code>null</code>
+    * 
+    * @see #getProfileDomain() 
+    * @see #getProfileServer() 
+    * @see #getProfileName()
+    */
+   public ProfileKey getProfileKey()
+   {
+      if (this.profileKey == null)
+      {
+         this.profileKey = new ProfileKey(getProfileDomain(), getProfileServer(), getProfileName());
+      }
+      return this.profileKey;
+   }
+   
+   // -------------------------------------------------------------- Protected
+   
+   /**
+    * Create a profile repository source meta data.
+    * 
+    * @param type the repository type.
+    * @param uri the uri
+    * @return the profile source meta data.
+    */
+   protected ProfileSourceMetaData createSource(URI[] uris, boolean hotDeployment)
+   {
+      AbstractProfileSourceMetaData source = null;
+      if(hotDeployment)
+      {
+         source = new HotDeploymentProfileSourceMetaData();
+      }
+      else
+      {
+         source = new ImmutableProfileSourceMetaData();
+      }
+      List<String> sources = new ArrayList<String>();
+      for(URI uri : uris)
+         sources.add(uri.toString());
+      source.setSources(sources);
+      return source;
+   }
+   
+   // ----------------------------------------------------------------  Private
+
+   
+   /**
+    * Create a profile meta data.
+    * 
+    * @param name the profile name.
+    * @param repositoryType the repository type.
+    * @param uris the repository uris.
+    * @param subProfiles a list of profile dependencies.
+    * @return the profile meta data.
+    */
+   private ProfileMetaData createProfileMetaData(boolean hotDeployment, URI[] uris, String[] subProfiles)
+   {
+      // Create profile
+      BasicProfileMetaData metaData = new BasicProfileMetaData();
+      metaData.setDomain(getProfileDomain());
+      metaData.setServer(getProfileServer());
+      metaData.setName(getProfileName());
+      
+      // Create profile sources
+      ProfileSourceMetaData source = createSource(uris, hotDeployment);
+      metaData.setSource(source);
+      
+      List<SubProfileMetaData> profileList = new ArrayList<SubProfileMetaData>();
+      for(String subProfile : subProfiles)
+      {
+         BasicSubProfileMetaData md = new BasicSubProfileMetaData();
+         md.setName(subProfile);
+         profileList.add(md);
+      }
+      metaData.setSubprofiles(profileList);
+      
+      return metaData;
+   }
+   
+   private synchronized void activateProfile(boolean validate) throws Exception
+   {
+      if (this.profileService == null)
+      {
+         throw new IllegalStateException("Must configure the ProfileService");
+      }
+      ProfileKey profKey = getProfileKey();
+      if (this.profileService.getActiveProfileKeys().contains(profKey) == false)
+      {         
+         this.profileService.activateProfile(getProfileKey());
+         if (validate)
+         {
+            // Validate if the activation was successful
+            this.profileService.validateProfile(getProfileKey());
+         }
+         
+         this.activated = true;
+      }
+      else
+      {
+         log.warn("Profile " + profKey + " is already activated");
+      }
+      
+   }
+
+   /**
+    * Tells the ProfileService to {@link ProfileService#releaseProfile(ProfileKey) release the profile}. 
+    * Called by the HASingletonController when we are no longer the singleton master.
+    */
+   private synchronized void releaseProfile() throws Exception
+   {
+      if (this.activated)
+      {         
+         try
+         {
+            this.profileService.deactivateProfile(getProfileKey());
+         }
+         catch (NoSuchProfileException e)
+         {
+            log.warn("No Profile is registered under key " + getProfileKey());
+         }
+         
+         this.activated = false;
+      }
+   }
+   
+}


Property changes on: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/ondemand/OnDemandContextProfileManager.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision




More information about the jboss-cvs-commits mailing list