Author: remy.maucherat(a)jboss.com
Date: 2010-01-25 07:54:58 -0500 (Mon, 25 Jan 2010)
New Revision: 1367
Added:
trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java
Modified:
trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
trunk/webapps/docs/changelog.xml
Log:
- Add the plumbing for on demand.
- Submitted by Brian Stansberry.
Modified: trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 12:52:45 UTC (rev
1366)
+++ trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 12:54:58 UTC (rev
1367)
@@ -51,12 +51,20 @@
*/
protected String defaultHostName = null;
+
/**
* Context associated with this wrapper, used for wrapper mapping.
*/
protected Context context = new Context();
+ /**
+ * Listeners for lazy loading of web applications.
+ */
+ protected final List<OnDemandContextMappingListener> lazyLoadListeners =
+ new ArrayList<OnDemandContextMappingListener>();
+
+
// --------------------------------------------------------- Public Methods
@@ -201,6 +209,46 @@
/**
+ * Add a new undeployed Context to an existing Host.
+ *
+ * @param hostName Virtual host name this context belongs to
+ * @param path Context path
+ */
+ public void addOnDemandContext(String hostName, String path) {
+
+ Host[] hosts = this.hosts;
+ int pos = find(hosts, hostName);
+ if( pos <0 ) {
+ addHost(hostName, new String[0], "");
+ hosts = this.hosts;
+ pos = find(hosts, hostName);
+ }
+ if (pos < 0) {
+ logger.error("No host found: " + hostName);
+ }
+ Host host = hosts[pos];
+ if (host.name.equals(hostName)) {
+ int slashCount = slashCount(path);
+ synchronized (host) {
+ Context[] contexts = host.contextList.contexts;
+ // Update nesting
+ if (slashCount > host.contextList.nesting) {
+ host.contextList.nesting = slashCount;
+ }
+ Context[] newContexts = new Context[contexts.length + 1];
+ Context newContext = new Context();
+ newContext.name = path;
+ newContext.loaded = false;
+ if (insertMap(contexts, newContexts, newContext)) {
+ host.contextList.contexts = newContexts;
+ }
+ }
+ }
+
+ }
+
+
+ /**
* Add a new Context to an existing Host.
*
* @param hostName Virtual host name this context belongs to
@@ -240,6 +288,11 @@
newContext.resources = resources;
if (insertMap(contexts, newContexts, newContext)) {
host.contextList.contexts = newContexts;
+ } else {
+ newContexts = new Context[contexts.length];
+ if (insertLazyLoadedContext(contexts, newContexts, newContext)) {
+ host.contextList.contexts = newContexts;
+ }
}
}
}
@@ -584,6 +637,23 @@
}
+ public void registerOnDemandContextMappingListener(OnDemandContextMappingListener
listener) {
+ if (listener != null) {
+ synchronized (lazyLoadListeners) {
+ lazyLoadListeners.add(listener);
+ }
+ }
+ }
+
+ public void removeOnDemandContextMappingListener(OnDemandContextMappingListener
listener) {
+ if (listener != null) {
+ synchronized (lazyLoadListeners) {
+ lazyLoadListeners.remove(listener);
+ }
+ }
+ }
+
+
// -------------------------------------------------------- Private Methods
@@ -597,6 +667,7 @@
uri.setLimit(-1);
Context[] contexts = null;
+ Host mappedHost = null;
Context context = null;
int nesting = 0;
@@ -605,18 +676,20 @@
Host[] hosts = this.hosts;
int pos = findIgnoreCase(hosts, host);
if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
- mappingData.host = hosts[pos].object;
- contexts = hosts[pos].contextList.contexts;
- nesting = hosts[pos].contextList.nesting;
+ mappedHost = hosts[pos];
+ mappingData.host = mappedHost.object;
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
} else {
if (defaultHostName == null) {
return;
}
pos = find(hosts, defaultHostName);
if ((pos != -1) && (defaultHostName.equals(hosts[pos].name))) {
- mappingData.host = hosts[pos].object;
- contexts = hosts[pos].contextList.contexts;
- nesting = hosts[pos].contextList.nesting;
+ mappedHost = hosts[pos];
+ mappingData.host = mappedHost.object;
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
} else {
return;
}
@@ -625,47 +698,27 @@
// Context mapping
if (mappingData.context == null) {
- int pos = find(contexts, uri);
- if (pos == -1) {
- return;
- }
-
- int lastSlash = -1;
- int uriEnd = uri.getEnd();
- int length = -1;
- boolean found = false;
- while (pos >= 0) {
- if (uri.startsWith(contexts[pos].name)) {
- length = contexts[pos].name.length();
- if (uri.getLength() == length) {
- found = true;
- break;
- } else if (uri.startsWithIgnoreCase("/", length)) {
- found = true;
- break;
+ context = findContext(uri, contexts, nesting);
+ if (context != null) {
+ if (!context.loaded) {
+ notifyLazyLoadContextMappingListeners(mappedHost, context);
+ // See if the notification resulted in deploying the context
+ // First reestablish refs to the host fields as adding
+ // the real context will have changed them
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
+ context = findContext(uri, contexts, nesting);
+ if (context != null && !context.loaded) {
+ // notification did not result in deployment
+ // don't map to the unloaded context
+ context = null;
}
}
- if (lastSlash == -1) {
- lastSlash = nthSlash(uri, nesting + 1);
- } else {
- lastSlash = lastSlash(uri);
+ if (context != null) {
+ mappingData.context = context.object;
+ mappingData.contextPath.setString(context.name);
}
- uri.setEnd(lastSlash);
- pos = find(contexts, uri);
}
- uri.setEnd(uriEnd);
-
- if (!found) {
- if (contexts[0].name.equals("")) {
- context = contexts[0];
- }
- } else {
- context = contexts[pos];
- }
- if (context != null) {
- mappingData.context = context.object;
- mappingData.contextPath.setString(context.name);
- }
}
// Wrapper mapping
@@ -676,6 +729,63 @@
}
+ private void notifyLazyLoadContextMappingListeners(Host mappedHost, Context context)
{
+ synchronized (context) {
+ if (context.replaced)
+ return;
+ synchronized (lazyLoadListeners) {
+ for (OnDemandContextMappingListener listener : lazyLoadListeners)
+ listener.onDemandContextMapped(mappedHost.name, context.name);
+ }
+ }
+
+ }
+
+
+ private Context findContext(CharChunk uri, Context[] contexts, int nesting) {
+ int pos = find(contexts, uri);
+ if (pos == -1) {
+ return null;
+ }
+
+ Context foundContext = null;
+
+ int lastSlash = -1;
+ int uriEnd = uri.getEnd();
+ int length = -1;
+ boolean found = false;
+ while (pos >= 0) {
+ if (uri.startsWith(contexts[pos].name)) {
+ length = contexts[pos].name.length();
+ if (uri.getLength() == length) {
+ found = true;
+ break;
+ } else if (uri.startsWithIgnoreCase("/", length)) {
+ found = true;
+ break;
+ }
+ }
+ if (lastSlash == -1) {
+ lastSlash = nthSlash(uri, nesting + 1);
+ } else {
+ lastSlash = lastSlash(uri);
+ }
+ uri.setEnd(lastSlash);
+ pos = find(contexts, uri);
+ }
+ uri.setEnd(uriEnd);
+
+ if (!found) {
+ if (contexts[0].name.equals("")) {
+ foundContext = contexts[0];
+ }
+ } else {
+ foundContext = contexts[pos];
+ }
+ return foundContext;
+ }
+
+
/**
* Wrapper mapping.
*/
@@ -879,7 +989,6 @@
MappingData mappingData) {
int pathEnd = path.getEnd();
- int pathOffset = path.getOffset();
int lastSlash = -1;
int length = -1;
@@ -1274,6 +1383,27 @@
}
+ private static final boolean insertLazyLoadedContext(Context[] oldMap, Context[]
newMap, Context newElement) {
+ int pos = find(oldMap, newElement.name);
+ if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
+ if (oldMap[pos].loaded || !newElement.loaded) {
+ return false;
+ }
+ else {
+ // We're going to replace the old context in the map, but mark
+ // it as replaced so anyone with a ref to it knows to look again
+ oldMap[pos].replaced = true;
+ System.arraycopy(oldMap, 0, newMap, 0, oldMap.length);
+ newMap[pos] = newElement;
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+
// ------------------------------------------------- MapElement Inner Class
@@ -1321,6 +1451,8 @@
public Wrapper[] wildcardWrappers = new Wrapper[0];
public Wrapper[] extensionWrappers = new Wrapper[0];
public int nesting = 0;
+ private boolean loaded = true;
+ private volatile boolean replaced = false;
}
Added: trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java
(rev 0)
+++
trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java 2010-01-25
12:54:58 UTC (rev 1367)
@@ -0,0 +1,35 @@
+/*
+ * 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.apache.tomcat.util.http.mapper;
+
+/**
+ * A listener for notifications when a request is being mapped to a context
+ * path that was
+ * {@link Mapper#addOnDemandContext(String, String) registered as an on-demand context}.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface OnDemandContextMappingListener
+{
+ void onDemandContextMapped(String host, String context);
+}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2010-01-25 12:52:45 UTC (rev 1366)
+++ trunk/webapps/docs/changelog.xml 2010-01-25 12:54:58 UTC (rev 1367)
@@ -27,6 +27,13 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <add>
+ On demand webapp startup plumbing, submitted by Brian Stansberry. (remm)
+ </add>
+ </changelog>
+ </subsection>
<subsection name="Jasper">
<changelog>
<fix>