Merge remote-tracking branch 'origin/master' into jetty-8

Conflicts:
	jetty-npn/pom.xml
	jetty-osgi/jetty-osgi-boot-jsp/META-INF/MANIFEST.MF
	jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java
	jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java
	jetty-osgi/jetty-osgi-boot-logback/META-INF/MANIFEST.MF
	jetty-osgi/jetty-osgi-boot-warurl/META-INF/MANIFEST.MF
	jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF
	jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java
	jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
	jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
	jetty-osgi/jetty-osgi-equinoxtools/META-INF/MANIFEST.MF
	jetty-osgi/jetty-osgi-httpservice/META-INF/MANIFEST.MF
	jetty-spdy/spdy-jetty-http-webapp/pom.xml
	jetty-spdy/spdy-jetty-http/pom.xml
	jetty-spdy/spdy-jetty/pom.xml
This commit is contained in:
Greg Wilkins 2012-04-12 16:02:34 +10:00
commit ee2d289f63
57 changed files with 2121 additions and 1944 deletions

View File

@ -171,7 +171,7 @@ public class HttpClient extends AggregateLifeCycle implements HttpBuffers, Attri
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Set the ThreadPool. /** Set the ThreadPool.
* The threadpool passed is added via {@link #addBean(Object)} so that * The threadpool passed is added via {@link #addBean(Object)} so that
* it's lifecycle may be managed as a {@link AggregateLifeCycle}. * it's lifecycle may be managed as a {@link AggregateLifeCycle}.
* @param threadPool the threadPool to set * @param threadPool the threadPool to set
*/ */
@ -878,7 +878,7 @@ public class HttpClient extends AggregateLifeCycle implements HttpBuffers, Attri
@Deprecated @Deprecated
public void setProvider(String provider) public void setProvider(String provider)
{ {
setProvider(provider); _sslContextFactory.setProvider(provider);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */

View File

@ -260,7 +260,6 @@ public class HttpParser implements Parser
{ {
_state=STATE_END; _state=STATE_END;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
@ -326,7 +325,6 @@ public class HttpParser implements Parser
if (!isComplete() && !isIdle()) if (!isComplete() && !isIdle())
throw new EofException(); throw new EofException();
returnBuffers();
return -1; return -1;
} }
length=_buffer.length(); length=_buffer.length();
@ -440,7 +438,6 @@ public class HttpParser implements Parser
_state=STATE_SEEKING_EOF; _state=STATE_SEEKING_EOF;
_handler.headerComplete(); _handler.headerComplete();
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
break; break;
@ -470,7 +467,6 @@ public class HttpParser implements Parser
_state=STATE_SEEKING_EOF; _state=STATE_SEEKING_EOF;
_handler.headerComplete(); _handler.headerComplete();
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
} }
@ -634,7 +630,6 @@ public class HttpParser implements Parser
_handler.headerComplete(); _handler.headerComplete();
_state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
default: default:
@ -840,7 +835,6 @@ public class HttpParser implements Parser
{ {
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?STATE_END:STATE_SEEKING_EOF;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
@ -860,7 +854,6 @@ public class HttpParser implements Parser
{ {
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?STATE_END:STATE_SEEKING_EOF;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
} }
// TODO adjust the _buffer to keep unconsumed content // TODO adjust the _buffer to keep unconsumed content
return 1; return 1;
@ -895,7 +888,6 @@ public class HttpParser implements Parser
_eol=_buffer.get(); _eol=_buffer.get();
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?STATE_END:STATE_SEEKING_EOF;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
else else
@ -926,7 +918,6 @@ public class HttpParser implements Parser
_eol=_buffer.get(); _eol=_buffer.get();
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?STATE_END:STATE_SEEKING_EOF;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
returnBuffers();
return 1; return 1;
} }
else else

View File

@ -13,8 +13,6 @@
package org.eclipse.jetty.http.gzip; package org.eclipse.jetty.http.gzip;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
@ -32,7 +30,7 @@ import org.eclipse.jetty.util.ByteArrayOutputStream2;
/** /**
* Skeletal implementation of a CompressedStream. This class adds compression features to a ServletOutputStream and takes care of setting response headers, etc. * Skeletal implementation of a CompressedStream. This class adds compression features to a ServletOutputStream and takes care of setting response headers, etc.
* Major work and configuration is done here. Subclasses using different kinds of compression only have to implement the abstract methods doCompress() and * Major work and configuration is done here. Subclasses using different kinds of compression only have to implement the abstract methods doCompress() and
* setContentEncoding() using the desired compression and setting the appropiate Content-Encoding header string. * setContentEncoding() using the desired compression and setting the appropriate Content-Encoding header string.
*/ */
public abstract class AbstractCompressedStream extends ServletOutputStream public abstract class AbstractCompressedStream extends ServletOutputStream
{ {

View File

@ -2,13 +2,35 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-parent</artifactId>
<version>8.1.3-SNAPSHOT</version> <version>19</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.npn</groupId> <groupId>org.eclipse.jetty.npn</groupId>
<artifactId>npn-api</artifactId> <artifactId>npn-api</artifactId>
<version>1.0.1-SNAPSHOT</version>
<name>Jetty :: Next Protocol Negotiation :: API</name> <name>Jetty :: Next Protocol Negotiation :: API</name>
<scm>
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
<developerConnection>scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</developerConnection>
<url>http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-npn</url>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<useReleaseProfile>false</useReleaseProfile>
<goals>deploy</goals>
<arguments>-Peclipse-release</arguments>
<preparationGoals>clean install</preparationGoals>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -28,98 +28,106 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil; import org.osgi.framework.FrameworkUtil;
/** /**
* Plug bundles that contains tld files so that jasper will discover them * Plug bundles that contains tld files so that jasper will discover them and
* and set them up in jetty. * set them up in jetty.
* *
* For example: -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh * For example:
* Otherwise use an attribute to the WebAppDeployer * -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet
* &lt;New class="org.eclipse.jetty.deploy.providers.WebAppProvider"&gt; * ,com.opensymphony.module.sitemesh Otherwise use an attribute to the
* .... * WebAppDeployer &lt;New
* &lt;Set name="tldBundles"&gt;&ltProperty name="org.eclipse.jetty.osgi.tldsbundles" default="" /&gt;&lt;/Set&gt; * class="org.eclipse.jetty.deploy.providers.WebAppProvider"&gt; .... &lt;Set
* &lt;New&gt; * name="tldBundles"&gt;&ltProperty name="org.eclipse.jetty.osgi.tldsbundles"
* default="" /&gt;&lt;/Set&gt; &lt;New&gt;
*/ */
public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistrationCustomizer public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistrationCustomizer
{ {
/** /**
* To plug into jasper bundles that contain tld files * To plug into jasper bundles that contain tld files please use a list of
* please use a list of bundle's symbolic names: * bundle's symbolic names:
* -Djetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh * -Djetty.osgi.tldbundles=org.springframework.web.servlet
*/ * ,com.opensymphony.module.sitemesh
public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; */
public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles";
/**
* Union of the tld bundles defined system wide and the one defines as an attribute of the AppProvider. /**
* @param provider * Union of the tld bundles defined system wide and the one defines as an
* @return * attribute of the AppProvider.
*/ *
private static Collection<String> getTldBundles(OSGiAppProvider provider) * @param provider
{ * @return
String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); */
String att = (String)provider.getTldBundles(); private static Collection<String> getTldBundles(OSGiAppProvider provider)
if (sysprop == null && att == null) {
{ String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES);
return Collections.emptySet(); String att = (String) provider.getTldBundles();
} if (sysprop == null && att == null) { return Collections.emptySet(); }
if (att == null) if (att == null)
{ {
att = sysprop; att = sysprop;
} }
else if (sysprop != null) else if (sysprop != null)
{ {
att = att + "," + sysprop; att = att + "," + sysprop;
} }
Collection<String> tldbundles = new HashSet<String>(); Collection<String> tldbundles = new HashSet<String>();
StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false); StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false);
while (tokenizer.hasMoreTokens()) while (tokenizer.hasMoreTokens())
{ {
tldbundles.add(tokenizer.nextToken()); tldbundles.add(tokenizer.nextToken());
} }
return tldbundles; return tldbundles;
} }
/** /**
* @return The location of the jars that contain tld files. * @return The location of the jars that contain tld files. Jasper will
* Jasper will discover them. * discover them.
*/ */
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
{ {
List<URL> urls = new ArrayList<URL>(); List<URL> urls = new ArrayList<URL>();
//naive way of finding those bundles. // naive way of finding those bundles.
//lots of assumptions: for example we assume a single version of each bundle that would contain tld files. // lots of assumptions: for example we assume a single version of each
//this is probably good enough as those tlds are loaded system-wide on jetty. // bundle that would contain tld files.
//to do better than this we need to do it on a per webapp basis. // this is probably good enough as those tlds are loaded system-wide on
//probably using custom properties in the ContextHandler service // jetty.
//and mirroring those in the MANIFEST.MF // to do better than this we need to do it on a per webapp basis.
// probably using custom properties in the ContextHandler service
Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); // and mirroring those in the MANIFEST.MF
Collection<String> tldbundles = getTldBundles(provider);
for (Bundle bundle : bundles) Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles();
{ Collection<String> tldbundles = getTldBundles(provider);
if (tldbundles.contains(bundle.getSymbolicName())) for (Bundle bundle : bundles)
{ {
registerTldBundle(locatorHelper, bundle, urls); if (tldbundles.contains(bundle.getSymbolicName()))
} {
} registerTldBundle(locatorHelper, bundle, urls);
}
return urls.toArray(new URL[urls.size()]); }
return urls.toArray(new URL[urls.size()]);
} }
/** /**
* Resolves the bundle that contains tld files as a set of URLs that will be * Resolves the bundle that contains tld files as a set of URLs that will be
* passed to jasper as a URLClassLoader later on. * passed to jasper as a URLClassLoader later on. Usually that would be a
* Usually that would be a single URL per bundle. * single URL per bundle. But we do some more work if there are jars
* But we do some more work if there are jars embedded in the bundle. * embedded in the bundle.
* *
* The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such * The jasper TldScanner expects a URLClassloader to parse a jar for the
* tag-libraries. Please note that it will work if and only if the bundle is a jar (!) Currently we just hardcode the bundle that contains the jstl * /META-INF/*.tld it may contain. We place the bundles that we know contain
* implemenation. * such tag-libraries. Please note that it will work if and only if the
* bundle is a jar (!) Currently we just hardcode the bundle that contains
* the jstl implemenation.
* *
* A workaround when the tld cannot be parsed with this method is to copy and paste it inside the WEB-INF of the webapplication where it is used. * A workaround when the tld cannot be parsed with this method is to copy
* and paste it inside the WEB-INF of the webapplication where it is used.
* *
* Support only 2 types of packaging for the bundle: - the bundle is a jar (recommended for runtime.) - the bundle is a folder and contain jars in the root * Support only 2 types of packaging for the bundle: - the bundle is a jar
* and/or in the lib folder (nice for PDE developement situations) Unsupported: the bundle is a jar that embeds more jars. * (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE developement situations)
* Unsupported: the bundle is a jar that embeds more jars.
* *
* @param locatorHelper * @param locatorHelper
* @param bundle * @param bundle
@ -152,9 +160,9 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
} }
else else
{ {
urls.add(jasperLocation.toURI().toURL()); urls.add(jasperLocation.toURI().toURL());
} }
} }
} }

View File

@ -36,48 +36,52 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* Fix various shortcomings with the way jasper parses the tld files. * Fix various shortcomings with the way jasper parses the tld files. Plugs the
* Plugs the JSTL tlds assuming that they are packaged with the bundle that contains the JSTL classes. * JSTL tlds assuming that they are packaged with the bundle that contains the
* JSTL classes.
* <p> * <p>
* Pluggable tlds at the server level are handled by {@link PluggableWebAppRegistrationCustomizerImpl}. * Pluggable tlds at the server level are handled by
* {@link PluggableWebAppRegistrationCustomizerImpl}.
* </p> * </p>
*/ */
public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer
{ {
/**
* Default name of a class that belongs to the jstl bundle.
* From that class we locate the corresponding bundle and register it
* as a bundle that contains tld files.
*/
private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
//used to be "org.apache.jasper.runtime.JspFactoryImpl" but now
//the standard tag library implementation are stored in a separate bundle.
//DISABLED please use the tld bundle argument for the OSGiAppProvider
// /**
// * Default name of a class that belongs to the bundle where the Java server Faces tld files are defined.
// * This is the sun's reference implementation.
// */
// private static String DEFAUT_JSF_IMPL_CLASS = "com.sun.faces.config.ConfigureListener";
/** /**
* Default jsp factory implementation. * Default name of a class that belongs to the jstl bundle. From that class
* Idally jasper is osgified and we can use services. * we locate the corresponding bundle and register it as a bundle that
* In the mean time we statically set the jsp factory implementation. * contains tld files.
* bug #299733 */
*/ private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
// used to be "org.apache.jasper.runtime.JspFactoryImpl" but now
// the standard tag library implementation are stored in a separate bundle.
// DISABLED please use the tld bundle argument for the OSGiAppProvider
// /**
// * Default name of a class that belongs to the bundle where the Java
// server Faces tld files are defined.
// * This is the sun's reference implementation.
// */
// private static String DEFAUT_JSF_IMPL_CLASS =
// "com.sun.faces.config.ConfigureListener";
/**
* Default jsp factory implementation. Idally jasper is osgified and we can
* use services. In the mean time we statically set the jsp factory
* implementation. bug #299733
*/
private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
public WebappRegistrationCustomizerImpl() public WebappRegistrationCustomizerImpl()
{ {
fixupDtdResolution(); fixupDtdResolution();
try try
{ {
//sanity check: // sanity check:
Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet"); Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
//System.err.println("found the jsp servlet: " + cl.getName()); // System.err.println("found the jsp servlet: " + cl.getName());
} }
catch (Exception e) catch (Exception e)
{ {
@ -87,18 +91,18 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
} }
try try
{ {
//bug #299733 // bug #299733
JspFactory fact = JspFactory.getDefaultFactory(); JspFactory fact = JspFactory.getDefaultFactory();
if (fact == null) if (fact == null)
{ //bug #299733 { // bug #299733
//JspFactory does a simple Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") // JspFactory does a simple
//however its bundles does not import the jasper package // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl")
//so it fails. let's help things out: // however its bundles does not import the jasper package
fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader() // so it fails. let's help things out:
.loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance(); fact = (JspFactory) JettyBootstrapActivator.class.getClassLoader().loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance();
JspFactory.setDefaultFactory(fact); JspFactory.setDefaultFactory(fact);
} }
} }
catch (Exception e) catch (Exception e)
{ {
@ -106,84 +110,90 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
* The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such * The jasper TldScanner expects a URLClassloader to parse a jar for the
* tag-libraries. Please note that it will work if and only if the bundle is a jar (!) Currently we just hardcode the bundle that contains the jstl * /META-INF/*.tld it may contain. We place the bundles that we know contain
* implemenation. * such tag-libraries. Please note that it will work if and only if the
* bundle is a jar (!) Currently we just hardcode the bundle that contains
* the jstl implemenation.
* *
* A workaround when the tld cannot be parsed with this method is to copy and paste it inside the WEB-INF of the webapplication where it is used. * A workaround when the tld cannot be parsed with this method is to copy
* and paste it inside the WEB-INF of the webapplication where it is used.
* *
* Support only 2 types of packaging for the bundle: - the bundle is a jar (recommended for runtime.) - the bundle is a folder and contain jars in the root * Support only 2 types of packaging for the bundle: - the bundle is a jar
* and/or in the lib folder (nice for PDE developement situations) Unsupported: the bundle is a jar that embeds more jars. * (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE developement situations)
* Unsupported: the bundle is a jar that embeds more jars.
* *
* @return array of URLs * @return array of URLs
* @throws Exception * @throws Exception
*/ */
public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception
{ {
HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>(); HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
//Look for the jstl bundle // Look for the jstl bundle
//We assume the jstl's tlds are defined there. // We assume the jstl's tlds are defined there.
//We assume that the jstl bundle is imported by this bundle // We assume that the jstl bundle is imported by this bundle
//So we can look for this class using this bundle's classloader: // So we can look for this class using this bundle's classloader:
Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); Class<?> jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
classesToAddToTheTldBundles.add(jstlClass); classesToAddToTheTldBundles.add(jstlClass);
ArrayList<URL> urls = new ArrayList<URL>(); ArrayList<URL> urls = new ArrayList<URL>();
for (Class<?> cl : classesToAddToTheTldBundles) for (Class<?> cl : classesToAddToTheTldBundles)
{ {
Bundle tldBundle = FrameworkUtil.getBundle(cl); Bundle tldBundle = FrameworkUtil.getBundle(cl);
File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle); File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle);
if (tldBundleLocation != null && tldBundleLocation.isDirectory()) if (tldBundleLocation != null && tldBundleLocation.isDirectory())
{ {
// try to find the jar files inside this folder // try to find the jar files inside this folder
for (File f : tldBundleLocation.listFiles()) for (File f : tldBundleLocation.listFiles())
{ {
if (f.getName().endsWith(".jar") && f.isFile()) if (f.getName().endsWith(".jar") && f.isFile())
{ {
urls.add(f.toURI().toURL()); urls.add(f.toURI().toURL());
} }
else if (f.isDirectory() && f.getName().equals("lib")) else if (f.isDirectory() && f.getName().equals("lib"))
{ {
for (File f2 : tldBundleLocation.listFiles()) for (File f2 : tldBundleLocation.listFiles())
{ {
if (f2.getName().endsWith(".jar") && f2.isFile()) if (f2.getName().endsWith(".jar") && f2.isFile())
{ {
urls.add(f2.toURI().toURL()); urls.add(f2.toURI().toURL());
} }
} }
} }
} }
} }
else if (tldBundleLocation != null) else if (tldBundleLocation != null)
{ {
urls.add(tldBundleLocation.toURI().toURL()); urls.add(tldBundleLocation.toURI().toURL());
} }
} }
return urls.toArray(new URL[urls.size()]); return urls.toArray(new URL[urls.size()]);
} }
/** /**
* Jasper resolves the dtd when it parses a taglib descriptor. * Jasper resolves the dtd when it parses a taglib descriptor. It uses this
* It uses this code to do that: ParserUtils.getClass().getResourceAsStream(resourcePath); where * code to do that:
* resourcePath is for example: /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd Unfortunately, * ParserUtils.getClass().getResourceAsStream(resourcePath); where
* the dtd file is not in the exact same classloader as * resourcePath is for example:
* ParserUtils class and the dtds are packaged in 2 separate bundles. * /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd Unfortunately, the
* OSGi does not look in the dependencies' classloader when a resource is searched. * dtd file is not in the exact same classloader as ParserUtils class and
* the dtds are packaged in 2 separate bundles. OSGi does not look in the
* dependencies' classloader when a resource is searched.
* <p> * <p>
* The workaround consists of setting the entity resolver. That is a patch * The workaround consists of setting the entity resolver. That is a patch
* added to the version of glassfish-jasper-jetty. IT is also present in the latest * added to the version of glassfish-jasper-jetty. IT is also present in the
* version of glassfish jasper. Could not use introspection to set new value * latest version of glassfish jasper. Could not use introspection to set
* on a static friendly field :( * new value on a static friendly field :(
* </p> * </p>
*/ */
void fixupDtdResolution() void fixupDtdResolution()
{ {
try try
{ {
@ -198,29 +208,23 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
} }
/** /**
* Instead of using the ParserUtil's classloader, we use a class that is indeed next to the resource for sure. * Instead of using the ParserUtil's classloader, we use a class that is
* indeed next to the resource for sure.
*/ */
static class MyFixedupEntityResolver implements EntityResolver static class MyFixedupEntityResolver implements EntityResolver
{ {
/** /**
* Same values than in ParserUtils... * Same values than in ParserUtils...
*/ */
static final String[] CACHED_DTD_PUBLIC_IDS = static final String[] CACHED_DTD_PUBLIC_IDS = { Constants.TAGLIB_DTD_PUBLIC_ID_11, Constants.TAGLIB_DTD_PUBLIC_ID_12,
{ Constants.TAGLIB_DTD_PUBLIC_ID_11, Constants.TAGLIB_DTD_PUBLIC_ID_12, Constants.WEBAPP_DTD_PUBLIC_ID_22, Constants.WEBAPP_DTD_PUBLIC_ID_23, };
Constants.WEBAPP_DTD_PUBLIC_ID_22, Constants.WEBAPP_DTD_PUBLIC_ID_23, };
static final String[] CACHED_DTD_RESOURCE_PATHS = static final String[] CACHED_DTD_RESOURCE_PATHS = { Constants.TAGLIB_DTD_RESOURCE_PATH_11, Constants.TAGLIB_DTD_RESOURCE_PATH_12,
{ Constants.TAGLIB_DTD_RESOURCE_PATH_11, Constants.WEBAPP_DTD_RESOURCE_PATH_22, Constants.WEBAPP_DTD_RESOURCE_PATH_23, };
Constants.TAGLIB_DTD_RESOURCE_PATH_12,
Constants.WEBAPP_DTD_RESOURCE_PATH_22, static final String[] CACHED_SCHEMA_RESOURCE_PATHS = { Constants.TAGLIB_SCHEMA_RESOURCE_PATH_20, Constants.TAGLIB_SCHEMA_RESOURCE_PATH_21,
Constants.WEBAPP_DTD_RESOURCE_PATH_23, }; Constants.WEBAPP_SCHEMA_RESOURCE_PATH_24, Constants.WEBAPP_SCHEMA_RESOURCE_PATH_25, };
static final String[] CACHED_SCHEMA_RESOURCE_PATHS = {
Constants.TAGLIB_SCHEMA_RESOURCE_PATH_20,
Constants.TAGLIB_SCHEMA_RESOURCE_PATH_21,
Constants.WEBAPP_SCHEMA_RESOURCE_PATH_24,
Constants.WEBAPP_SCHEMA_RESOURCE_PATH_25,
};
public InputSource resolveEntity(String publicId, String systemId) throws SAXException public InputSource resolveEntity(String publicId, String systemId) throws SAXException
{ {
for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++) for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++)
@ -241,10 +245,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
input = this.getClass().getResourceAsStream(resourcePath); input = this.getClass().getResourceAsStream(resourcePath);
} }
} }
if (input == null) if (input == null) { throw new SAXException(Localizer.getMessage("jsp.error.internal.filenotfound", resourcePath)); }
{
throw new SAXException(Localizer.getMessage("jsp.error.internal.filenotfound",resourcePath));
}
InputSource isrc = new InputSource(input); InputSource isrc = new InputSource(input);
return isrc; return isrc;
} }
@ -253,5 +254,5 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
return null; return null;
} }
} }
} }

View File

@ -19,15 +19,14 @@ import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
/** /**
* Pseudo fragment activator. * Pseudo fragment activator. Called by the main org.eclipse.jetty.osgi.boot
* Called by the main org.eclipse.jetty.osgi.boot bundle. * bundle. Please note: this is not a real BundleActivator. Simply something
* Please note: this is not a real BundleActivator. Simply something called back by * called back by the host bundle.
* the host bundle.
* <p> * <p>
* It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: * It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: this is
* this is because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name * because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name of this
* of this fragment. From that name, the PackageadminTracker will call * fragment. From that name, the PackageadminTracker will call this class. IN a
* this class. IN a different package it won't be called. * different package it won't be called.
* </p> * </p>
*/ */
public class FragmentActivator implements BundleActivator public class FragmentActivator implements BundleActivator
@ -35,8 +34,9 @@ public class FragmentActivator implements BundleActivator
/** /**
* *
*/ */
public void start(BundleContext context) throws Exception { public void start(BundleContext context) throws Exception
System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString()); {
System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString());
WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl()); WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl());
WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl()); WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl());
} }
@ -44,7 +44,8 @@ public class FragmentActivator implements BundleActivator
/** /**
* *
*/ */
public void stop(BundleContext context) throws Exception { public void stop(BundleContext context) throws Exception
{
} }
} }

View File

@ -35,123 +35,120 @@ import org.osgi.util.tracker.ServiceTracker;
* Replacement for {@link TagLibConfiguration} for the OSGi integration. * Replacement for {@link TagLibConfiguration} for the OSGi integration.
* </p> * </p>
* <p> * <p>
* In the case of a WAB, tlds can be located in OSGi bundles that are dependencies * In the case of a WAB, tlds can be located in OSGi bundles that are
* of the WAB. * dependencies of the WAB. It is expected that each WAB lists the
* It is expected that each WAB lists the symbolic-names of the bundles that contain * symbolic-names of the bundles that contain tld files. The list is defined as
* tld files. The list is defined as the value of the header 'Require-TldBundle' * the value of the header 'Require-TldBundle'
* </p> * </p>
* <p> * <p>
* Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971 * Discussions about this are logged in
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
* </p> * </p>
*/ */
public class TagLibOSGiConfiguration extends TagLibConfiguration public class TagLibOSGiConfiguration extends TagLibConfiguration
{ {
private static final Logger LOG = Log.getLogger(TagLibOSGiConfiguration.class); private static final Logger LOG = Log.getLogger(TagLibOSGiConfiguration.class);
private ServiceTracker packageAdminServiceTracker = null; private ServiceTracker packageAdminServiceTracker = null;
/** /**
* Override the preConfigure; locates the bundles that contain * Override the preConfigure; locates the bundles that contain tld files
* tld files according to the value of the manifest header Require-TldBundle. * according to the value of the manifest header Require-TldBundle.
* <p> * <p>
* Set or add to the property TldProcessor.TLDResources the list of located jars * Set or add to the property TldProcessor.TLDResources the list of located
* so that the super class will scan those. * jars so that the super class will scan those.
* </p> * </p>
*/ */
public void preConfigure(WebAppContext context) throws Exception public void preConfigure(WebAppContext context) throws Exception
{ {
String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); String requireTldBundle = (String) context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null) if (requireTldBundle != null)
{ {
Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle); Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
if (resources != null && !resources.isEmpty()) if (resources != null && !resources.isEmpty())
{ {
Collection<Resource> previouslySet = (Collection<Resource>) Collection<Resource> previouslySet = (Collection<Resource>) context.getAttribute(TagLibConfiguration.TLD_RESOURCES);
context.getAttribute(TagLibConfiguration.TLD_RESOURCES); if (previouslySet != null)
if (previouslySet != null) {
{ resources.addAll(previouslySet);
resources.addAll(previouslySet); }
} context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources);
context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources); }
} }
} super.preConfigure(context);
super.preConfigure(context);
} }
/** /**
* @param requireTldBundle The comma separated list of bundles' symbolic names * @param requireTldBundle The comma separated list of bundles' symbolic
* that contain tld for this osgi webapp. * names that contain tld for this osgi webapp.
* @return The collection of jars or folders that match those bundles. * @return The collection of jars or folders that match those bundles.
*/ */
private Collection<Resource> getRequireTldBundleAsJettyResources( private Collection<Resource> getRequireTldBundleAsJettyResources(WebAppContext context, String requireTldBundle)
WebAppContext context, String requireTldBundle)
{ {
Bundle bundle = (Bundle) Bundle bundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); PackageAdmin packAdmin = getBundleAdmin();
PackageAdmin packAdmin = getBundleAdmin(); String[] symbNames = requireTldBundle.split(", ");
String[] symbNames = requireTldBundle.split(", "); Collection<Resource> tlds = new LinkedHashSet<Resource>();
Collection<Resource> tlds = new LinkedHashSet<Resource>(); for (String symbName : symbNames)
for (String symbName : symbNames) {
{ Bundle[] bs = packAdmin.getBundles(symbName, null);
Bundle[] bs = packAdmin.getBundles(symbName, null); if (bs == null || bs.length == 0)
if (bs == null || bs.length == 0) {
{ throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
throw new IllegalArgumentException("Unable to locate the bundle '" + "' specified in the "
+ symbName + "' specified in the " + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE + " of the manifest of "
+ " of the manifest of " + bundle.getSymbolicName());
+ bundle.getSymbolicName()); }
} // take the first one as it is the most recent version?
//take the first one as it is the most recent version? Enumeration<URL> en = bs[0].findEntries("META-INF", "*.tld", false);
Enumeration<URL> en = bs[0].findEntries("META-INF", "*.tld", false); boolean atLeastOneTldFound = false;
boolean atLeastOneTldFound = false; while (en.hasMoreElements())
while (en.hasMoreElements()) {
{ atLeastOneTldFound = true;
atLeastOneTldFound = true; URL oriUrl = en.nextElement();
URL oriUrl = en.nextElement(); URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl);
URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl); Resource tldResource;
Resource tldResource; try
try {
{ tldResource = Resource.newResource(url);
tldResource = Resource.newResource(url); }
} catch (IOException e)
catch (IOException e) {
{ throw new IllegalArgumentException("Unable to locate the " + "tld resource in '"
throw new IllegalArgumentException("Unable to locate the " + url.toString()
+ "tld resource in '" + "' in the bundle '"
+ url.toString() + bs[0].getSymbolicName()
+ "' in the bundle '" + bs[0].getSymbolicName() + "' while registering the "
+ "' while registering the " + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE + " of the manifest of "
+ " of the manifest of " + bundle.getSymbolicName(), e);
+ bundle.getSymbolicName(), e); }
} tlds.add(tldResource);
tlds.add(tldResource); }
} if (!atLeastOneTldFound)
if (!atLeastOneTldFound) {
{ LOG.warn("No '/META-INF/*.tld' resources were found " + " in the bundle '"
LOG.warn("No '/META-INF/*.tld' resources were found " + bs[0].getSymbolicName()
+ " in the bundle '" + bs[0].getSymbolicName() + "' while registering the "
+ "' while registering the " + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE + " of the manifest of "
+ " of the manifest of " + bundle.getSymbolicName());
+ bundle.getSymbolicName()); }
} }
} return tlds;
return tlds;
} }
private PackageAdmin getBundleAdmin() private PackageAdmin getBundleAdmin()
{ {
if (packageAdminServiceTracker == null) if (packageAdminServiceTracker == null)
{ {
Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle(); Bundle bootBundle = ((BundleReference) OSGiWebappConstants.class.getClassLoader()).getBundle();
packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(), packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null);
PackageAdmin.class.getName(), null); packageAdminServiceTracker.open();
packageAdminServiceTracker.open(); }
} return (PackageAdmin) packageAdminServiceTracker.getService();
return (PackageAdmin) packageAdminServiceTracker.getService(); }
}
} }

View File

@ -24,12 +24,13 @@ import java.util.jar.Manifest;
import org.eclipse.jetty.osgi.boot.warurl.internal.WarBundleManifestGenerator; import org.eclipse.jetty.osgi.boot.warurl.internal.WarBundleManifestGenerator;
import org.eclipse.jetty.osgi.boot.warurl.internal.WarURLConnection; import org.eclipse.jetty.osgi.boot.warurl.internal.WarURLConnection;
import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.service.url.AbstractURLStreamHandlerService; import org.osgi.service.url.AbstractURLStreamHandlerService;
/** /**
* RFC-66: support for the "war" protocol * RFC-66: support for the "war" protocol We are reusing the parsing of the
* We are reusing the parsing of the query string from jetty. * query string from jetty. If we wanted to not depend on jetty at all we could
* If we wanted to not depend on jetty at all we could duplicate that method here * duplicate that method here
*/ */
public class WarUrlStreamHandler extends AbstractURLStreamHandlerService public class WarUrlStreamHandler extends AbstractURLStreamHandlerService
{ {
@ -40,11 +41,11 @@ public class WarUrlStreamHandler extends AbstractURLStreamHandlerService
@Override @Override
public URLConnection openConnection(URL url) throws IOException public URLConnection openConnection(URL url) throws IOException
{ {
//remove the war scheme. // remove the war scheme.
URL actual = new URL(url.toString().substring("war:".length())); URL actual = new URL(url.toString().substring("war:".length()));
//let's do some basic tests: see if this is a folder or not. // let's do some basic tests: see if this is a folder or not.
//if it is a folder. we will try to support it. // if it is a folder. we will try to support it.
if (actual.getProtocol().equals("file")) if (actual.getProtocol().equals("file"))
{ {
File file = new File(URIUtil.encodePath(actual.getPath())); File file = new File(URIUtil.encodePath(actual.getPath()));
@ -52,34 +53,48 @@ public class WarUrlStreamHandler extends AbstractURLStreamHandlerService
{ {
if (file.isDirectory()) if (file.isDirectory())
{ {
//TODO (not mandatory for rfc66 though) // TODO (not mandatory for rfc66 though)
} }
} }
} }
// if (actual.toString().startsWith("file:/") && ! actual.to) // if (actual.toString().startsWith("file:/") && ! actual.to)
URLConnection ori = (URLConnection) actual.openConnection(); URLConnection ori = (URLConnection) actual.openConnection();
ori.setDefaultUseCaches(Resource.getDefaultUseCaches());
JarURLConnection jarOri = null; JarURLConnection jarOri = null;
try { try
{
if (ori instanceof JarURLConnection) if (ori instanceof JarURLConnection)
{ {
jarOri = (JarURLConnection)ori; jarOri = (JarURLConnection) ori;
} }
else else
{ {
jarOri = (JarURLConnection) new URL("jar:"+actual.toString() + "!/").openConnection(); jarOri = (JarURLConnection) new URL("jar:" + actual.toString() + "!/").openConnection();
jarOri.setDefaultUseCaches(Resource.getDefaultUseCaches());
} }
Manifest mf = WarBundleManifestGenerator.createBundleManifest( Manifest mf = WarBundleManifestGenerator.createBundleManifest(jarOri.getManifest(), url, jarOri.getJarFile());
jarOri.getManifest(), url, jarOri.getJarFile()); try
try { jarOri.getJarFile().close(); jarOri = null; } catch (Throwable t) {} {
return new WarURLConnection(actual,mf); jarOri.getJarFile().close();
jarOri = null;
}
catch (Throwable t)
{
}
return new WarURLConnection(actual, mf);
} }
finally finally
{ {
if (jarOri != null) try { jarOri.getJarFile().close(); } catch (Throwable t) {} if (jarOri != null) try
{
jarOri.getJarFile().close();
}
catch (Throwable t)
{
}
} }
} }
} }

View File

@ -31,6 +31,7 @@ import java.util.jar.Manifest;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.resource.Resource;
/** /**
* Facade for a URLConnection that will read a jar and substitute its * Facade for a URLConnection that will read a jar and substitute its
@ -106,6 +107,7 @@ public class WarURLConnection extends URLConnection
{ {
super(url); super(url);
_conn = url.openConnection(); _conn = url.openConnection();
_conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
_mf = mf; _mf = mf;
} }
@Override @Override

View File

@ -66,15 +66,19 @@ public class JettyBootstrapActivator implements BundleActivator
} }
private ServiceRegistration _registeredServer; private ServiceRegistration _registeredServer;
private Server _server; private Server _server;
private JettyContextHandlerServiceTracker _jettyContextHandlerTracker; private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
private PackageAdminServiceTracker _packageAdminServiceTracker; private PackageAdminServiceTracker _packageAdminServiceTracker;
private BundleTracker _webBundleTracker; private BundleTracker _webBundleTracker;
private BundleContext _bundleContext; private BundleContext _bundleContext;
// private ServiceRegistration _jettyServerFactoryService; // private ServiceRegistration _jettyServerFactoryService;
private JettyServerServiceTracker _jettyServerServiceTracker; private JettyServerServiceTracker _jettyServerServiceTracker;
/** /**
* Setup a new jetty Server, registers it as a service. Setup the Service * Setup a new jetty Server, registers it as a service. Setup the Service
@ -93,30 +97,31 @@ public class JettyBootstrapActivator implements BundleActivator
// should activate. // should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context); _packageAdminServiceTracker = new PackageAdminServiceTracker(context);
_jettyServerServiceTracker = new JettyServerServiceTracker(); _jettyServerServiceTracker = new JettyServerServiceTracker();
context.addServiceListener(_jettyServerServiceTracker,"(objectclass=" + Server.class.getName() + ")"); context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
// Register the Jetty Server Factory as a ManagedServiceFactory:
// Properties jettyServerMgdFactoryServiceProps = new Properties();
// jettyServerMgdFactoryServiceProps.put("pid",
// OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
// _jettyServerFactoryService = context.registerService(
// ManagedServiceFactory.class.getName(), new
// JettyServersManagedFactory(),
// jettyServerMgdFactoryServiceProps);
//Register the Jetty Server Factory as a ManagedServiceFactory:
// Properties jettyServerMgdFactoryServiceProps = new Properties();
// jettyServerMgdFactoryServiceProps.put("pid", OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
// _jettyServerFactoryService = context.registerService(
// ManagedServiceFactory.class.getName(), new JettyServersManagedFactory(),
// jettyServerMgdFactoryServiceProps);
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker); _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
// the tracker in charge of the actual deployment // the tracker in charge of the actual deployment
// and that will configure and start the jetty server. // and that will configure and start the jetty server.
context.addServiceListener(_jettyContextHandlerTracker,"(objectclass=" + ContextHandler.class.getName() + ")"); context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
//see if we shoult start a default jetty instance right now. // see if we shoult start a default jetty instance right now.
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context); DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
// now ready to support the Extender pattern: // now ready to support the Extender pattern:
_webBundleTracker = new BundleTracker(context, _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
_webBundleTracker.open(); _webBundleTracker.open();
} }
/* /*
@ -129,12 +134,12 @@ public class JettyBootstrapActivator implements BundleActivator
{ {
try try
{ {
if (_webBundleTracker != null) if (_webBundleTracker != null)
{ {
_webBundleTracker.close(); _webBundleTracker.close();
_webBundleTracker = null; _webBundleTracker = null;
} }
if (_jettyContextHandlerTracker != null) if (_jettyContextHandlerTracker != null)
{ {
_jettyContextHandlerTracker.stop(); _jettyContextHandlerTracker.stop();
@ -143,7 +148,7 @@ public class JettyBootstrapActivator implements BundleActivator
} }
if (_jettyServerServiceTracker != null) if (_jettyServerServiceTracker != null)
{ {
_jettyServerServiceTracker.stop(); _jettyServerServiceTracker.stop();
context.removeServiceListener(_jettyServerServiceTracker); context.removeServiceListener(_jettyServerServiceTracker);
_jettyServerServiceTracker = null; _jettyServerServiceTracker = null;
} }
@ -165,31 +170,31 @@ public class JettyBootstrapActivator implements BundleActivator
} }
finally finally
{ {
_registeredServer = null; _registeredServer = null;
} }
} }
// if (_jettyServerFactoryService != null) // if (_jettyServerFactoryService != null)
// { // {
// try // try
// { // {
// _jettyServerFactoryService.unregister(); // _jettyServerFactoryService.unregister();
// } // }
// catch (IllegalArgumentException ill) // catch (IllegalArgumentException ill)
// { // {
// // already unregistered. // // already unregistered.
// } // }
// finally // finally
// { // {
// _jettyServerFactoryService = null; // _jettyServerFactoryService = null;
// } // }
// } // }
} }
finally finally
{ {
if (_server != null) if (_server != null)
{ {
_server.stop(); _server.stop();
} }
INSTANCE = null; INSTANCE = null;
} }
@ -200,27 +205,25 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker * registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment. * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
* *
* @param contributor * @param contributor The bundle
* The bundle * @param webappFolderPath The path to the root of the webapp. Must be a
* @param webappFolderPath * path relative to bundle; either an absolute path.
* The path to the root of the webapp. Must be a path relative to * @param contextPath The context path. Must start with "/"
* bundle; either an absolute path.
* @param contextPath
* The context path. Must start with "/"
* @throws Exception * @throws Exception
*/ */
public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
{ {
checkBundleActivated(); checkBundleActivated();
WebAppContext contextHandler = new WebAppContext(); WebAppContext contextHandler = new WebAppContext();
Dictionary dic = new Hashtable(); Dictionary dic = new Hashtable();
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath); dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath); dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
String requireTldBundle = (String)contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null) { if (requireTldBundle != null)
dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle); {
dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
} }
contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
} }
/** /**
@ -228,24 +231,20 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker * registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment. * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
* *
* @param contributor * @param contributor The bundle
* The bundle * @param webappFolderPath The path to the root of the webapp. Must be a
* @param webappFolderPath * path relative to bundle; either an absolute path.
* The path to the root of the webapp. Must be a path relative to * @param contextPath The context path. Must start with "/"
* bundle; either an absolute path. * @param dic TODO: parameter description
* @param contextPath
* The context path. Must start with "/"
* @param dic
* TODO: parameter description
* @throws Exception * @throws Exception
*/ */
public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
{ {
checkBundleActivated(); checkBundleActivated();
WebAppContext contextHandler = new WebAppContext(); WebAppContext contextHandler = new WebAppContext();
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath); dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath); dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
} }
/** /**
@ -253,16 +252,14 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker * registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment. * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
* *
* @param contributor * @param contributor The bundle that registers a new context
* The bundle that registers a new context * @param contextFilePath The path to the file inside the bundle that
* @param contextFilePath * defines the context.
* The path to the file inside the bundle that defines the
* context.
* @throws Exception * @throws Exception
*/ */
public static void registerContext(Bundle contributor, String contextFilePath) throws Exception public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
{ {
registerContext(contributor,contextFilePath,new Hashtable<String, String>()); registerContext(contributor, contextFilePath, new Hashtable<String, String>());
} }
/** /**
@ -270,33 +267,30 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker * registers it as an OSGi service. The tracker
* {@link JettyContextHandlerServiceTracker} will do the actual deployment. * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
* *
* @param contributor * @param contributor The bundle that registers a new context
* The bundle that registers a new context * @param contextFilePath The path to the file inside the bundle that
* @param contextFilePath * defines the context.
* The path to the file inside the bundle that defines the * @param dic TODO: parameter description
* context.
* @param dic
* TODO: parameter description
* @throws Exception * @throws Exception
*/ */
public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
{ {
checkBundleActivated(); checkBundleActivated();
ContextHandler contextHandler = new ContextHandler(); ContextHandler contextHandler = new ContextHandler();
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH,contextFilePath); dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE,Boolean.TRUE.toString()); dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
} }
public static void unregister(String contextPath) public static void unregister(String contextPath)
{ {
// todo // todo
} }
/** /**
* Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
* when one fo the static methods to register a webapp is called we should make sure that * when one fo the static methods to register a webapp is called we should
* the bundle is started. * make sure that the bundle is started.
*/ */
private static void checkBundleActivated() private static void checkBundleActivated()
{ {
@ -313,7 +307,7 @@ public class JettyBootstrapActivator implements BundleActivator
} }
} }
} }
/** /**
* @return The bundle context for this bundle. * @return The bundle context for this bundle.
*/ */
@ -322,6 +316,5 @@ public class JettyBootstrapActivator implements BundleActivator
checkBundleActivated(); checkBundleActivated();
return INSTANCE._bundleContext; return INSTANCE._bundleContext;
} }
} }

View File

@ -348,7 +348,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
} }
catch (IOException e) catch (IOException e)
{ {
e.printStackTrace(); LOG.warn(e);
return null; return null;
} }
} }
@ -369,11 +369,11 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
} }
catch (IOException e) catch (IOException e)
{ {
e.printStackTrace(); LOG.warn(e);
return null; return null;
} }
} }
public boolean isExtract() public boolean isExtract()
{ {
return _extractWars; return _extractWars;

View File

@ -167,18 +167,21 @@ public class DefaultJettyAtJettyHomeHelper {
*/ */
private static String getJettyConfigurationURLs(File jettyhome) private static String getJettyConfigurationURLs(File jettyhome)
{ {
String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml"); String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml");
StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false); StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false);
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
while (tokenizer.hasMoreTokens()) while (tokenizer.hasMoreTokens())
{ {
String next = tokenizer.nextToken().trim(); String next = tokenizer.nextToken().trim();
if (!next.startsWith("/") && next.indexOf(':') == -1) if (!next.startsWith("/") && next.indexOf(':') == -1)
{ {
try { try
{
next = new File(jettyhome, next).toURI().toURL().toString(); next = new File(jettyhome, next).toURI().toURL().toString();
} catch (MalformedURLException e) { }
e.printStackTrace(); catch (MalformedURLException e)
{
LOG.warn(e);
continue; continue;
} }
} }
@ -225,7 +228,7 @@ public class DefaultJettyAtJettyHomeHelper {
} }
if (enUrls == null || !enUrls.hasMoreElements()) if (enUrls == null || !enUrls.hasMoreElements())
{ {
System.err.println("Unable to locate a jetty configuration file for " + etcFile); LOG.warn("Unable to locate a jetty configuration file for " + etcFile);
} }
if (enUrls != null) if (enUrls != null)
{ {

View File

@ -24,6 +24,8 @@ import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHom
import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry; import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper; import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle; import org.osgi.framework.Bundle;
@ -52,15 +54,17 @@ import org.osgi.framework.ServiceReference;
*/ */
public class JettyContextHandlerServiceTracker implements ServiceListener public class JettyContextHandlerServiceTracker implements ServiceListener
{ {
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
/** New style: ability to manage multiple jetty instances */
private final IManagedJettyServerRegistry _registry; /** New style: ability to manage multiple jetty instances */
private final IManagedJettyServerRegistry _registry;
/** The context-handler to deactivate indexed by context handler */ /** The context-handler to deactivate indexed by context handler */
private Map<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>(); private Map<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>();
/** /**
* The index is the bundle-symbolic-name/path/to/context/file when there is such thing * The index is the bundle-symbolic-name/path/to/context/file when there is
* such thing
*/ */
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>(); private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
@ -72,7 +76,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
*/ */
public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception
{ {
_registry = registry; _registry = registry;
} }
public void stop() throws Exception public void stop() throws Exception
@ -85,19 +89,16 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// nothing to stop in the WebappRegistrationHelper // nothing to stop in the WebappRegistrationHelper
} }
/** /**
* @param contextHome Parent folder where the context files can override the context files * @param contextHome Parent folder where the context files can override the
* defined in the web bundles: equivalent to the contexts folder in a traditional * context files defined in the web bundles: equivalent to the
* jetty installation. * contexts folder in a traditional jetty installation. when
* when null, just do nothing. * null, just do nothing.
*/ */
protected void setupContextHomeScanner(File contextHome) throws IOException protected void setupContextHomeScanner(File contextHome) throws IOException
{ {
if (contextHome == null) if (contextHome == null) { return; }
{
return;
}
final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath(); final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath();
_scanner = new Scanner(); _scanner = new Scanner();
_scanner.setRecursive(true); _scanner.setRecursive(true);
@ -132,8 +133,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
/** /**
* Receives notification that a service has had a lifecycle change. * Receives notification that a service has had a lifecycle change.
* *
* @param ev * @param ev The <code>ServiceEvent</code> object.
* The <code>ServiceEvent</code> object.
*/ */
public void serviceChanged(ServiceEvent ev) public void serviceChanged(ServiceEvent ev)
{ {
@ -148,12 +148,11 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
{ {
try try
{ {
getWebBundleDeployerHelp(sr).unregister(ctxtHandler); getWebBundleDeployerHelp(sr).unregister(ctxtHandler);
} }
catch (Exception e) catch (Exception e)
{ {
// TODO Auto-generated catch block __logger.warn(e);
e.printStackTrace();
} }
} }
} }
@ -170,30 +169,32 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
{ {
Bundle contributor = sr.getBundle(); Bundle contributor = sr.getBundle();
BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext(); BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
ContextHandler contextHandler = (ContextHandler)context.getService(sr); ContextHandler contextHandler = (ContextHandler) context.getService(sr);
if (contextHandler.getServer() != null) if (contextHandler.getServer() != null)
{ {
// is configured elsewhere. // is configured elsewhere.
return; return;
} }
String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextHandler instanceof WebAppContext && contextFilePath == null) if (contextHandler instanceof WebAppContext && contextFilePath == null)
//it could be a web-application that will in fact be configured via a context file. // it could be a web-application that will in fact be configured
//that case is identified by the fact that the contextFilePath is not null // via a context file.
//in that case we must use the register context methods. // that case is identified by the fact that the contextFilePath
// is not null
// in that case we must use the register context methods.
{ {
WebAppContext webapp = (WebAppContext)contextHandler; WebAppContext webapp = (WebAppContext) contextHandler;
String contextPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH); String contextPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH);
if (contextPath == null) if (contextPath == null)
{ {
contextPath = webapp.getContextPath(); contextPath = webapp.getContextPath();
} }
String webXmlPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH); String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH);
if (webXmlPath == null) if (webXmlPath == null)
{ {
webXmlPath = webapp.getDescriptor(); webXmlPath = webapp.getDescriptor();
} }
String defaultWebXmlPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH); String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH);
if (defaultWebXmlPath == null) if (defaultWebXmlPath == null)
{ {
String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME); String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME);
@ -202,7 +203,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
File etc = new File(jettyHome, "etc"); File etc = new File(jettyHome, "etc");
if (etc.exists() && etc.isDirectory()) if (etc.exists() && etc.isDirectory())
{ {
File webDefault = new File (etc, "webdefault.xml"); File webDefault = new File(etc, "webdefault.xml");
if (webDefault.exists()) if (webDefault.exists())
defaultWebXmlPath = webDefault.getAbsolutePath(); defaultWebXmlPath = webDefault.getAbsolutePath();
else else
@ -212,83 +213,80 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
defaultWebXmlPath = webapp.getDefaultsDescriptor(); defaultWebXmlPath = webapp.getDefaultsDescriptor();
} }
} }
String war = (String)sr.getProperty("war"); String war = (String) sr.getProperty("war");
try try
{ {
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null) if (deployerHelper == null)
{ {
} }
else else
{ {
WebAppContext handler = deployerHelper WebAppContext handler = deployerHelper.registerWebapplication(contributor,
.registerWebapplication(contributor,war,contextPath, war,
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), contextPath,
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
webXmlPath,defaultWebXmlPath,webapp); (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
webXmlPath, defaultWebXmlPath, webapp);
if (handler != null) if (handler != null)
{ {
registerInIndex(handler,sr); registerInIndex(handler, sr);
} }
} }
} }
catch (Throwable e) catch (Throwable e)
{ {
e.printStackTrace(); __logger.warn(e);
} }
} }
else else
{ {
// consider this just an empty skeleton: // consider this just an empty skeleton:
if (contextFilePath == null) if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); }
{
throw new IllegalArgumentException("the property contextFilePath is required");
}
try try
{ {
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
if (deployerHelper == null) if (deployerHelper == null)
{ {
//more warnings? // more warnings?
} }
else else
{ {
if (Boolean.TRUE.toString().equals(sr.getProperty( if (Boolean.TRUE.toString().equals(sr.getProperty(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE)))
IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE))) {
{ contextHandler = null;
contextHandler = null; }
} ContextHandler handler = deployerHelper.registerContext(contributor,
ContextHandler handler = deployerHelper.registerContext(contributor,contextFilePath, contextFilePath,
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE),
(String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE),
contextHandler); contextHandler);
if (handler != null) if (handler != null)
{ {
registerInIndex(handler,sr); registerInIndex(handler, sr);
} }
} }
} }
catch (Throwable e) catch (Throwable e)
{ {
// TODO Auto-generated catch block __logger.warn(e);
e.printStackTrace();
} }
} }
} }
break; break;
} }
} }
private void registerInIndex(ContextHandler handler, ServiceReference sr) private void registerInIndex(ContextHandler handler, ServiceReference sr)
{ {
_indexByServiceReference.put(sr,handler); _indexByServiceReference.put(sr, handler);
String key = getSymbolicNameAndContextFileKey(sr); String key = getSymbolicNameAndContextFileKey(sr);
if (key != null) if (key != null)
{ {
_indexByContextFile.put(key,sr); _indexByContextFile.put(key, sr);
} }
} }
@ -320,11 +318,8 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
*/ */
private String getSymbolicNameAndContextFileKey(ServiceReference sr) private String getSymbolicNameAndContextFileKey(ServiceReference sr)
{ {
String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH);
if (contextFilePath != null) if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; }
{
return sr.getBundle().getSymbolicName() + "/" + contextFilePath;
}
return null; return null;
} }
@ -336,17 +331,14 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath) public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath)
{ {
String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath); String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath);
if (key == null) if (key == null) { return; }
{
return;
}
ServiceReference sr = _indexByContextFile.get(key); ServiceReference sr = _indexByContextFile.get(key);
if (sr == null) if (sr == null)
{ {
// nothing to do? // nothing to do?
return; return;
} }
serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED,sr)); serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
} }
/** /**
@ -362,36 +354,31 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
// warning? // warning?
return null; return null;
} }
return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\','/'); return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\', '/');
} }
/** /**
* @return The server on which this webapp is meant to be deployed * @return The server on which this webapp is meant to be deployed
*/ */
private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName) private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
{ {
if (_registry == null) if (_registry == null) { return null; }
{ if (managedServerName == null)
return null; {
} managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
if (managedServerName == null) }
{ ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName);
managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME; // System.err.println("Returning " + managedServerName + " = " +
} // wrapper);
ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName); return wrapper;
//System.err.println("Returning " + managedServerName + " = " + wrapper);
return wrapper;
} }
private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr) private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr)
{ {
if (_registry == null) if (_registry == null) { return null; }
{ String managedServerName = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
return null; ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName);
} return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null;
String managedServerName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName);
return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null;
} }
} }

View File

@ -247,7 +247,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
catch (IOException e) catch (IOException e)
{ {
// nevermind. just trying our best // nevermind. just trying our best
e.printStackTrace(); __logger.ignore(e);
} }
return true; return true;
} }
@ -279,7 +279,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
catch (Throwable t) catch (Throwable t)
{ {
// humf that will hurt if it does not work. // humf that will hurt if it does not work.
t.printStackTrace(); __logger.warn("Unable to set webappcontext", t);
} }
} }
} }

View File

@ -85,6 +85,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* implementation should set a different implementation. * implementation should set a different implementation.
*/ */
public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null; public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null;
/** /**
* By default set to: {@link DefaultBundleClassLoaderHelper}. It supports * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports
* equinox and apache-felix fragment bundles that are specific to an OSGi * equinox and apache-felix fragment bundles that are specific to an OSGi
@ -97,8 +98,9 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* equinox and apache-felix fragment bundles that are specific to an OSGi * equinox and apache-felix fragment bundles that are specific to an OSGi
* implementation should set a different implementation. * implementation should set a different implementation.
* <p> * <p>
* Several of those objects can be added here: For example we could have an optional fragment that setups * Several of those objects can be added here: For example we could have an
* a specific implementation of JSF for the whole of jetty-osgi. * optional fragment that setups a specific implementation of JSF for the
* whole of jetty-osgi.
* </p> * </p>
*/ */
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>(); public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
@ -127,7 +129,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
// setup the custom BundleClassLoaderHelper // setup the custom BundleClassLoaderHelper
try try
{ {
BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance(); BUNDLE_CLASS_LOADER_HELPER = (BundleClassLoaderHelper) Class.forName(BundleClassLoaderHelper.CLASS_NAME).newInstance();
} }
catch (Throwable t) catch (Throwable t)
{ {
@ -137,7 +139,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
// setup the custom FileLocatorHelper // setup the custom FileLocatorHelper
try try
{ {
BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance(); BUNDLE_FILE_LOCATOR_HELPER = (BundleFileLocatorHelper) Class.forName(BundleFileLocatorHelper.CLASS_NAME).newInstance();
} }
catch (Throwable t) catch (Throwable t)
{ {
@ -150,31 +152,25 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
/** /**
* Deploy a new web application on the jetty server. * Deploy a new web application on the jetty server.
* *
* @param bundle * @param bundle The bundle
* The bundle * @param webappFolderPath The path to the root of the webapp. Must be a
* @param webappFolderPath * path relative to bundle; either an absolute path.
* The path to the root of the webapp. Must be a path relative to * @param contextPath The context path. Must start with "/"
* bundle; either an absolute path.
* @param contextPath
* The context path. Must start with "/"
* @param extraClasspath * @param extraClasspath
* @param overrideBundleInstallLocation * @param overrideBundleInstallLocation
* @param requireTldBundle The list of bundles's symbolic names that contain * @param requireTldBundle The list of bundles's symbolic names that contain
* tld files that are required by this WAB. * tld files that are required by this WAB.
* @param webXmlPath * @param webXmlPath
* @param defaultWebXmlPath * @param defaultWebXmlPath TODO: parameter description
* TODO: parameter description
* @return The contexthandler created and started * @return The contexthandler created and started
* @throws Exception * @throws Exception
*/ */
public WebAppContext registerWebapplication(Bundle bundle, public WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
String webappFolderPath, String contextPath, String extraClasspath, String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath, String defaultWebXmlPath,
String overrideBundleInstallLocation, WebAppContext webAppContext) throws Exception
String requireTldBundle, String webXmlPath, {
String defaultWebXmlPath, WebAppContext webAppContext) throws Exception File bundleInstall = overrideBundleInstallLocation == null ? BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle) : new File(
{ overrideBundleInstallLocation);
File bundleInstall = overrideBundleInstallLocation == null?BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle):new File(
overrideBundleInstallLocation);
File webapp = null; File webapp = null;
URL baseWebappInstallURL = null; URL baseWebappInstallURL = null;
@ -186,7 +182,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
else if (bundleInstall != null && bundleInstall.isDirectory()) else if (bundleInstall != null && bundleInstall.isDirectory())
{ {
webapp = new File(bundleInstall,webappFolderPath); webapp = new File(bundleInstall, webappFolderPath);
} }
else if (bundleInstall != null) else if (bundleInstall != null)
{ {
@ -201,26 +197,31 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
{ {
webapp = bundleInstall; webapp = bundleInstall;
} }
if (baseWebappInstallURL == null && (webapp == null || !webapp.exists())) if (baseWebappInstallURL == null && (webapp == null || !webapp.exists())) { throw new IllegalArgumentException(
{ "Unable to locate " + webappFolderPath
throw new IllegalArgumentException("Unable to locate " + webappFolderPath + " inside " + " inside "
+ (bundleInstall != null?bundleInstall.getAbsolutePath():"unlocated bundle '" + bundle.getSymbolicName() + "'")); + (bundleInstall != null ? bundleInstall.getAbsolutePath() : "unlocated bundle '" + bundle.getSymbolicName()
} + "'")); }
if (baseWebappInstallURL == null && webapp != null) if (baseWebappInstallURL == null && webapp != null)
{ {
baseWebappInstallURL = webapp.toURI().toURL(); baseWebappInstallURL = webapp.toURI().toURL();
} }
return registerWebapplication(bundle,webappFolderPath,baseWebappInstallURL,contextPath, return registerWebapplication(bundle, webappFolderPath, baseWebappInstallURL, contextPath, extraClasspath, bundleInstall, requireTldBundle, webXmlPath,
extraClasspath,bundleInstall,requireTldBundle,webXmlPath,defaultWebXmlPath,webAppContext); defaultWebXmlPath, webAppContext);
} }
/* (non-Javadoc) /*
* @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerWebapplication(org.osgi.framework.Bundle, java.lang.String, java.io.File, java.lang.String, java.lang.String, java.io.File, java.lang.String, java.lang.String) * (non-Javadoc)
*
* @see
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
* registerWebapplication(org.osgi.framework.Bundle, java.lang.String,
* java.io.File, java.lang.String, java.lang.String, java.io.File,
* java.lang.String, java.lang.String)
*/ */
private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp, private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp, URL baseWebappInstallURL, String contextPath,
URL baseWebappInstallURL, String contextPath, String extraClasspath, File bundleInstall, String extraClasspath, File bundleInstall, String requireTldBundle, String webXmlPath,
String requireTldBundle, String webXmlPath, String defaultWebXmlPath, WebAppContext context) String defaultWebXmlPath, WebAppContext context) throws Exception
throws Exception
{ {
ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
@ -228,10 +229,16 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
try try
{ {
//apply any META-INF/context.xml file that is found to configure the webapp first
applyMetaInfContextXml (contributor, context);
// make sure we provide access to all the jetty bundles by going // make sure we provide access to all the jetty bundles by going
// through this bundle. // through this bundle.
OSGiWebappClassLoader composite = createWebappClassLoader(contributor); OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
// configure with access to all jetty classes and also all the classes // configure with access to all jetty classes and also all the
// classes
// that the contributor gives access to. // that the contributor gives access to.
Thread.currentThread().setContextClassLoader(composite); Thread.currentThread().setContextClassLoader(composite);
@ -248,7 +255,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
else else
{ {
webXml = new File(bundleInstall,webXmlPath); webXml = new File(bundleInstall, webXmlPath);
} }
if (webXml.exists()) if (webXml.exists())
{ {
@ -258,7 +265,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0) if (defaultWebXmlPath == null || defaultWebXmlPath.length() == 0)
{ {
//use the one defined by the OSGiAppProvider. // use the one defined by the OSGiAppProvider.
defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor(); defaultWebXmlPath = _wrapper.getOSGiAppProvider().getDefaultsDescriptor();
} }
if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0) if (defaultWebXmlPath != null && defaultWebXmlPath.length() != 0)
@ -270,7 +277,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
else else
{ {
defaultWebXml = new File(bundleInstall,defaultWebXmlPath); defaultWebXml = new File(bundleInstall, defaultWebXmlPath);
} }
if (defaultWebXml.exists()) if (defaultWebXml.exists())
{ {
@ -278,12 +285,11 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
} }
//other parameters that might be defines on the OSGiAppProvider: // other parameters that might be defines on the OSGiAppProvider:
context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority()); context.setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
configureWebappClassLoader(contributor,context,composite, requireTldBundle); configureWebappClassLoader(contributor, context, composite, requireTldBundle);
configureWebAppContext(context,contributor,requireTldBundle); configureWebAppContext(context, contributor, requireTldBundle);
// @see // @see
// org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext) // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
@ -293,21 +299,22 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
oldServerClasses = context.getServerClasses(); oldServerClasses = context.getServerClasses();
context.setServerClasses(null); context.setServerClasses(null);
_wrapper.getOSGiAppProvider().addContext(contributor,pathInBundleToWebApp,context); _wrapper.getOSGiAppProvider().addContext(contributor, pathInBundleToWebApp, context);
//support for patch resources. ideally this should be done inside a configurator. // support for patch resources. ideally this should be done inside a
List<Resource> patchResources = // configurator.
(List<Resource>)context.getAttribute(WebInfConfiguration.RESOURCE_URLS+".patch"); List<Resource> patchResources = (List<Resource>) context.getAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch");
if (patchResources != null) if (patchResources != null)
{ {
LinkedList<Resource> resourcesPath = new LinkedList<Resource>(); LinkedList<Resource> resourcesPath = new LinkedList<Resource>();
//place the patch resources at the beginning of the lookup path. // place the patch resources at the beginning of the lookup
// path.
resourcesPath.addAll(patchResources); resourcesPath.addAll(patchResources);
//then place the ones from the host web bundle. // then place the ones from the host web bundle.
Resource hostResources = context.getBaseResource(); Resource hostResources = context.getBaseResource();
if (hostResources instanceof ResourceCollection) if (hostResources instanceof ResourceCollection)
{ {
for (Resource re : ((ResourceCollection)hostResources).getResources()) for (Resource re : ((ResourceCollection) hostResources).getResources())
{ {
resourcesPath.add(re); resourcesPath.add(re);
} }
@ -317,8 +324,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
resourcesPath.add(hostResources); resourcesPath.add(hostResources);
} }
ResourceCollection rc = new ResourceCollection(resourcesPath.toArray( ResourceCollection rc = new ResourceCollection(resourcesPath.toArray(new Resource[resourcesPath.size()]));
new Resource[resourcesPath.size()]));
context.setBaseResource(rc); context.setBaseResource(rc);
} }
@ -335,38 +341,41 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
/* (non-Javadoc) /*
* @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#unregister(org.eclipse.jetty.server.handler.ContextHandler) * (non-Javadoc)
*
* @see
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
* unregister(org.eclipse.jetty.server.handler.ContextHandler)
*/ */
public void unregister(ContextHandler contextHandler) throws Exception public void unregister(ContextHandler contextHandler) throws Exception
{ {
_wrapper.getOSGiAppProvider().removeContext(contextHandler); _wrapper.getOSGiAppProvider().removeContext(contextHandler);
} }
/* (non-Javadoc) /*
* @see org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#registerContext(org.osgi.framework.Bundle, java.lang.String, java.lang.String, java.lang.String) * (non-Javadoc)
*
* @see
* org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper#
* registerContext(org.osgi.framework.Bundle, java.lang.String,
* java.lang.String, java.lang.String)
*/ */
public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation,
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) String requireTldBundle, ContextHandler handler) throws Exception
throws Exception
{ {
File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile(); File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
if (contextsHome != null) if (contextsHome != null)
{ {
File prodContextFile = new File(contextsHome,contributor.getSymbolicName() + "/" + contextFileRelativePath); File prodContextFile = new File(contextsHome, contributor.getSymbolicName() + "/" + contextFileRelativePath);
if (prodContextFile.exists()) if (prodContextFile.exists()) { return registerContext(contributor, contextFileRelativePath, prodContextFile, extraClasspath,
{ overrideBundleInstallLocation, requireTldBundle, handler); }
return registerContext(contributor,contextFileRelativePath,prodContextFile,extraClasspath,
overrideBundleInstallLocation,requireTldBundle,handler);
}
} }
File rootFolder = overrideBundleInstallLocation != null File rootFolder = overrideBundleInstallLocation != null ? Resource.newResource(overrideBundleInstallLocation).getFile() : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor);
? Resource.newResource(overrideBundleInstallLocation).getFile() File contextFile = rootFolder != null ? new File(rootFolder, contextFileRelativePath) : null;
: BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(contributor);
File contextFile = rootFolder != null?new File(rootFolder,contextFileRelativePath):null;
if (contextFile != null && contextFile.exists()) if (contextFile != null && contextFile.exists())
{ {
return registerContext(contributor,contextFileRelativePath,contextFile,extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler); return registerContext(contributor, contextFileRelativePath, contextFile, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler);
} }
else else
{ {
@ -382,11 +391,15 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
URL contextURL = contributor.getEntry(contextFileRelativePath); URL contextURL = contributor.getEntry(contextFileRelativePath);
if (contextURL != null) if (contextURL != null)
{ {
Resource r = Resource.newResource(contextURL); Resource r = Resource.newResource(contextURL);
return registerContext(contributor,contextFileRelativePath,r.getInputStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler); return registerContext(contributor, contextFileRelativePath, r.getInputStream(), extraClasspath, overrideBundleInstallLocation,
requireTldBundle, handler);
} }
throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle " throw new IllegalArgumentException("Could not find the context " + "file "
+ contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:"")); + contextFileRelativePath
+ " for the bundle "
+ contributor.getSymbolicName()
+ (overrideBundleInstallLocation != null ? " using the install location " + overrideBundleInstallLocation : ""));
} }
} }
@ -400,16 +413,14 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @param classInBundle * @param classInBundle
* @throws Exception * @throws Exception
*/ */
private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile, private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile, String extraClasspath,
String extraClasspath, String overrideBundleInstallLocation, String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception
String requireTldBundle, ContextHandler handler) throws Exception {
{
InputStream contextFileInputStream = null; InputStream contextFileInputStream = null;
try try
{ {
contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile)); contextFileInputStream = new BufferedInputStream(new FileInputStream(contextFile));
return registerContext(contributor, pathInBundle, contextFileInputStream, return registerContext(contributor, pathInBundle, contextFileInputStream, extraClasspath, overrideBundleInstallLocation, requireTldBundle, handler);
extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
} }
finally finally
{ {
@ -424,11 +435,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* happen. * happen.
* @throws Exception * @throws Exception
*/ */
private ContextHandler registerContext(Bundle contributor, private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream, String extraClasspath,
String pathInsideBundle, InputStream contextFileInputStream, String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception
String extraClasspath, String overrideBundleInstallLocation,
String requireTldBundle, ContextHandler handler)
throws Exception
{ {
ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
String[] oldServerClasses = null; String[] oldServerClasses = null;
@ -442,24 +450,23 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
// classes // classes
// that the contributor gives access to. // that the contributor gives access to.
Thread.currentThread().setContextClassLoader(composite); Thread.currentThread().setContextClassLoader(composite);
ContextHandler context = createContextHandler(handler, contributor, ContextHandler context = createContextHandler(handler, contributor, contextFileInputStream, extraClasspath, overrideBundleInstallLocation,
contextFileInputStream,extraClasspath, requireTldBundle);
overrideBundleInstallLocation,requireTldBundle); if (context == null)
if (context == null) {
{
return null;// did not happen return null;// did not happen
} }
// ok now register this webapp. we checked when we started jetty // ok now register this webapp. we checked when we started jetty
// that there // that there
// was at least one such handler for webapps. // was at least one such handler for webapps.
//the actual registration must happen via the new Deployment API. // the actual registration must happen via the new Deployment API.
// _ctxtHandler.addHandler(context); // _ctxtHandler.addHandler(context);
configureWebappClassLoader(contributor,context,composite, requireTldBundle); configureWebappClassLoader(contributor, context, composite, requireTldBundle);
if (context instanceof WebAppContext) if (context instanceof WebAppContext)
{ {
webAppContext = (WebAppContext)context; webAppContext = (WebAppContext) context;
// @see // @see
// org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext) // org.eclipse.jetty.webapp.JettyWebXmlConfiguration#configure(WebAppContext)
oldServerClasses = webAppContext.getServerClasses(); oldServerClasses = webAppContext.getServerClasses();
@ -485,60 +492,58 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @see {WebAppDeployer#scan} around the comment * @see {WebAppDeployer#scan} around the comment
* <code>// configure it</code> * <code>// configure it</code>
*/ */
protected void configureWebAppContext(ContextHandler wah, Bundle contributor, protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle) throws IOException
String requireTldBundle) throws IOException {
{
// rfc66 // rfc66
wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext()); wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, contributor.getBundleContext());
//spring-dm-1.2.1 looks for the BundleContext as a different attribute. // spring-dm-1.2.1 looks for the BundleContext as a different attribute.
//not a spec... but if we want to support // not a spec... but if we want to support
//org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
//then we need to do this to: // then we need to do this to:
wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), wah.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), contributor.getBundleContext());
contributor.getBundleContext());
//also pass the bundle directly. sometimes a bundle does not have a bundlecontext. // also pass the bundle directly. sometimes a bundle does not have a
//it is still useful to have access to the Bundle from the servlet context. // bundlecontext.
// it is still useful to have access to the Bundle from the servlet
// context.
wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor); wah.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, contributor);
//pass the value of the require tld bundle so that the TagLibOSGiConfiguration // pass the value of the require tld bundle so that the
//can pick it up. // TagLibOSGiConfiguration
// can pick it up.
wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle); wah.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundle);
if (wah instanceof WebAppContext)
{
if (_wrapper.getOSGiAppProvider().getConfigurationClasses() != null)
{
((WebAppContext)wah).setConfigurationClasses(_wrapper.getOSGiAppProvider().getConfigurationClasses());
}
}
Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor); Bundle[] fragments = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(contributor);
if (fragments != null && fragments.length != 0) if (fragments != null && fragments.length != 0)
{ {
//sorted extra resource base found in the fragments. // sorted extra resource base found in the fragments.
//the resources are either overriding the resourcebase found in the web-bundle // the resources are either overriding the resourcebase found in the
//or appended. // web-bundle
//amongst each resource we sort them according to the alphabetical order // or appended.
//of the name of the internal folder and the symbolic name of the fragment. // amongst each resource we sort them according to the alphabetical
//this is useful to make sure that the lookup path of those // order
//resource base defined by fragments is always the same. // of the name of the internal folder and the symbolic name of the
//This natural order could be abused to define the order in which the base resources are // fragment.
//looked up. // this is useful to make sure that the lookup path of those
TreeMap<String,Resource> patchResourcesPath = new TreeMap<String,Resource>(); // resource base defined by fragments is always the same.
TreeMap<String,Resource> appendedResourcesPath = new TreeMap<String,Resource>(); // This natural order could be abused to define the order in which
for (Bundle frag : fragments) { // the base resources are
String fragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH); // looked up.
String patchFragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH); TreeMap<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
for (Bundle frag : fragments)
{
String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
if (fragFolder != null) if (fragFolder != null)
{ {
URL fragUrl = frag.getEntry(fragFolder); URL fragUrl = frag.getEntry(fragFolder);
if (fragUrl == null) if (fragUrl == null) { throw new IllegalArgumentException("Unable to locate " + fragFolder
{ + " inside "
throw new IllegalArgumentException("Unable to locate " + fragFolder + " inside " + " the fragment '"
+ " the fragment '" + frag.getSymbolicName() + "'"); + frag.getSymbolicName()
} + "'"); }
fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl); fragUrl = DefaultFileLocatorHelper.getLocalURL(fragUrl);
String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder; String key = fragFolder.startsWith("/") ? fragFolder.substring(1) : fragFolder;
appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl)); appendedResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(fragUrl));
@ -546,11 +551,11 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
if (patchFragFolder != null) if (patchFragFolder != null)
{ {
URL patchFragUrl = frag.getEntry(patchFragFolder); URL patchFragUrl = frag.getEntry(patchFragFolder);
if (patchFragUrl == null) if (patchFragUrl == null) { throw new IllegalArgumentException("Unable to locate " + patchFragUrl
{ + " inside "
throw new IllegalArgumentException("Unable to locate " + patchFragUrl + " inside " + " the fragment '"
+ " the fragment '" + frag.getSymbolicName() + "'"); + frag.getSymbolicName()
} + "'"); }
patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl); patchFragUrl = DefaultFileLocatorHelper.getLocalURL(patchFragUrl);
String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder; String key = patchFragFolder.startsWith("/") ? patchFragFolder.substring(1) : patchFragFolder;
patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl)); patchResourcesPath.put(key + ";" + frag.getSymbolicName(), Resource.newResource(patchFragUrl));
@ -567,26 +572,28 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
if (wah instanceof WebAppContext) if (wah instanceof WebAppContext)
{ {
//This is the equivalent of what MetaInfConfiguration does. For OSGi bundles without the JarScanner // This is the equivalent of what MetaInfConfiguration does. For
WebAppContext webappCtxt = (WebAppContext)wah; // OSGi bundles without the JarScanner
//take care of the web-fragments, meta-inf resources and tld resources: WebAppContext webappCtxt = (WebAppContext) wah;
//similar to what MetaInfConfiguration does. // take care of the web-fragments, meta-inf resources and tld
List<Resource> frags = (List<Resource>)wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES); // resources:
List<Resource> resfrags = (List<Resource>)wah.getAttribute(WebInfConfiguration.RESOURCE_URLS); // similar to what MetaInfConfiguration does.
List<Resource> tldfrags = (List<Resource>)wah.getAttribute(TagLibConfiguration.TLD_RESOURCES); List<Resource> frags = (List<Resource>) wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
List<Resource> resfrags = (List<Resource>) wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
List<Resource> tldfrags = (List<Resource>) wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
for (Bundle frag : fragments) for (Bundle frag : fragments)
{ {
URL webFrag = frag.getEntry("/META-INF/web-fragment.xml"); URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true); Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false); Enumeration<URL> tldEnum = frag.findEntries("/META-INF", "*.tld", false);
if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) if (webFrag != null || (resEnum != null && resEnum.hasMoreElements()) || (tldEnum != null && tldEnum.hasMoreElements()))
|| (tldEnum != null && tldEnum.hasMoreElements()))
{ {
try try
{ {
File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag); File fragFile = BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(frag);
//add it to the webinf jars collection: // add it to the webinf jars collection:
//no need to check that it was not there yet: it was not there yet for sure. // no need to check that it was not there yet: it
// was not there yet for sure.
Resource fragFileAsResource = Resource.newResource(fragFile.toURI()); Resource fragFileAsResource = Resource.newResource(fragFile.toURI());
webappCtxt.getMetaData().addWebInfJar(fragFileAsResource); webappCtxt.getMetaData().addWebInfJar(fragFileAsResource);
@ -604,9 +611,10 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
URL resourcesEntry = frag.getEntry("/META-INF/resources/"); URL resourcesEntry = frag.getEntry("/META-INF/resources/");
if (resourcesEntry == null) if (resourcesEntry == null)
{ {
//probably we found some fragments to a bundle. // probably we found some fragments to a
//those are already contributed. // bundle.
//so we skip this. // those are already contributed.
// so we skip this.
} }
else else
{ {
@ -615,8 +623,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
resfrags = new ArrayList<Resource>(); resfrags = new ArrayList<Resource>();
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags); wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
} }
resfrags.add(Resource.newResource( resfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
} }
} }
if (tldEnum != null && tldEnum.hasMoreElements()) if (tldEnum != null && tldEnum.hasMoreElements())
@ -628,42 +635,38 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
while (tldEnum.hasMoreElements()) while (tldEnum.hasMoreElements())
{ {
URL tldUrl = tldEnum.nextElement(); URL tldUrl = tldEnum.nextElement();
tldfrags.add(Resource.newResource( tldfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(tldUrl)));
DefaultFileLocatorHelper.getLocalURL(tldUrl)));
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
__logger.warn("Unable to locate the bundle " + frag.getBundleId(),e); __logger.warn("Unable to locate the bundle " + frag.getBundleId(), e);
} }
} }
} }
} }
} }
}
}
/** /**
* @See {@link ContextDeployer#scan} * @See {@link ContextDeployer#scan}
* @param contextFile * @param contextFile
* @return * @return
*/ */
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, File contextFile, String extraClasspath,
Bundle bundle, File contextFile, String extraClasspath,
String overrideBundleInstallLocation, String requireTldBundle) String overrideBundleInstallLocation, String requireTldBundle)
{ {
try try
{ {
return createContextHandler(handlerToConfigure,bundle, return createContextHandler(handlerToConfigure, bundle, new BufferedInputStream(new FileInputStream(contextFile)), extraClasspath,
new BufferedInputStream(new FileInputStream(contextFile)), overrideBundleInstallLocation, requireTldBundle);
extraClasspath,overrideBundleInstallLocation,requireTldBundle);
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
e.printStackTrace(); __logger.warn(e);
} }
return null; return null;
} }
@ -674,8 +677,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @return * @return
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, protected ContextHandler createContextHandler(ContextHandler handlerToConfigure, Bundle bundle, InputStream contextInputStream, String extraClasspath,
Bundle bundle, InputStream contextInputStream, String extraClasspath,
String overrideBundleInstallLocation, String requireTldBundle) String overrideBundleInstallLocation, String requireTldBundle)
{ {
/* /*
@ -693,16 +695,16 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
{ {
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream); XmlConfiguration xmlConfiguration = new XmlConfiguration(contextInputStream);
HashMap properties = new HashMap(); HashMap properties = new HashMap();
properties.put("Server",_wrapper.getServer()); properties.put("Server", _wrapper.getServer());
// insert the bundle's location as a property. // insert the bundle's location as a property.
setThisBundleHomeProperty(bundle,properties,overrideBundleInstallLocation); setThisBundleHomeProperty(bundle, properties, overrideBundleInstallLocation);
xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.getProperties().putAll(properties);
ContextHandler context = null; ContextHandler context = null;
if (handlerToConfigure == null) if (handlerToConfigure == null)
{ {
context = (ContextHandler)xmlConfiguration.configure(); context = (ContextHandler) xmlConfiguration.configure();
} }
else else
{ {
@ -712,11 +714,11 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
if (context instanceof WebAppContext) if (context instanceof WebAppContext)
{ {
((WebAppContext)context).setExtraClasspath(extraClasspath); ((WebAppContext) context).setExtraClasspath(extraClasspath);
((WebAppContext)context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority()); ((WebAppContext) context).setParentLoaderPriority(_wrapper.getOSGiAppProvider().isParentLoaderPriority());
if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0) if (_wrapper.getOSGiAppProvider().getDefaultsDescriptor() != null && _wrapper.getOSGiAppProvider().getDefaultsDescriptor().length() != 0)
{ {
((WebAppContext)context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor()); ((WebAppContext) context).setDefaultsDescriptor(_wrapper.getOSGiAppProvider().getDefaultsDescriptor());
} }
} }
@ -729,18 +731,15 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
catch (SAXException e) catch (SAXException e)
{ {
// TODO Auto-generated catch block __logger.warn(e);
e.printStackTrace();
} }
catch (IOException e) catch (IOException e)
{ {
// TODO Auto-generated catch block __logger.warn(e);
e.printStackTrace();
} }
catch (Throwable e) catch (Throwable e)
{ {
// TODO Auto-generated catch block __logger.warn(e);
e.printStackTrace();
} }
finally finally
{ {
@ -782,13 +781,12 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
{ {
if (context instanceof WebAppContext) if (context instanceof WebAppContext)
{ {
WebAppContext webappCtxt = (WebAppContext)context; WebAppContext webappCtxt = (WebAppContext) context;
context.setClassLoader(webappClassLoader); context.setClassLoader(webappClassLoader);
webappClassLoader.setWebappContext(webappCtxt); webappClassLoader.setWebappContext(webappCtxt);
String pathsToRequiredBundles = getPathsToRequiredBundles(context, requireTldBundle); String pathsToRequiredBundles = getPathsToRequiredBundles(context, requireTldBundle);
if (pathsToRequiredBundles != null) if (pathsToRequiredBundles != null) webappClassLoader.addClassPath(pathsToRequiredBundles);
webappClassLoader.addClassPath(pathsToRequiredBundles);
} }
else else
{ {
@ -804,23 +802,48 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
// we use a temporary WebAppContext object. // we use a temporary WebAppContext object.
// if this is a real webapp we will set it on it a bit later: once we // if this is a real webapp we will set it on it a bit later: once we
// know. // know.
OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader( OSGiWebappClassLoader webappClassLoader = new OSGiWebappClassLoader(_wrapper.getParentClassLoaderForWebapps(), new WebAppContext(), contributor,
_wrapper.getParentClassLoaderForWebapps(),new WebAppContext(),contributor,BUNDLE_CLASS_LOADER_HELPER); BUNDLE_CLASS_LOADER_HELPER);
/* DEBUG
try {
Class c = webappClassLoader.loadClass("org.glassfish.jsp.api.ResourceInjector");
System.err.println("LOADED org.glassfish.jsp.api.ResourceInjector from "+c.getClassLoader());
}
catch (Exception e) {e.printStackTrace();}
try {
Class c = webappClassLoader.loadClass("org.apache.jasper.xmlparser.ParserUtils");
System.err.println("LOADED org.apache.jasper.xmlparser.ParserUtils from "+c.getClassLoader());
}
catch (Exception e) {e.printStackTrace();}
*/
return webappClassLoader; return webappClassLoader;
} }
protected void applyMetaInfContextXml (Bundle bundle, ContextHandler contextHandler)
throws Exception
{
if (bundle == null)
return;
if (contextHandler == null)
return;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
__logger.info("Context classloader = "+cl);
try
{
Thread.currentThread().setContextClassLoader(_wrapper.getParentClassLoaderForWebapps());
//find if there is a META-INF/context.xml file
URL contextXmlUrl = bundle.getEntry("/META-INF/jetty-webapp-context.xml");
if (contextXmlUrl == null)
return;
//Apply it just as the standard jetty ContextProvider would do
__logger.info("Applying "+contextXmlUrl+" to "+contextHandler);
XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXmlUrl);
HashMap properties = new HashMap();
properties.put("Server", _wrapper.getServer());
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(contextHandler);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/** /**
* Set the property &quot;this.bundle.install&quot; to point to the location * Set the property &quot;this.bundle.install&quot; to point to the location
* of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is * of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
@ -830,10 +853,9 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
{ {
try try
{ {
File location = overrideBundleInstallLocation != null?new File(overrideBundleInstallLocation):BUNDLE_FILE_LOCATOR_HELPER File location = overrideBundleInstallLocation != null ? new File(overrideBundleInstallLocation) : BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle);
.getBundleInstallLocation(bundle); properties.put("this.bundle.install", location.getCanonicalPath());
properties.put("this.bundle.install",location.getCanonicalPath()); properties.put("this.bundle.install.url", bundle.getEntry("/").toString());
properties.put("this.bundle.install.url",bundle.getEntry("/").toString());
} }
catch (Throwable t) catch (Throwable t)
{ {
@ -841,14 +863,12 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
} }
} }
private String getPathsToRequiredBundles(ContextHandler context, String requireTldBundle) throws Exception
private String getPathsToRequiredBundles (ContextHandler context, String requireTldBundle) throws Exception
{ {
if (requireTldBundle == null) if (requireTldBundle == null) return null;
return null;
StringBuilder paths = new StringBuilder(); StringBuilder paths = new StringBuilder();
Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); Bundle bundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
PackageAdmin packAdmin = getBundleAdmin(); PackageAdmin packAdmin = getBundleAdmin();
DefaultFileLocatorHelper fileLocatorHelper = new DefaultFileLocatorHelper(); DefaultFileLocatorHelper fileLocatorHelper = new DefaultFileLocatorHelper();
@ -857,19 +877,16 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
for (String symbName : symbNames) for (String symbName : symbNames)
{ {
Bundle[] bs = packAdmin.getBundles(symbName, null); Bundle[] bs = packAdmin.getBundles(symbName, null);
if (bs == null || bs.length == 0) if (bs == null || bs.length == 0) { throw new IllegalArgumentException("Unable to locate the bundle '" + symbName
{ + "' specified in the "
throw new IllegalArgumentException("Unable to locate the bundle '" + OSGiWebappConstants.REQUIRE_TLD_BUNDLE
+ symbName + "' specified in the " + " of the manifest of "
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE + bundle.getSymbolicName()); }
+ " of the manifest of "
+ bundle.getSymbolicName());
}
File f = fileLocatorHelper.getBundleInstallLocation(bs[0]); File f = fileLocatorHelper.getBundleInstallLocation(bs[0]);
if (paths.length() > 0) if (paths.length() > 0)
paths.append(", "); paths.append(", ");
__logger.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
paths.append(f.toURI().toURL().toString()); paths.append(f.toURI().toURL().toString());
} }
@ -878,13 +895,12 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
private PackageAdmin getBundleAdmin() private PackageAdmin getBundleAdmin()
{ {
Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle(); Bundle bootBundle = ((BundleReference) OSGiWebappConstants.class.getClassLoader()).getBundle();
ServiceTracker serviceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null); ServiceTracker serviceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null);
serviceTracker.open(); serviceTracker.open();
return (PackageAdmin) serviceTracker.getService(); return (PackageAdmin) serviceTracker.getService();
} }
} }

View File

@ -24,7 +24,6 @@ import org.osgi.framework.BundleEvent;
import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer; import org.osgi.util.tracker.BundleTrackerCustomizer;
/** /**
* Support bundles that declare the webapp directly through headers in their * Support bundles that declare the webapp directly through headers in their
* manifest. * manifest.
@ -49,133 +48,131 @@ import org.osgi.util.tracker.BundleTrackerCustomizer;
* *
* @author hmalphettes * @author hmalphettes
*/ */
public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer { public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
{
private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class); private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
/**
* A bundle is being added to the <code>BundleTracker</code>.
*
* <p>
* This method is called before a bundle which matched the search parameters
* of the <code>BundleTracker</code> is added to the
* <code>BundleTracker</code>. This method should return the object to be
* tracked for the specified <code>Bundle</code>. The returned object is
* stored in the <code>BundleTracker</code> and is available from the
* {@link BundleTracker#getObject(Bundle) getObject} method.
*
* @param bundle The <code>Bundle</code> being added to the
* <code>BundleTracker</code>.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event
* associated with the call to this method.
* @return The object to be tracked for the specified <code>Bundle</code>
* object or <code>null</code> if the specified <code>Bundle</code>
* object should not be tracked.
*/
public Object addingBundle(Bundle bundle, BundleEvent event)
{
if (bundle.getState() == Bundle.ACTIVE)
{
boolean isWebBundle = register(bundle);
return isWebBundle ? bundle : null;
}
else if (bundle.getState() == Bundle.STOPPING)
{
unregister(bundle);
}
else
{
// we should not be called in that state as
// we are registered only for ACTIVE and STOPPING
}
return null;
}
/** /**
* A bundle is being added to the <code>BundleTracker</code>. * A bundle tracked by the <code>BundleTracker</code> has been modified.
* *
* <p> * <p>
* This method is called before a bundle which matched the search parameters * This method is called when a bundle being tracked by the
* of the <code>BundleTracker</code> is added to the * <code>BundleTracker</code> has had its state modified.
* <code>BundleTracker</code>. This method should return the object to be *
* tracked for the specified <code>Bundle</code>. The returned object is * @param bundle The <code>Bundle</code> whose state has been modified.
* stored in the <code>BundleTracker</code> and is available from the * @param event The bundle event which caused this customizer method to be
* {@link BundleTracker#getObject(Bundle) getObject} method. * called or <code>null</code> if there is no bundle event
* * associated with the call to this method.
* @param bundle The <code>Bundle</code> being added to the * @param object The tracked object for the specified bundle.
* <code>BundleTracker</code>. */
* @param event The bundle event which caused this customizer method to be public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
* called or <code>null</code> if there is no bundle event associated {
* with the call to this method. // nothing the web-bundle was already track. something changed.
* @return The object to be tracked for the specified <code>Bundle</code> // we only reload the webapps if the bundle is stopped and restarted.
* object or <code>null</code> if the specified <code>Bundle</code> if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
* object should not be tracked. {
*/ unregister(bundle);
public Object addingBundle(Bundle bundle, BundleEvent event) }
{ if (bundle.getState() == Bundle.ACTIVE)
if (bundle.getState() == Bundle.ACTIVE) {
{ register(bundle);
boolean isWebBundle = register(bundle); }
return isWebBundle ? bundle : null; }
}
else if (bundle.getState() == Bundle.STOPPING)
{
unregister(bundle);
}
else
{
//we should not be called in that state as
//we are registered only for ACTIVE and STOPPING
}
return null;
}
/** /**
* A bundle tracked by the <code>BundleTracker</code> has been modified. * A bundle tracked by the <code>BundleTracker</code> has been removed.
* *
* <p> * <p>
* This method is called when a bundle being tracked by the * This method is called after a bundle is no longer being tracked by the
* <code>BundleTracker</code> has had its state modified. * <code>BundleTracker</code>.
* *
* @param bundle The <code>Bundle</code> whose state has been modified. * @param bundle The <code>Bundle</code> that has been removed.
* @param event The bundle event which caused this customizer method to be * @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event associated * called or <code>null</code> if there is no bundle event
* with the call to this method. * associated with the call to this method.
* @param object The tracked object for the specified bundle. * @param object The tracked object for the specified bundle.
*/ */
public void modifiedBundle(Bundle bundle, BundleEvent event, public void removedBundle(Bundle bundle, BundleEvent event, Object object)
Object object) {
{ unregister(bundle);
//nothing the web-bundle was already track. something changed. }
//we only reload the webapps if the bundle is stopped and restarted.
// System.err.println(bundle.getSymbolicName());
if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
{
unregister(bundle);
}
if (bundle.getState() == Bundle.ACTIVE)
{
register(bundle);
}
}
/** /**
* A bundle tracked by the <code>BundleTracker</code> has been removed. * @param bundle
* * @return true if this bundle in indeed a web-bundle.
* <p> */
* This method is called after a bundle is no longer being tracked by the
* <code>BundleTracker</code>.
*
* @param bundle The <code>Bundle</code> that has been removed.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event associated
* with the call to this method.
* @param object The tracked object for the specified bundle.
*/
public void removedBundle(Bundle bundle, BundleEvent event,
Object object)
{
unregister(bundle);
}
/**
* @param bundle
* @return true if this bundle in indeed a web-bundle.
*/
private boolean register(Bundle bundle) private boolean register(Bundle bundle)
{ {
Dictionary<?, ?> dic = bundle.getHeaders(); Dictionary<?, ?> dic = bundle.getHeaders();
String warFolderRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH); String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
if (warFolderRelativePath != null) if (warFolderRelativePath != null)
{ {
String contextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); String contextPath = getWebContextPath(bundle, dic, false);// (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null || !contextPath.startsWith("/")) if (contextPath == null || !contextPath.startsWith("/"))
{ {
LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH + LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
": " + warFolderRelativePath + "' in the bundle " + bundle.getSymbolicName() + + ": "
" is not valid: there is no Web-ContextPath defined in the manifest."); + warFolderRelativePath
return false; + "' in the bundle "
+ bundle.getSymbolicName()
+ " is not valid: there is no Web-ContextPath defined in the manifest.");
return false;
} }
// create the corresponding service and publish it in the context of // create the corresponding service and publish it in the context of
// the contributor bundle. // the contributor bundle.
try try
{ {
JettyBootstrapActivator.registerWebapplication(bundle,warFolderRelativePath,contextPath); JettyBootstrapActivator.registerWebapplication(bundle, warFolderRelativePath, contextPath);
return true; return true;
} }
catch (Throwable e) catch (Throwable e)
{ {
LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e); LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e);
return true;//maybe it did not work maybe it did. safer to track this bundle. return true;// maybe it did not work maybe it did. safer to track this bundle.
} }
} }
else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null) else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null)
{ {
String contextFileRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (contextFileRelativePath == null) if (contextFileRelativePath == null)
{ {
// nothing to register here. // nothing to register here.
@ -187,12 +184,11 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
{ {
try try
{ {
JettyBootstrapActivator.registerContext(bundle,path.trim()); JettyBootstrapActivator.registerContext(bundle, path.trim());
} }
catch (Throwable e) catch (Throwable e)
{ {
// TODO Auto-generated catch block LOG.warn(e);
e.printStackTrace();
} }
} }
return true; return true;
@ -203,8 +199,8 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
// (draft) of the spec: just a couple of posts on the // (draft) of the spec: just a couple of posts on the
// world-wide-web. // world-wide-web.
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml"); URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null) if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
{ {
return false;// no webapp in here return false;// no webapp in here
} }
// this is risky: should we make sure that there is no classes and // this is risky: should we make sure that there is no classes and
@ -215,39 +211,39 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
// pointing to files and folders inside WEB-INF. We should // pointing to files and folders inside WEB-INF. We should
// filter-out // filter-out
// META-INF too // META-INF too
String rfc66ContextPath = getWebContextPath(bundle,dic); String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null);
try try
{ {
JettyBootstrapActivator.registerWebapplication(bundle,".",rfc66ContextPath); JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath);
return true; return true;
} }
catch (Throwable e) catch (Throwable e)
{ {
// TODO Auto-generated catch block LOG.warn(e);
e.printStackTrace(); return true;// maybe it did not work maybe it did. safer to track this bundle.
return true;//maybe it did not work maybe it did. safer to track this bundle.
} }
} }
} }
private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic) private String getWebContextPath(Bundle bundle, Dictionary<?, ?> dic)
{ {
String rfc66ContextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (rfc66ContextPath == null) if (rfc66ContextPath == null)
{ {
if (!webinfWebxmlExists) { return null; }
// extract from the last token of the bundle's location: // extract from the last token of the bundle's location:
// (really ? // (really ?
// could consider processing the symbolic name as an alternative // could consider processing the symbolic name as an alternative
// the location will often reflect the version. // the location will often reflect the version.
// maybe this is relevant when the file is a war) // maybe this is relevant when the file is a war)
String location = bundle.getLocation(); String location = bundle.getLocation();
String toks[] = location.replace('\\','/').split("/"); String toks[] = location.replace('\\', '/').split("/");
rfc66ContextPath = toks[toks.length - 1]; rfc66ContextPath = toks[toks.length - 1];
// remove .jar, .war etc: // remove .jar, .war etc:
int lastDot = rfc66ContextPath.lastIndexOf('.'); int lastDot = rfc66ContextPath.lastIndexOf('.');
if (lastDot != -1) if (lastDot != -1)
{ {
rfc66ContextPath = rfc66ContextPath.substring(0,lastDot); rfc66ContextPath = rfc66ContextPath.substring(0, lastDot);
} }
} }
if (!rfc66ContextPath.startsWith("/")) if (!rfc66ContextPath.startsWith("/"))
@ -265,7 +261,4 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
// webapps registered in that bundle. // webapps registered in that bundle.
} }
} }

View File

@ -25,6 +25,7 @@ import java.util.zip.ZipFile;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.FileResource; import org.eclipse.jetty.util.resource.FileResource;
import org.osgi.framework.Bundle; import org.osgi.framework.Bundle;
@ -43,11 +44,13 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// The url nuxeo and felix return is created directly from the File so it // The url nuxeo and felix return is created directly from the File so it
// should work. // should work.
private static Field BUNDLE_ENTRY_FIELD = null; private static Field BUNDLE_ENTRY_FIELD = null;
private static Field FILE_FIELD = null; private static Field FILE_FIELD = null;
private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null;// ZipBundleFile private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null;// ZipBundleFile
// inside
// DirZipBundleEntry // inside
// DirZipBundleEntry
private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile
@ -56,8 +59,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
* spirit of OSGi but quite necessary to support self-contained webapps and * spirit of OSGi but quite necessary to support self-contained webapps and
* other situations. * other situations.
* *
* @param bundle * @param bundle The bundle
* The bundle
* @return Its installation location as a file. * @return Its installation location as a file.
* @throws Exception * @throws Exception
*/ */
@ -67,11 +69,12 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// grab the MANIFEST.MF's url // grab the MANIFEST.MF's url
// and then do what it takes. // and then do what it takes.
URL url = bundle.getEntry("/META-INF/MANIFEST.MF"); URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
// System.err.println(url.toString() + " " + url.toURI() + " " + url.getProtocol());
if (url.getProtocol().equals("file")) if (url.getProtocol().equals("file"))
{ {
// some osgi frameworks do use the file protocole directly in some // some osgi frameworks do use the file protocole directly in some
// situations. Do use the FileResource to transform the URL into a File: URL#toURI is broken // situations. Do use the FileResource to transform the URL into a
// File: URL#toURI is broken
return new FileResource(url).getFile().getParentFile().getParentFile(); return new FileResource(url).getFile().getParentFile().getParentFile();
} }
else if (url.getProtocol().equals("bundleentry")) else if (url.getProtocol().equals("bundleentry"))
@ -79,7 +82,10 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// say hello to equinox who has its own protocol. // say hello to equinox who has its own protocol.
// we use introspection like there is no tomorrow to get access to // we use introspection like there is no tomorrow to get access to
// the File // the File
URLConnection con = url.openConnection(); URLConnection con = url.openConnection();
con.setUseCaches(Resource.getDefaultUseCaches()); //work around problems where url connections cache references to jars
if (BUNDLE_ENTRY_FIELD == null) if (BUNDLE_ENTRY_FIELD == null)
{ {
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry"); BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
@ -93,13 +99,16 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
FILE_FIELD = bundleEntry.getClass().getDeclaredField("file"); FILE_FIELD = bundleEntry.getClass().getDeclaredField("file");
FILE_FIELD.setAccessible(true); FILE_FIELD.setAccessible(true);
} }
File f = (File)FILE_FIELD.get(bundleEntry); File f = (File) FILE_FIELD.get(bundleEntry);
return f.getParentFile().getParentFile(); return f.getParentFile().getParentFile();
} }
else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry")) else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry"))
{ {
url = bundle.getEntry("/"); url = bundle.getEntry("/");
con = url.openConnection(); con = url.openConnection();
con.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_ENTRY_FIELD == null) if (BUNDLE_ENTRY_FIELD == null)
{// this one will be a DirZipBundleEntry {// this one will be a DirZipBundleEntry
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry"); BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
@ -117,7 +126,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile"); ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile");
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true); ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true);
} }
ZipFile zipFile = (ZipFile)ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile); ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile);
return new File(zipFile.getName()); return new File(zipFile.getName());
} }
else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry")) else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry"))
@ -130,7 +139,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{ {
// observed this on felix-2.0.0 // observed this on felix-2.0.0
String location = bundle.getLocation(); String location = bundle.getLocation();
// System.err.println("location " + location); // System.err.println("location " + location);
if (location.startsWith("file:/")) if (location.startsWith("file:/"))
{ {
URI uri = new URI(URIUtil.encodePath(location)); URI uri = new URI(URIUtil.encodePath(location));
@ -138,27 +147,32 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
} }
else if (location.startsWith("file:")) else if (location.startsWith("file:"))
{ {
//location defined in the BundleArchive m_bundleArchive // location defined in the BundleArchive m_bundleArchive
//it is relative to relative to the BundleArchive's m_archiveRootDir // it is relative to relative to the BundleArchive's
File res = new File(location.substring("file:".length())); // m_archiveRootDir
if (!res.exists()) File res = new File(location.substring("file:".length()));
{ if (!res.exists())
return null; {
// Object bundleArchive = getFelixBundleArchive(bundle); return null;
// File archiveRoot = getFelixBundleArchiveRootDir(bundleArchive); // Object bundleArchive = getFelixBundleArchive(bundle);
// String currentLocation = getFelixBundleArchiveCurrentLocation(bundleArchive); // File archiveRoot =
// System.err.println("Got the archive root " + archiveRoot.getAbsolutePath() // getFelixBundleArchiveRootDir(bundleArchive);
// + " current location " + currentLocation + " is directory ?"); // String currentLocation =
// res = new File(archiveRoot, currentLocation != null // getFelixBundleArchiveCurrentLocation(bundleArchive);
// ? currentLocation : location.substring("file:".length())); // System.err.println("Got the archive root " +
} // archiveRoot.getAbsolutePath()
return res; // + " current location " + currentLocation +
// " is directory ?");
// res = new File(archiveRoot, currentLocation != null
// ? currentLocation : location.substring("file:".length()));
}
return res;
} }
else if (location.startsWith("reference:file:")) else if (location.startsWith("reference:file:"))
{ {
location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8"); location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8");
File file = new File(location.substring("file:".length())); File file = new File(location.substring("file:".length()));
return file; return file;
} }
} }
return null; return null;
@ -179,37 +193,40 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
path = path.substring(1); path = path.substring(1);
} }
File bundleInstall = getBundleInstallLocation(bundle); File bundleInstall = getBundleInstallLocation(bundle);
File webapp = path != null && path.length() != 0?new File(bundleInstall,path):bundleInstall; File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall;
if (!webapp.exists()) if (!webapp.exists())
{ {
throw new IllegalArgumentException("Unable to locate " + path + " inside " + bundle.getSymbolicName() + " (" throw new IllegalArgumentException("Unable to locate " + path
+ (bundleInstall != null?bundleInstall.getAbsolutePath():" no_bundle_location ") + ")"); + " inside "
+ bundle.getSymbolicName()
+ " ("
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ")
+ ")");
} }
return webapp; return webapp;
} }
/** /**
* Helper method equivalent to Bundle#getEntry(String entryPath) except that * Helper method equivalent to Bundle#getEntry(String entryPath) except that
* it searches for entries in the fragments by using the Bundle#findEntries method. * it searches for entries in the fragments by using the Bundle#findEntries
* * method.
* @param bundle *
* @param entryPath * @param bundle
* @return null or all the entries found for that path. * @param entryPath
*/ * @return null or all the entries found for that path.
public Enumeration<URL> findEntries(Bundle bundle, String entryPath) */
{ public Enumeration<URL> findEntries(Bundle bundle, String entryPath)
int last = entryPath.lastIndexOf('/'); {
String path = last != -1 && last < entryPath.length() -2 int last = entryPath.lastIndexOf('/');
? entryPath.substring(0, last) : "/"; String path = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(0, last) : "/";
if (!path.startsWith("/")) if (!path.startsWith("/"))
{ {
path = "/" + path; path = "/" + path;
} }
String pattern = last != -1 && last < entryPath.length() -2 String pattern = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(last + 1) : entryPath;
? entryPath.substring(last+1) : entryPath; Enumeration<URL> enUrls = bundle.findEntries(path, pattern, false);
Enumeration<URL> enUrls = bundle.findEntries(path, pattern, false); return enUrls;
return enUrls; }
}
/** /**
* If the bundle is a jar, returns the jar. If the bundle is a folder, look * If the bundle is a jar, returns the jar. If the bundle is a folder, look
@ -256,76 +273,84 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
return new File[] { jasperLocation }; return new File[] { jasperLocation };
} }
} }
//introspection on equinox to invoke the getLocalURL method on BundleURLConnection // introspection on equinox to invoke the getLocalURL method on
//equivalent to using the FileLocator without depending on an equinox class. // BundleURLConnection
private static Method BUNDLE_URL_CONNECTION_getLocalURL = null; // equivalent to using the FileLocator without depending on an equinox
private static Method BUNDLE_URL_CONNECTION_getFileURL = null; // class.
/** private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
* Only useful for equinox: on felix we get the file:// or jar:// url already.
* Other OSGi implementations have not been tested private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. file: /**
* jar: or http: etc.). * Only useful for equinox: on felix we get the file:// or jar:// url
* </p> * already. Other OSGi implementations have not been tested
* @return a URL to the bundle entry that uses a common protocol * <p>
*/ * Get a URL to the bundle entry that uses a common protocol (i.e. file:
public static URL getLocalURL(URL url) { * jar: or http: etc.).
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) { * </p>
try { *
URLConnection conn = url.openConnection(); * @return a URL to the bundle entry that uses a common protocol
if (BUNDLE_URL_CONNECTION_getLocalURL == null && */
conn.getClass().getName().equals( public static URL getLocalURL(URL url)
"org.eclipse.osgi.framework.internal.core.BundleURLConnection")) { {
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null); if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true); {
} try
if (BUNDLE_URL_CONNECTION_getLocalURL != null) { {
return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); URLConnection conn = url.openConnection();
} conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
} catch (Throwable t) { if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName()
System.err.println("Unable to locate the OSGi url: '" + url + "'."); .equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
t.printStackTrace(); {
} BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
} BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
return url; }
} if (BUNDLE_URL_CONNECTION_getLocalURL != null) { return (URL) BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); }
/** }
* Only useful for equinox: on felix we get the file:// url already. catch (Throwable t)
* Other OSGi implementations have not been tested {
* <p> System.err.println("Unable to locate the OSGi url: '" + url + "'.");
* Get a URL to the content of the bundle entry that uses the file: protocol. t.printStackTrace();
* The content of the bundle entry may be downloaded or extracted to the local }
* file system in order to create a file: URL. }
* @return a URL to the content of the bundle entry that uses the file: protocol return url;
* </p> }
*/
public static URL getFileURL(URL url) /**
{ * Only useful for equinox: on felix we get the file:// url already. Other
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) * OSGi implementations have not been tested
{ * <p>
try * Get a URL to the content of the bundle entry that uses the file:
{ * protocol. The content of the bundle entry may be downloaded or extracted
URLConnection conn = url.openConnection(); * to the local file system in order to create a file: URL.
if (BUNDLE_URL_CONNECTION_getFileURL == null && *
conn.getClass().getName().equals( * @return a URL to the content of the bundle entry that uses the file:
"org.eclipse.osgi.framework.internal.core.BundleURLConnection")) * protocol
{ * </p>
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null); */
BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true); public static URL getFileURL(URL url)
} {
if (BUNDLE_URL_CONNECTION_getFileURL != null) if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{ {
return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); try
} {
} URLConnection conn = url.openConnection();
catch (Throwable t) conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
{ if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName()
t.printStackTrace(); .equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
} {
} BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
return url; BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
} }
if (BUNDLE_URL_CONNECTION_getFileURL != null) { return (URL) BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); }
}
catch (Throwable t)
{
t.printStackTrace();
}
}
return url;
}
} }

View File

@ -14,3 +14,4 @@ Import-Package: javax.servlet;version="2.6.0",
org.eclipse.jetty.servlet;version="8.1.3", org.eclipse.jetty.servlet;version="8.1.3",
org.eclipse.jetty.util.component;version="8.1.3" org.eclipse.jetty.util.component;version="8.1.3"
Export-Package: org.eclipse.jetty.osgi.httpservice;version="8.1.3" Export-Package: org.eclipse.jetty.osgi.httpservice;version="8.1.3"

View File

@ -1,149 +0,0 @@
package org.eclipse.jetty.rewrite.handler;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.ScopedHandler;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** A handle that uses regular expressions to select the target.
* <p>
* This handler applies a list of regex to target name mappings to the URIs of requests.
* If the regex matches the URI, then the mapped target name is used in the nested
* call to {@link #doScope(String, Request, HttpServletRequest, HttpServletResponse)}.
* <p>
* This handler should be installed as the first handler in a Context. It can be configured
* either with direct calls to {@link #addPatternTarget(String, String)} or by setting
* the context init parameters "org.eclipse.jetty.rewrite.handler.REGEX_MAPPINGS" to a comma
* separated list of strings in the format regex==target.
*/
public class RegexTargetHandler extends ScopedHandler
{
private final static Logger LOG = Log.getLogger(RegexTargetHandler.class);
public final static String REGEX_MAPPINGS="org.eclipse.jetty.rewrite.handler.REGEX_MAPPINGS";
static class RegexMapping
{
RegexMapping(String regex,String target)
{
_pattern=Pattern.compile(regex);
_target=target;
}
final Pattern _pattern;
final String _target;
public String toString()
{
return _pattern+"=="+_target;
}
}
final private List<RegexTargetHandler.RegexMapping> _patterns = new CopyOnWriteArrayList<RegexTargetHandler.RegexMapping>();
/* ------------------------------------------------------------ */
/** Add a pattern to target mapping.
* @param pattern The regular expression pattern to match.
* @param target The target (normally servlet name) to handle the request
*/
public void addPatternTarget(String pattern,String target)
{
_patterns.add(new RegexMapping(pattern,target));
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
super.doStart();
Context context = ContextHandler.getCurrentContext();
if (context!=null)
{
String config=context.getInitParameter(REGEX_MAPPINGS);
LOG.debug("{}={}",REGEX_MAPPINGS,config);
String[] mappings=config.split("\\s*,\\s*");
for (String mapping : mappings)
{
mapping=mapping.trim();
String[] parts=mapping.split("\\s*==\\s*");
if (parts.length==2)
{
String pattern=parts[0];
String target=parts[1];
addPatternTarget(pattern,target);
}
else
LOG.warn("Bad regex mapping: "+mapping);
}
}
}
/* ------------------------------------------------------------ */
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
for (RegexTargetHandler.RegexMapping rm : _patterns)
{
Matcher m=rm._pattern.matcher(target);
if (m.matches())
{
String new_target = rm._target;
final String sp;
final String pi;
if (m.groupCount()==1&&target.endsWith(m.group(1)))
{
pi=m.group(1);
sp=target.substring(0,target.length()-pi.length());
}
else
{
sp=target;
pi=null;
}
baseRequest.setServletPath(sp);
baseRequest.setPathInfo(pi);
baseRequest.setAttribute("org.eclipse.jetty.servlet.REGEX_PATH",target);
super.nextScope(new_target,baseRequest,request,response);
return;
}
}
super.nextScope(target,baseRequest,request,response);
}
/* ------------------------------------------------------------ */
@Override
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
String path=(String)baseRequest.getAttribute("org.eclipse.jetty.servlet.REGEX_PATH");
if (path==null)
path=target;
else
baseRequest.setAttribute("org.eclipse.jetty.servlet.REGEX_PATH",null);
super.nextHandle(path,baseRequest,request,response);
}
/* ------------------------------------------------------------ */
public void dump(Appendable out, String indent) throws IOException
{
AggregateLifeCycle.dumpObject(out,this);
AggregateLifeCycle.dump(out,indent,_patterns,Collections.singletonList(getHandler()));
}
}

View File

@ -18,6 +18,7 @@ import java.util.regex.Matcher;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
/** /**
@ -89,14 +90,20 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public void applyURI(Request request, String oldTarget, String newTarget) throws IOException public void applyURI(Request request, String oldTarget, String newTarget) throws IOException
{ {
request.setRequestURI(newTarget); if (_query==null)
if (_query!=null) {
request.setRequestURI(newTarget);
}
else
{ {
String query=(String)request.getAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q"); String query=(String)request.getAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q");
if (_queryGroup||request.getQueryString()==null)
request.setQueryString(query); if (!_queryGroup && request.getQueryString()!=null)
else query=request.getQueryString()+"&"+query;
request.setQueryString(request.getQueryString()+"&"+query); HttpURI uri=new HttpURI(newTarget+"?"+query);
request.setUri(uri);
request.setRequestURI(newTarget);
request.setQueryString(query);
} }
} }

View File

@ -1,214 +0,0 @@
// ========================================================================
// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.rewrite.handler;
import static junit.framework.Assert.assertEquals;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import junit.framework.Assert;
import org.eclipse.jetty.rewrite.handler.RegexTargetHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class RegexTargetHandlerTest
{
private static Server __server = new Server(0);
private static int __port;
@BeforeClass
public static void setup() throws Exception
{
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
__server.setHandler(context);
// Serve some hello world servlets
context.addServlet(DispatchServletServlet.class,"/dispatch/*");
context.addServlet(new ServletHolder("HelloAll",new HelloServlet("Hello World")),"/*");
context.addServlet(new ServletHolder("Italian",new HelloServlet("Buongiorno Mondo")),"/it/*");
context.addServlet(new ServletHolder("French", new HelloServlet("Bonjour le Monde")),"/fr/*");
RegexTargetHandler regexHandler=new RegexTargetHandler();
regexHandler.setHandler(context.getHandler());
context.setHandler(regexHandler);
context.getInitParams().put(RegexTargetHandler.REGEX_MAPPINGS,
" .*\\.fr==French, \n"+
"/ciao(/.*)$==Italian");
__server.start();
__port=__server.getConnectors()[0].getLocalPort();
}
@AfterClass
public static void shutdown() throws Exception
{
__server.stop();
}
@Test
public void testNormal() throws Exception
{
String[] response=getResponse("/normal");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Hello World",response[1]);
assertEquals("",response[2]);
assertEquals("/normal",response[3]);
response=getResponse("/it/info");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Buongiorno Mondo",response[1]);
assertEquals("/it",response[2]);
assertEquals("/info",response[3]);
}
@Test
public void testFullMatch() throws Exception
{
String[] response=getResponse("/some/thing.fr");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Bonjour le Monde",response[1]);
assertEquals("/some/thing.fr",response[2]);
assertEquals("null",response[3]);
}
@Test
public void testCaptureMatch() throws Exception
{
String[] response=getResponse("/ciao/info");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Buongiorno Mondo",response[1]);
assertEquals("/ciao",response[2]);
assertEquals("/info",response[3]);
}
@Test
public void testDispatchFullMatch() throws Exception
{
String[] response=getResponse("/dispatch/xxx?forward=/some/thing.fr");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Bonjour le Monde",response[1]);
assertEquals("/some/thing.fr",response[2]);
assertEquals("null",response[3]);
}
@Test
public void testDispatchCaptureMatch() throws Exception
{
String[] response=getResponse("/dispatch/xxx?forward=/ciao/info");
assertEquals("HTTP/1.1 200 OK",response[0]);
assertEquals("Buongiorno Mondo",response[1]);
assertEquals("/ciao",response[2]);
assertEquals("/info",response[3]);
}
private String[] getResponse(String uri) throws Exception
{
Socket socket = new Socket("127.0.0.1",__port);
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.print("GET "+uri+" HTTP/1.0\r\n\r\n");
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String[] response=new String[4];
response[0]=in.readLine();
//System.err.println(response[0]);
String line=in.readLine();
while(line.length()>0)
line=in.readLine();
response[1]=in.readLine();
//System.err.println(response[1]);
response[2]=in.readLine();
//System.err.println(response[2]);
response[3]=in.readLine();
//System.err.println(response[3]);
socket.close();
return response;
}
public static class HelloServlet extends HttpServlet implements Servlet
{
final String _hello;
public HelloServlet(String hello)
{
_hello=hello;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setStatus(200);
response.getWriter().println(_hello);
response.getWriter().println(request.getServletPath());
response.getWriter().println(request.getPathInfo());
}
}
public static class DispatchServletServlet extends HttpServlet implements Servlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
RequestDispatcher dispatcher = null;
if(request.getParameter("include")!=null)
{
dispatcher = getServletContext().getRequestDispatcher(request.getParameter("include"));
dispatcher.include(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response));
}
else if(request.getParameter("forward")!=null)
{
dispatcher = getServletContext().getRequestDispatcher(request.getParameter("forward"));
dispatcher.forward(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response));
}
}
}
}

View File

@ -14,6 +14,10 @@ package org.eclipse.jetty.rewrite.handler;
import java.io.IOException; import java.io.IOException;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -23,21 +27,21 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{ {
private String[][] _tests= private String[][] _tests=
{ {
{"/foo/bar",null,".*","/replace","/replace",null}, {"/foo0/bar",null,".*","/replace","/replace",null},
{"/foo/bar","n=v",".*","/replace","/replace","n=v"}, {"/foo1/bar","n=v",".*","/replace","/replace","n=v"},
{"/foo/bar",null,"/xxx.*","/replace",null,null}, {"/foo2/bar",null,"/xxx.*","/replace",null,null},
{"/foo/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx",null}, {"/foo3/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo3/xxx",null},
{"/foo/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo"}, {"/foo4/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo4"},
{"/foo/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo"}, {"/foo5/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo5"},
{"/foo/bar",null,"/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","p2=bar&p1=foo"}, {"/foo6/bar",null,"/(.*)/(.*)","/foo6/bar?p2=$2&p1=$1","/foo6/bar","p2=bar&p1=foo6"},
{"/foo/bar","n=v","/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","n=v&p2=bar&p1=foo"}, {"/foo7/bar","n=v","/(.*)/(.*)","/foo7/bar?p2=$2&p1=$1","/foo7/bar","n=v&p2=bar&p1=foo7"},
{"/foo/bar",null,"/(foo)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo/xxx",null}, {"/foo8/bar",null,"/(foo8)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo8/xxx",null},
{"/foo/$bar",null,".*","/$replace","/$replace",null}, {"/foo9/$bar",null,".*","/$replace","/$replace",null},
{"/foo/$bar",null,"/foo/(.*)","/$1/replace","/$bar/replace",null}, {"/fooA/$bar",null,"/fooA/(.*)","/$1/replace","/$bar/replace",null},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"}, {"/fooB/bar/info",null,"/fooB/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"}, {"/fooC/bar/info",null,"/fooC/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"}, {"/fooD/bar/info","n=v","/fooD/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&p1=bar"}, {"/fooE/bar/info","n=v","/fooE/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&p1=bar"},
}; };
private RewriteRegexRule _rule; private RewriteRegexRule _rule;
@ -53,20 +57,35 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{ {
for (String[] test : _tests) for (String[] test : _tests)
{ {
reset();
_request.setRequestURI(null);
String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3]; String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3];
_rule.setRegex(test[2]); _rule.setRegex(test[2]);
_rule.setReplacement(test[3]); _rule.setReplacement(test[3]);
_request.setRequestURI(test[0]); _request.setUri(new HttpURI(test[0]+(test[1]==null?"":("?"+test[1]))));
_request.setQueryString(test[1]); _request.getRequestURI();
_request.getAttributes().clearAttributes();
String result = _rule.matchAndApply(test[0], _request, _response); String result = _rule.matchAndApply(test[0], _request, _response);
assertEquals(t, test[4], result); assertEquals(t, test[4], result);
_rule.applyURI(_request,test[0],result); _rule.applyURI(_request,test[0],result);
assertEquals(t,test[4], _request.getRequestURI()); if (result!=null)
assertEquals(t,test[5], _request.getQueryString()); {
assertEquals(t,test[4], _request.getRequestURI());
assertEquals(t,test[5], _request.getQueryString());
}
if (test[5]!=null)
{
MultiMap<String> params=new MultiMap<String>();
UrlEncoded.decodeTo(test[5],params,StringUtil.__UTF8);
for (String n:params.keySet())
assertEquals(params.getString(n),_request.getParameter(n));
}
} }
} }
@ -78,6 +97,7 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
container.addRule(_rule); container.addRule(_rule);
for (String[] test : _tests) for (String[] test : _tests)
{ {
reset();
String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3]; String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3];
_rule.setRegex(test[2]); _rule.setRegex(test[2]);
_rule.setReplacement(test[3]); _rule.setReplacement(test[3]);

View File

@ -385,7 +385,7 @@ public class ResponseTest
{ {
String[][] tests={ String[][] tests={
{"/other/location?name=value","http://myhost:8888/other/location;jsessionid=12345?name=value"}, {"/other/location?name=value","http://myhost:8888/other/location;jsessionid=12345?name=value"},
{"/other/location","http://myhost:8888/other/location"}, /* {"/other/location","http://myhost:8888/other/location"},
{"/other/l%20cation","http://myhost:8888/other/l%20cation"}, {"/other/l%20cation","http://myhost:8888/other/l%20cation"},
{"location","http://myhost:8888/path/location"}, {"location","http://myhost:8888/path/location"},
{"./location","http://myhost:8888/path/location"}, {"./location","http://myhost:8888/path/location"},
@ -393,7 +393,8 @@ public class ResponseTest
{"/other/l%20cation","http://myhost:8888/other/l%20cation"}, {"/other/l%20cation","http://myhost:8888/other/l%20cation"},
{"l%20cation","http://myhost:8888/path/l%20cation"}, {"l%20cation","http://myhost:8888/path/l%20cation"},
{"./l%20cation","http://myhost:8888/path/l%20cation"}, {"./l%20cation","http://myhost:8888/path/l%20cation"},
{"../l%20cation","http://myhost:8888/l%20cation"}, {"../l%20cation","http://myhost:8888/l%20cation"},*/
{"../locati%C3%abn","http://myhost:8888/locati%C3%ABn"},
}; };
for (int i=1;i<tests.length;i++) for (int i=1;i<tests.length;i++)

View File

@ -99,8 +99,6 @@ public class SSLEngineTest
connector.setRequestHeaderSize(512); connector.setRequestHeaderSize(512);
server.setConnectors(new Connector[]{connector }); server.setConnectors(new Connector[]{connector });
server.setHandler(new HelloWorldHandler());
server.start();
} }
@AfterClass @AfterClass
@ -109,10 +107,15 @@ public class SSLEngineTest
server.stop(); server.stop();
server.join(); server.join();
} }
@Test @Test
public void testBigResponse() throws Exception public void testBigResponse() throws Exception
{ {
server.stop();
server.setHandler(new HelloWorldHandler());
server.start();
SSLContext ctx=SSLContext.getInstance("TLS"); SSLContext ctx=SSLContext.getInstance("TLS");
ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom()); ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom());
@ -123,7 +126,7 @@ public class SSLEngineTest
String request = String request =
"GET /?dump=102400 HTTP/1.1\r\n"+ "GET /?dump=102400 HTTP/1.1\r\n"+
"Host: localhost:8080\r\n"+ "Host: localhost:"+port+"\r\n"+
"Connection: close\r\n"+ "Connection: close\r\n"+
"\r\n"; "\r\n";
@ -138,6 +141,10 @@ public class SSLEngineTest
@Test @Test
public void testRequestJettyHttps() throws Exception public void testRequestJettyHttps() throws Exception
{ {
server.stop();
server.setHandler(new HelloWorldHandler());
server.start();
final int loops=10; final int loops=10;
final int numConns=10; final int numConns=10;
@ -204,8 +211,7 @@ public class SSLEngineTest
@Test @Test
public void testServletPost() throws Exception public void testServletPost() throws Exception
{ {
stopServer(); server.stop();
StreamHandler handler = new StreamHandler(); StreamHandler handler = new StreamHandler();
server.setHandler(handler); server.setHandler(handler);
server.start(); server.start();
@ -308,6 +314,7 @@ public class SSLEngineTest
{ {
ServletOutputStream out=response.getOutputStream(); ServletOutputStream out=response.getOutputStream();
byte[] buf = new byte[Integer.valueOf(request.getParameter("dump"))]; byte[] buf = new byte[Integer.valueOf(request.getParameter("dump"))];
// System.err.println("DUMP "+buf.length);
for (int i=0;i<buf.length;i++) for (int i=0;i<buf.length;i++)
buf[i]=(byte)('0'+(i%10)); buf[i]=(byte)('0'+(i%10));
out.write(buf); out.write(buf);

View File

@ -1240,7 +1240,7 @@ public class ServletHandler extends ScopedHandler
{ {
if(LOG.isDebugEnabled()) if(LOG.isDebugEnabled())
LOG.debug("Not Found "+request.getRequestURI()); LOG.debug("Not Found "+request.getRequestURI());
response.sendError(HttpServletResponse.SC_NOT_FOUND); //Override to send an error back, eg with: response.sendError(HttpServletResponse.SC_NOT_FOUND);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */

View File

@ -150,21 +150,6 @@ public class GzipFilter extends UserAgentFilter
{ {
} }
/* ------------------------------------------------------------ */
public String selectCompression(String encodingHeader)
{
// TODO, this could be a little more robust.
// prefer gzip over deflate
if (encodingHeader!=null)
{
if (encodingHeader.toLowerCase().contains(GZIP))
return GZIP;
if (encodingHeader.toLowerCase().contains(DEFLATE))
return DEFLATE;
}
return null;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) * @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
@ -176,7 +161,6 @@ public class GzipFilter extends UserAgentFilter
HttpServletRequest request=(HttpServletRequest)req; HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)res; HttpServletResponse response=(HttpServletResponse)res;
String ae = request.getHeader("accept-encoding");
String compressionType = selectCompression(request.getHeader("accept-encoding")); String compressionType = selectCompression(request.getHeader("accept-encoding"));
if (compressionType!=null && !response.containsHeader("Content-Encoding") && !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod())) if (compressionType!=null && !response.containsHeader("Content-Encoding") && !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod()))
{ {
@ -223,6 +207,21 @@ public class GzipFilter extends UserAgentFilter
} }
} }
/* ------------------------------------------------------------ */
private String selectCompression(String encodingHeader)
{
// TODO, this could be a little more robust.
// prefer gzip over deflate
if (encodingHeader!=null)
{
if (encodingHeader.toLowerCase().contains(GZIP))
return GZIP;
else if (encodingHeader.toLowerCase().contains(DEFLATE))
return DEFLATE;
}
return null;
}
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType) protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
{ {
CompressedResponseWrapper wrappedResponse = null; CompressedResponseWrapper wrappedResponse = null;
@ -304,7 +303,7 @@ public class GzipFilter extends UserAgentFilter
} }
/** /**
* Checks to see if the UserAgent is excluded * Checks to see if the userAgent is excluded
* *
* @param ua * @param ua
* the user agent * the user agent
@ -322,7 +321,7 @@ public class GzipFilter extends UserAgentFilter
return true; return true;
} }
} }
else if (_excludedAgentPatterns != null) if (_excludedAgentPatterns != null)
{ {
for (Pattern pattern : _excludedAgentPatterns) for (Pattern pattern : _excludedAgentPatterns)
{ {
@ -337,9 +336,9 @@ public class GzipFilter extends UserAgentFilter
} }
/** /**
* Checks to see if the Path is excluded * Checks to see if the path is excluded
* *
* @param ua * @param requestURI
* the request uri * the request uri
* @return boolean true if excluded * @return boolean true if excluded
*/ */
@ -347,6 +346,16 @@ public class GzipFilter extends UserAgentFilter
{ {
if (requestURI == null) if (requestURI == null)
return false; return false;
if (_excludedPaths != null)
{
for (String excludedPath : _excludedPaths)
{
if (requestURI.startsWith(excludedPath))
{
return true;
}
}
}
if (_excludedPathPatterns != null) if (_excludedPathPatterns != null)
{ {
for (Pattern pattern : _excludedPathPatterns) for (Pattern pattern : _excludedPathPatterns)

View File

@ -31,11 +31,10 @@ public class GzipFilterDefaultTest
public static Collection<String[]> data() public static Collection<String[]> data()
{ {
String[][] data = new String[][] String[][] data = new String[][]
{ {
{ GzipFilter.GZIP }, { GzipFilter.GZIP },
{ GzipFilter.DEFLATE } { GzipFilter.DEFLATE } };
};
return Arrays.asList(data); return Arrays.asList(data);
} }
@ -166,20 +165,88 @@ public class GzipFilterDefaultTest
@Test @Test
public void testUserAgentExclusion() throws Exception public void testUserAgentExclusion() throws Exception
{ {
GzipTester tester = new GzipTester(testingdir, compressionType); GzipTester tester = new GzipTester(testingdir,compressionType);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class); FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludedAgents", "foo"); holder.setInitParameter("excludedAgents","foo");
tester.setUserAgent("foo"); tester.setUserAgent("foo");
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize); tester.prepareServerFile("file.txt",filesize);
try try
{ {
tester.start(); tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200); tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
} }
finally
{
tester.stop();
}
}
@Test
public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludedAgents","bar");
holder.setInitParameter("excludeAgentPatterns","fo.*");
tester.setUserAgent("foo");
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
}
finally
{
tester.stop();
}
}
@Test
public void testExcludePaths() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludePaths","/context/");
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
}
finally
{
tester.stop();
}
}
@Test
public void testExcludePathPatterns() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludePathPatterns","/cont.*");
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
}
finally finally
{ {
tester.stop(); tester.stop();

View File

@ -12,13 +12,9 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Jetty :: SPDY :: Parent</name> <name>Jetty :: SPDY :: Parent</name>
<properties> <properties>
<!-- <npn.version>1.0.0.v20120402</npn.version>
npn version only needs to change when there are changes in that binary, not </properties>
with each and every release.
-->
<npn.version>7.6.2.v20120308</npn.version>
</properties>
<modules> <modules>
<module>spdy-core</module> <module>spdy-core</module>

View File

@ -12,8 +12,9 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>

View File

@ -68,8 +68,9 @@ public interface IStream extends Stream
* of true puts the stream into closed state.</p> * of true puts the stream into closed state.</p>
* *
* @param close whether the close state should be updated * @param close whether the close state should be updated
* @param local whether the close is local or remote
*/ */
public void updateCloseState(boolean close); public void updateCloseState(boolean close, boolean local);
/** /**
* <p>Processes the given control frame, * <p>Processes the given control frame,

View File

@ -18,7 +18,6 @@ package org.eclipse.jetty.spdy;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.InterruptedByTimeoutException; import java.nio.channels.InterruptedByTimeoutException;
import java.util.Deque;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -62,12 +61,12 @@ import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame; import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
import org.eclipse.jetty.spdy.generator.Generator; import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser; import org.eclipse.jetty.spdy.parser.Parser;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class StandardSession implements ISession, Parser.Listener, Handler<StandardSession.FrameBytes> public class StandardSession implements ISession, Parser.Listener, Handler<StandardSession.FrameBytes>
{ {
private static final Logger logger = LoggerFactory.getLogger(Session.class); private static final Logger logger = Log.getLogger(Session.class);
private static final ThreadLocal<Integer> handlerInvocations = new ThreadLocal<Integer>() private static final ThreadLocal<Integer> handlerInvocations = new ThreadLocal<Integer>()
{ {
@Override @Override
@ -79,7 +78,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private final List<Listener> listeners = new CopyOnWriteArrayList<>(); private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>(); private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
private final Deque<FrameBytes> queue = new LinkedList<>(); private final LinkedList<FrameBytes> queue = new LinkedList<>();
private final ByteBufferPool bufferPool; private final ByteBufferPool bufferPool;
private final Executor threadPool; private final Executor threadPool;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
@ -399,6 +398,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private void onSyn(SynStreamFrame frame) private void onSyn(SynStreamFrame frame)
{ {
IStream stream = newStream(frame); IStream stream = newStream(frame);
stream.updateCloseState(frame.isClose(), false);
logger.debug("Opening {}", stream); logger.debug("Opening {}", stream);
int streamId = frame.getStreamId(); int streamId = frame.getStreamId();
IStream existing = streams.putIfAbsent(streamId, stream); IStream existing = streams.putIfAbsent(streamId, stream);
@ -430,6 +430,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private IStream createStream(SynStreamFrame synStream, StreamFrameListener listener) private IStream createStream(SynStreamFrame synStream, StreamFrameListener listener)
{ {
IStream stream = newStream(synStream); IStream stream = newStream(synStream);
stream.updateCloseState(synStream.isClose(), true);
stream.setStreamFrameListener(listener); stream.setStreamFrameListener(listener);
if (streams.putIfAbsent(synStream.getStreamId(), stream) != null) if (streams.putIfAbsent(synStream.getStreamId(), stream) != null)
{ {
@ -732,10 +733,15 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
{ {
ByteBuffer buffer = generator.control(frame); ByteBuffer buffer = generator.control(frame);
logger.debug("Queuing {} on {}", frame, stream); logger.debug("Queuing {} on {}", frame, stream);
ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(handler, context, frame, buffer); ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream, handler, context, frame, buffer);
if (timeout > 0) if (timeout > 0)
frameBytes.task = scheduler.schedule(frameBytes, timeout, unit); frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
enqueueLast(frameBytes);
// Special handling for PING frames, they must be sent as soon as possible
if (ControlFrameType.PING == frame.getType())
prepend(frameBytes);
else
append(frameBytes);
} }
flush(); flush();
@ -766,10 +772,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context) public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context)
{ {
logger.debug("Queuing {} on {}", dataInfo, stream); logger.debug("Queuing {} on {}", dataInfo, stream);
DataFrameBytes<C> frameBytes = new DataFrameBytes<>(handler, context, stream, dataInfo); DataFrameBytes<C> frameBytes = new DataFrameBytes<>(stream, handler, context, dataInfo);
if (timeout > 0) if (timeout > 0)
frameBytes.task = scheduler.schedule(frameBytes, timeout, unit); frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
enqueueLast(frameBytes); append(frameBytes);
flush(); flush();
} }
@ -781,56 +787,73 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
@Override @Override
public void flush() public void flush()
{ {
FrameBytes frameBytes; FrameBytes frameBytes = null;
ByteBuffer buffer; ByteBuffer buffer = null;
synchronized (queue) synchronized (queue)
{ {
if (flushing) if (flushing || queue.isEmpty())
return; return;
frameBytes = queue.poll(); Set<IStream> stalledStreams = null;
if (frameBytes == null) for (int i = 0; i < queue.size(); ++i)
return;
FrameBytes stalled = null;
while (true)
{ {
frameBytes = queue.get(i);
if (stalledStreams != null && stalledStreams.contains(frameBytes.getStream()))
continue;
buffer = frameBytes.getByteBuffer(); buffer = frameBytes.getByteBuffer();
if (buffer != null) if (buffer != null)
{
queue.remove(i);
break; break;
}
if (stalledStreams == null)
stalledStreams = new HashSet<>();
stalledStreams.add(frameBytes.getStream());
// We are stalled: enqueue as last so other frames can be flushed
enqueueLast(frameBytes);
if (stalled == null)
stalled = frameBytes;
else if (stalled == frameBytes)
return;
logger.debug("Flush stalled for {}, {} frame(s) in queue", frameBytes, queue.size()); logger.debug("Flush stalled for {}, {} frame(s) in queue", frameBytes, queue.size());
frameBytes = queue.poll();
} }
if (buffer == null)
return;
flushing = true; flushing = true;
logger.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size()); logger.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size());
} }
logger.debug("Writing {} frame bytes of {}", buffer.remaining(), frameBytes);
write(buffer, this, frameBytes); write(buffer, this, frameBytes);
} }
private void enqueueLast(FrameBytes frameBytes) private void append(FrameBytes frameBytes)
{ {
// TODO: handle priority; e.g. use queues to prioritize the buffers ?
synchronized (queue) synchronized (queue)
{ {
queue.offerLast(frameBytes); int index = queue.size();
while (index > 0)
{
FrameBytes element = queue.get(index - 1);
if (element.compareTo(frameBytes) >= 0)
break;
--index;
}
queue.add(index, frameBytes);
} }
} }
private void enqueueFirst(FrameBytes frameBytes) private void prepend(FrameBytes frameBytes)
{ {
synchronized (queue) synchronized (queue)
{ {
queue.offerFirst(frameBytes); int index = 0;
while (index < queue.size())
{
FrameBytes element = queue.get(index);
if (element.compareTo(frameBytes) <= 0)
break;
++index;
}
queue.add(index, frameBytes);
} }
} }
@ -854,42 +877,44 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes) protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{ {
if (controller != null) if (controller != null)
{
logger.debug("Writing {} frame bytes of {}", buffer.remaining(), frameBytes);
controller.write(buffer, handler, frameBytes); controller.write(buffer, handler, frameBytes);
}
} }
private <C> void complete(final Handler<C> handler, final C context) private <C> void complete(final Handler<C> handler, final C context)
{ {
if (handler != null) // Applications may send and queue up a lot of frames and
// if we call Handler.completed() only synchronously we risk
// starvation (for the last frames sent) and stack overflow.
// Therefore every some invocation, we dispatch to a new thread
Integer invocations = handlerInvocations.get();
if (invocations >= 4)
{ {
// Applications may send and queue up a lot of frames and execute(new Runnable()
// if we call Handler.completed() only synchronously we risk
// starvation (for the last frames sent) and stack overflow.
// Therefore every some invocation, we dispatch to a new thread
Integer invocations = handlerInvocations.get();
if (invocations >= 4)
{ {
execute(new Runnable() @Override
public void run()
{ {
@Override if (handler != null)
public void run()
{
notifyHandlerCompleted(handler, context); notifyHandlerCompleted(handler, context);
flush();
}
});
}
else
{
handlerInvocations.set(invocations + 1);
try
{
notifyHandlerCompleted(handler, context);
flush(); flush();
} }
finally });
{ }
handlerInvocations.set(invocations); else
} {
handlerInvocations.set(invocations + 1);
try
{
if (handler != null)
notifyHandlerCompleted(handler, context);
flush();
}
finally
{
handlerInvocations.set(invocations);
} }
} }
} }
@ -920,8 +945,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
} }
} }
public interface FrameBytes public interface FrameBytes extends Comparable<FrameBytes>
{ {
public IStream getStream();
public abstract ByteBuffer getByteBuffer(); public abstract ByteBuffer getByteBuffer();
public abstract void complete(); public abstract void complete();
@ -929,16 +956,31 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private abstract class AbstractFrameBytes<C> implements FrameBytes, Runnable private abstract class AbstractFrameBytes<C> implements FrameBytes, Runnable
{ {
private final IStream stream;
private final Handler<C> handler; private final Handler<C> handler;
private final C context; private final C context;
protected volatile ScheduledFuture<?> task; protected volatile ScheduledFuture<?> task;
protected AbstractFrameBytes(Handler<C> handler, C context) protected AbstractFrameBytes(IStream stream, Handler<C> handler, C context)
{ {
this.stream = stream;
this.handler = handler; this.handler = handler;
this.context = context; this.context = context;
} }
@Override
public IStream getStream()
{
return stream;
}
@Override
public int compareTo(FrameBytes that)
{
// If this.stream.priority > that.stream.priority => -1 (this.stream has less priority than that.stream)
return that.getStream().getPriority() - getStream().getPriority();
}
@Override @Override
public void complete() public void complete()
{ {
@ -966,9 +1008,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private final ControlFrame frame; private final ControlFrame frame;
private final ByteBuffer buffer; private final ByteBuffer buffer;
private ControlFrameBytes(Handler<C> handler, C context, ControlFrame frame, ByteBuffer buffer) private ControlFrameBytes(IStream stream, Handler<C> handler, C context, ControlFrame frame, ByteBuffer buffer)
{ {
super(handler, context); super(stream, handler, context);
this.frame = frame; this.frame = frame;
this.buffer = buffer; this.buffer = buffer;
} }
@ -1003,15 +1045,13 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private class DataFrameBytes<C> extends AbstractFrameBytes<C> private class DataFrameBytes<C> extends AbstractFrameBytes<C>
{ {
private final IStream stream;
private final DataInfo dataInfo; private final DataInfo dataInfo;
private int size; private int size;
private volatile ByteBuffer buffer; private volatile ByteBuffer buffer;
private DataFrameBytes(Handler<C> handler, C context, IStream stream, DataInfo dataInfo) private DataFrameBytes(IStream stream, Handler<C> handler, C context, DataInfo dataInfo)
{ {
super(handler, context); super(stream, handler, context);
this.stream = stream;
this.dataInfo = dataInfo; this.dataInfo = dataInfo;
} }
@ -1020,6 +1060,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
{ {
try try
{ {
IStream stream = getStream();
int windowSize = stream.getWindowSize(); int windowSize = stream.getWindowSize();
if (windowSize <= 0) if (windowSize <= 0)
return null; return null;
@ -1042,6 +1083,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
public void complete() public void complete()
{ {
bufferPool.release(buffer); bufferPool.release(buffer);
IStream stream = getStream();
stream.updateWindowSize(-size); stream.updateWindowSize(-size);
if (dataInfo.available() > 0) if (dataInfo.available() > 0)
@ -1049,12 +1091,12 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
// We have written a frame out of this DataInfo, but there is more to write. // We have written a frame out of this DataInfo, but there is more to write.
// We need to keep the correct ordering of frames, to avoid that another // We need to keep the correct ordering of frames, to avoid that another
// DataInfo for the same stream is written before this one is finished. // DataInfo for the same stream is written before this one is finished.
enqueueFirst(this); prepend(this);
} }
else else
{ {
super.complete(); super.complete();
stream.updateCloseState(dataInfo.isClose()); stream.updateCloseState(dataInfo.isClose(), true);
if (stream.isClosed()) if (stream.isClosed())
removeStream(stream); removeStream(stream);
} }
@ -1063,7 +1105,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
@Override @Override
public String toString() public String toString()
{ {
return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), stream); return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), getStream());
} }
} }
} }

View File

@ -39,27 +39,25 @@ import org.eclipse.jetty.spdy.frames.HeadersFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame; import org.eclipse.jetty.spdy.frames.SynReplyFrame;
import org.eclipse.jetty.spdy.frames.SynStreamFrame; import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame; import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class StandardStream implements IStream public class StandardStream implements IStream
{ {
private static final Logger logger = LoggerFactory.getLogger(Stream.class); private static final Logger logger = Log.getLogger(Stream.class);
private final Map<String, Object> attributes = new ConcurrentHashMap<>(); private final Map<String, Object> attributes = new ConcurrentHashMap<>();
private final SynStreamFrame frame; private final SynStreamFrame frame;
private final ISession session; private final ISession session;
private final AtomicInteger windowSize; private final AtomicInteger windowSize;
private volatile StreamFrameListener listener; private volatile StreamFrameListener listener;
private volatile boolean opened; private volatile OpenState openState = OpenState.SYN_SENT;
private volatile boolean halfClosed; private volatile CloseState closeState = CloseState.OPENED;
private volatile boolean closed;
public StandardStream(SynStreamFrame frame, ISession session, int windowSize) public StandardStream(SynStreamFrame frame, ISession session, int windowSize)
{ {
this.frame = frame; this.frame = frame;
this.session = session; this.session = session;
this.windowSize = new AtomicInteger(windowSize); this.windowSize = new AtomicInteger(windowSize);
this.halfClosed = frame.isClose();
} }
@Override @Override
@ -95,7 +93,8 @@ public class StandardStream implements IStream
public boolean isHalfClosed() public boolean isHalfClosed()
{ {
return halfClosed; CloseState closeState = this.closeState;
return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.REMOTELY_CLOSED;
} }
@Override @Override
@ -123,14 +122,38 @@ public class StandardStream implements IStream
} }
@Override @Override
public void updateCloseState(boolean close) public void updateCloseState(boolean close, boolean local)
{ {
if (close) if (close)
{ {
if (isHalfClosed()) switch (closeState)
closed = true; {
else case OPENED:
halfClosed = true; {
closeState = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED;
break;
}
case LOCALLY_CLOSED:
{
if (local)
throw new IllegalStateException();
else
closeState = CloseState.CLOSED;
break;
}
case REMOTELY_CLOSED:
{
if (local)
closeState = CloseState.CLOSED;
else
throw new IllegalStateException();
break;
}
default:
{
throw new IllegalStateException();
}
}
} }
} }
@ -141,14 +164,14 @@ public class StandardStream implements IStream
{ {
case SYN_STREAM: case SYN_STREAM:
{ {
opened = true; openState = OpenState.SYN_RECV;
break; break;
} }
case SYN_REPLY: case SYN_REPLY:
{ {
opened = true; openState = OpenState.REPLY_RECV;
SynReplyFrame synReply = (SynReplyFrame)frame; SynReplyFrame synReply = (SynReplyFrame)frame;
updateCloseState(synReply.isClose()); updateCloseState(synReply.isClose(), false);
ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose()); ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose());
notifyOnReply(replyInfo); notifyOnReply(replyInfo);
break; break;
@ -156,7 +179,7 @@ public class StandardStream implements IStream
case HEADERS: case HEADERS:
{ {
HeadersFrame headers = (HeadersFrame)frame; HeadersFrame headers = (HeadersFrame)frame;
updateCloseState(headers.isClose()); updateCloseState(headers.isClose(), false);
HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression()); HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression());
notifyOnHeaders(headersInfo); notifyOnHeaders(headersInfo);
break; break;
@ -183,13 +206,13 @@ public class StandardStream implements IStream
@Override @Override
public void process(DataFrame frame, ByteBuffer data) public void process(DataFrame frame, ByteBuffer data)
{ {
if (!opened) if (!canReceive())
{ {
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
return; return;
} }
updateCloseState(frame.isClose()); updateCloseState(frame.isClose(), false);
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress()) ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress())
{ {
@ -286,7 +309,8 @@ public class StandardStream implements IStream
@Override @Override
public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler) public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{ {
updateCloseState(replyInfo.isClose()); openState = OpenState.REPLY_SENT;
updateCloseState(replyInfo.isClose(), true);
SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders()); SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders());
session.control(this, frame, timeout, unit, handler, null); session.control(this, frame, timeout, unit, handler, null);
} }
@ -302,6 +326,17 @@ public class StandardStream implements IStream
@Override @Override
public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler) public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{ {
if (!canSend())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame on a closed stream");
}
// Cannot update the close state here, because the data that we send may // Cannot update the close state here, because the data that we send may
// be flow controlled, so we need the stream to update the window size. // be flow controlled, so we need the stream to update the window size.
session.data(this, dataInfo, timeout, unit, handler, null); session.data(this, dataInfo, timeout, unit, handler, null);
@ -318,7 +353,18 @@ public class StandardStream implements IStream
@Override @Override
public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler) public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler)
{ {
updateCloseState(headersInfo.isClose()); if (!canSend())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame on a closed stream");
}
updateCloseState(headersInfo.isClose(), true);
HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders()); HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders());
session.control(this, frame, timeout, unit, handler, null); session.control(this, frame, timeout, unit, handler, null);
} }
@ -326,12 +372,40 @@ public class StandardStream implements IStream
@Override @Override
public boolean isClosed() public boolean isClosed()
{ {
return closed; return closeState == CloseState.CLOSED;
}
private boolean isLocallyClosed()
{
CloseState closeState = this.closeState;
return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.CLOSED;
} }
@Override @Override
public String toString() public String toString()
{ {
return String.format("stream=%d v%d closed=%s", getId(), session.getVersion(), isClosed() ? "true" : isHalfClosed() ? "half" : "false"); return String.format("stream=%d v%d %s", getId(), session.getVersion(), closeState);
}
private boolean canSend()
{
OpenState openState = this.openState;
return openState == OpenState.SYN_SENT || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT;
}
private boolean canReceive()
{
OpenState openState = this.openState;
return openState == OpenState.SYN_RECV || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT;
}
private enum OpenState
{
SYN_SENT, SYN_RECV, REPLY_SENT, REPLY_RECV
}
private enum CloseState
{
OPENED, LOCALLY_CLOSED, REMOTELY_CLOSED, CLOSED
} }
} }

View File

@ -18,8 +18,8 @@ package org.eclipse.jetty.spdy.api;
import java.util.EventListener; import java.util.EventListener;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
/** /**
* <p>A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening * <p>A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening
@ -122,7 +122,7 @@ public interface SessionFrameListener extends EventListener
*/ */
public static class Adapter implements SessionFrameListener public static class Adapter implements SessionFrameListener
{ {
private static final Logger logger = LoggerFactory.getLogger(Adapter.class); private static final Logger logger = Log.getLogger(Adapter.class);
@Override @Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)

View File

@ -65,7 +65,7 @@ public class SettingsGenerator extends ControlFrameGenerator
case SPDY.V2: case SPDY.V2:
{ {
// In v2 the format is 24 bits of ID + 8 bits of flag // In v2 the format is 24 bits of ID + 8 bits of flag
int idAndFlags = (id << 8) + flags; int idAndFlags = (id << 8) + (flags & 0xFF);
// A bug in the Chromium implementation forces v2 to have // A bug in the Chromium implementation forces v2 to have
// the 3 ID bytes little endian, so we swap first and third // the 3 ID bytes little endian, so we swap first and third
int result = idAndFlags & 0x00_FF_00_FF; int result = idAndFlags & 0x00_FF_00_FF;

View File

@ -58,6 +58,10 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
state = State.STATUS_CODE; state = State.STATUS_CODE;
break; break;
} }
default:
{
throw new IllegalStateException();
}
} }
} }
else else
@ -87,6 +91,10 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
state = State.STATUS_CODE; state = State.STATUS_CODE;
break; break;
} }
default:
{
throw new IllegalStateException();
}
} }
} }
break; break;

View File

@ -27,12 +27,12 @@ import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.SessionStatus; import org.eclipse.jetty.spdy.api.SessionStatus;
import org.eclipse.jetty.spdy.frames.ControlFrame; import org.eclipse.jetty.spdy.frames.ControlFrame;
import org.eclipse.jetty.spdy.frames.DataFrame; import org.eclipse.jetty.spdy.frames.DataFrame;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class Parser public class Parser
{ {
private static final Logger logger = LoggerFactory.getLogger(Parser.class); private static final Logger logger = Log.getLogger(Parser.class);
private final List<Listener> listeners = new CopyOnWriteArrayList<>(); private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final ControlFrameParser controlFrameParser; private final ControlFrameParser controlFrameParser;
private final DataFrameParser dataFrameParser; private final DataFrameParser dataFrameParser;

View File

@ -22,7 +22,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.spdy.api.Handler; import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.SPDY;
@ -94,17 +93,15 @@ public class AsyncTimeoutTest
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor()); Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator) Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator)
{ {
private final AtomicInteger flushes = new AtomicInteger();
@Override @Override
public void flush() protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{ {
try try
{ {
int flushes = this.flushes.incrementAndGet(); // Wait if we're writing the data frame (control frame's first byte is 0x80)
if (flushes == 3) if (buffer.get(0) == 0)
unit.sleep(2 * timeout); unit.sleep(2 * timeout);
super.flush(); super.write(buffer, handler, frameBytes);
} }
catch (InterruptedException x) catch (InterruptedException x)
{ {

View File

@ -1,63 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<parent> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>org.eclipse.jetty.spdy</groupId> <parent>
<artifactId>spdy-parent</artifactId> <groupId>org.eclipse.jetty.spdy</groupId>
<version>8.1.3-SNAPSHOT</version> <artifactId>spdy-parent</artifactId>
</parent> <version>8.1.3-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion> </parent>
<artifactId>spdy-jetty-http-webapp</artifactId> <modelVersion>4.0.0</modelVersion>
<packaging>war</packaging> <artifactId>spdy-jetty-http-webapp</artifactId>
<name>Jetty :: SPDY :: Jetty HTTP Web Application</name> <packaging>war</packaging>
<build> <name>Jetty :: SPDY :: Jetty HTTP Web Application</name>
<plugins>
<plugin> <build>
<groupId>org.apache.maven.plugins</groupId> <plugins>
<artifactId>maven-assembly-plugin</artifactId> <plugin>
<executions> <groupId>org.apache.maven.plugins</groupId>
<execution> <artifactId>maven-assembly-plugin</artifactId>
<phase>package</phase> <executions>
<goals> <execution>
<goal>single</goal> <phase>package</phase>
</goals> <goals>
<configuration> <goal>single</goal>
<descriptorRefs> </goals>
<descriptorRef>config</descriptorRef> <configuration>
</descriptorRefs> <descriptorRefs>
</configuration> <descriptorRef>config</descriptorRef>
</execution> </descriptorRefs>
</executions> </configuration>
</plugin> </execution>
<!-- </executions>
</plugin>
<plugin> <!--
<groupId>org.mortbay.jetty</groupId> <plugin>
<artifactId>jetty-maven-plugin</artifactId> <groupId>org.mortbay.jetty</groupId>
<version>${project.version}</version> <artifactId>jetty-maven-plugin</artifactId>
<configuration>
<stopPort>8888</stopPort>
<stopKey>quit</stopKey>
<jvmArgs>
-Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
</jvmArgs>
<jettyXml>${basedir}/src/main/config/jetty-spdy.xml</jettyXml>
<contextPath>/</contextPath>
</configuration>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty-http</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> <configuration>
<dependency> <stopPort>8888</stopPort>
<groupId>org.slf4j</groupId> <stopKey>quit</stopKey>
<artifactId>slf4j-log4j12</artifactId> <jvmArgs>
<version>${slf4j-version}</version> -Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
</dependency> -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
</dependencies> </jvmArgs>
</plugin> <jettyXml>${basedir}/src/main/config/etc/jetty-spdy.xml</jettyXml>
--> <contextPath>/</contextPath>
</plugins> </configuration>
</build> <dependencies>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-version}</version>
</dependency>
</dependencies>
</plugin>
-->
</plugins>
</build>
</project> </project>

View File

@ -9,13 +9,6 @@
<Set name="trustStore">src/main/resources/truststore.jks</Set> <Set name="trustStore">src/main/resources/truststore.jks</Set>
<Set name="trustStorePassword">storepwd</Set> <Set name="trustStorePassword">storepwd</Set>
<Set name="protocol">TLSv1</Set> <Set name="protocol">TLSv1</Set>
<Set name="includeProtocols">
<Array type="java.lang.String">
<Item>TLSv1</Item>
<Item>TLSv1.1</Item>
<Item>TLSv1.2</Item>
</Array>
</Set>
</New> </New>
<Call name="addConnector"> <Call name="addConnector">

View File

@ -1,85 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>org.eclipse.jetty.spdy</groupId> <groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId> <artifactId>spdy-parent</artifactId>
<version>8.1.3-SNAPSHOT</version> <version>8.1.3-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http</artifactId> <artifactId>spdy-jetty-http</artifactId>
<name>Jetty :: SPDY :: Jetty HTTP Layer</name> <name>Jetty :: SPDY :: Jetty HTTP Layer</name>
<build>
<plugins> <build>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
<argLine>-Xbootclasspath/p:${build.directory}/npn/npn-boot-${npn.version}.jar </argLine>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
<plugins> <plugins>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<configuration> <executions>
<skip>true</skip> <execution>
</configuration> <id>copy</id>
</plugin> <phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</profile>
</profiles> <dependencies>
<dependencies> <dependency>
<dependency> <groupId>org.eclipse.jetty.spdy</groupId>
<groupId>org.eclipse.jetty.spdy</groupId> <artifactId>spdy-jetty</artifactId>
<artifactId>spdy-jetty</artifactId> <version>${project.version}</version>
<version>${project.version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>junit</groupId>
<groupId>junit</groupId> <artifactId>junit</artifactId>
<artifactId>junit</artifactId> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.eclipse.jetty.npn</groupId>
<groupId>org.eclipse.jetty.npn</groupId> <artifactId>npn-api</artifactId>
<artifactId>npn-api</artifactId> <version>${project.version}</version>
<version>${project.version}</version> <scope>test</scope>
<scope>test</scope> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.slf4j</groupId>
<groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId>
<artifactId>slf4j-log4j12</artifactId> <version>${slf4j-version}</version>
<version>${slf4j-version}</version> <scope>test</scope>
<scope>test</scope> </dependency>
</dependency> </dependencies>
</dependencies>
</project> </project>

View File

@ -50,13 +50,14 @@ import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers; import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo; import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.Stream;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implements AsyncConnection public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implements AsyncConnection
{ {
private static final Logger logger = LoggerFactory.getLogger(ServerHTTPSPDYAsyncConnection.class); private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnection.class);
private static final ByteBuffer ZERO_BYTES = ByteBuffer.allocate(0); private static final ByteBuffer ZERO_BYTES = ByteBuffer.allocate(0);
private static final DataInfo END_OF_CONTENT = new ByteBufferDataInfo(ZERO_BYTES, true);
private final Queue<Runnable> tasks = new LinkedList<>(); private final Queue<Runnable> tasks = new LinkedList<>();
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>(); private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
@ -65,7 +66,6 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
private Headers headers; // No need for volatile, guarded by state private Headers headers; // No need for volatile, guarded by state
private DataInfo dataInfo; // No need for volatile, guarded by state private DataInfo dataInfo; // No need for volatile, guarded by state
private NIOBuffer buffer; // No need for volatile, guarded by state private NIOBuffer buffer; // No need for volatile, guarded by state
private boolean complete; // No need for volatile, guarded by state
private volatile State state = State.INITIAL; private volatile State state = State.INITIAL;
private boolean dispatched; // Guarded by synchronization on tasks private boolean dispatched; // Guarded by synchronization on tasks
@ -160,7 +160,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
logger.debug("HTTP > {} {} {}", new Object[]{m, u, v}); logger.debug("HTTP > {} {} {}", new Object[]{m, u, v});
startRequest(new ByteArrayBuffer(m), new ByteArrayBuffer(u), new ByteArrayBuffer(v)); startRequest(new ByteArrayBuffer(m), new ByteArrayBuffer(u), new ByteArrayBuffer(v));
state = State.HEADERS; updateState(State.HEADERS);
handle(); handle();
break; break;
} }
@ -261,6 +261,12 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
{ {
} }
private void updateState(State newState)
{
logger.debug("State update {} -> {}", state, newState);
state = newState;
}
public void beginRequest(final Headers headers) public void beginRequest(final Headers headers)
{ {
this.headers = headers.isEmpty() ? null : headers; this.headers = headers.isEmpty() ? null : headers;
@ -270,7 +276,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
public void run() public void run()
{ {
if (!headers.isEmpty()) if (!headers.isEmpty())
state = State.REQUEST; updateState(State.REQUEST);
handle(); handle();
} }
}); });
@ -284,7 +290,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
@Override @Override
public void run() public void run()
{ {
state = state == State.INITIAL ? State.REQUEST : State.HEADERS; updateState(state == State.INITIAL ? State.REQUEST : State.HEADERS);
handle(); handle();
} }
}); });
@ -292,7 +298,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
public void content(final DataInfo dataInfo, boolean endRequest) public void content(final DataInfo dataInfo, boolean endRequest)
{ {
dataInfos.offer(new ByteBufferDataInfo(dataInfo.asByteBuffer(false), dataInfo.isClose(), dataInfo.isCompress()) // We need to copy the dataInfo since we do not know when its bytes
// will be consumed. When the copy is consumed, we consume also the
// original, so the implementation can send a window update.
ByteBufferDataInfo copyDataInfo = new ByteBufferDataInfo(dataInfo.asByteBuffer(false), dataInfo.isClose(), dataInfo.isCompress())
{ {
@Override @Override
public void consume(int delta) public void consume(int delta)
@ -300,8 +309,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
super.consume(delta); super.consume(delta);
dataInfo.consume(delta); dataInfo.consume(delta);
} }
}); };
complete = endRequest; logger.debug("Queuing last={} content {}", endRequest, copyDataInfo);
dataInfos.offer(copyDataInfo);
if (endRequest)
dataInfos.offer(END_OF_CONTENT);
post(new Runnable() post(new Runnable()
{ {
@Override @Override
@ -310,10 +322,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
logger.debug("HTTP > {} bytes of content", dataInfo.length()); logger.debug("HTTP > {} bytes of content", dataInfo.length());
if (state == State.HEADERS) if (state == State.HEADERS)
{ {
state = State.HEADERS_COMPLETE; updateState(State.HEADERS_COMPLETE);
handle(); handle();
} }
state = State.CONTENT; updateState(State.CONTENT);
handle(); handle();
} }
}); });
@ -327,10 +339,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
{ {
if (state == State.HEADERS) if (state == State.HEADERS)
{ {
state = State.HEADERS_COMPLETE; updateState(State.HEADERS_COMPLETE);
handle(); handle();
} }
state = State.FINAL; updateState(State.FINAL);
handle(); handle();
} }
}); });
@ -343,10 +355,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
@Override @Override
public void run() public void run()
{ {
State currentState = state; State oldState = state;
state = State.ASYNC; updateState(State.ASYNC);
handle(); handle();
state = currentState; updateState(oldState);
} }
}); });
} }
@ -370,12 +382,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
else else
{ {
// The application has consumed the buffer, so consume also the DataInfo // The application has consumed the buffer, so consume also the DataInfo
if (dataInfo.consumed() == 0) dataInfo.consume(dataInfo.length());
dataInfo.consume(dataInfo.length()); logger.debug("Consumed {} content bytes, queue size {}", dataInfo.consumed(), dataInfos.size());
dataInfo = null; dataInfo = null;
buffer = null; buffer = null;
if (complete && dataInfos.isEmpty())
return null;
// Loop to get content bytes from DataInfos // Loop to get content bytes from DataInfos
} }
} }
@ -388,9 +398,13 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
logger.debug("Waited {} ms for content bytes", elapsed); logger.debug("Waited {} ms for content bytes", elapsed);
if (dataInfo != null) if (dataInfo != null)
{ {
// Only consume if it's the last DataInfo if (dataInfo == END_OF_CONTENT)
boolean consume = complete && dataInfos.isEmpty(); {
ByteBuffer byteBuffer = dataInfo.asByteBuffer(consume); logger.debug("End of content bytes, queue size {}", dataInfos.size());
return null;
}
ByteBuffer byteBuffer = dataInfo.asByteBuffer(false);
buffer = byteBuffer.isDirect() ? new DirectNIOBuffer(byteBuffer, false) : new IndirectNIOBuffer(byteBuffer, false); buffer = byteBuffer.isDirect() ? new DirectNIOBuffer(byteBuffer, false) : new IndirectNIOBuffer(byteBuffer, false);
// Loop to return the buffer // Loop to return the buffer
} }

View File

@ -33,13 +33,13 @@ import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnectionFactory public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnectionFactory
{ {
private static final String CONNECTION_ATTRIBUTE = "org.eclipse.jetty.spdy.http.connection"; private static final String CONNECTION_ATTRIBUTE = "org.eclipse.jetty.spdy.http.connection";
private static final Logger logger = LoggerFactory.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class); private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class);
private final Connector connector; private final Connector connector;
@ -75,7 +75,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
logger.debug("Received {} on {}", synInfo, stream); logger.debug("Received {} on {}", synInfo, stream);
HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(stream); HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream);
ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector, ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector,
asyncEndPoint, connector.getServer(), asyncEndPoint, connector.getServer(),
(SPDYAsyncConnection)endPoint.getConnection(), stream); (SPDYAsyncConnection)endPoint.getConnection(), stream);
@ -133,10 +133,12 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
private class HTTPSPDYAsyncEndPoint extends EmptyAsyncEndPoint private class HTTPSPDYAsyncEndPoint extends EmptyAsyncEndPoint
{ {
private final AsyncEndPoint endPoint;
private final Stream stream; private final Stream stream;
public HTTPSPDYAsyncEndPoint(Stream stream) private HTTPSPDYAsyncEndPoint(AsyncEndPoint endPoint, Stream stream)
{ {
this.endPoint = endPoint;
this.stream = stream; this.stream = stream;
} }
@ -146,5 +148,41 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
ServerHTTPSPDYAsyncConnection connection = (ServerHTTPSPDYAsyncConnection)stream.getAttribute(CONNECTION_ATTRIBUTE); ServerHTTPSPDYAsyncConnection connection = (ServerHTTPSPDYAsyncConnection)stream.getAttribute(CONNECTION_ATTRIBUTE);
connection.async(); connection.async();
} }
@Override
public String getLocalAddr()
{
return endPoint.getLocalAddr();
}
@Override
public String getLocalHost()
{
return endPoint.getLocalHost();
}
@Override
public int getLocalPort()
{
return endPoint.getLocalPort();
}
@Override
public String getRemoteAddr()
{
return endPoint.getRemoteAddr();
}
@Override
public String getRemoteHost()
{
return endPoint.getRemoteHost();
}
@Override
public int getRemotePort()
{
return endPoint.getRemotePort();
}
} }
} }

View File

@ -190,6 +190,14 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
Assert.assertEquals("POST", httpRequest.getMethod()); Assert.assertEquals("POST", httpRequest.getMethod());
Assert.assertEquals("1", httpRequest.getParameter("a")); Assert.assertEquals("1", httpRequest.getParameter("a"));
Assert.assertEquals("2", httpRequest.getParameter("b")); Assert.assertEquals("2", httpRequest.getParameter("b"));
Assert.assertNotNull(httpRequest.getRemoteHost());
Assert.assertNotNull(httpRequest.getRemotePort());
Assert.assertNotNull(httpRequest.getRemoteAddr());
Assert.assertNotNull(httpRequest.getLocalPort());
Assert.assertNotNull(httpRequest.getLocalName());
Assert.assertNotNull(httpRequest.getLocalAddr());
Assert.assertNotNull(httpRequest.getServerPort());
Assert.assertNotNull(httpRequest.getServerName());
handlerLatch.countDown(); handlerLatch.countDown();
} }
}), null); }), null);
@ -519,7 +527,6 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
@Override @Override
public void onData(Stream stream, DataInfo dataInfo) public void onData(Stream stream, DataInfo dataInfo)
{ {
contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining()); contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
if (dataInfo.isClose()) if (dataInfo.isClose())
{ {

View File

@ -1,89 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<parent> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>org.eclipse.jetty.spdy</groupId> <parent>
<artifactId>spdy-parent</artifactId> <groupId>org.eclipse.jetty.spdy</groupId>
<version>8.1.3-SNAPSHOT</version> <artifactId>spdy-parent</artifactId>
</parent> <version>8.1.3-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion> </parent>
<artifactId>spdy-jetty</artifactId> <modelVersion>4.0.0</modelVersion>
<name>Jetty :: SPDY :: Jetty Binding</name> <artifactId>spdy-jetty</artifactId>
<build> <name>Jetty :: SPDY :: Jetty Binding</name>
<plugins>
<plugin> <build>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
<argLine>-Xbootclasspath/p:${build.directory}/npn/npn-boot-${npn.version}.jar </argLine>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
<plugins> <plugins>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<configuration> <executions>
<skip>true</skip> <execution>
</configuration> <id>copy</id>
</plugin> <phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</profile>
</profiles> <dependencies>
<dependencies> <dependency>
<dependency> <groupId>org.eclipse.jetty.spdy</groupId>
<groupId>org.eclipse.jetty.spdy</groupId> <artifactId>spdy-core</artifactId>
<artifactId>spdy-core</artifactId> <version>${project.version}</version>
<version>${project.version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.eclipse.jetty</groupId>
<groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId>
<artifactId>jetty-server</artifactId> <version>${project.version}</version>
<version>${project.version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.eclipse.jetty.npn</groupId>
<groupId>org.eclipse.jetty.npn</groupId> <artifactId>npn-api</artifactId>
<artifactId>npn-api</artifactId> <version>${npn.version}</version>
<version>${project.version}</version> <scope>provided</scope>
<scope>provided</scope> </dependency>
</dependency> <dependency>
<dependency> <groupId>junit</groupId>
<groupId>junit</groupId> <artifactId>junit</artifactId>
<artifactId>junit</artifactId> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.slf4j</groupId>
<groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId>
<artifactId>slf4j-log4j12</artifactId> <version>${slf4j-version}</version>
<version>${slf4j-version}</version> <scope>test</scope>
<scope>test</scope> </dependency>
</dependency> </dependencies>
</dependencies>
</project> </project>

View File

@ -30,12 +30,12 @@ import org.eclipse.jetty.io.nio.NIOBuffer;
import org.eclipse.jetty.spdy.api.Handler; import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.parser.Parser; import org.eclipse.jetty.spdy.parser.Parser;
import org.slf4j.Logger; import org.eclipse.jetty.util.log.Log;
import org.slf4j.LoggerFactory; import org.eclipse.jetty.util.log.Logger;
public class SPDYAsyncConnection extends AbstractConnection implements AsyncConnection, Controller<StandardSession.FrameBytes>, IdleListener public class SPDYAsyncConnection extends AbstractConnection implements AsyncConnection, Controller<StandardSession.FrameBytes>, IdleListener
{ {
private static final Logger logger = LoggerFactory.getLogger(SPDYAsyncConnection.class); private static final Logger logger = Log.getLogger(SPDYAsyncConnection.class);
private final ByteBufferPool bufferPool; private final ByteBufferPool bufferPool;
private final Parser parser; private final Parser parser;
private volatile Session session; private volatile Session session;
@ -181,7 +181,7 @@ public class SPDYAsyncConnection extends AbstractConnection implements AsyncConn
} }
catch (IOException x) catch (IOException x)
{ {
logger.trace("", x); logger.ignore(x);
} }
} }

View File

@ -41,14 +41,14 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.util.thread.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SPDYServerConnector extends SelectChannelConnector public class SPDYServerConnector extends SelectChannelConnector
{ {
private static final Logger logger = LoggerFactory.getLogger(SPDYServerConnector.class); private static final Logger logger = Log.getLogger(SPDYServerConnector.class);
// Order is important on server side, so we use a LinkedHashMap // Order is important on server side, so we use a LinkedHashMap
private final Map<String, AsyncConnectionFactory> factories = new LinkedHashMap<>(); private final Map<String, AsyncConnectionFactory> factories = new LinkedHashMap<>();

View File

@ -307,7 +307,7 @@ public class FlowControlTest extends AbstractTest
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS); Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS);
final int length = 5 * windowSize; final int length = 5 * windowSize;
stream.data(new BytesDataInfo(new byte[length], true)); stream.data(new BytesDataInfo(new byte[length], true));

View File

@ -0,0 +1,164 @@
package org.eclipse.jetty.spdy;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.api.BytesDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.SessionStatus;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.spdy.frames.ControlFrameType;
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
import org.eclipse.jetty.spdy.generator.Generator;
import org.junit.Assert;
import org.junit.Test;
public class ProtocolViolationsTest extends AbstractTest
{
@Test
public void testSendDataBeforeReplyIsIllegal() throws Exception
{
final CountDownLatch resetLatch = new CountDownLatch(1);
final CountDownLatch latch = new CountDownLatch(1);
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
try
{
stream.data(new StringDataInfo("failure", true));
return null;
}
catch (IllegalStateException x)
{
latch.countDown();
return null;
}
}
}), new SessionFrameListener.Adapter()
{
@Override
public void onRst(Session session, RstInfo rstInfo)
{
Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus());
resetLatch.countDown();
}
});
session.syn(new SynInfo(true), null);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testReceiveDataBeforeReplyIsIllegal() throws Exception
{
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("localhost", 0));
Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
session.syn(new SynInfo(true), null);
SocketChannel channel = server.accept();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
channel.read(readBuffer);
readBuffer.flip();
int streamId = readBuffer.getInt(8);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
byte[] bytes = new byte[1];
ByteBuffer writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
channel.write(writeBuffer);
readBuffer.clear();
channel.read(readBuffer);
readBuffer.flip();
Assert.assertEquals(ControlFrameType.RST_STREAM.getCode(), readBuffer.getShort(2));
Assert.assertEquals(streamId, readBuffer.getInt(8));
writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode()));
channel.write(writeBuffer);
channel.shutdownOutput();
channel.close();
server.close();
}
@Test(expected = IllegalStateException.class)
public void testSendDataAfterCloseIsIllegal() throws Exception
{
Session session = startClient(startServer(null), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
stream.data(new StringDataInfo("test", true));
}
@Test(expected = IllegalStateException.class)
public void testSendHeadersAfterCloseIsIllegal() throws Exception
{
Session session = startClient(startServer(null), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
stream.headers(new HeadersInfo(new Headers(), true));
}
@Test
public void testDataSentAfterCloseIsDiscardedByRecipient() throws Exception
{
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("localhost", 0));
Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataLatch.countDown();
}
});
SocketChannel channel = server.accept();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
channel.read(readBuffer);
readBuffer.flip();
int streamId = readBuffer.getInt(8);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Headers()));
channel.write(writeBuffer);
byte[] bytes = new byte[1];
writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
channel.write(writeBuffer);
// Write again to simulate the faulty condition
writeBuffer.flip();
channel.write(writeBuffer);
Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode()));
channel.write(writeBuffer);
channel.shutdownOutput();
channel.close();
server.close();
}
}

View File

@ -64,6 +64,8 @@ public class ResetStreamTest extends AbstractTest
Assert.assertEquals(0, serverSession.getStreams().size()); Assert.assertEquals(0, serverSession.getStreams().size());
Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
// Need to sleep a while to give the chance to the implementation to remove the stream
TimeUnit.SECONDS.sleep(1);
Assert.assertEquals(0, clientSession.getStreams().size()); Assert.assertEquals(0, clientSession.getStreams().size());
} }

View File

@ -87,25 +87,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest
} }
}); });
List<Callable<Object>> tasks = new ArrayList<>();
for (int i = 0; i < count; ++i)
{
tasks.add(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
synCompletedData(session, headers, iterations);
return null;
}
});
}
ExecutorService threadPool = Executors.newFixedThreadPool(count); ExecutorService threadPool = Executors.newFixedThreadPool(count);
List<Future<Object>> futures = threadPool.invokeAll(tasks); List<Callable<Object>> tasks = new ArrayList<>();
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
tasks.clear(); tasks.clear();
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
@ -120,11 +103,38 @@ public class SynDataReplyDataLoadTest extends AbstractTest
} }
}); });
} }
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
long end = System.nanoTime();
System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
futures = threadPool.invokeAll(tasks); tasks.clear();
for (Future<Object> future : futures) for (int i = 0; i < count; ++i)
future.get(iterations, TimeUnit.SECONDS); {
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS)); tasks.add(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
synCompletedData(session, headers, iterations);
return null;
}
});
}
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
long end = System.nanoTime();
System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
threadPool.shutdown(); threadPool.shutdown();
} }

View File

@ -30,12 +30,10 @@ import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Handler; import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Headers; import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo; import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener; import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.StringDataInfo; import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
@ -324,39 +322,6 @@ public class SynReplyTest extends AbstractTest
Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS));
} }
@Test
public void testSynDataRst() throws Exception
{
final AtomicReference<RstInfo> ref = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
// Do not send the reply, we expect a RST_STREAM
stream.data(new StringDataInfo("foo", true));
return null;
}
@Override
public void onRst(Session session, RstInfo rstInfo)
{
ref.set(rstInfo);
latch.countDown();
}
};
Session session = startClient(startServer(serverSessionFrameListener), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
RstInfo rstInfo = ref.get();
Assert.assertNotNull(rstInfo);
Assert.assertEquals(stream.getId(), rstInfo.getStreamId());
Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus());
}
@Test @Test
public void testSynReplyDataSynReplyData() throws Exception public void testSynReplyDataSynReplyData() throws Exception
{ {

View File

@ -287,7 +287,25 @@ public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable
*/ */
public Map<K,String[]> toStringArrayMap() public Map<K,String[]> toStringArrayMap()
{ {
HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2); HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2)
{
public String toString()
{
StringBuilder b=new StringBuilder();
b.append('{');
for (K k:keySet())
{
if(b.length()>1)
b.append(',');
b.append(k);
b.append('=');
b.append(Arrays.asList(get(k)));
}
b.append('}');
return b.toString();
}
};
for(Map.Entry<K,Object> entry: _map.entrySet()) for(Map.Entry<K,Object> entry: _map.entrySet())
{ {

View File

@ -14,6 +14,10 @@
package org.eclipse.jetty.util; package org.eclipse.jetty.util;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import org.eclipse.jetty.util.log.Log;
@ -67,6 +71,7 @@ public class URIUtil
*/ */
public static StringBuilder encodePath(StringBuilder buf, String path) public static StringBuilder encodePath(StringBuilder buf, String path)
{ {
byte[] bytes=null;
if (buf==null) if (buf==null)
{ {
loop: loop:
@ -84,8 +89,23 @@ public class URIUtil
case '<': case '<':
case '>': case '>':
case ' ': case ' ':
buf=new StringBuilder(path.length()<<1); buf=new StringBuilder(path.length()*2);
break loop; break loop;
default:
if (c>127)
{
try
{
bytes=path.getBytes(URIUtil.__CHARSET);
}
catch (UnsupportedEncodingException e)
{
throw new IllegalStateException(e);
}
buf=new StringBuilder(path.length()*2);
break loop;
}
} }
} }
if (buf==null) if (buf==null)
@ -94,41 +114,91 @@ public class URIUtil
synchronized(buf) synchronized(buf)
{ {
for (int i=0;i<path.length();i++) if (bytes!=null)
{ {
char c=path.charAt(i); for (int i=0;i<bytes.length;i++)
switch(c)
{ {
case '%': byte c=bytes[i];
buf.append("%25"); switch(c)
continue; {
case '?': case '%':
buf.append("%3F"); buf.append("%25");
continue; continue;
case ';': case '?':
buf.append("%3B"); buf.append("%3F");
continue; continue;
case '#': case ';':
buf.append("%23"); buf.append("%3B");
continue; continue;
case '"': case '#':
buf.append("%22"); buf.append("%23");
continue; continue;
case '\'': case '"':
buf.append("%27"); buf.append("%22");
continue; continue;
case '<': case '\'':
buf.append("%3C"); buf.append("%27");
continue; continue;
case '>': case '<':
buf.append("%3E"); buf.append("%3C");
continue; continue;
case ' ': case '>':
buf.append("%20"); buf.append("%3E");
continue; continue;
default: case ' ':
buf.append(c); buf.append("%20");
continue; continue;
default:
if (c<0)
{
buf.append('%');
TypeUtil.toHex(c,buf);
}
else
buf.append((char)c);
continue;
}
}
}
else
{
for (int i=0;i<path.length();i++)
{
char c=path.charAt(i);
switch(c)
{
case '%':
buf.append("%25");
continue;
case '?':
buf.append("%3F");
continue;
case ';':
buf.append("%3B");
continue;
case '#':
buf.append("%23");
continue;
case '"':
buf.append("%22");
continue;
case '\'':
buf.append("%27");
continue;
case '<':
buf.append("%3C");
continue;
case '>':
buf.append("%3E");
continue;
case ' ':
buf.append("%20");
continue;
default:
buf.append(c);
continue;
}
} }
} }
} }

View File

@ -77,7 +77,7 @@ public class StdErrLog extends AbstractLogger
private int _level = LEVEL_INFO; private int _level = LEVEL_INFO;
// Level that this Logger was configured as (remembered in special case of .setDebugEnabled()) // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
private int _configuredLevel; private int _configuredLevel;
private PrintStream _stderr = System.err; private PrintStream _stderr = null;
private boolean _source = __source; private boolean _source = __source;
// Print the long form names, otherwise use abbreviated // Print the long form names, otherwise use abbreviated
private boolean _printLongNames = __long; private boolean _printLongNames = __long;
@ -271,7 +271,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":WARN:",msg,args); format(buffer,":WARN:",msg,args);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -286,7 +286,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":WARN:",msg,thrown); format(buffer,":WARN:",msg,thrown);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -296,7 +296,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":INFO:",msg,args); format(buffer,":INFO:",msg,args);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -311,7 +311,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":INFO:",msg,thrown); format(buffer,":INFO:",msg,thrown);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -369,7 +369,7 @@ public class StdErrLog extends AbstractLogger
public void setStdErrStream(PrintStream stream) public void setStdErrStream(PrintStream stream)
{ {
this._stderr = stream; this._stderr = stream==System.err?null:stream;
} }
public void debug(String msg, Object... args) public void debug(String msg, Object... args)
@ -378,7 +378,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":DBUG:",msg,args); format(buffer,":DBUG:",msg,args);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -393,7 +393,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":DBUG:",msg,thrown); format(buffer,":DBUG:",msg,thrown);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
@ -616,7 +616,7 @@ public class StdErrLog extends AbstractLogger
{ {
StringBuilder buffer = new StringBuilder(64); StringBuilder buffer = new StringBuilder(64);
format(buffer,":IGNORED:","",ignored); format(buffer,":IGNORED:","",ignored);
_stderr.println(buffer); (_stderr==null?System.err:_stderr).println(buffer);
} }
} }
} }

View File

@ -305,7 +305,8 @@ public class WebInfConfiguration extends AbstractConfiguration
} }
catch(IOException e) catch(IOException e)
{ {
LOG.warn("tmpdir",e); System.exit(1); tmpDir = null;
throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
} }
} }
} }

View File

@ -359,7 +359,6 @@
<module>jetty-http</module> <module>jetty-http</module>
<module>jetty-websocket</module> <module>jetty-websocket</module>
<module>jetty-continuation</module> <module>jetty-continuation</module>
<module>jetty-npn</module>
<module>jetty-server</module> <module>jetty-server</module>
<module>jetty-client</module> <module>jetty-client</module>
<module>jetty-xml</module> <module>jetty-xml</module>