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:
commit
ee2d289f63
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
* <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
|
* ,com.opensymphony.module.sitemesh Otherwise use an attribute to the
|
||||||
* ....
|
* WebAppDeployer <New
|
||||||
* <Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles" default="" /></Set>
|
* class="org.eclipse.jetty.deploy.providers.WebAppProvider"> .... <Set
|
||||||
* <New>
|
* name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles"
|
||||||
|
* default="" /></Set> <New>
|
||||||
*/
|
*/
|
||||||
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.
|
* Union of the tld bundles defined system wide and the one defines as an
|
||||||
* @param provider
|
* attribute of the AppProvider.
|
||||||
* @return
|
*
|
||||||
*/
|
* @param provider
|
||||||
private static Collection<String> getTldBundles(OSGiAppProvider provider)
|
* @return
|
||||||
{
|
*/
|
||||||
String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES);
|
private static Collection<String> getTldBundles(OSGiAppProvider provider)
|
||||||
String att = (String)provider.getTldBundles();
|
{
|
||||||
if (sysprop == null && att == null)
|
String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES);
|
||||||
{
|
String att = (String) provider.getTldBundles();
|
||||||
return Collections.emptySet();
|
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
|
||||||
|
// and mirroring those in the MANIFEST.MF
|
||||||
|
|
||||||
Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles();
|
Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles();
|
||||||
Collection<String> tldbundles = getTldBundles(provider);
|
Collection<String> tldbundles = getTldBundles(provider);
|
||||||
for (Bundle bundle : bundles)
|
for (Bundle bundle : bundles)
|
||||||
{
|
{
|
||||||
if (tldbundles.contains(bundle.getSymbolicName()))
|
if (tldbundles.contains(bundle.getSymbolicName()))
|
||||||
{
|
{
|
||||||
registerTldBundle(locatorHelper, bundle, urls);
|
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,7 +160,7 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
urls.add(jasperLocation.toURI().toURL());
|
urls.add(jasperLocation.toURI().toURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,38 +36,42 @@ 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.
|
* Default name of a class that belongs to the jstl bundle. From that class
|
||||||
* From that class we locate the corresponding bundle and register it
|
* we locate the corresponding bundle and register it as a bundle that
|
||||||
* as a bundle that contains tld files.
|
* contains tld files.
|
||||||
*/
|
*/
|
||||||
private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
|
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
|
// used to be "org.apache.jasper.runtime.JspFactoryImpl" but now
|
||||||
// /**
|
// the standard tag library implementation are stored in a separate bundle.
|
||||||
// * 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";
|
|
||||||
|
|
||||||
/**
|
// DISABLED please use the tld bundle argument for the OSGiAppProvider
|
||||||
* Default jsp factory implementation.
|
// /**
|
||||||
* Idally jasper is osgified and we can use services.
|
// * Default name of a class that belongs to the bundle where the Java
|
||||||
* In the mean time we statically set the jsp factory implementation.
|
// server Faces tld files are defined.
|
||||||
* bug #299733
|
// * This is the sun's reference implementation.
|
||||||
*/
|
// */
|
||||||
private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
|
// 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()
|
||||||
{
|
{
|
||||||
|
@ -75,9 +79,9 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
|
||||||
|
|
||||||
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,15 +91,15 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,82 +112,88 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -66,16 +66,20 @@ 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
|
||||||
* tracker for the jetty ContextHandlers that are in charge of deploying the
|
* tracker for the jetty ContextHandlers that are in charge of deploying the
|
||||||
|
@ -93,28 +97,29 @@ 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:
|
// Register the Jetty Server Factory as a ManagedServiceFactory:
|
||||||
// Properties jettyServerMgdFactoryServiceProps = new Properties();
|
// Properties jettyServerMgdFactoryServiceProps = new Properties();
|
||||||
// jettyServerMgdFactoryServiceProps.put("pid", OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
|
// jettyServerMgdFactoryServiceProps.put("pid",
|
||||||
// _jettyServerFactoryService = context.registerService(
|
// OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
|
||||||
// ManagedServiceFactory.class.getName(), new JettyServersManagedFactory(),
|
// _jettyServerFactoryService = context.registerService(
|
||||||
// jettyServerMgdFactoryServiceProps);
|
// 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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -130,11 +135,11 @@ 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,22 +267,19 @@ 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)
|
||||||
|
@ -295,8 +289,8 @@ public class JettyBootstrapActivator implements BundleActivator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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()
|
||||||
{
|
{
|
||||||
|
@ -323,5 +317,4 @@ public class JettyBootstrapActivator implements BundleActivator
|
||||||
return INSTANCE._bundleContext;
|
return INSTANCE._bundleContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,7 +369,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
LOG.warn(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 */
|
/** New style: ability to manage multiple jetty instances */
|
||||||
private final IManagedJettyServerRegistry _registry;
|
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
|
||||||
|
@ -87,17 +91,14 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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,7 +354,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
|
||||||
// warning?
|
// warning?
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\','/');
|
return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\', '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,28 +362,23 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
|
||||||
*/
|
*/
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -383,10 +392,14 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
||||||
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,9 +450,8 @@ 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
|
||||||
|
@ -453,13 +460,13 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
||||||
// 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())
|
||||||
|
@ -629,41 +636,37 @@ 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 "this.bundle.install" to point to the location
|
* Set the property "this.bundle.install" 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 tracked by the <code>BundleTracker</code> has been modified.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is called when a bundle being tracked by the
|
||||||
|
* <code>BundleTracker</code> has had its state modified.
|
||||||
|
*
|
||||||
|
* @param bundle The <code>Bundle</code> whose state has been modified.
|
||||||
|
* @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 modifiedBundle(Bundle bundle, BundleEvent event, Object object)
|
||||||
|
{
|
||||||
|
// nothing the web-bundle was already track. something changed.
|
||||||
|
// we only reload the webapps if the bundle is stopped and restarted.
|
||||||
|
if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
|
||||||
|
{
|
||||||
|
unregister(bundle);
|
||||||
|
}
|
||||||
|
if (bundle.getState() == Bundle.ACTIVE)
|
||||||
|
{
|
||||||
|
register(bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bundle is being added to the <code>BundleTracker</code>.
|
* A bundle tracked by the <code>BundleTracker</code> has been removed.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This method is called before a bundle which matched the search parameters
|
* This method is called after a bundle is no longer being tracked by the
|
||||||
* of the <code>BundleTracker</code> is added to the
|
* <code>BundleTracker</code>.
|
||||||
* <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> that has been removed.
|
||||||
* 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 removedBundle(Bundle bundle, BundleEvent event, Object object)
|
||||||
* called or <code>null</code> if there is no bundle event associated
|
{
|
||||||
* with the call to this method.
|
unregister(bundle);
|
||||||
* @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 tracked by the <code>BundleTracker</code> has been modified.
|
* @param bundle
|
||||||
*
|
* @return true if this bundle in indeed a web-bundle.
|
||||||
* <p>
|
*/
|
||||||
* This method is called when a bundle being tracked by the
|
|
||||||
* <code>BundleTracker</code> has had its state modified.
|
|
||||||
*
|
|
||||||
* @param bundle The <code>Bundle</code> whose state has been modified.
|
|
||||||
* @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 modifiedBundle(Bundle bundle, BundleEvent event,
|
|
||||||
Object object)
|
|
||||||
{
|
|
||||||
//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.
|
|
||||||
*
|
|
||||||
* <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;
|
||||||
|
@ -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.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -257,75 +274,83 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// introspection on equinox to invoke the getLocalURL method on
|
||||||
|
// BundleURLConnection
|
||||||
|
// equivalent to using the FileLocator without depending on an equinox
|
||||||
|
// class.
|
||||||
|
private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
|
||||||
|
|
||||||
//introspection on equinox to invoke the getLocalURL method on BundleURLConnection
|
private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
|
||||||
//equivalent to using the FileLocator without depending on an equinox class.
|
|
||||||
private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
|
/**
|
||||||
private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
|
* Only useful for equinox: on felix we get the file:// or jar:// url
|
||||||
/**
|
* already. Other OSGi implementations have not been tested
|
||||||
* Only useful for equinox: on felix we get the file:// or jar:// url already.
|
* <p>
|
||||||
* Other OSGi implementations have not been tested
|
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
|
||||||
* <p>
|
* jar: or http: etc.).
|
||||||
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
|
* </p>
|
||||||
* jar: or http: etc.).
|
*
|
||||||
* </p>
|
* @return a URL to the bundle entry that uses a common protocol
|
||||||
* @return a URL to the bundle entry that uses a common protocol
|
*/
|
||||||
*/
|
public static URL getLocalURL(URL url)
|
||||||
public static URL getLocalURL(URL url) {
|
{
|
||||||
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) {
|
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
|
||||||
try {
|
{
|
||||||
URLConnection conn = url.openConnection();
|
try
|
||||||
if (BUNDLE_URL_CONNECTION_getLocalURL == null &&
|
{
|
||||||
conn.getClass().getName().equals(
|
URLConnection conn = url.openConnection();
|
||||||
"org.eclipse.osgi.framework.internal.core.BundleURLConnection")) {
|
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
|
||||||
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
|
if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName()
|
||||||
BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
|
.equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||||
}
|
{
|
||||||
if (BUNDLE_URL_CONNECTION_getLocalURL != null) {
|
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
|
||||||
return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null);
|
BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
if (BUNDLE_URL_CONNECTION_getLocalURL != null) { return (URL) BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); }
|
||||||
System.err.println("Unable to locate the OSGi url: '" + url + "'.");
|
}
|
||||||
t.printStackTrace();
|
catch (Throwable t)
|
||||||
}
|
{
|
||||||
}
|
System.err.println("Unable to locate the OSGi url: '" + url + "'.");
|
||||||
return url;
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* Only useful for equinox: on felix we get the file:// url already.
|
return url;
|
||||||
* Other OSGi implementations have not been tested
|
}
|
||||||
* <p>
|
|
||||||
* 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 to the local
|
* Only useful for equinox: on felix we get the file:// url already. Other
|
||||||
* file system in order to create a file: URL.
|
* OSGi implementations have not been tested
|
||||||
* @return a URL to the content of the bundle entry that uses the file: protocol
|
* <p>
|
||||||
* </p>
|
* 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
|
||||||
public static URL getFileURL(URL url)
|
* to the local file system in order to create a file: URL.
|
||||||
{
|
*
|
||||||
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
|
* @return a URL to the content of the bundle entry that uses the file:
|
||||||
{
|
* protocol
|
||||||
try
|
* </p>
|
||||||
{
|
*/
|
||||||
URLConnection conn = url.openConnection();
|
public static URL getFileURL(URL url)
|
||||||
if (BUNDLE_URL_CONNECTION_getFileURL == null &&
|
{
|
||||||
conn.getClass().getName().equals(
|
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
|
||||||
"org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
{
|
||||||
{
|
try
|
||||||
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
|
{
|
||||||
BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
|
URLConnection conn = url.openConnection();
|
||||||
}
|
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
|
||||||
if (BUNDLE_URL_CONNECTION_getFileURL != null)
|
if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName()
|
||||||
{
|
.equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||||
return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null);
|
{
|
||||||
}
|
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
|
||||||
}
|
BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
|
||||||
catch (Throwable t)
|
}
|
||||||
{
|
if (BUNDLE_URL_CONNECTION_getFileURL != null) { return (URL) BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); }
|
||||||
t.printStackTrace();
|
}
|
||||||
}
|
catch (Throwable t)
|
||||||
}
|
{
|
||||||
return url;
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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++)
|
||||||
|
|
|
@ -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
|
||||||
|
@ -110,9 +108,14 @@ public class SSLEngineTest
|
||||||
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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -31,10 +31,9 @@ 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,10 +165,10 @@ 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;
|
||||||
|
@ -178,7 +177,75 @@ public class GzipFilterDefaultTest
|
||||||
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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<descriptorRefs>
|
|
||||||
<descriptorRef>config</descriptorRef>
|
|
||||||
</descriptorRefs>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
<plugin>
|
<build>
|
||||||
<groupId>org.mortbay.jetty</groupId>
|
<plugins>
|
||||||
<artifactId>jetty-maven-plugin</artifactId>
|
<plugin>
|
||||||
<version>${project.version}</version>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<stopPort>8888</stopPort>
|
<executions>
|
||||||
<stopKey>quit</stopKey>
|
<execution>
|
||||||
<jvmArgs>
|
<phase>package</phase>
|
||||||
-Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
|
<goals>
|
||||||
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
|
<goal>single</goal>
|
||||||
</jvmArgs>
|
</goals>
|
||||||
<jettyXml>${basedir}/src/main/config/jetty-spdy.xml</jettyXml>
|
<configuration>
|
||||||
<contextPath>/</contextPath>
|
<descriptorRefs>
|
||||||
</configuration>
|
<descriptorRef>config</descriptorRef>
|
||||||
<dependencies>
|
</descriptorRefs>
|
||||||
<dependency>
|
</configuration>
|
||||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
</execution>
|
||||||
<artifactId>spdy-jetty-http</artifactId>
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!--
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.mortbay.jetty</groupId>
|
||||||
|
<artifactId>jetty-maven-plugin</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>
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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())
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue