Merge remote-tracking branch 'origin/master' into jetty-8
This commit is contained in:
commit
34954bc617
|
@ -1020,7 +1020,8 @@ public class HttpParser implements Parser
|
|||
// Are we full?
|
||||
if (_buffer.space() == 0)
|
||||
{
|
||||
LOG.warn("Full {}",_buffer.toDetailString());
|
||||
LOG.warn("HttpParser Full for {} ",_endp);
|
||||
_buffer.clear();
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
}
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
|||
public void doNotCompress() throws IOException
|
||||
{
|
||||
if (_compressedOutputStream != null)
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException("Compressed output stream is already assigned.");
|
||||
if (_out == null || _bOut != null)
|
||||
{
|
||||
_doNotCompress = true;
|
||||
|
|
|
@ -124,7 +124,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
public void setStatus(int sc)
|
||||
{
|
||||
super.setStatus(sc);
|
||||
if (sc<200 || sc==204 || sc==205 ||sc>=300)
|
||||
if (sc<200 || sc==204 || sc==205 || sc>=300)
|
||||
noCompression();
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
else if (_writer!=null)
|
||||
throw new IllegalStateException("getWriter() called");
|
||||
|
||||
return (ServletOutputStream)_compressedStream;
|
||||
return _compressedStream;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -366,7 +366,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
}
|
||||
|
||||
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minCompressSize);
|
||||
_writer=newWriter((OutputStream)_compressedStream,getCharacterEncoding());
|
||||
_writer=newWriter(_compressedStream,getCharacterEncoding());
|
||||
}
|
||||
return _writer;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Configure the deployment manager -->
|
||||
<!-- =========================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
|
||||
<Set name="contexts">
|
||||
<Ref id="Contexts" />
|
||||
</Set>
|
||||
<Call name="setContextAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
|
||||
</Call>
|
||||
<!-- Providers of OSGi Apps -->
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
|
||||
<!--
|
||||
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
|
||||
-->
|
||||
<Set name="scanInterval">5</Set>
|
||||
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
|
||||
<!-- comma separated list of bundle symbolic names that contain custom tag libraries (*.tld files) -->
|
||||
<!-- if those bundles don't exist or can't be loaded no errors or warning will be issued! -->
|
||||
<!-- This default value plugs in the tld files of the reference implementation of JSF -->
|
||||
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles" default="javax.faces.jsf-impl" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure the Jetty Server Nested inside another -->
|
||||
<!-- Servlet Container. -->
|
||||
<!-- -->
|
||||
<!-- Documentation of this file format can be found at: -->
|
||||
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
|
||||
<!-- -->
|
||||
<!-- =============================================================== -->
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New id="NestedConnector" class="org.eclipse.jetty.nested.NestedConnector">
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="forwarded">true</Set>
|
||||
<Set name="forwardedHostHeader">x-forwarded_for</Set>
|
||||
<Set name="forwardedCipherSuiteHeader">sslclientcipher</Set>
|
||||
<Set name="forwardedSslSessionIdHeader">sslsessionid</Set>
|
||||
<Call name="addLifeCycleListener">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.osgi.nested.NestedConnectorListener" id="NestedConnectorListener">
|
||||
<Set name="nestedConnector"><Ref id="NestedConnector"/></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
|
@ -1,113 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure the Jetty Server Nested -->
|
||||
<!-- inside another Servlet Container -->
|
||||
<!-- -->
|
||||
<!-- Documentation of this file format can be found at: -->
|
||||
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
|
||||
<!-- -->
|
||||
<!-- =============================================================== -->
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New id="NestedConnector" class="org.eclipse.jetty.nested.NestedConnector">
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="forwarded">true</Set>
|
||||
<Set name="forwardedHostHeader">x-forwarded_for</Set>
|
||||
<Set name="forwardedCipherSuiteHeader">sslclientcipher</Set>
|
||||
<Set name="forwardedSslSessionIdHeader">sslsessionid</Set>
|
||||
<Call name="addLifeCycleListener">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.osgi.nested.NestedConnectorListener" id="NestedConnectorListener">
|
||||
<Set name="nestedConnector"><Ref id="NestedConnector"/></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set handler Collection Structure -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="handler">
|
||||
<New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
|
||||
<Set name="handlers">
|
||||
<Array type="org.eclipse.jetty.server.Handler">
|
||||
<Item>
|
||||
<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
|
||||
</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</New>
|
||||
</Set>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Configure the deployment manager -->
|
||||
<!-- =========================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
|
||||
<Set name="contexts">
|
||||
<Ref id="Contexts" />
|
||||
</Set>
|
||||
<Call name="setContextAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
<Arg>.*/servlet-api-[^/]*\.jar$</Arg>
|
||||
</Call>
|
||||
<!-- Providers of OSGi Apps -->
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
|
||||
<!--
|
||||
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
|
||||
-->
|
||||
<Set name="scanInterval">5</Set>
|
||||
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
|
||||
<!-- comma separated list of bundle symbolic names that
|
||||
contain custom tag libraries (*.tld files)
|
||||
if those bundles don't exist or can't be loaded no errors or warning will be issued!
|
||||
this default value is to plug the tld files of the reference implementation of JSF -->
|
||||
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles"
|
||||
default="javax.faces.jsf-impl" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- extra options -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="stopAtShutdown">true</Set>
|
||||
<Set name="sendServerVersion">true</Set>
|
||||
<Set name="sendDateHeader">true</Set>
|
||||
<Set name="gracefulShutdown">1000</Set>
|
||||
<Set name="dumpAfterStart">false</Set>
|
||||
<Set name="dumpBeforeStop">false</Set>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Configure Jetty's JNDI impl by default -->
|
||||
<!-- =========================================================== -->
|
||||
<Call class="java.lang.System" name="setProperty">
|
||||
<Arg>java.naming.factory.initial</Arg>
|
||||
<Arg><Property name="java.naming.factory.initial" default="org.eclipse.jetty.jndi.InitialContextFactory"/></Arg>
|
||||
</Call>
|
||||
<Call class="java.lang.System" name="setProperty">
|
||||
<Arg>java.naming.factory.url.pkgs</Arg>
|
||||
<Arg><Property name="java.naming.factory.url.pkgs" default="org.eclipse.jetty.jndi"/></Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Add connector -->
|
||||
<!-- =========================================================== -->
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
|
||||
<Set name="host"><Property name="jetty.host" /></Set>
|
||||
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
|
||||
<Set name="maxIdleTime">300000</Set>
|
||||
<Set name="Acceptors">2</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="confidentialPort">8443</Set>
|
||||
<Set name="lowResourcesConnections">20000</Set>
|
||||
<Set name="lowResourcesMaxIdleTime">5000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
|
@ -1,23 +1,21 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure the Jetty Server -->
|
||||
<!-- -->
|
||||
<!-- Documentation of this file format can be found at: -->
|
||||
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
|
||||
<!-- -->
|
||||
<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
|
||||
<!-- =============================================================== -->
|
||||
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Server Thread Pool -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="ThreadPool">
|
||||
<!-- Default queued blocking threadpool
|
||||
-->
|
||||
<!-- Default queued blocking threadpool -->
|
||||
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
|
||||
<Set name="minThreads">10</Set>
|
||||
<Set name="maxThreads">200</Set>
|
||||
|
@ -26,27 +24,6 @@
|
|||
</Set>
|
||||
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set connectors -->
|
||||
<!-- =========================================================== -->
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
|
||||
<Set name="host"><Property name="jetty.host" /></Set>
|
||||
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
|
||||
<Set name="maxIdleTime">300000</Set>
|
||||
<Set name="Acceptors">2</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="confidentialPort">8443</Set>
|
||||
<Set name="lowResourcesConnections">20000</Set>
|
||||
<Set name="lowResourcesMaxIdleTime">5000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set handler Collection Structure -->
|
||||
<!-- =========================================================== -->
|
||||
|
@ -68,40 +45,6 @@
|
|||
</New>
|
||||
</Set>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Configure the deployment manager -->
|
||||
<!-- =========================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
|
||||
<Set name="contexts">
|
||||
<Ref id="Contexts" />
|
||||
</Set>
|
||||
<Call name="setContextAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
<Arg>.*/servlet-api-[^/]*\.jar$</Arg>
|
||||
</Call>
|
||||
<!-- Providers of OSGi Apps -->
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
|
||||
<!--
|
||||
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
|
||||
-->
|
||||
<Set name="scanInterval">5</Set>
|
||||
<Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
|
||||
<!-- comma separated list of bundle symbolic names that
|
||||
contain custom tag libraries (*.tld files)
|
||||
if those bundles don't exist or can't be loaded no errors or warning will be issued!
|
||||
this default value is to plug the tld files of the reference implementation of JSF -->
|
||||
<Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldbundles"
|
||||
default="javax.faces.jsf-impl" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- extra options -->
|
||||
|
@ -112,9 +55,10 @@
|
|||
<Set name="gracefulShutdown">1000</Set>
|
||||
<Set name="dumpAfterStart">false</Set>
|
||||
<Set name="dumpBeforeStop">false</Set>
|
||||
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Configure Jetty's JNDI impl by default -->
|
||||
<!-- jetty-jndi by default -->
|
||||
<!-- =========================================================== -->
|
||||
<Call class="java.lang.System" name="setProperty">
|
||||
<Arg>java.naming.factory.initial</Arg>
|
|
@ -35,22 +35,17 @@ import org.osgi.framework.ServiceRegistration;
|
|||
import org.osgi.util.tracker.BundleTracker;
|
||||
|
||||
/**
|
||||
* Experiment: bootstrap jetty's complete distrib from an OSGi bundle. Progress:
|
||||
* Bootstrap jetty and publish a default Server instance as an OSGi service.
|
||||
*
|
||||
* Listen for other Server instances to be published as services and support them as deployment targets.
|
||||
*
|
||||
* Listen for Bundles to be activated, and deploy those that represent webapps to one of the known Server instances.
|
||||
*
|
||||
* <ol>
|
||||
* <li>basic servlet [ok]</li>
|
||||
* <li>basic jetty.xml [ok]</li>
|
||||
* <li>basic jetty.xml and jetty-plus.xml [ok]</li>
|
||||
* <li>basic jsp [ok with modifications]
|
||||
* <ul>
|
||||
* <li>Needed to modify the headers of jdt.core-3.1.1 so that its dependency on
|
||||
* eclipse.runtime, eclipse.resources and eclipse.text are optional. Also we
|
||||
* should depend on the latest jdt.core from eclipse-3.5 not from eclipse-3.1.1
|
||||
* although that will require actual changes to jasper as some internal APIs of
|
||||
* jdt.core have changed.</li>
|
||||
* <li>Modifications to org.mortbay.jetty.jsp-2.1-glassfish: made all imports to
|
||||
* ant, xalan and sun packages optional.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>basic jsp [ok]</li>
|
||||
* <li>jsp with tag-libs [ok]</li>
|
||||
* <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
|
||||
* </ul>
|
||||
|
@ -77,7 +72,6 @@ public class JettyBootstrapActivator implements BundleActivator
|
|||
|
||||
private BundleContext _bundleContext;
|
||||
|
||||
// private ServiceRegistration _jettyServerFactoryService;
|
||||
private JettyServerServiceTracker _jettyServerServiceTracker;
|
||||
|
||||
/**
|
||||
|
@ -97,35 +91,24 @@ public class JettyBootstrapActivator implements BundleActivator
|
|||
// should activate.
|
||||
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
|
||||
|
||||
// track Server instances that we should support as deployment targets
|
||||
_jettyServerServiceTracker = new JettyServerServiceTracker();
|
||||
context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
|
||||
|
||||
// Register the Jetty Server Factory as a ManagedServiceFactory:
|
||||
// Properties jettyServerMgdFactoryServiceProps = new Properties();
|
||||
// jettyServerMgdFactoryServiceProps.put("pid",
|
||||
// OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
|
||||
// _jettyServerFactoryService = context.registerService(
|
||||
// ManagedServiceFactory.class.getName(), new
|
||||
// JettyServersManagedFactory(),
|
||||
// jettyServerMgdFactoryServiceProps);
|
||||
|
||||
// track ContextHandler class instances and deploy them to one of the known Servers
|
||||
_jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
|
||||
|
||||
// the tracker in charge of the actual deployment
|
||||
// and that will configure and start the jetty server.
|
||||
context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
|
||||
|
||||
// see if we shoult start a default jetty instance right now.
|
||||
// Create a default jetty instance right now.
|
||||
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
|
||||
|
||||
// now ready to support the Extender pattern:
|
||||
// track Bundles and deploy those that represent webapps to one of the known Servers
|
||||
_webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
|
||||
_webBundleTracker.open();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/**
|
||||
* Stop the activator.
|
||||
*
|
||||
* @see
|
||||
* org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
|
||||
|
@ -173,22 +156,6 @@ public class JettyBootstrapActivator implements BundleActivator
|
|||
_registeredServer = null;
|
||||
}
|
||||
}
|
||||
// if (_jettyServerFactoryService != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// _jettyServerFactoryService.unregister();
|
||||
// }
|
||||
// catch (IllegalArgumentException ill)
|
||||
// {
|
||||
// // already unregistered.
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// _jettyServerFactoryService = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -215,7 +182,7 @@ public class JettyBootstrapActivator implements BundleActivator
|
|||
{
|
||||
checkBundleActivated();
|
||||
WebAppContext contextHandler = new WebAppContext();
|
||||
Dictionary dic = new Hashtable();
|
||||
Dictionary<String,String> dic = new Hashtable<String,String>();
|
||||
dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
|
||||
dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
|
||||
String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
|
||||
|
@ -289,7 +256,7 @@ public class JettyBootstrapActivator implements BundleActivator
|
|||
|
||||
/**
|
||||
* 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
|
||||
* when one of the static methods to register a webapp is called we should
|
||||
* make sure that the bundle is started.
|
||||
*/
|
||||
private static void checkBundleActivated()
|
||||
|
|
|
@ -55,26 +55,31 @@ import org.osgi.framework.Constants;
|
|||
* <p>
|
||||
* When the parameter autoInstallOSGiBundles is set to true, OSGi bundles that
|
||||
* are located in the monitored directory are installed and started after the
|
||||
* framework as finished auto-starting all the other bundles.
|
||||
* Warning: only use this for development.
|
||||
* framework as finished auto-starting all the other bundles. Warning: only use
|
||||
* this for development.
|
||||
* </p>
|
||||
*/
|
||||
public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(OSGiAppProvider.class);
|
||||
|
||||
|
||||
private boolean _extractWars = true;
|
||||
|
||||
private boolean _parentLoaderPriority = false;
|
||||
|
||||
private String _defaultsDescriptor;
|
||||
|
||||
private String _tldBundles;
|
||||
|
||||
private String[] _configurationClasses;
|
||||
|
||||
private boolean _autoInstallOSGiBundles = true;
|
||||
|
||||
//Keep track of the bundles that were installed and that are waiting for the
|
||||
//framework to complete its initialization.
|
||||
|
||||
// Keep track of the bundles that were installed and that are waiting for
|
||||
// the
|
||||
// framework to complete its initialization.
|
||||
Set<Bundle> _pendingBundlesToStart = null;
|
||||
|
||||
|
||||
/**
|
||||
* When a context file corresponds to a deployed bundle and is changed we
|
||||
* reload the corresponding bundle.
|
||||
|
@ -82,14 +87,11 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
private static class Filter implements FilenameFilter
|
||||
{
|
||||
OSGiAppProvider _enclosedInstance;
|
||||
|
||||
|
||||
public boolean accept(File dir, String name)
|
||||
{
|
||||
File file = new File(dir,name);
|
||||
if (fileMightBeAnOSGiBundle(file))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
File file = new File(dir, name);
|
||||
if (fileMightBeAnOSGiBundle(file)) { return true; }
|
||||
if (!file.isDirectory())
|
||||
{
|
||||
String contextName = getDeployedAppName(name);
|
||||
|
@ -104,8 +106,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* @param contextFileName
|
||||
* for example myContext.xml
|
||||
* @param contextFileName for example myContext.xml
|
||||
* @return The context, for example: myContext; null if this was not a
|
||||
* suitable contextFileName.
|
||||
*/
|
||||
|
@ -114,47 +115,52 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
String lowername = contextFileName.toLowerCase();
|
||||
if (lowername.endsWith(".xml"))
|
||||
{
|
||||
String contextName = contextFileName.substring(0,lowername.length() - ".xml".length());
|
||||
String contextName = contextFileName.substring(0, lowername.length() - ".xml".length());
|
||||
return contextName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading the display name of a webapp is really not sufficient for indexing the various
|
||||
* deployed ContextHandlers.
|
||||
* Reading the display name of a webapp is really not sufficient for
|
||||
* indexing the various deployed ContextHandlers.
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private String getContextHandlerAppName(ContextHandler context) {
|
||||
private String getContextHandlerAppName(ContextHandler context)
|
||||
{
|
||||
String appName = context.getDisplayName();
|
||||
if (appName == null || appName.length() == 0 || getDeployedApps().containsKey(appName)) {
|
||||
if (context instanceof WebAppContext)
|
||||
{
|
||||
appName = ((WebAppContext)context).getContextPath();
|
||||
if (getDeployedApps().containsKey(appName)) {
|
||||
appName = "noDisplayName"+context.getClass().getSimpleName()+context.hashCode();
|
||||
}
|
||||
} else {
|
||||
appName = "noDisplayName"+context.getClass().getSimpleName()+context.hashCode();
|
||||
}
|
||||
if (appName == null || appName.length() == 0 || getDeployedApps().containsKey(appName))
|
||||
{
|
||||
if (context instanceof WebAppContext)
|
||||
{
|
||||
appName = ((WebAppContext) context).getContextPath();
|
||||
if (getDeployedApps().containsKey(appName))
|
||||
{
|
||||
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
appName = "noDisplayName" + context.getClass().getSimpleName() + context.hashCode();
|
||||
}
|
||||
}
|
||||
return appName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default OSGiAppProvider consutructed when none are defined in the
|
||||
* Default OSGiAppProvider constructed when none are defined in the
|
||||
* jetty.xml configuration.
|
||||
*/
|
||||
public OSGiAppProvider()
|
||||
{
|
||||
super(new Filter());
|
||||
((Filter)super._filenameFilter)._enclosedInstance = this;
|
||||
((Filter) super._filenameFilter)._enclosedInstance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default OSGiAppProvider consutructed when none are defined in the
|
||||
* Default OSGiAppProvider constructed when none are defined in the
|
||||
* jetty.xml configuration.
|
||||
*
|
||||
* @param contextsDir
|
||||
|
@ -164,7 +170,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
this();
|
||||
setMonitoredDirResource(Resource.newResource(contextsDir.toURI()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ContextHandler that was created by WebappRegistractionHelper
|
||||
*
|
||||
|
@ -180,15 +186,15 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
// we don't support this situation at this point.
|
||||
// once the WebAppRegistrationHelper is refactored, the code
|
||||
// that creates the ContextHandler will actually be here.
|
||||
throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is construsted");
|
||||
throw new IllegalStateException("The App must be passed the " + "instance of the ContextHandler when it is constructed");
|
||||
}
|
||||
if (_configurationClasses != null && wah instanceof WebAppContext)
|
||||
if (_configurationClasses != null && wah instanceof WebAppContext)
|
||||
{
|
||||
((WebAppContext)wah).setConfigurationClasses(_configurationClasses);
|
||||
((WebAppContext) wah).setConfigurationClasses(_configurationClasses);
|
||||
}
|
||||
|
||||
|
||||
if (_defaultsDescriptor != null)
|
||||
((WebAppContext)wah).setDefaultsDescriptor(_defaultsDescriptor);
|
||||
((WebAppContext) wah).setDefaultsDescriptor(_defaultsDescriptor);
|
||||
return app.getContextHandler();
|
||||
}
|
||||
|
||||
|
@ -198,24 +204,23 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
@Override
|
||||
public void setDeploymentManager(DeploymentManager deploymentManager)
|
||||
{
|
||||
// _manager=deploymentManager;
|
||||
super.setDeploymentManager(deploymentManager);
|
||||
}
|
||||
|
||||
private static String getOriginId(Bundle contributor, String pathInBundle)
|
||||
{
|
||||
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() +
|
||||
(pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
|
||||
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (pathInBundle.startsWith("/") ? pathInBundle : "/" + pathInBundle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @throws Exception
|
||||
*/
|
||||
public void addContext(Bundle contributor, String pathInBundle, ContextHandler context) throws Exception
|
||||
{
|
||||
addContext(getOriginId(contributor, pathInBundle), context);
|
||||
addContext(getOriginId(contributor, pathInBundle), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @throws Exception
|
||||
|
@ -223,19 +228,17 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
public void addContext(String originId, ContextHandler context) throws Exception
|
||||
{
|
||||
// TODO apply configuration specific to this provider
|
||||
if (context instanceof WebAppContext)
|
||||
{
|
||||
((WebAppContext)context).setExtractWAR(isExtract());
|
||||
}
|
||||
if (context instanceof WebAppContext)
|
||||
{
|
||||
((WebAppContext) context).setExtractWAR(isExtract());
|
||||
}
|
||||
|
||||
// wrap context as an App
|
||||
App app = new App(getDeploymentManager(),this,originId,context);
|
||||
App app = new App(getDeploymentManager(), this, originId, context);
|
||||
String appName = getContextHandlerAppName(context);
|
||||
getDeployedApps().put(appName,app);
|
||||
getDeployedApps().put(appName, app);
|
||||
getDeploymentManager().addApp(app);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called by the scanner of the context files directory. If we find the
|
||||
|
@ -253,31 +256,32 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
// and reload the corresponding App.
|
||||
// see the 2 pass of the refactoring of the WebAppRegistrationHelper.
|
||||
String name = getDeployedAppName(filename);
|
||||
if (name != null)
|
||||
{
|
||||
return getDeployedApps().get(name);
|
||||
}
|
||||
if (name != null) { return getDeployedApps().get(name); }
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeContext(ContextHandler context) throws Exception
|
||||
{
|
||||
String appName = getContextHandlerAppName(context);
|
||||
String appName = getContextHandlerAppName(context);
|
||||
App app = getDeployedApps().remove(context.getDisplayName());
|
||||
if (app == null) {
|
||||
//try harder to undeploy this context handler.
|
||||
//see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=330098
|
||||
appName = null;
|
||||
for (Entry<String,App> deployedApp : getDeployedApps().entrySet()) {
|
||||
if (deployedApp.getValue().getContextHandler() == context) {
|
||||
app = deployedApp.getValue();
|
||||
appName = deployedApp.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appName != null) {
|
||||
getDeployedApps().remove(appName);
|
||||
}
|
||||
if (app == null)
|
||||
{
|
||||
// try harder to undeploy this context handler.
|
||||
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=330098
|
||||
appName = null;
|
||||
for (Entry<String, App> deployedApp : getDeployedApps().entrySet())
|
||||
{
|
||||
if (deployedApp.getValue().getContextHandler() == context)
|
||||
{
|
||||
app = deployedApp.getValue();
|
||||
appName = deployedApp.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appName != null)
|
||||
{
|
||||
getDeployedApps().remove(appName);
|
||||
}
|
||||
}
|
||||
if (app != null)
|
||||
{
|
||||
|
@ -285,8 +289,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
}
|
||||
}
|
||||
|
||||
// //copied from WebAppProvider as the parameters are identical.
|
||||
// //only removed the parameer related to extractWars.
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Get the parentLoaderPriority.
|
||||
|
@ -302,8 +305,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
/**
|
||||
* Set the parentLoaderPriority.
|
||||
*
|
||||
* @param parentLoaderPriority
|
||||
* the parentLoaderPriority to set
|
||||
* @param parentLoaderPriority the parentLoaderPriority to set
|
||||
*/
|
||||
public void setParentLoaderPriority(boolean parentLoaderPriority)
|
||||
{
|
||||
|
@ -325,8 +327,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
/**
|
||||
* Set the defaultsDescriptor.
|
||||
*
|
||||
* @param defaultsDescriptor
|
||||
* the defaultsDescriptor to set
|
||||
* @param defaultsDescriptor the defaultsDescriptor to set
|
||||
*/
|
||||
public void setDefaultsDescriptor(String defaultsDescriptor)
|
||||
{
|
||||
|
@ -342,8 +343,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
try
|
||||
{
|
||||
Resource monitoredDir = getMonitoredDirResource();
|
||||
if (monitoredDir == null)
|
||||
return null;
|
||||
if (monitoredDir == null) return null;
|
||||
return monitoredDir.getFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -363,8 +363,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
try
|
||||
{
|
||||
Resource monitoredDir = getMonitoredDirResource();
|
||||
if (monitoredDir == null)
|
||||
return null;
|
||||
if (monitoredDir == null) return null;
|
||||
return monitoredDir.getFile().toURI().toString();
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -381,28 +380,29 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
|
||||
public void setExtract(boolean extract)
|
||||
{
|
||||
_extractWars=extract;
|
||||
_extractWars = extract;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when this app provider locates osgi bundles and features in
|
||||
* its monitored directory and installs them. By default true if there is a folder to monitor.
|
||||
* its monitored directory and installs them. By default true if
|
||||
* there is a folder to monitor.
|
||||
*/
|
||||
public boolean isAutoInstallOSGiBundles()
|
||||
{
|
||||
return _autoInstallOSGiBundles;
|
||||
return _autoInstallOSGiBundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* <autoInstallOSGiBundles>true</autoInstallOSGiBundles>
|
||||
*
|
||||
* @param installingOSGiBundles
|
||||
*/
|
||||
public void setAutoInstallOSGiBundles(boolean installingOSGiBundles)
|
||||
{
|
||||
_autoInstallOSGiBundles=installingOSGiBundles;
|
||||
_autoInstallOSGiBundles = installingOSGiBundles;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the directory in which to look for context XML files.
|
||||
|
@ -423,33 +423,33 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
{
|
||||
setMonitoredDirName(contextsDir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param tldBundles Comma separated list of bundles that contain tld jars
|
||||
* that should be setup on the jetty instances created here.
|
||||
* that should be setup on the jetty instances created here.
|
||||
*/
|
||||
public void setTldBundles(String tldBundles)
|
||||
{
|
||||
_tldBundles = tldBundles;
|
||||
_tldBundles = tldBundles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return The list of bundles that contain tld jars that should be setup
|
||||
* on the jetty instances created here.
|
||||
* @return The list of bundles that contain tld jars that should be setup on
|
||||
* the jetty instances created here.
|
||||
*/
|
||||
public String getTldBundles()
|
||||
{
|
||||
return _tldBundles;
|
||||
return _tldBundles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param configurations The configuration class names.
|
||||
*/
|
||||
public void setConfigurationClasses(String[] configurations)
|
||||
{
|
||||
_configurationClasses = configurations==null?null:(String[])configurations.clone();
|
||||
}
|
||||
|
||||
_configurationClasses = configurations == null ? null : (String[]) configurations.clone();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*
|
||||
|
@ -467,41 +467,41 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
{
|
||||
if (isAutoInstallOSGiBundles())
|
||||
{
|
||||
if (getMonitoredDirResource() == null)
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.info("Disable autoInstallOSGiBundles as there is not contexts folder to monitor.");
|
||||
}
|
||||
else
|
||||
{
|
||||
File scandir = null;
|
||||
try
|
||||
{
|
||||
scandir = getMonitoredDirResource().getFile();
|
||||
if (!scandir.exists() || !scandir.isDirectory())
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + scandir.getAbsolutePath() + " does not exist.");
|
||||
scandir = null;
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + getMonitoredDirResource().getURI() + " does not exist.");
|
||||
scandir = null;
|
||||
}
|
||||
if (scandir != null)
|
||||
{
|
||||
for (File file : scandir.listFiles())
|
||||
{
|
||||
if (fileMightBeAnOSGiBundle(file))
|
||||
{
|
||||
installBundle(file, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getMonitoredDirResource() == null)
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.info("Disable autoInstallOSGiBundles as there is not contexts folder to monitor.");
|
||||
}
|
||||
else
|
||||
{
|
||||
File scandir = null;
|
||||
try
|
||||
{
|
||||
scandir = getMonitoredDirResource().getFile();
|
||||
if (!scandir.exists() || !scandir.isDirectory())
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + scandir.getAbsolutePath() + " does not exist.");
|
||||
scandir = null;
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
setAutoInstallOSGiBundles(false);
|
||||
LOG.warn("Disable autoInstallOSGiBundles as the contexts folder '" + getMonitoredDirResource().getURI() + " does not exist.");
|
||||
scandir = null;
|
||||
}
|
||||
if (scandir != null)
|
||||
{
|
||||
for (File file : scandir.listFiles())
|
||||
{
|
||||
if (fileMightBeAnOSGiBundle(file))
|
||||
{
|
||||
installBundle(file, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.doStart();
|
||||
if (isAutoInstallOSGiBundles())
|
||||
|
@ -510,10 +510,10 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
super.addScannerListener(scanCycleListner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the file is a jar or a folder, we look if it looks like an OSGi bundle.
|
||||
* In that case we install it and start it.
|
||||
* When the file is a jar or a folder, we look if it looks like an OSGi
|
||||
* bundle. In that case we install it and start it.
|
||||
* <p>
|
||||
* Really a simple trick to get going quickly with development.
|
||||
* </p>
|
||||
|
@ -531,7 +531,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
super.fileAdded(filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param file
|
||||
* @return
|
||||
|
@ -540,15 +540,9 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
{
|
||||
if (file.isDirectory())
|
||||
{
|
||||
if (new File(file,"META-INF/MANIFEST.MF").exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (file.getName().endsWith(".jar"))
|
||||
{
|
||||
return true;
|
||||
if (new File(file, "META-INF/MANIFEST.MF").exists()) { return true; }
|
||||
}
|
||||
else if (file.getName().endsWith(".jar")) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -579,72 +573,73 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
super.fileRemoved(filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a bundle according to its location.
|
||||
* In the version 1.6 of org.osgi.framework, BundleContext.getBundle(String) is what we want.
|
||||
* However to support older versions of OSGi. We use our own local refrence mechanism.
|
||||
* Returns a bundle according to its location. In the version 1.6 of
|
||||
* org.osgi.framework, BundleContext.getBundle(String) is what we want.
|
||||
* However to support older versions of OSGi. We use our own local reference
|
||||
* mechanism.
|
||||
*
|
||||
* @param location
|
||||
* @return
|
||||
*/
|
||||
protected Bundle getBundle(BundleContext bc, String location)
|
||||
{
|
||||
//not available in older versions of OSGi:
|
||||
//return bc.getBundle(location);
|
||||
for (Bundle b : bc.getBundles())
|
||||
{
|
||||
if (b.getLocation().equals(location))
|
||||
{
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
// not available in older versions of OSGi:
|
||||
// return bc.getBundle(location);
|
||||
for (Bundle b : bc.getBundles())
|
||||
{
|
||||
if (b.getLocation().equals(location)) { return b; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected synchronized Bundle installBundle(File file, boolean start)
|
||||
{
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
BundleContext bc = JettyBootstrapActivator.getBundleContext();
|
||||
String location = file.toURI().toString();
|
||||
Bundle b = getBundle(bc, location);
|
||||
if (b == null)
|
||||
if (b == null)
|
||||
{
|
||||
b = bc.installBundle(location);
|
||||
}
|
||||
if (b == null)
|
||||
{
|
||||
//not sure we will ever be here,
|
||||
//most likely a BundleException was thrown
|
||||
LOG.warn("The file " + location + " is not an OSGi bundle.");
|
||||
return null;
|
||||
// not sure we will ever be here,
|
||||
// most likely a BundleException was thrown
|
||||
LOG.warn("The file " + location + " is not an OSGi bundle.");
|
||||
return null;
|
||||
}
|
||||
if (start && b.getHeaders().get(Constants.FRAGMENT_HOST) == null)
|
||||
{//not a fragment, try to start it. if the framework has finished auto-starting.
|
||||
if (!PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
||||
{
|
||||
if (_pendingBundlesToStart == null)
|
||||
{
|
||||
_pendingBundlesToStart = new HashSet<Bundle>();
|
||||
}
|
||||
_pendingBundlesToStart.add(b);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
b.start();
|
||||
}
|
||||
{
|
||||
// not a fragment, try to start it. if the framework has finished
|
||||
// auto-starting.
|
||||
if (!PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
||||
{
|
||||
if (_pendingBundlesToStart == null)
|
||||
{
|
||||
_pendingBundlesToStart = new HashSet<Bundle>();
|
||||
}
|
||||
_pendingBundlesToStart.add(b);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
b.start();
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
catch (BundleException e)
|
||||
{
|
||||
LOG.warn("Unable to " + (start? "start":"install") + " the bundle " + file.getAbsolutePath(), e);
|
||||
LOG.warn("Unable to " + (start ? "start" : "install") + " the bundle " + file.getAbsolutePath(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected void uninstallBundle(File file)
|
||||
{
|
||||
try
|
||||
|
@ -658,7 +653,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
LOG.warn("Unable to uninstall the bundle " + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void updateBundle(File file)
|
||||
{
|
||||
try
|
||||
|
@ -682,53 +677,52 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider
|
|||
LOG.warn("Unable to update the bundle " + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* At the end of each scan, if there are some bundles to be started,
|
||||
* look if the framework has completed its autostart. In that case start those bundles.
|
||||
* At the end of each scan, if there are some bundles to be started, look if the
|
||||
* framework has completed its autostart. In that case start those bundles.
|
||||
*/
|
||||
class AutoStartWhenFrameworkHasCompleted implements Scanner.ScanCycleListener
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AutoStartWhenFrameworkHasCompleted.class);
|
||||
|
||||
private final OSGiAppProvider _appProvider;
|
||||
|
||||
AutoStartWhenFrameworkHasCompleted(OSGiAppProvider appProvider)
|
||||
{
|
||||
_appProvider = appProvider;
|
||||
}
|
||||
|
||||
public void scanStarted(int cycle) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void scanEnded(int cycle) throws Exception
|
||||
{
|
||||
if (_appProvider._pendingBundlesToStart != null && PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
||||
{
|
||||
Iterator<Bundle> it = _appProvider._pendingBundlesToStart.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Bundle b = it.next();
|
||||
if (b.getHeaders().get(Constants.FRAGMENT_HOST) != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
b.start();
|
||||
}
|
||||
catch (BundleException e)
|
||||
{
|
||||
LOG.warn("Unable to start the bundle " + b.getLocation(), e);
|
||||
}
|
||||
private static final Logger LOG = Log.getLogger(AutoStartWhenFrameworkHasCompleted.class);
|
||||
|
||||
}
|
||||
_appProvider._pendingBundlesToStart = null;
|
||||
}
|
||||
}
|
||||
private final OSGiAppProvider _appProvider;
|
||||
|
||||
AutoStartWhenFrameworkHasCompleted(OSGiAppProvider appProvider)
|
||||
{
|
||||
_appProvider = appProvider;
|
||||
}
|
||||
|
||||
public void scanStarted(int cycle) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void scanEnded(int cycle) throws Exception
|
||||
{
|
||||
if (_appProvider._pendingBundlesToStart != null && PackageAdminServiceTracker.INSTANCE.frameworkHasCompletedAutostarts())
|
||||
{
|
||||
Iterator<Bundle> it = _appProvider._pendingBundlesToStart.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Bundle b = it.next();
|
||||
if (b.getHeaders().get(Constants.FRAGMENT_HOST) != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
b.start();
|
||||
}
|
||||
catch (BundleException e)
|
||||
{
|
||||
LOG.warn("Unable to start the bundle " + b.getLocation(), e);
|
||||
}
|
||||
|
||||
}
|
||||
_appProvider._pendingBundlesToStart = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public class OSGiWebappConstants
|
|||
/** web app context path */
|
||||
public static final String SERVICE_PROP_CONTEXT_PATH = "contextPath";
|
||||
|
||||
/** Path to the web application base folderr */
|
||||
/** Path to the web application base folder */
|
||||
public static final String SERVICE_PROP_WAR = "war";
|
||||
|
||||
/** Extra classpath */
|
||||
|
|
|
@ -32,14 +32,14 @@ import org.osgi.framework.Bundle;
|
|||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
* Called by the {@link JettyBootstrapActivator} during the starting of the bundle.
|
||||
* If the system property 'jetty.home' is defined and points to a folder,
|
||||
* then setup the corresponding jetty server and starts it.
|
||||
* Called by the {@link JettyBootstrapActivator} during the starting of the
|
||||
* bundle. If the system property 'jetty.home' is defined and points to a
|
||||
* folder, then setup the corresponding jetty server.
|
||||
*/
|
||||
public class DefaultJettyAtJettyHomeHelper {
|
||||
public class DefaultJettyAtJettyHomeHelper
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(DefaultJettyAtJettyHomeHelper.class);
|
||||
|
||||
|
||||
/**
|
||||
* contains a comma separated list of pathes to the etc/jetty-*.xml files
|
||||
* used to configure jetty. By default the value is 'etc/jetty.xml' when the
|
||||
|
@ -48,44 +48,66 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
public static final String SYS_PROP_JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS;
|
||||
|
||||
/**
|
||||
* Usual system property used as the hostname for a typical jetty configuration.
|
||||
* Usual system property used as the hostname for a typical jetty
|
||||
* configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_HOME = "jetty.home";
|
||||
/**
|
||||
* System property to point to a bundle that embeds a jetty configuration
|
||||
* and that jetty configuration should be the default jetty server.
|
||||
* First we look for jetty.home. If we don't find it then we look for this property.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
|
||||
/**
|
||||
* Usual system property used as the hostname for a typical jetty configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_HOST = "jetty.host";
|
||||
/**
|
||||
* Usual system property used as the port for http for a typical jetty configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_PORT = "jetty.port";
|
||||
/**
|
||||
* Usual system property used as the port for https for a typical jetty configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
|
||||
|
||||
/**
|
||||
* Called by the JettyBootStrapActivator.
|
||||
* If the system property jetty.home is defined and points to a folder,
|
||||
* deploys the corresponding jetty server.
|
||||
* System property to point to a bundle that embeds a jetty configuration
|
||||
* and that jetty configuration should be the default jetty server. First we
|
||||
* look for jetty.home. If we don't find it then we look for this property.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_HOME_BUNDLE = "jetty.home.bundle";
|
||||
|
||||
/**
|
||||
* Usual system property used as the hostname for a typical jetty
|
||||
* configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_HOST = "jetty.host";
|
||||
|
||||
/**
|
||||
* Usual system property used as the port for http for a typical jetty
|
||||
* configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_PORT = "jetty.port";
|
||||
|
||||
/**
|
||||
* Usual system property used as the port for https for a typical jetty
|
||||
* configuration.
|
||||
*/
|
||||
public static final String SYS_PROP_JETTY_PORT_SSL = "jetty.port.ssl";
|
||||
|
||||
/**
|
||||
* Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES
|
||||
*/
|
||||
public static final String DEFAULT_JETTY_ETC_FILES = "jetty.xml,jetty-selector.xml,jetty-deployer.xml";
|
||||
|
||||
/**
|
||||
* Default location within bundle of a jetty home dir.
|
||||
*/
|
||||
public static final String DEFAULT_JETTYHOME = "/jettyhome";
|
||||
|
||||
/**
|
||||
* Called by the JettyBootStrapActivator. If the system property jetty.home
|
||||
* is defined and points to a folder, creates a corresponding jetty
|
||||
* server.
|
||||
* <p>
|
||||
* If the system property jetty.home.bundle is defined and points to a bundle.
|
||||
* Look for the configuration of jetty inside that bundle and deploys the corresponding bundle.
|
||||
* If the system property jetty.home.bundle is defined and points to a
|
||||
* bundle, look for the configuration of jetty inside that bundle.
|
||||
* </p>
|
||||
* <p>
|
||||
* In both cases reads the system property 'jetty.etc.config.urls' to locate the configuration
|
||||
* files for the deployed jetty. It is a comma spearate list of URLs or relative paths inside the bundle or folder
|
||||
* to the config files. If underfined it defaults to 'etc/jetty.xml'.
|
||||
* In both cases reads the system property 'jetty.etc.config.urls' to locate
|
||||
* the configuration files for the deployed jetty. It is a comma separated
|
||||
* list of URLs or relative paths inside the bundle or folder to the config
|
||||
* files. If undefined it defaults to 'etc/jetty.xml'. In the case of the jetty.home.bundle,
|
||||
* if no etc/jetty.xml file is found in the bundle, it will look for
|
||||
* /jettyhome/etc/jetty-osgi-default.xml
|
||||
* </p>
|
||||
* <p>
|
||||
* In both cases the system properties jetty.host, jetty.port and jetty.port.ssl are passed to the configuration files
|
||||
* that might use them as part of their properties.
|
||||
* In both cases the system properties jetty.host, jetty.port and
|
||||
* jetty.port.ssl are passed to the configuration files that might use them
|
||||
* as part of their properties.
|
||||
* </p>
|
||||
*/
|
||||
public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
|
||||
|
@ -97,15 +119,14 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
if (jettyHomeSysProp != null)
|
||||
{
|
||||
jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp);
|
||||
//bug 329621
|
||||
if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"")
|
||||
|| (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'"))) {
|
||||
// bug 329621
|
||||
if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'")))
|
||||
{
|
||||
jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1);
|
||||
}
|
||||
if (jettyHomeBundleSysProp != null)
|
||||
{
|
||||
LOG.warn("Both the jetty.home property and the jetty.home.bundle property are defined."
|
||||
+ " jetty.home.bundle is not taken into account.");
|
||||
LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored.");
|
||||
}
|
||||
jettyHome = new File(jettyHomeSysProp);
|
||||
if (!jettyHome.exists() || !jettyHome.isDirectory())
|
||||
|
@ -134,12 +155,12 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
}
|
||||
if (jettyHome == null && jettyHomeBundle == null)
|
||||
{
|
||||
LOG.warn("No default jetty started.");
|
||||
LOG.warn("No default jetty created.");
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = new Server();
|
||||
Dictionary properties = new Hashtable();
|
||||
Dictionary<String,String> properties = new Hashtable<String,String>();
|
||||
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME);
|
||||
|
||||
String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle);
|
||||
|
@ -147,21 +168,25 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
|
||||
LOG.info("Configuring the default jetty server with " + configURLs);
|
||||
|
||||
//these properties usually are the ones passed to this type of configuration.
|
||||
setProperty(properties,SYS_PROP_JETTY_HOME,System.getProperty(SYS_PROP_JETTY_HOME));
|
||||
setProperty(properties,SYS_PROP_JETTY_HOST,System.getProperty(SYS_PROP_JETTY_HOST));
|
||||
setProperty(properties,SYS_PROP_JETTY_PORT,System.getProperty(SYS_PROP_JETTY_PORT));
|
||||
setProperty(properties,SYS_PROP_JETTY_PORT_SSL,System.getProperty(SYS_PROP_JETTY_PORT_SSL));
|
||||
// these properties usually are the ones passed to this type of
|
||||
// configuration.
|
||||
setProperty(properties, SYS_PROP_JETTY_HOME, System.getProperty(SYS_PROP_JETTY_HOME));
|
||||
setProperty(properties, SYS_PROP_JETTY_HOST, System.getProperty(SYS_PROP_JETTY_HOST));
|
||||
setProperty(properties, SYS_PROP_JETTY_PORT, System.getProperty(SYS_PROP_JETTY_PORT));
|
||||
setProperty(properties, SYS_PROP_JETTY_PORT_SSL, System.getProperty(SYS_PROP_JETTY_PORT_SSL));
|
||||
|
||||
//register the Server instance as an OSGi service.
|
||||
bundleContext.registerService(Server.class.getName(), server, properties);
|
||||
// hookNestedConnectorToBridgeServlet(server);
|
||||
// hookNestedConnectorToBridgeServlet(server);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum setup for the location of the configuration files given a jettyhome folder.
|
||||
* Reads the system property jetty.etc.config.urls and look for the corresponding jetty
|
||||
* configuration files that will be used to setup the jetty server.
|
||||
* Minimum setup for the location of the configuration files given a
|
||||
* jettyhome folder. Reads the system property jetty.etc.config.urls and
|
||||
* look for the corresponding jetty configuration files that will be used to
|
||||
* setup the jetty server.
|
||||
*
|
||||
* @param jettyhome
|
||||
* @return
|
||||
*/
|
||||
|
@ -191,17 +216,19 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Minimum setup for the location of the configuration files given a configuration
|
||||
* embedded inside a bundle.
|
||||
* Reads the system property jetty.etc.config.urls and look for the corresponding jetty
|
||||
* configuration files that will be used to setup the jetty server.
|
||||
* Minimum setup for the location of the configuration files given a
|
||||
* configuration embedded inside a bundle. Reads the system property
|
||||
* jetty.etc.config.urls and look for the corresponding jetty configuration
|
||||
* files that will be used to setup the jetty server.
|
||||
*
|
||||
* @param jettyhome
|
||||
* @return
|
||||
*/
|
||||
private static String getJettyConfigurationURLs(Bundle configurationBundle)
|
||||
{
|
||||
String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml");
|
||||
StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false);
|
||||
String files = System.getProperty(SYS_PROP_JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES);
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(files, ";,", false);
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
||||
while (tokenizer.hasMoreTokens())
|
||||
|
@ -209,22 +236,25 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
String etcFile = tokenizer.nextToken().trim();
|
||||
if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1)
|
||||
{
|
||||
//file path is absolute
|
||||
appendToCommaSeparatedList(res, etcFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT
|
||||
.findEntries(configurationBundle, etcFile);
|
||||
//relative file path
|
||||
Enumeration<URL> enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, etcFile);
|
||||
|
||||
//default for org.eclipse.osgi.boot where we look inside jettyhome for the default embedded configuration.
|
||||
//default inside jettyhome. this way fragments to the bundle can define their own configuration.
|
||||
if ((enUrls == null || !enUrls.hasMoreElements()) && etcFile.endsWith("etc/jetty.xml"))
|
||||
// default for org.eclipse.osgi.boot where we look inside
|
||||
// jettyhome for the default embedded configuration.
|
||||
// default inside jettyhome. this way fragments to the bundle
|
||||
// can define their own configuration.
|
||||
if ((enUrls == null || !enUrls.hasMoreElements()))
|
||||
{
|
||||
enUrls = BundleFileLocatorHelper.DEFAULT
|
||||
.findEntries(configurationBundle, "/jettyhome/etc/jetty-osgi-default.xml");
|
||||
System.err.println("Configuring jetty with the default embedded configuration:" +
|
||||
"bundle: " + configurationBundle.getSymbolicName() +
|
||||
" config: /jettyhome/etc/jetty-osgi-default.xml");
|
||||
String tmp = DEFAULT_JETTYHOME+"/etc/"+etcFile;
|
||||
enUrls = BundleFileLocatorHelper.DEFAULT.findEntries(configurationBundle, tmp);
|
||||
LOG.info("Configuring jetty with the default embedded configuration:" + "bundle: "
|
||||
+ configurationBundle.getSymbolicName()
|
||||
+ " config: "+tmp);
|
||||
}
|
||||
if (enUrls == null || !enUrls.hasMoreElements())
|
||||
{
|
||||
|
@ -251,7 +281,7 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
buffer.append(value);
|
||||
}
|
||||
|
||||
private static void setProperty(Dictionary properties, String key, String value)
|
||||
private static void setProperty(Dictionary<String,String> properties, String key, String value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
|
@ -261,30 +291,27 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
|
||||
/**
|
||||
* recursively substitute the ${sysprop} by their actual system property.
|
||||
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no sysprop is defined.
|
||||
* Not the most efficient code but we are shooting for simplicity and speed of development here.
|
||||
* ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
|
||||
* sysprop is defined. Not the most efficient code but we are shooting for
|
||||
* simplicity and speed of development here.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String resolvePropertyValue(String value)
|
||||
{
|
||||
{
|
||||
int ind = value.indexOf("${");
|
||||
if (ind == -1) {
|
||||
return value;
|
||||
}
|
||||
if (ind == -1) { return value; }
|
||||
int ind2 = value.indexOf('}', ind);
|
||||
if (ind2 == -1) {
|
||||
return value;
|
||||
}
|
||||
String sysprop = value.substring(ind+2, ind2);
|
||||
if (ind2 == -1) { return value; }
|
||||
String sysprop = value.substring(ind + 2, ind2);
|
||||
String defaultValue = null;
|
||||
int comma = sysprop.indexOf(',');
|
||||
if (comma != -1 && comma+1 != sysprop.length())
|
||||
if (comma != -1 && comma + 1 != sysprop.length())
|
||||
{
|
||||
defaultValue = sysprop.substring(comma+1);
|
||||
defaultValue = sysprop.substring(comma + 1);
|
||||
defaultValue = resolvePropertyValue(defaultValue);
|
||||
sysprop = sysprop.substring(0,comma);
|
||||
sysprop = sysprop.substring(0, comma);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -293,7 +320,7 @@ public class DefaultJettyAtJettyHomeHelper {
|
|||
|
||||
String v = System.getProperty(sysprop);
|
||||
|
||||
String reminder = value.length() > ind2 + 1 ? value.substring(ind2+1) : "";
|
||||
String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
|
||||
reminder = resolvePropertyValue(reminder);
|
||||
if (v != null)
|
||||
{
|
||||
|
|
|
@ -17,12 +17,14 @@ package org.eclipse.jetty.osgi.boot.internal.serverfactory;
|
|||
/**
|
||||
* Keeps track of the running jetty servers. They are named.
|
||||
*/
|
||||
public interface IManagedJettyServerRegistry {
|
||||
public interface IManagedJettyServerRegistry
|
||||
{
|
||||
|
||||
/**
|
||||
* @param managedServerName The server name
|
||||
* @return the corresponding jetty server wrapped with its deployment properties.
|
||||
* @return the corresponding jetty server wrapped with its deployment
|
||||
* properties.
|
||||
*/
|
||||
public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName);
|
||||
|
||||
public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName);
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.osgi.framework.ServiceReference;
|
|||
public class JettyServerServiceTracker implements ServiceListener, IManagedJettyServerRegistry
|
||||
{
|
||||
private static Logger LOG = Log.getLogger(JettyServerServiceTracker.class.getName());
|
||||
|
||||
|
||||
/**
|
||||
* Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin
|
||||
* service.
|
||||
|
@ -86,15 +86,16 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty
|
|||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.getType() == ServiceEvent.UNREGISTERING)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// modified, meaning: we reload it. now that we stopped it;
|
||||
// we can register it.
|
||||
|
||||
if (ev.getType() == ServiceEvent.UNREGISTERING)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// modified, meaning: we reload it. now that we stopped it;
|
||||
// we can register it.
|
||||
}
|
||||
}
|
||||
case ServiceEvent.REGISTERED:
|
||||
{
|
||||
|
@ -113,7 +114,7 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
LOG.warn(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2009 Intalio, Inc.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.
|
||||
// Contributors:
|
||||
// Hugues Malphettes - initial API and implementation
|
||||
// ========================================================================
|
||||
package org.eclipse.jetty.osgi.boot.internal.serverfactory;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.service.cm.Configuration;
|
||||
import org.osgi.service.cm.ConfigurationAdmin;
|
||||
import org.osgi.service.cm.ConfigurationException;
|
||||
import org.osgi.service.cm.ManagedServiceFactory;
|
||||
|
||||
/**
|
||||
* Manages the deployment of jetty server instances.
|
||||
* Not sure this is bringing much compared to the JettyServerServiceTracker.
|
||||
*
|
||||
* @author hmalphettes
|
||||
*/
|
||||
public class JettyServersManagedFactory implements ManagedServiceFactory, IManagedJettyServerRegistry
|
||||
{
|
||||
|
||||
/**
|
||||
* key to configure the server according to a jetty home folder. the value
|
||||
* is the corresponding java.io.File
|
||||
*/
|
||||
public static final String JETTY_HOME = "jettyhome";
|
||||
/** key to configure the server according to a jetty.xml file */
|
||||
public static final String JETTY_CONFIG_XML = "jettyxml";
|
||||
|
||||
/**
|
||||
* invoke jetty-factory class. the value of this property is the instance of
|
||||
* that class to call back.
|
||||
*/
|
||||
public static final String JETTY_FACTORY = "jettyfactory";
|
||||
|
||||
/**
|
||||
* default property in jetty.xml that is used as the value of the http port.
|
||||
*/
|
||||
public static final String JETTY_HTTP_PORT = "jetty.http.port";
|
||||
/**
|
||||
* default property in jetty.xml that is used as the value of the https
|
||||
* port.
|
||||
*/
|
||||
public static final String JETTY_HTTPS_PORT = "jetty.http.port";
|
||||
|
||||
/**
|
||||
* Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin service.
|
||||
*/
|
||||
private Map<String, ServerInstanceWrapper> _serversIndexedByPID = new HashMap<String, ServerInstanceWrapper>();
|
||||
/**
|
||||
* PID -> {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME}
|
||||
*/
|
||||
private Map<String, String> _serversNameIndexedByPID = new HashMap<String, String>();
|
||||
/**
|
||||
* {@link OSGiWebappConstants#MANAGED_JETTY_SERVER_NAME} -> PID
|
||||
*/
|
||||
private Map<String, String> _serversPIDIndexedByName = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Return a descriptive name of this factory.
|
||||
*
|
||||
* @return the name for the factory, which might be localized
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
public void updated(String pid, Dictionary properties) throws ConfigurationException
|
||||
{
|
||||
ServerInstanceWrapper serverInstanceWrapper = getServerByPID(pid);
|
||||
deleted(pid);
|
||||
// do we need to collect the currently deployed http services and
|
||||
// webapps
|
||||
// to be able to re-deploy them later?
|
||||
// probably not. simply restart and see the various service trackers
|
||||
// do everything that is needed.
|
||||
String name = (String)properties.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
|
||||
if (name == null)
|
||||
{
|
||||
throw new ConfigurationException(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME,
|
||||
"The name of the server is mandatory");
|
||||
}
|
||||
serverInstanceWrapper = new ServerInstanceWrapper(name);
|
||||
_serversIndexedByPID.put(pid, serverInstanceWrapper);
|
||||
_serversNameIndexedByPID.put(pid, name);
|
||||
_serversPIDIndexedByName.put(name, pid);
|
||||
try
|
||||
{
|
||||
serverInstanceWrapper.start(new Server(), properties);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ConfigurationException(null, "Error starting jetty server instance", e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void deleted(String pid)
|
||||
{
|
||||
ServerInstanceWrapper server = (ServerInstanceWrapper)_serversIndexedByPID.remove(pid);
|
||||
String name = _serversNameIndexedByPID.remove(pid);
|
||||
if (name != null)
|
||||
{
|
||||
_serversPIDIndexedByName.remove(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
//something incorrect going on.
|
||||
}
|
||||
if (server != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ServerInstanceWrapper getServerByPID(String pid)
|
||||
{
|
||||
return _serversIndexedByPID.get(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param managedServerName The server name
|
||||
* @return the corresponding jetty server wrapped with its deployment properties.
|
||||
*/
|
||||
public ServerInstanceWrapper getServerInstanceWrapper(String managedServerName)
|
||||
{
|
||||
String pid = _serversPIDIndexedByName.get(managedServerName);
|
||||
return pid != null ? _serversIndexedByPID.get(pid) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create and configure a new Jetty Server via the ManagedServiceFactory
|
||||
* @param contributor
|
||||
* @param serverName
|
||||
* @param urlsToJettyXml
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void createNewServer(Bundle contributor, String serverName, String urlsToJettyXml) throws Exception
|
||||
{
|
||||
ServiceReference configurationAdminReference =
|
||||
contributor.getBundleContext().getServiceReference( ConfigurationAdmin.class.getName() );
|
||||
|
||||
ConfigurationAdmin confAdmin = (ConfigurationAdmin) contributor.getBundleContext()
|
||||
.getService( configurationAdminReference );
|
||||
|
||||
Configuration configuration = confAdmin.createFactoryConfiguration(
|
||||
OSGiServerConstants.MANAGED_JETTY_SERVER_FACTORY_PID, contributor.getLocation() );
|
||||
Dictionary properties = new Hashtable();
|
||||
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
|
||||
|
||||
StringBuilder actualBundleUrls = new StringBuilder();
|
||||
StringTokenizer tokenizer = new StringTokenizer(urlsToJettyXml, ",", false);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
if (actualBundleUrls.length() != 0)
|
||||
{
|
||||
actualBundleUrls.append(",");
|
||||
}
|
||||
String token = tokenizer.nextToken();
|
||||
if (token.indexOf(':') != -1)
|
||||
{
|
||||
//a complete url. no change needed:
|
||||
actualBundleUrls.append(token);
|
||||
}
|
||||
else if (token.startsWith("/"))
|
||||
{
|
||||
//url relative to the contributor bundle:
|
||||
URL url = contributor.getEntry(token);
|
||||
if (url == null)
|
||||
{
|
||||
actualBundleUrls.append(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
actualBundleUrls.append(url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, actualBundleUrls.toString());
|
||||
configuration.update(properties);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ package org.eclipse.jetty.osgi.boot.internal.serverfactory;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Dictionary;
|
||||
|
@ -45,8 +44,9 @@ import org.eclipse.jetty.xml.XmlConfiguration;
|
|||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/**
|
||||
* Exposes a Jetty Server to be managed by an OSGi ManagedServiceFactory
|
||||
* Configure and start it. Can also be used from the ManagedServiceFactory
|
||||
* ServerInstanceWrapper
|
||||
*
|
||||
* Configures and starts a jetty Server instance.
|
||||
*/
|
||||
public class ServerInstanceWrapper
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ public class ServerInstanceWrapper
|
|||
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
|
||||
try
|
||||
{
|
||||
// passing this bundle's classloader as the context classlaoder
|
||||
// passing this bundle's classloader as the context classloader
|
||||
// makes sure there is access to all the jetty's bundles
|
||||
ClassLoader libExtClassLoader = null;
|
||||
String sharedURLs = (String) props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
|
||||
|
@ -185,9 +185,8 @@ public class ServerInstanceWrapper
|
|||
{
|
||||
Thread.currentThread().setContextClassLoader(contextCl);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void stop()
|
||||
{
|
||||
|
@ -212,14 +211,14 @@ public class ServerInstanceWrapper
|
|||
* /META-INF/*.tld it may contain. We place the bundles that we know contain
|
||||
* 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.
|
||||
* the jstl implementation.
|
||||
*
|
||||
* 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 and/or in the lib folder (nice for PDE developement situations)
|
||||
* the root and/or in the lib folder (nice for PDE development situations)
|
||||
* Unsupported: the bundle is a jar that embeds more jars.
|
||||
*
|
||||
* @return
|
||||
|
@ -249,6 +248,8 @@ public class ServerInstanceWrapper
|
|||
List<URL> jettyConfigurations = jettyConfigurationUrls != null ? extractResources(jettyConfigurationUrls) : null;
|
||||
if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
|
||||
Map<String, Object> id_map = new HashMap<String, Object>();
|
||||
|
||||
//TODO need to put in the id of the server being configured
|
||||
id_map.put("Server", server);
|
||||
Map<String, String> properties = new HashMap<String, String>();
|
||||
Enumeration<Object> en = props.keys();
|
||||
|
@ -304,12 +305,11 @@ public class ServerInstanceWrapper
|
|||
}
|
||||
|
||||
/**
|
||||
* Must be called after the server is configured.
|
||||
* Must be called after the server is configured.
|
||||
*
|
||||
* Locate the actual instance of the ContextDeployer and WebAppDeployer that
|
||||
* was created when configuring the server through jetty.xml. If there is no
|
||||
* such thing it won't be possible to deploy webapps from a context and we
|
||||
* throw IllegalStateExceptions.
|
||||
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
|
||||
*
|
||||
* The server must have an instance of OSGiAppProvider. If one is not provided, it is created.
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
|
@ -363,9 +363,12 @@ public class ServerInstanceWrapper
|
|||
if (jettyContextsHome != null)
|
||||
{
|
||||
File contextsHome = new File(jettyContextsHome);
|
||||
if (!contextsHome.exists() || !contextsHome.isDirectory()) { throw new IllegalArgumentException(
|
||||
"the ${jetty.osgi.contexts.home} '" + jettyContextsHome
|
||||
+ " must exist and be a folder"); }
|
||||
if (!contextsHome.exists() || !contextsHome.isDirectory())
|
||||
{
|
||||
throw new IllegalArgumentException("the ${jetty.osgi.contexts.home} '"
|
||||
+ jettyContextsHome
|
||||
+ " must exist and be a folder");
|
||||
}
|
||||
return contextsHome;
|
||||
}
|
||||
return new File(jettyHome, "/contexts");
|
||||
|
|
|
@ -20,70 +20,65 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
import org.osgi.framework.Bundle;
|
||||
|
||||
/**
|
||||
* Internal interface for the class that deploys a webapp on a server.
|
||||
* Used as we migrate from the single instance of the jety server to multiple jetty servers.
|
||||
* Internal interface for the class that deploys a webapp on a server. Used as
|
||||
* we migrate from the single instance of the jety server to multiple jetty
|
||||
* servers.
|
||||
*/
|
||||
public interface IWebBundleDeployerHelper {
|
||||
public interface IWebBundleDeployerHelper
|
||||
{
|
||||
|
||||
/** when this property is present, the type of context handler registered is not
|
||||
* known in advance. */
|
||||
/**
|
||||
* when this property is present, the type of context handler registered is
|
||||
* not known in advance.
|
||||
*/
|
||||
public static final String INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE = "unknownContextHandlerType";
|
||||
|
||||
|
||||
/**
|
||||
* Deploy a new web application on the jetty server.
|
||||
*
|
||||
* @param bundle
|
||||
* The bundle
|
||||
* @param webappFolderPath
|
||||
* The path to the root of the webapp. Must be a path relative to
|
||||
* bundle; either an absolute path.
|
||||
* @param contextPath
|
||||
* The context path. Must start with "/"
|
||||
* @param extraClasspath
|
||||
* @param overrideBundleInstallLocation
|
||||
* @param requireTldBundle The list of bundles's symbolic names that contain
|
||||
* tld files that are required by this WAB.
|
||||
* @param webXmlPath
|
||||
* @param defaultWebXmlPath
|
||||
* TODO: parameter description
|
||||
* @return The contexthandler created and started
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract WebAppContext registerWebapplication(Bundle bundle,
|
||||
String webappFolderPath, String contextPath, String extraClasspath,
|
||||
String overrideBundleInstallLocation,
|
||||
String requireTldBundle, String webXmlPath,
|
||||
String defaultWebXmlPath, WebAppContext webAppContext) throws Exception;
|
||||
/**
|
||||
* Deploy a new web application on the jetty server.
|
||||
*
|
||||
* @param bundle The bundle
|
||||
* @param webappFolderPath The path to the root of the webapp. Must be a
|
||||
* path relative to bundle; either an absolute path.
|
||||
* @param contextPath The context path. Must start with "/"
|
||||
* @param extraClasspath
|
||||
* @param overrideBundleInstallLocation
|
||||
* @param requireTldBundle The list of bundles's symbolic names that contain
|
||||
* tld files that are required by this WAB.
|
||||
* @param webXmlPath
|
||||
* @param defaultWebXmlPath TODO: parameter description
|
||||
* @return The contexthandler created and started
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath,
|
||||
String defaultWebXmlPath, WebAppContext webAppContext) throws Exception;
|
||||
|
||||
/**
|
||||
* Stop a ContextHandler and remove it from the collection.
|
||||
*
|
||||
* @see ContextDeployer#undeploy
|
||||
* @param contextHandler
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void unregister(ContextHandler contextHandler)
|
||||
throws Exception;
|
||||
/**
|
||||
* Stop a ContextHandler and remove it from the collection.
|
||||
*
|
||||
* @see ContextDeployer#undeploy
|
||||
* @param contextHandler
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void unregister(ContextHandler contextHandler) throws Exception;
|
||||
|
||||
/**
|
||||
* This type of registration relies on jetty's complete context xml file.
|
||||
* Context encompasses jndi and all other things. This makes the definition
|
||||
* of the webapp a lot more self-contained.
|
||||
*
|
||||
* @param contributor
|
||||
* @param contextFileRelativePath
|
||||
* @param extraClasspath
|
||||
* @param overrideBundleInstallLocation
|
||||
* @param requireTldBundle The list of bundles'symbolic name that contain tld files for this webapp.
|
||||
* @param handler the context handler passed in the server
|
||||
* reference that will be configured, deployed and started.
|
||||
* @return The contexthandler created and started
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract ContextHandler registerContext(Bundle contributor,
|
||||
String contextFileRelativePath, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle,
|
||||
ContextHandler handler) throws Exception;
|
||||
/**
|
||||
* This type of registration relies on jetty's complete context xml file.
|
||||
* Context encompasses jndi and all other things. This makes the definition
|
||||
* of the webapp a lot more self-contained.
|
||||
*
|
||||
* @param contributor
|
||||
* @param contextFileRelativePath
|
||||
* @param extraClasspath
|
||||
* @param overrideBundleInstallLocation
|
||||
* @param requireTldBundle The list of bundles'symbolic name that contain
|
||||
* tld files for this webapp.
|
||||
* @param handler the context handler passed in the server reference that
|
||||
* will be configured, deployed and started.
|
||||
* @return The contexthandler created and started
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception;
|
||||
|
||||
}
|
|
@ -55,7 +55,7 @@ import org.osgi.framework.ServiceReference;
|
|||
public class JettyContextHandlerServiceTracker implements ServiceListener
|
||||
{
|
||||
private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName());
|
||||
|
||||
|
||||
/** New style: ability to manage multiple jetty instances */
|
||||
private final IManagedJettyServerRegistry _registry;
|
||||
|
||||
|
@ -213,7 +213,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
|
|||
defaultWebXmlPath = webapp.getDefaultsDescriptor();
|
||||
}
|
||||
}
|
||||
String war = (String) sr.getProperty("war");
|
||||
String war = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WAR);
|
||||
try
|
||||
{
|
||||
IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr);
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* Helper to create a URL class-loader with the jars inside
|
||||
|
@ -52,9 +51,9 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
* be required for some cases. For example jaspers' TldLocationsCache (replaced
|
||||
* by TldScanner for servlet-3.0). <br/>
|
||||
* Also all the dependencies of those libraries must be resolvable directly from
|
||||
* the JettyBooStrapper bundle as it is set as the parent classloader. For
|
||||
* the JettyBootstrapActivator bundle as it is set as the parent classloader. For
|
||||
* example: if atomikos is placed in lib/ext it will work if and only if
|
||||
* JettyBootStrapper import the necessary packages from javax.naming*,
|
||||
* JettyBootstrapActivator import the necessary packages from javax.naming*,
|
||||
* javax.transaction*, javax.mail* etc Most of the common cases of javax are
|
||||
* added as optional import packages into jetty bootstrapper plugin. When there
|
||||
* are not covered: please make a request or create a fragment or register a
|
||||
|
@ -92,22 +91,18 @@ public class LibExtClassLoaderHelper
|
|||
* is the JettyBootStrapper (an osgi classloader.
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
public static ClassLoader createLibEtcClassLoader(File jettyHome, Server server,
|
||||
ClassLoader parentClassLoader) throws MalformedURLException
|
||||
public static ClassLoader createLibEtcClassLoader(File jettyHome, Server server, ClassLoader parentClassLoader) throws MalformedURLException
|
||||
{
|
||||
if (jettyHome == null)
|
||||
{
|
||||
return parentClassLoader;
|
||||
}
|
||||
if (jettyHome == null) { return parentClassLoader; }
|
||||
ArrayList<URL> urls = new ArrayList<URL>();
|
||||
File jettyResources = new File(jettyHome,"resources");
|
||||
File jettyResources = new File(jettyHome, "resources");
|
||||
if (jettyResources.exists())
|
||||
{
|
||||
// make sure it contains something else than README:
|
||||
Map<String, File> jettyResFiles = new HashMap<String, File>();
|
||||
for (File f : jettyResources.listFiles())
|
||||
{
|
||||
jettyResFiles.put(f.getName(),f);
|
||||
jettyResFiles.put(f.getName(), f);
|
||||
if (f.getName().toLowerCase().startsWith("readme"))
|
||||
{
|
||||
continue;
|
||||
|
@ -120,9 +115,9 @@ public class LibExtClassLoaderHelper
|
|||
}
|
||||
}
|
||||
}
|
||||
processFilesInResourcesFolder(jettyHome,jettyResFiles);
|
||||
processFilesInResourcesFolder(jettyHome, jettyResFiles);
|
||||
}
|
||||
File libExt = new File(jettyHome,"lib/ext");
|
||||
File libExt = new File(jettyHome, "lib/ext");
|
||||
if (libExt.exists())
|
||||
{
|
||||
for (File f : libExt.listFiles())
|
||||
|
@ -140,55 +135,52 @@ public class LibExtClassLoaderHelper
|
|||
}
|
||||
}
|
||||
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]),parentClassLoader);
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param server
|
||||
* @return a url classloader with the jars of resources, lib/ext and the
|
||||
* jars passed in the other argument. The parent classloader usually
|
||||
* is the JettyBootStrapper (an osgi classloader).
|
||||
* If there was no extra jars to insert, then just return the parentClassLoader.
|
||||
* is the JettyBootStrapper (an osgi classloader). If there was no
|
||||
* extra jars to insert, then just return the parentClassLoader.
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars,
|
||||
List<URL> otherJarsOrFolder, Server server,
|
||||
ClassLoader parentClassLoader) throws MalformedURLException
|
||||
public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, Server server, ClassLoader parentClassLoader)
|
||||
throws MalformedURLException
|
||||
{
|
||||
if (jarsContainerOrJars == null && otherJarsOrFolder == null)
|
||||
{
|
||||
return parentClassLoader;
|
||||
}
|
||||
List<URL> urls = new ArrayList<URL>();
|
||||
if (otherJarsOrFolder != null)
|
||||
{
|
||||
urls.addAll(otherJarsOrFolder);
|
||||
}
|
||||
if (jarsContainerOrJars != null)
|
||||
{
|
||||
for (File libExt : jarsContainerOrJars)
|
||||
{
|
||||
if (libExt.isDirectory())
|
||||
{
|
||||
for (File f : libExt.listFiles())
|
||||
{
|
||||
if (f.getName().endsWith(".jar"))
|
||||
{
|
||||
// cheap to tolerate folders so let's do it.
|
||||
URL url = f.toURI().toURL();
|
||||
if (f.isFile())
|
||||
{// is this necessary anyways?
|
||||
url = new URL("jar:" + url.toString() + "!/");
|
||||
}
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]),parentClassLoader);
|
||||
if (jarsContainerOrJars == null && otherJarsOrFolder == null) { return parentClassLoader; }
|
||||
List<URL> urls = new ArrayList<URL>();
|
||||
if (otherJarsOrFolder != null)
|
||||
{
|
||||
urls.addAll(otherJarsOrFolder);
|
||||
}
|
||||
if (jarsContainerOrJars != null)
|
||||
{
|
||||
for (File libExt : jarsContainerOrJars)
|
||||
{
|
||||
if (libExt.isDirectory())
|
||||
{
|
||||
for (File f : libExt.listFiles())
|
||||
{
|
||||
if (f.getName().endsWith(".jar"))
|
||||
{
|
||||
// cheap to tolerate folders so let's do it.
|
||||
URL url = f.toURI().toURL();
|
||||
if (f.isFile())
|
||||
{
|
||||
// is this necessary anyways?
|
||||
url = new URL("jar:" + url.toString() + "!/");
|
||||
}
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When we find files typically used for central logging configuration we do
|
||||
* what it takes in this method to do what the user expects. Without
|
||||
|
@ -207,7 +199,7 @@ public class LibExtClassLoaderHelper
|
|||
{
|
||||
for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
|
||||
{
|
||||
processor.processFilesInResourcesFolder(jettyHome,childrenFiles);
|
||||
processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,12 +55,12 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
|
||||
public static void addClassThatIdentifiesAJarThatMustBeRejected(Class<?> zclass)
|
||||
{
|
||||
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.','/') + ".class");
|
||||
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.', '/') + ".class");
|
||||
}
|
||||
|
||||
public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName)
|
||||
{
|
||||
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.','/') + ".class");
|
||||
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.', '/') + ".class");
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -69,34 +69,37 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
}
|
||||
|
||||
private ClassLoader _osgiBundleClassLoader;
|
||||
|
||||
private Bundle _contributor;
|
||||
|
||||
private boolean _lookInOsgiFirst = true;
|
||||
|
||||
private Set<String> _libsAlreadyInManifest = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* @param parent The parent classloader. In this case
|
||||
* @param parent The parent classloader. In this case
|
||||
* @param context The WebAppContext
|
||||
* @param contributor The bundle that defines this web-application.
|
||||
* @throws IOException
|
||||
*/
|
||||
public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor,
|
||||
BundleClassLoaderHelper bundleClassLoaderHelper) throws IOException
|
||||
public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor, BundleClassLoaderHelper bundleClassLoaderHelper)
|
||||
throws IOException
|
||||
{
|
||||
super(parent,context);
|
||||
super(parent, context);
|
||||
_contributor = contributor;
|
||||
_osgiBundleClassLoader = bundleClassLoaderHelper.getBundleClassLoader(contributor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>Bundle</code> that defined this web-application.
|
||||
*
|
||||
* @return The <code>Bundle</code> object associated with this
|
||||
* <code>BundleReference</code>.
|
||||
*/
|
||||
public Bundle getBundle()
|
||||
{
|
||||
return _contributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>Bundle</code> that defined this web-application.
|
||||
*
|
||||
* @return The <code>Bundle</code> object associated with this
|
||||
* <code>BundleReference</code>.
|
||||
*/
|
||||
public Bundle getBundle()
|
||||
{
|
||||
return _contributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the manifest. If the manifest is already configured to loads a few
|
||||
|
@ -123,7 +126,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
return Collections.enumeration(toList(urls, osgiUrls));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URL getResource(String name)
|
||||
{
|
||||
|
@ -132,37 +135,37 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
URL url = _osgiBundleClassLoader.getResource(name);
|
||||
return url != null ? url : super.getResource(name);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
URL url = super.getResource(name);
|
||||
return url != null ? url : _osgiBundleClassLoader.getResource(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
|
||||
{
|
||||
List<URL> list = new ArrayList<URL>();
|
||||
while (e!=null && e.hasMoreElements())
|
||||
while (e != null && e.hasMoreElements())
|
||||
list.add(e.nextElement());
|
||||
while (e2!=null && e2.hasMoreElements())
|
||||
while (e2 != null && e2.hasMoreElements())
|
||||
list.add(e2.nextElement());
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
*
|
||||
*/
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException
|
||||
{
|
||||
try
|
||||
{
|
||||
return _lookInOsgiFirst?_osgiBundleClassLoader.loadClass(name):super.findClass(name);
|
||||
return _lookInOsgiFirst ? _osgiBundleClassLoader.loadClass(name) : super.findClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _lookInOsgiFirst?super.findClass(name):_osgiBundleClassLoader.loadClass(name);
|
||||
return _lookInOsgiFirst ? super.findClass(name) : _osgiBundleClassLoader.loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException cne2)
|
||||
{
|
||||
|
@ -179,7 +182,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
public void addClassPath(String classPath) throws IOException
|
||||
{
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(classPath,",;");
|
||||
StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String path = tokenizer.nextToken();
|
||||
|
@ -187,7 +190,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
|
||||
// Resolve file path if possible
|
||||
File file = resource.getFile();
|
||||
if (file != null && isAcceptableLibrary(file,JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
|
||||
if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
|
||||
{
|
||||
super.addClassPath(path);
|
||||
}
|
||||
|
@ -211,10 +214,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
{
|
||||
for (String criteria : pathToClassFiles)
|
||||
{
|
||||
if (new File(file,criteria).exists())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (new File(file, criteria).exists()) { return false; }
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -225,22 +225,18 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
jar = new JarFile(file);
|
||||
for (String criteria : pathToClassFiles)
|
||||
{
|
||||
if (jar.getEntry(criteria) != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (jar.getEntry(criteria) != null) { return false; }
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (jar != null)
|
||||
try
|
||||
{
|
||||
jar.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
}
|
||||
if (jar != null) try
|
||||
{
|
||||
jar.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +266,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
_contextField = WebAppClassLoader.class.getDeclaredField("_context");
|
||||
_contextField.setAccessible(true);
|
||||
}
|
||||
_contextField.set(this,webappContext);
|
||||
_contextField.set(this, webappContext);
|
||||
if (webappContext.getExtraClasspath() != null)
|
||||
{
|
||||
addClassPath(webappContext.getExtraClasspath());
|
||||
|
|
|
@ -167,10 +167,10 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
*/
|
||||
public WebAppContext registerWebapplication(Bundle bundle, String webappFolderPath, String contextPath, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle, String webXmlPath, String defaultWebXmlPath,
|
||||
WebAppContext webAppContext) throws Exception
|
||||
{
|
||||
File bundleInstall = overrideBundleInstallLocation == null ? BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle) : new File(
|
||||
overrideBundleInstallLocation);
|
||||
WebAppContext webAppContext)
|
||||
throws Exception
|
||||
{
|
||||
File bundleInstall = overrideBundleInstallLocation == null ? BUNDLE_FILE_LOCATOR_HELPER.getBundleInstallLocation(bundle) : new File(overrideBundleInstallLocation);
|
||||
File webapp = null;
|
||||
URL baseWebappInstallURL = null;
|
||||
|
||||
|
@ -197,11 +197,12 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
{
|
||||
webapp = bundleInstall;
|
||||
}
|
||||
if (baseWebappInstallURL == null && (webapp == null || !webapp.exists())) { throw new IllegalArgumentException(
|
||||
"Unable to locate " + webappFolderPath
|
||||
+ " inside "
|
||||
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : "unlocated bundle '" + bundle.getSymbolicName()
|
||||
+ "'")); }
|
||||
if (baseWebappInstallURL == null && (webapp == null || !webapp.exists()))
|
||||
{
|
||||
throw new IllegalArgumentException("Unable to locate " + webappFolderPath
|
||||
+ " inside "
|
||||
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : "unlocated bundle '" + bundle.getSymbolicName()+ "'"));
|
||||
}
|
||||
if (baseWebappInstallURL == null && webapp != null)
|
||||
{
|
||||
baseWebappInstallURL = webapp.toURI().toURL();
|
||||
|
@ -221,19 +222,19 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
*/
|
||||
private WebAppContext registerWebapplication(Bundle contributor, String pathInBundleToWebApp, URL baseWebappInstallURL, String contextPath,
|
||||
String extraClasspath, File bundleInstall, String requireTldBundle, String webXmlPath,
|
||||
String defaultWebXmlPath, WebAppContext context) throws Exception
|
||||
String defaultWebXmlPath, WebAppContext context)
|
||||
throws Exception
|
||||
{
|
||||
|
||||
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
|
||||
String[] oldServerClasses = null;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
//apply any META-INF/context.xml file that is found to configure the webapp first
|
||||
applyMetaInfContextXml (contributor, context);
|
||||
|
||||
|
||||
// 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
|
||||
// through this bundle.
|
||||
OSGiWebappClassLoader composite = createWebappClassLoader(contributor);
|
||||
|
@ -241,10 +242,10 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
// classes
|
||||
// that the contributor gives access to.
|
||||
Thread.currentThread().setContextClassLoader(composite);
|
||||
|
||||
//converts bundleentry: protocol
|
||||
|
||||
// converts bundleentry: protocol
|
||||
baseWebappInstallURL = DefaultFileLocatorHelper.getLocalURL(baseWebappInstallURL);
|
||||
|
||||
|
||||
context.setWar(baseWebappInstallURL.toString());
|
||||
context.setContextPath(contextPath);
|
||||
context.setExtraClasspath(extraClasspath);
|
||||
|
@ -341,7 +342,6 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
}
|
||||
Thread.currentThread().setContextClassLoader(contextCl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -351,7 +351,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* 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);
|
||||
}
|
||||
|
@ -365,7 +366,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* java.lang.String, java.lang.String)
|
||||
*/
|
||||
public ContextHandler registerContext(Bundle contributor, String contextFileRelativePath, String extraClasspath, String overrideBundleInstallLocation,
|
||||
String requireTldBundle, ContextHandler handler) throws Exception
|
||||
String requireTldBundle, ContextHandler handler)
|
||||
throws Exception
|
||||
{
|
||||
File contextsHome = _wrapper.getOSGiAppProvider().getContextXmlDirAsFile();
|
||||
if (contextsHome != null)
|
||||
|
@ -417,7 +419,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* @throws Exception
|
||||
*/
|
||||
private ContextHandler registerContext(Bundle contributor, String pathInBundle, File contextFile, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception
|
||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
|
||||
throws Exception
|
||||
{
|
||||
InputStream contextFileInputStream = null;
|
||||
try
|
||||
|
@ -439,7 +442,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* @throws Exception
|
||||
*/
|
||||
private ContextHandler registerContext(Bundle contributor, String pathInsideBundle, InputStream contextFileInputStream, String extraClasspath,
|
||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler) throws Exception
|
||||
String overrideBundleInstallLocation, String requireTldBundle, ContextHandler handler)
|
||||
throws Exception
|
||||
{
|
||||
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
|
||||
String[] oldServerClasses = null;
|
||||
|
@ -455,9 +459,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
Thread.currentThread().setContextClassLoader(composite);
|
||||
ContextHandler context = createContextHandler(handler, contributor, contextFileInputStream, extraClasspath, overrideBundleInstallLocation,
|
||||
requireTldBundle);
|
||||
if (context == null)
|
||||
{
|
||||
return null;// did not happen
|
||||
if (context == null) { return null;// did not happen
|
||||
}
|
||||
|
||||
// ok now register this webapp. we checked when we started jetty
|
||||
|
@ -495,7 +497,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* @see {WebAppDeployer#scan} around the comment
|
||||
* <code>// configure it</code>
|
||||
*/
|
||||
protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle) throws IOException
|
||||
protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle)
|
||||
throws IOException
|
||||
{
|
||||
// rfc66
|
||||
wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, contributor.getBundleContext());
|
||||
|
@ -651,7 +654,6 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -780,7 +782,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
* @param classInBundle
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader, String requireTldBundle) throws Exception
|
||||
protected void configureWebappClassLoader(Bundle contributor, ContextHandler context, OSGiWebappClassLoader webappClassLoader, String requireTldBundle)
|
||||
throws Exception
|
||||
{
|
||||
if (context instanceof WebAppContext)
|
||||
{
|
||||
|
@ -800,7 +803,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
/**
|
||||
* No matter what the type of webapp, we create a WebappClassLoader.
|
||||
*/
|
||||
protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor) throws Exception
|
||||
protected OSGiWebappClassLoader createWebappClassLoader(Bundle contributor)
|
||||
throws Exception
|
||||
{
|
||||
// we use a temporary WebAppContext object.
|
||||
// if this is a real webapp we will set it on it a bit later: once we
|
||||
|
@ -810,28 +814,24 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
return webappClassLoader;
|
||||
}
|
||||
|
||||
|
||||
protected void applyMetaInfContextXml (Bundle bundle, ContextHandler contextHandler)
|
||||
protected void applyMetaInfContextXml(Bundle bundle, ContextHandler contextHandler)
|
||||
throws Exception
|
||||
{
|
||||
if (bundle == null)
|
||||
return;
|
||||
if (contextHandler == null)
|
||||
return;
|
||||
if (bundle == null) return;
|
||||
if (contextHandler == null) return;
|
||||
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
__logger.info("Context classloader = "+cl);
|
||||
__logger.info("Context classloader = " + cl);
|
||||
try
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(_wrapper.getParentClassLoaderForWebapps());
|
||||
|
||||
//find if there is a META-INF/context.xml file
|
||||
// find if there is a META-INF/context.xml file
|
||||
URL contextXmlUrl = bundle.getEntry("/META-INF/jetty-webapp-context.xml");
|
||||
if (contextXmlUrl == null)
|
||||
return;
|
||||
if (contextXmlUrl == null) return;
|
||||
|
||||
//Apply it just as the standard jetty ContextProvider would do
|
||||
__logger.info("Applying "+contextXmlUrl+" to "+contextHandler);
|
||||
// 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();
|
||||
|
@ -844,9 +844,7 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
Thread.currentThread().setContextClassLoader(cl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the property "this.bundle.install" to point to the location
|
||||
* of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
|
||||
|
@ -866,7 +864,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
}
|
||||
}
|
||||
|
||||
private String getPathsToRequiredBundles(ContextHandler context, Bundle bundle, String requireTldBundle) throws Exception
|
||||
private String getPathsToRequiredBundles(ContextHandler context, Bundle bundle, String requireTldBundle)
|
||||
throws Exception
|
||||
{
|
||||
if (requireTldBundle == null) return null;
|
||||
|
||||
|
@ -883,11 +882,10 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
+ "' specified in the "
|
||||
+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
|
||||
+ " of the manifest of "
|
||||
+ (bundle==null?"unknown":bundle.getSymbolicName())); }
|
||||
+ (bundle == null ? "unknown" : bundle.getSymbolicName())); }
|
||||
|
||||
File f = fileLocatorHelper.getBundleInstallLocation(bs[0]);
|
||||
if (paths.length() > 0)
|
||||
paths.append(", ");
|
||||
if (paths.length() > 0) paths.append(", ");
|
||||
__logger.debug("getPathsToRequiredBundles: bundle path=" + bs[0].getLocation() + " uri=" + f.toURI());
|
||||
paths.append(f.toURI().toURL().toString());
|
||||
}
|
||||
|
@ -903,6 +901,5 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
|
|||
|
||||
return (PackageAdmin) serviceTracker.getService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
|
|||
String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH);
|
||||
if (warFolderRelativePath != null)
|
||||
{
|
||||
String contextPath = getWebContextPath(bundle, dic, false);// (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
|
||||
String contextPath = getWebContextPath(bundle, dic, false);
|
||||
if (contextPath == null || !contextPath.startsWith("/"))
|
||||
{
|
||||
LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
|
||||
|
@ -199,7 +199,7 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
|
|||
// (draft) of the spec: just a couple of posts on the
|
||||
// world-wide-web.
|
||||
URL rfc66Webxml = bundle.getEntry("/WEB-INF/web.xml");
|
||||
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
|
||||
if (rfc66Webxml == null && dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) == null)
|
||||
{
|
||||
return false;// no webapp in here
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ public interface BundleFileLocatorHelper
|
|||
/** The default instance supports felix and equinox */
|
||||
public static BundleFileLocatorHelper DEFAULT = new DefaultFileLocatorHelper();
|
||||
|
||||
|
||||
/**
|
||||
* Works with equinox, felix, nuxeo and probably more. Not exactly in the
|
||||
* spirit of OSGi but quite necessary to support self-contained webapps and
|
||||
|
@ -43,8 +42,7 @@ public interface BundleFileLocatorHelper
|
|||
* Currently only works with bundles that are not jar.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle
|
||||
* The bundle
|
||||
* @param bundle The bundle
|
||||
* @return Its installation location as a file.
|
||||
* @throws Exception
|
||||
*/
|
||||
|
@ -75,16 +73,15 @@ public interface BundleFileLocatorHelper
|
|||
* embedded inside it.
|
||||
*/
|
||||
public File[] locateJarsInsideBundle(Bundle bundle) throws Exception;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper method equivalent to Bundle#getEntry(String entryPath) except that
|
||||
* it searches for entries in the fragments by using the findEntries method.
|
||||
*
|
||||
* @param bundle
|
||||
* @param entryPath
|
||||
* @return null or all the entries found for that path.
|
||||
*/
|
||||
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
|
||||
* Helper method equivalent to Bundle#getEntry(String entryPath) except that
|
||||
* it searches for entries in the fragments by using the findEntries method.
|
||||
*
|
||||
* @param bundle
|
||||
* @param entryPath
|
||||
* @return null or all the entries found for that path.
|
||||
*/
|
||||
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
|
||||
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ package org.eclipse.jetty.osgi.boot.utils.internal;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelper;
|
||||
|
@ -29,7 +27,9 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
{
|
||||
|
||||
private static boolean identifiedOsgiImpl = false;
|
||||
|
||||
private static boolean isEquinox = false;
|
||||
|
||||
private static boolean isFelix = false;
|
||||
|
||||
private static void init(Bundle bundle)
|
||||
|
@ -54,8 +54,6 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
isFelix = false;
|
||||
}
|
||||
}
|
||||
// System.err.println("isEquinox=" + isEquinox);
|
||||
// System.err.println("isFelix=" + isFelix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,10 +64,10 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
*/
|
||||
public ClassLoader getBundleClassLoader(Bundle bundle)
|
||||
{
|
||||
String bundleActivator = (String)bundle.getHeaders().get("Bundle-Activator");
|
||||
String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
|
||||
if (bundleActivator == null)
|
||||
{
|
||||
bundleActivator = (String)bundle.getHeaders().get("Jetty-ClassInBundle");
|
||||
bundleActivator = (String) bundle.getHeaders().get("Jetty-ClassInBundle");
|
||||
}
|
||||
if (bundleActivator != null)
|
||||
{
|
||||
|
@ -93,14 +91,12 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
{
|
||||
return internalGetEquinoxBundleClassLoader(bundle);
|
||||
}
|
||||
else if (isFelix)
|
||||
{
|
||||
return internalGetFelixBundleClassLoader(bundle);
|
||||
}
|
||||
else if (isFelix) { return internalGetFelixBundleClassLoader(bundle); }
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method Equinox_BundleHost_getBundleLoader_method;
|
||||
|
||||
private static Method Equinox_BundleLoader_createClassLoader_method;
|
||||
|
||||
private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle)
|
||||
|
@ -110,18 +106,18 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
{
|
||||
if (Equinox_BundleHost_getBundleLoader_method == null)
|
||||
{
|
||||
Equinox_BundleHost_getBundleLoader_method = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost")
|
||||
.getDeclaredMethod("getBundleLoader",new Class[] {});
|
||||
Equinox_BundleHost_getBundleLoader_method =
|
||||
bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost").getDeclaredMethod("getBundleLoader", new Class[] {});
|
||||
Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
|
||||
}
|
||||
Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle,new Object[] {});
|
||||
Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {});
|
||||
if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
|
||||
{
|
||||
Equinox_BundleLoader_createClassLoader_method = bundleLoader.getClass().getClassLoader().loadClass(
|
||||
"org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader",new Class[] {});
|
||||
Equinox_BundleLoader_createClassLoader_method =
|
||||
bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {});
|
||||
Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
|
||||
}
|
||||
return (ClassLoader)Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader,new Object[] {});
|
||||
return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {});
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
|
@ -131,6 +127,7 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
}
|
||||
|
||||
private static Field Felix_BundleImpl_m_modules_field;
|
||||
|
||||
private static Field Felix_ModuleImpl_m_classLoader_field;
|
||||
|
||||
private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
|
||||
|
@ -142,8 +139,7 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
// and return the private field m_classLoader of ModuleImpl
|
||||
if (Felix_BundleImpl_m_modules_field == null)
|
||||
{
|
||||
Felix_BundleImpl_m_modules_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl").getDeclaredField(
|
||||
"m_modules");
|
||||
Felix_BundleImpl_m_modules_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl").getDeclaredField("m_modules");
|
||||
Felix_BundleImpl_m_modules_field.setAccessible(true);
|
||||
}
|
||||
|
||||
|
@ -151,26 +147,25 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
Object currentModuleImpl;
|
||||
try
|
||||
{
|
||||
Object[] moduleArray = (Object[])Felix_BundleImpl_m_modules_field.get(bundle);
|
||||
Object[] moduleArray = (Object[]) Felix_BundleImpl_m_modules_field.get(bundle);
|
||||
currentModuleImpl = moduleArray[moduleArray.length - 1];
|
||||
}
|
||||
catch (Throwable t2)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> moduleArray = (List<Object>)Felix_BundleImpl_m_modules_field.get(bundle);
|
||||
List<Object> moduleArray = (List<Object>) Felix_BundleImpl_m_modules_field.get(bundle);
|
||||
currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
if (Felix_ModuleImpl_m_classLoader_field == null && currentModuleImpl != null)
|
||||
{
|
||||
Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField(
|
||||
"m_classLoader");
|
||||
Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader");
|
||||
Felix_ModuleImpl_m_classLoader_field.setAccessible(true);
|
||||
}
|
||||
// first make sure that the classloader is ready:
|
||||
// the m_classLoader field must be initialized by the
|
||||
// ModuleImpl.getClassLoader() private method.
|
||||
ClassLoader cl = (ClassLoader)Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
|
||||
ClassLoader cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
|
||||
if (cl == null)
|
||||
{
|
||||
// looks like it was not ready:
|
||||
|
@ -178,15 +173,11 @@ public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
|
|||
// ModuleImpl.getClassLoader() private method.
|
||||
// this call will do that.
|
||||
bundle.loadClass("java.lang.Object");
|
||||
cl = (ClassLoader)Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
|
||||
// System.err.println("Got the bundle class loader of felix_: "
|
||||
// + cl);
|
||||
cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
|
||||
return cl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// System.err.println("Got the bundle class loader of felix: " +
|
||||
// cl);
|
||||
return cl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
// grab the MANIFEST.MF's url
|
||||
// and then do what it takes.
|
||||
URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
|
||||
|
||||
|
||||
if (url.getProtocol().equals("file"))
|
||||
{
|
||||
// some osgi frameworks do use the file protocole directly in some
|
||||
|
@ -84,7 +84,12 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
// the File
|
||||
|
||||
URLConnection con = url.openConnection();
|
||||
con.setUseCaches(Resource.getDefaultUseCaches()); //work around problems where url connections cache references to jars
|
||||
con.setUseCaches(Resource.getDefaultUseCaches()); // work around
|
||||
// problems where
|
||||
// url connections
|
||||
// cache
|
||||
// references to
|
||||
// jars
|
||||
|
||||
if (BUNDLE_ENTRY_FIELD == null)
|
||||
{
|
||||
|
@ -105,10 +110,10 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry"))
|
||||
{
|
||||
url = bundle.getEntry("/");
|
||||
|
||||
|
||||
con = url.openConnection();
|
||||
con.setDefaultUseCaches(Resource.getDefaultUseCaches());
|
||||
|
||||
|
||||
if (BUNDLE_ENTRY_FIELD == null)
|
||||
{// this one will be a DirZipBundleEntry
|
||||
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
|
||||
|
@ -151,20 +156,18 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
// it is relative to relative to the BundleArchive's
|
||||
// m_archiveRootDir
|
||||
File res = new File(location.substring("file:".length()));
|
||||
if (!res.exists())
|
||||
{
|
||||
return null;
|
||||
// Object bundleArchive = getFelixBundleArchive(bundle);
|
||||
// File archiveRoot =
|
||||
// getFelixBundleArchiveRootDir(bundleArchive);
|
||||
// String currentLocation =
|
||||
// getFelixBundleArchiveCurrentLocation(bundleArchive);
|
||||
// System.err.println("Got the archive root " +
|
||||
// archiveRoot.getAbsolutePath()
|
||||
// + " current location " + currentLocation +
|
||||
// " is directory ?");
|
||||
// res = new File(archiveRoot, currentLocation != null
|
||||
// ? currentLocation : location.substring("file:".length()));
|
||||
if (!res.exists()) { return null;
|
||||
// Object bundleArchive = getFelixBundleArchive(bundle);
|
||||
// File archiveRoot =
|
||||
// getFelixBundleArchiveRootDir(bundleArchive);
|
||||
// String currentLocation =
|
||||
// getFelixBundleArchiveCurrentLocation(bundleArchive);
|
||||
// System.err.println("Got the archive root " +
|
||||
// archiveRoot.getAbsolutePath()
|
||||
// + " current location " + currentLocation +
|
||||
// " is directory ?");
|
||||
// res = new File(archiveRoot, currentLocation != null
|
||||
// ? currentLocation : location.substring("file:".length()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -194,15 +197,12 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
}
|
||||
File bundleInstall = getBundleInstallLocation(bundle);
|
||||
File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall;
|
||||
if (!webapp.exists())
|
||||
{
|
||||
throw new IllegalArgumentException("Unable to locate " + path
|
||||
+ " inside "
|
||||
+ bundle.getSymbolicName()
|
||||
+ " ("
|
||||
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ")
|
||||
+ ")");
|
||||
}
|
||||
if (!webapp.exists()) { throw new IllegalArgumentException("Unable to locate " + path
|
||||
+ " inside "
|
||||
+ bundle.getSymbolicName()
|
||||
+ " ("
|
||||
+ (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ")
|
||||
+ ")"); }
|
||||
return webapp;
|
||||
}
|
||||
|
||||
|
@ -300,8 +300,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
{
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
|
||||
if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName()
|
||||
.equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||
if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||
{
|
||||
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
|
||||
BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
|
||||
|
@ -337,8 +336,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
|
|||
{
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
|
||||
if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName()
|
||||
.equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||
if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
|
||||
{
|
||||
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
|
||||
BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
|
||||
|
|
|
@ -37,10 +37,15 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
private BundleContext _context;
|
||||
|
||||
private List<BundleActivator> _activatedFragments = new ArrayList<BundleActivator>();
|
||||
|
||||
private boolean _fragmentsWereActivated = false;
|
||||
//Use the deprecated StartLevel to stay compatible with older versions of OSGi.
|
||||
|
||||
// Use the deprecated StartLevel to stay compatible with older versions of
|
||||
// OSGi.
|
||||
private StartLevel _startLevel;
|
||||
|
||||
private int _maxStartLevel = 6;
|
||||
|
||||
public static PackageAdminServiceTracker INSTANCE = null;
|
||||
|
||||
public PackageAdminServiceTracker(BundleContext context)
|
||||
|
@ -51,7 +56,7 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
{
|
||||
try
|
||||
{
|
||||
_context.addServiceListener(this,"(objectclass=" + PackageAdmin.class.getName() + ")");
|
||||
_context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")");
|
||||
}
|
||||
catch (InvalidSyntaxException e)
|
||||
{
|
||||
|
@ -67,22 +72,21 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
{
|
||||
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
|
||||
_fragmentsWereActivated = sr != null;
|
||||
if (sr != null)
|
||||
invokeFragmentActivators(sr);
|
||||
|
||||
if (sr != null) invokeFragmentActivators(sr);
|
||||
|
||||
sr = _context.getServiceReference(StartLevel.class.getName());
|
||||
if (sr != null)
|
||||
{
|
||||
_startLevel = (StartLevel)_context.getService(sr);
|
||||
try
|
||||
{
|
||||
_maxStartLevel = Integer.parseInt(System.getProperty("osgi.startLevel","6"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//nevermind default on the usual.
|
||||
_maxStartLevel = 6;
|
||||
}
|
||||
_startLevel = (StartLevel) _context.getService(sr);
|
||||
try
|
||||
{
|
||||
_maxStartLevel = Integer.parseInt(System.getProperty("osgi.startLevel", "6"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// nevermind default on the usual.
|
||||
_maxStartLevel = 6;
|
||||
}
|
||||
}
|
||||
return _fragmentsWereActivated;
|
||||
}
|
||||
|
@ -93,8 +97,7 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
* the symbolic name of the fragment and the name of the class must be
|
||||
* 'FragmentActivator'.
|
||||
*
|
||||
* @param event
|
||||
* The <code>ServiceEvent</code> object.
|
||||
* @param event The <code>ServiceEvent</code> object.
|
||||
*/
|
||||
public void serviceChanged(ServiceEvent event)
|
||||
{
|
||||
|
@ -103,11 +106,12 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
invokeFragmentActivators(event.getServiceReference());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to access the PackageAdmin and return the fragments hosted by a bundle.
|
||||
* when we drop the support for the older versions of OSGi, we will stop using the PackageAdmin
|
||||
* service.
|
||||
* Helper to access the PackageAdmin and return the fragments hosted by a
|
||||
* bundle. when we drop the support for the older versions of OSGi, we will
|
||||
* stop using the PackageAdmin service.
|
||||
*
|
||||
* @param bundle
|
||||
* @return
|
||||
*/
|
||||
|
@ -115,17 +119,18 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
{
|
||||
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
|
||||
if (sr == null)
|
||||
{//we should never be here really.
|
||||
{// we should never be here really.
|
||||
return null;
|
||||
}
|
||||
PackageAdmin admin = (PackageAdmin)_context.getService(sr);
|
||||
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
|
||||
return admin.getFragments(bundle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fragments and the required-bundles of a bundle.
|
||||
* Recursively collect the required-bundles and fragment when the directive visibility:=reexport
|
||||
* is added to a required-bundle.
|
||||
* Returns the fragments and the required-bundles of a bundle. Recursively
|
||||
* collect the required-bundles and fragment when the directive
|
||||
* visibility:=reexport is added to a required-bundle.
|
||||
*
|
||||
* @param bundle
|
||||
* @param webFragOrAnnotationOrResources
|
||||
* @return
|
||||
|
@ -134,124 +139,122 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
{
|
||||
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
|
||||
if (sr == null)
|
||||
{//we should never be here really.
|
||||
{// we should never be here really.
|
||||
return null;
|
||||
}
|
||||
PackageAdmin admin = (PackageAdmin)_context.getService(sr);
|
||||
LinkedHashMap<String,Bundle> deps = new LinkedHashMap<String,Bundle>();
|
||||
collectFragmentsAndRequiredBundles(bundle, admin, deps, false);
|
||||
return deps.values().toArray(new Bundle[deps.size()]);
|
||||
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
|
||||
LinkedHashMap<String, Bundle> deps = new LinkedHashMap<String, Bundle>();
|
||||
collectFragmentsAndRequiredBundles(bundle, admin, deps, false);
|
||||
return deps.values().toArray(new Bundle[deps.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fragments and the required-bundles. Collects them transitively when the directive 'visibility:=reexport'
|
||||
* is added to a required-bundle.
|
||||
* Returns the fragments and the required-bundles. Collects them
|
||||
* transitively when the directive 'visibility:=reexport' is added to a
|
||||
* required-bundle.
|
||||
*
|
||||
* @param bundle
|
||||
* @param webFragOrAnnotationOrResources
|
||||
* @return
|
||||
*/
|
||||
protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String,Bundle> deps, boolean onlyReexport)
|
||||
protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
|
||||
{
|
||||
Bundle[] fragments = admin.getFragments(bundle);
|
||||
if (fragments != null)
|
||||
{
|
||||
//Also add the bundles required by the fragments.
|
||||
//this way we can inject onto an existing web-bundle a set of bundles that extend it
|
||||
for (Bundle f : fragments)
|
||||
{
|
||||
if (!deps.keySet().contains(f.getSymbolicName()))
|
||||
{
|
||||
deps.put(f.getSymbolicName(), f);
|
||||
collectRequiredBundles(f, admin, deps, onlyReexport);
|
||||
}
|
||||
}
|
||||
// Also add the bundles required by the fragments.
|
||||
// this way we can inject onto an existing web-bundle a set of
|
||||
// bundles that extend it
|
||||
for (Bundle f : fragments)
|
||||
{
|
||||
if (!deps.keySet().contains(f.getSymbolicName()))
|
||||
{
|
||||
deps.put(f.getSymbolicName(), f);
|
||||
collectRequiredBundles(f, admin, deps, onlyReexport);
|
||||
}
|
||||
}
|
||||
}
|
||||
collectRequiredBundles(bundle, admin, deps, onlyReexport);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simplistic but good enough parser for the Require-Bundle header.
|
||||
* Parses the version range attribute and the visibility directive.
|
||||
* A simplistic but good enough parser for the Require-Bundle header. Parses
|
||||
* the version range attribute and the visibility directive.
|
||||
*
|
||||
* @param onlyReexport true to collect resources and web-fragments transitively if and only if the directive visibility is reexport.
|
||||
* @param onlyReexport true to collect resources and web-fragments
|
||||
* transitively if and only if the directive visibility is
|
||||
* reexport.
|
||||
* @param bundle
|
||||
* @return The map of required bundles associated to the value of the jetty-web attribute.
|
||||
* @return The map of required bundles associated to the value of the
|
||||
* jetty-web attribute.
|
||||
*/
|
||||
protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String,Bundle> deps, boolean onlyReexport)
|
||||
protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map<String, Bundle> deps, boolean onlyReexport)
|
||||
{
|
||||
String requiredBundleHeader = (String)bundle.getHeaders().get("Require-Bundle");
|
||||
if (requiredBundleHeader == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String tok = tokenizer.nextToken().trim();
|
||||
StringTokenizer tokenizer2 = new StringTokenizer(tok, ";");
|
||||
String symbolicName = tokenizer2.nextToken().trim();
|
||||
if (deps.keySet().contains(symbolicName))
|
||||
{
|
||||
//was already added. 2 dependencies pointing at the same bundle.
|
||||
continue;
|
||||
}
|
||||
String versionRange = null;
|
||||
boolean reexport = false;
|
||||
while (tokenizer2.hasMoreTokens())
|
||||
{
|
||||
String next = tokenizer2.nextToken().trim();
|
||||
if (next.startsWith("bundle-version="))
|
||||
{
|
||||
if (next.startsWith("bundle-version=\"") || next.startsWith("bundle-version='"))
|
||||
{
|
||||
versionRange = next.substring("bundle-version=\"".length(), next.length()-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
versionRange = next.substring("bundle-version=".length());
|
||||
}
|
||||
}
|
||||
else if (next.equals("visibility:=reexport"))
|
||||
{
|
||||
reexport = true;
|
||||
}
|
||||
}
|
||||
if (!reexport && onlyReexport)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Bundle[] reqBundles = admin.getBundles(symbolicName, versionRange);
|
||||
if (reqBundles != null && reqBundles.length != 0)
|
||||
{
|
||||
Bundle reqBundle = null;
|
||||
for (Bundle b : reqBundles)
|
||||
{
|
||||
if (b.getState() == Bundle.ACTIVE || b.getState() == Bundle.STARTING)
|
||||
{
|
||||
reqBundle = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reqBundle == null)
|
||||
{
|
||||
//strange? in OSGi with Require-Bundle,
|
||||
//the dependent bundle is supposed to be active already
|
||||
reqBundle = reqBundles[0];
|
||||
}
|
||||
deps.put(reqBundle.getSymbolicName(),reqBundle);
|
||||
collectFragmentsAndRequiredBundles(reqBundle, admin, deps, true);
|
||||
}
|
||||
}
|
||||
String requiredBundleHeader = (String) bundle.getHeaders().get("Require-Bundle");
|
||||
if (requiredBundleHeader == null) { return; }
|
||||
StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String tok = tokenizer.nextToken().trim();
|
||||
StringTokenizer tokenizer2 = new StringTokenizer(tok, ";");
|
||||
String symbolicName = tokenizer2.nextToken().trim();
|
||||
if (deps.keySet().contains(symbolicName))
|
||||
{
|
||||
// was already added. 2 dependencies pointing at the same
|
||||
// bundle.
|
||||
continue;
|
||||
}
|
||||
String versionRange = null;
|
||||
boolean reexport = false;
|
||||
while (tokenizer2.hasMoreTokens())
|
||||
{
|
||||
String next = tokenizer2.nextToken().trim();
|
||||
if (next.startsWith("bundle-version="))
|
||||
{
|
||||
if (next.startsWith("bundle-version=\"") || next.startsWith("bundle-version='"))
|
||||
{
|
||||
versionRange = next.substring("bundle-version=\"".length(), next.length() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
versionRange = next.substring("bundle-version=".length());
|
||||
}
|
||||
}
|
||||
else if (next.equals("visibility:=reexport"))
|
||||
{
|
||||
reexport = true;
|
||||
}
|
||||
}
|
||||
if (!reexport && onlyReexport) { return; }
|
||||
Bundle[] reqBundles = admin.getBundles(symbolicName, versionRange);
|
||||
if (reqBundles != null && reqBundles.length != 0)
|
||||
{
|
||||
Bundle reqBundle = null;
|
||||
for (Bundle b : reqBundles)
|
||||
{
|
||||
if (b.getState() == Bundle.ACTIVE || b.getState() == Bundle.STARTING)
|
||||
{
|
||||
reqBundle = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reqBundle == null)
|
||||
{
|
||||
// strange? in OSGi with Require-Bundle,
|
||||
// the dependent bundle is supposed to be active already
|
||||
reqBundle = reqBundles[0];
|
||||
}
|
||||
deps.put(reqBundle.getSymbolicName(), reqBundle);
|
||||
collectFragmentsAndRequiredBundles(reqBundle, admin, deps, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void invokeFragmentActivators(ServiceReference sr)
|
||||
{
|
||||
PackageAdmin admin = (PackageAdmin)_context.getService(sr);
|
||||
PackageAdmin admin = (PackageAdmin) _context.getService(sr);
|
||||
Bundle[] fragments = admin.getFragments(_context.getBundle());
|
||||
if (fragments == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (fragments == null) { return; }
|
||||
for (Bundle frag : fragments)
|
||||
{
|
||||
// find a convention to look for a class inside the fragment.
|
||||
|
@ -261,7 +264,7 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
Class<?> c = Class.forName(fragmentActivator);
|
||||
if (c != null)
|
||||
{
|
||||
BundleActivator bActivator = (BundleActivator)c.newInstance();
|
||||
BundleActivator bActivator = (BundleActivator) c.newInstance();
|
||||
bActivator.start(_context);
|
||||
_activatedFragments.add(bActivator);
|
||||
}
|
||||
|
@ -304,61 +307,67 @@ public class PackageAdminServiceTracker implements ServiceListener
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the framework has completed all the start levels.
|
||||
*/
|
||||
public boolean frameworkHasCompletedAutostarts()
|
||||
{
|
||||
return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
|
||||
return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
|
||||
}
|
||||
|
||||
private static class ManifestTokenizer extends StringTokenizer {
|
||||
private static class ManifestTokenizer extends StringTokenizer
|
||||
{
|
||||
|
||||
public ManifestTokenizer(String header) {
|
||||
public ManifestTokenizer(String header)
|
||||
{
|
||||
super(header, ",");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nextToken() {
|
||||
public String nextToken()
|
||||
{
|
||||
String token = super.nextToken();
|
||||
|
||||
while (hasOpenQuote(token) && hasMoreTokens()) {
|
||||
while (hasOpenQuote(token) && hasMoreTokens())
|
||||
{
|
||||
token += "," + super.nextToken();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private boolean hasOpenQuote(String token) {
|
||||
private boolean hasOpenQuote(String token)
|
||||
{
|
||||
int i = -1;
|
||||
do {
|
||||
int quote = getQuote(token, i+1);
|
||||
if (quote < 0) {
|
||||
return false;
|
||||
}
|
||||
do
|
||||
{
|
||||
int quote = getQuote(token, i + 1);
|
||||
if (quote < 0) { return false; }
|
||||
|
||||
i = token.indexOf(quote, i+1);
|
||||
i = token.indexOf(quote, i+1);
|
||||
} while (i >= 0);
|
||||
i = token.indexOf(quote, i + 1);
|
||||
i = token.indexOf(quote, i + 1);
|
||||
}
|
||||
while (i >= 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getQuote(String token, int offset) {
|
||||
private int getQuote(String token, int offset)
|
||||
{
|
||||
int i = token.indexOf('"', offset);
|
||||
int j = token.indexOf('\'', offset);
|
||||
if (i < 0) {
|
||||
if (j < 0) {
|
||||
if (i < 0)
|
||||
{
|
||||
if (j < 0)
|
||||
{
|
||||
return -1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return '\'';
|
||||
}
|
||||
}
|
||||
if (j < 0) {
|
||||
return '"';
|
||||
}
|
||||
if (i < j) {
|
||||
return '"';
|
||||
}
|
||||
if (j < 0) { return '"'; }
|
||||
if (i < j) { return '"'; }
|
||||
return '\'';
|
||||
}
|
||||
|
||||
|
|
|
@ -22,236 +22,241 @@ import org.eclipse.jetty.util.component.LifeCycle;
|
|||
import org.osgi.framework.FrameworkUtil;
|
||||
|
||||
/**
|
||||
* Listens to the start and stop of the NestedConnector to register and unregister the NestedConnector
|
||||
* with the BridgeServlet.
|
||||
* Listens to the start and stop of the NestedConnector to register and
|
||||
* unregister the NestedConnector with the BridgeServlet.
|
||||
* <p>
|
||||
* All interactions with the BridgeServlet are done via introspection to avoid depending on it directly.
|
||||
* The BridgeServlet lives in the bootstrap-webapp; not inside equinox.
|
||||
* All interactions with the BridgeServlet are done via introspection to avoid
|
||||
* depending on it directly. The BridgeServlet lives in the bootstrap-webapp;
|
||||
* not inside equinox.
|
||||
* </p>
|
||||
*/
|
||||
public class NestedConnectorListener extends AbstractLifeCycleListener
|
||||
{
|
||||
|
||||
/** Name of the BridgeServlet class. By default org.eclipse.equinox.servletbridge.BridgeServlet */
|
||||
private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
|
||||
|
||||
/** Name of the static method on the BridgeServlet class to register the
|
||||
* servlet delegate. By default 'registerServletDelegate' */
|
||||
private String registerServletDelegateMethodName = "registerServletDelegate";
|
||||
|
||||
/** Name of the static method on the BridgeServlet class to register the
|
||||
* servlet delegate. By default 'unregisterServletDelegate' */
|
||||
private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
|
||||
|
||||
/** servlet that wraps this NestedConnector and uses the NestedConnector to service the requests. */
|
||||
private NestedConnectorServletDelegate _servletDelegate;
|
||||
|
||||
/**
|
||||
* The NestedConnector listened to.
|
||||
*/
|
||||
private NestedConnector nestedConnector;
|
||||
|
||||
/**
|
||||
* @param bridgeServletClassName Name of the class that is the BridgeServlet.
|
||||
* By default org.eclipse.equinox.servletbridge.BridgeServlet
|
||||
*/
|
||||
public void setBridgeServletClassName(String bridgeServletClassName)
|
||||
{
|
||||
this.bridgeServletClassName = bridgeServletClassName;
|
||||
}
|
||||
|
||||
public String getBridgeServletClassName()
|
||||
{
|
||||
return this.bridgeServletClassName;
|
||||
}
|
||||
public String getRegisterServletDelegateMethodName()
|
||||
{
|
||||
return this.registerServletDelegateMethodName;
|
||||
}
|
||||
public String getUnregisterServletDelegateMethodName()
|
||||
{
|
||||
return this.unregisterServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registerServletDelegateMethodName Name of the static method on the BridgeServlet class
|
||||
* to register the servlet delegate.
|
||||
*/
|
||||
public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
|
||||
{
|
||||
this.registerServletDelegateMethodName = registerServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param unregisterServletDelegateMethodName Name of the static method on the BridgeServlet class
|
||||
* to unregister the servlet delegate.
|
||||
*/
|
||||
public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
|
||||
{
|
||||
this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nestedConnector The NestedConnector that we are listening to here.
|
||||
*/
|
||||
public void setNestedConnector(NestedConnector nestedConnector)
|
||||
{
|
||||
this.nestedConnector = nestedConnector;
|
||||
}
|
||||
/**
|
||||
* @return The NestedConnector that we are listening to here.
|
||||
*/
|
||||
public NestedConnector getNestedConnector()
|
||||
{
|
||||
return this.nestedConnector;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Name of the BridgeServlet class. By default
|
||||
* org.eclipse.equinox.servletbridge.BridgeServlet
|
||||
*/
|
||||
private String bridgeServletClassName = "org.eclipse.equinox.servletbridge.BridgeServlet";
|
||||
|
||||
/**
|
||||
* Name of the static method on the BridgeServlet class to register the
|
||||
* servlet delegate. By default 'registerServletDelegate'
|
||||
*/
|
||||
private String registerServletDelegateMethodName = "registerServletDelegate";
|
||||
|
||||
/**
|
||||
* Name of the static method on the BridgeServlet class to register the
|
||||
* servlet delegate. By default 'unregisterServletDelegate'
|
||||
*/
|
||||
private String unregisterServletDelegateMethodName = "unregisterServletDelegate";
|
||||
|
||||
/**
|
||||
* servlet that wraps this NestedConnector and uses the NestedConnector to
|
||||
* service the requests.
|
||||
*/
|
||||
private NestedConnectorServletDelegate _servletDelegate;
|
||||
|
||||
/**
|
||||
* The NestedConnector listened to.
|
||||
*/
|
||||
private NestedConnector nestedConnector;
|
||||
|
||||
/**
|
||||
* @param bridgeServletClassName Name of the class that is the
|
||||
* BridgeServlet. By default
|
||||
* org.eclipse.equinox.servletbridge.BridgeServlet
|
||||
*/
|
||||
public void setBridgeServletClassName(String bridgeServletClassName)
|
||||
{
|
||||
this.bridgeServletClassName = bridgeServletClassName;
|
||||
}
|
||||
|
||||
public String getBridgeServletClassName()
|
||||
{
|
||||
return this.bridgeServletClassName;
|
||||
}
|
||||
|
||||
public String getRegisterServletDelegateMethodName()
|
||||
{
|
||||
return this.registerServletDelegateMethodName;
|
||||
}
|
||||
|
||||
public String getUnregisterServletDelegateMethodName()
|
||||
{
|
||||
return this.unregisterServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registerServletDelegateMethodName Name of the static method on the
|
||||
* BridgeServlet class to register the servlet delegate.
|
||||
*/
|
||||
public void setRegisterServletDelegateMethodName(String registerServletDelegateMethodName)
|
||||
{
|
||||
this.registerServletDelegateMethodName = registerServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param unregisterServletDelegateMethodName Name of the static method on
|
||||
* the BridgeServlet class to unregister the servlet delegate.
|
||||
*/
|
||||
public void setUnregisterServletDelegateMethodName(String unregisterServletDelegateMethodName)
|
||||
{
|
||||
this.unregisterServletDelegateMethodName = unregisterServletDelegateMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nestedConnector The NestedConnector that we are listening to here.
|
||||
*/
|
||||
public void setNestedConnector(NestedConnector nestedConnector)
|
||||
{
|
||||
this.nestedConnector = nestedConnector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The NestedConnector that we are listening to here.
|
||||
*/
|
||||
public NestedConnector getNestedConnector()
|
||||
{
|
||||
return this.nestedConnector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifeCycleStarted(LifeCycle event)
|
||||
{
|
||||
try
|
||||
{
|
||||
registerWithBridgeServlet();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof RuntimeException)
|
||||
{
|
||||
throw (RuntimeException)e;
|
||||
}
|
||||
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
registerWithBridgeServlet();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof RuntimeException) { throw (RuntimeException) e; }
|
||||
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void lifeCycleStopping(LifeCycle event)
|
||||
{
|
||||
try
|
||||
{
|
||||
unregisterWithBridgeServlet();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof RuntimeException)
|
||||
{
|
||||
throw (RuntimeException)e;
|
||||
}
|
||||
throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
unregisterWithBridgeServlet();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof RuntimeException) { throw (RuntimeException) e; }
|
||||
throw new RuntimeException("Unable to unregister the servlet delegate into the BridgeServlet.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into the BridgeServlet
|
||||
*/
|
||||
protected void registerWithBridgeServlet() throws Exception
|
||||
{
|
||||
_servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
|
||||
try
|
||||
{
|
||||
invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(),
|
||||
new Class[] {HttpServlet.class}, _servletDelegate);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
_servletDelegate.destroy();
|
||||
_servletDelegate = null;
|
||||
if (t instanceof Exception)
|
||||
{
|
||||
throw (Exception)t;
|
||||
}
|
||||
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhook into the BridgeServlet
|
||||
*/
|
||||
protected void unregisterWithBridgeServlet() throws Exception
|
||||
{
|
||||
if (_servletDelegate != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(),
|
||||
new Class[] {HttpServlet.class}, _servletDelegate);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (t instanceof Exception)
|
||||
{
|
||||
throw (Exception)t;
|
||||
}
|
||||
throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_servletDelegate.destroy();
|
||||
_servletDelegate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Hook into the BridgeServlet
|
||||
*/
|
||||
protected void registerWithBridgeServlet() throws Exception
|
||||
{
|
||||
_servletDelegate = new NestedConnectorServletDelegate(getNestedConnector());
|
||||
try
|
||||
{
|
||||
invokeStaticMethod(getBridgeServletClassName(), getRegisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
_servletDelegate.destroy();
|
||||
_servletDelegate = null;
|
||||
if (t instanceof Exception) { throw (Exception) t; }
|
||||
throw new RuntimeException("Unable to register the servlet delegate into the BridgeServlet.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clName
|
||||
* @param methName
|
||||
* @param argType
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object...args)
|
||||
throws Exception
|
||||
{
|
||||
Method m = getMethod(clName, methName, argType);
|
||||
m.invoke(null, args);
|
||||
}
|
||||
/**
|
||||
* Unhook into the BridgeServlet
|
||||
*/
|
||||
protected void unregisterWithBridgeServlet() throws Exception
|
||||
{
|
||||
if (_servletDelegate != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
invokeStaticMethod(getBridgeServletClassName(), getUnregisterServletDelegateMethodName(), new Class[] { HttpServlet.class }, _servletDelegate);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (t instanceof Exception) { throw (Exception) t; }
|
||||
throw new RuntimeException("Unable to unregister the servlet delegate from the BridgeServlet.", t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_servletDelegate.destroy();
|
||||
_servletDelegate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clName Class that belongs to the parent classloader of the OSGi framework.
|
||||
* @param methName Name of the method to find.
|
||||
* @param argType Argument types of the method to find.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static Method getMethod(String clName, String methName, Class... argType)
|
||||
throws Exception
|
||||
{
|
||||
Class bridgeServletClass = FrameworkUtil.class.getClassLoader()
|
||||
.loadClass(clName);
|
||||
return getMethod(bridgeServletClass, methName, argType);
|
||||
}
|
||||
private static Method getMethod(Class cl, String methName, Class... argType)
|
||||
throws Exception
|
||||
{
|
||||
Method meth = null;
|
||||
try
|
||||
{
|
||||
meth = cl.getMethod(methName, argType);
|
||||
return meth;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
for (Method m : cl.getMethods())
|
||||
{
|
||||
if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
|
||||
{
|
||||
int i = 0;
|
||||
for (Class p : m.getParameterTypes())
|
||||
{
|
||||
Class ap = argType[i];
|
||||
if (p.getName().equals(ap.getName())
|
||||
&& !p.equals(ap))
|
||||
{
|
||||
throw new IllegalStateException("The method \"" + m.toGenericString() +
|
||||
"\" was found. but the parameter class " + p.getName() + " is not the same " +
|
||||
" inside OSGi classloader (" + ap.getClassLoader() + ") and inside the " +
|
||||
cl.getName() + " classloader (" + p.getClassLoader() + ")." +
|
||||
" Are the ExtensionBundles correctly defined?");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param clName
|
||||
* @param methName
|
||||
* @param argType
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void invokeStaticMethod(String clName, String methName, Class[] argType, Object... args) throws Exception
|
||||
{
|
||||
Method m = getMethod(clName, methName, argType);
|
||||
m.invoke(null, args);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clName Class that belongs to the parent classloader of the OSGi
|
||||
* framework.
|
||||
* @param methName Name of the method to find.
|
||||
* @param argType Argument types of the method to find.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static Method getMethod(String clName, String methName, Class... argType) throws Exception
|
||||
{
|
||||
Class bridgeServletClass = FrameworkUtil.class.getClassLoader().loadClass(clName);
|
||||
return getMethod(bridgeServletClass, methName, argType);
|
||||
}
|
||||
|
||||
private static Method getMethod(Class cl, String methName, Class... argType) throws Exception
|
||||
{
|
||||
Method meth = null;
|
||||
try
|
||||
{
|
||||
meth = cl.getMethod(methName, argType);
|
||||
return meth;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
for (Method m : cl.getMethods())
|
||||
{
|
||||
if (m.getName().equals(methName) && m.getParameterTypes().length == argType.length)
|
||||
{
|
||||
int i = 0;
|
||||
for (Class p : m.getParameterTypes())
|
||||
{
|
||||
Class ap = argType[i];
|
||||
if (p.getName().equals(ap.getName()) && !p.equals(ap))
|
||||
{
|
||||
throw new IllegalStateException("The method \"" + m.toGenericString()
|
||||
+ "\" was found. but the parameter class "
|
||||
+ p.getName()
|
||||
+ " is not the same "
|
||||
+ " inside OSGi classloader ("
|
||||
+ ap.getClassLoader()
|
||||
+ ") and inside the "
|
||||
+ cl.getName()
|
||||
+ " classloader ("
|
||||
+ p.getClassLoader()
|
||||
+ ")."
|
||||
+ " Are the ExtensionBundles correctly defined?");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,20 +27,19 @@ import org.eclipse.jetty.nested.NestedConnector;
|
|||
*/
|
||||
public class NestedConnectorServletDelegate extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final NestedConnector _nestedConnector;
|
||||
|
||||
public NestedConnectorServletDelegate(NestedConnector nestedConnector)
|
||||
{
|
||||
_nestedConnector = nestedConnector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest req, ServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
_nestedConnector.service(req, res);
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final NestedConnector _nestedConnector;
|
||||
|
||||
public NestedConnectorServletDelegate(NestedConnector nestedConnector)
|
||||
{
|
||||
_nestedConnector = nestedConnector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
|
||||
{
|
||||
_nestedConnector.service(req, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.Set;
|
|||
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
|
@ -54,6 +55,7 @@ public class JettyPolicyRuntimeTest
|
|||
{
|
||||
System.setSecurityManager(null);
|
||||
Policy.setPolicy(null);
|
||||
IO.delete(new File ("/tmp", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -130,6 +132,7 @@ public class JettyPolicyRuntimeTest
|
|||
assertTrue ( test.canRead() );
|
||||
|
||||
File test2 = new File( "/tmp/foo" );
|
||||
test2.mkdirs();
|
||||
assertTrue ( test2.canRead() );
|
||||
|
||||
try
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -67,6 +68,7 @@ public class AsyncRequestReadTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception
|
||||
{
|
||||
final Socket socket = new Socket("localhost",connector.getLocalPort());
|
||||
|
@ -104,6 +106,7 @@ public class AsyncRequestReadTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void tests() throws Exception
|
||||
{
|
||||
runTest(64,4,4,20);
|
||||
|
|
|
@ -107,6 +107,36 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
|
||||
|
||||
/*
|
||||
* Feed a full header method
|
||||
*/
|
||||
@Test
|
||||
public void testFull() throws Exception
|
||||
{
|
||||
configureServer(new HelloWorldHandler());
|
||||
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
try
|
||||
{
|
||||
OutputStream os=client.getOutputStream();
|
||||
|
||||
byte[] buffer = new byte[64*1024];
|
||||
Arrays.fill(buffer,(byte)'A');
|
||||
|
||||
os.write(buffer);
|
||||
os.flush();
|
||||
|
||||
// Read the response.
|
||||
String response=readResponse(client);
|
||||
|
||||
Assert.assertTrue(response.contains("HTTP/1.1 413 FULL head"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package org.eclipse.jetty.server.ssl;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
@ -82,4 +83,17 @@ public class SslSocketServerTest extends HttpServerTestBase
|
|||
public void testAvailable() throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFull() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
super.testFull();
|
||||
}
|
||||
catch(SocketException e)
|
||||
{
|
||||
// For SSL Sockets, the response is closed before the 400 is sent???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -734,9 +734,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
byte[] data=null;
|
||||
String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH);
|
||||
|
||||
// handle ResourceCollection
|
||||
if (_resourceBase instanceof ResourceCollection)
|
||||
resource=_resourceBase.addPath(pathInContext);
|
||||
//If the DefaultServlet has a resource base set, use it
|
||||
if (_resourceBase != null)
|
||||
{
|
||||
// handle ResourceCollection
|
||||
if (_resourceBase instanceof ResourceCollection)
|
||||
resource=_resourceBase.addPath(pathInContext);
|
||||
}
|
||||
//Otherwise, try using the resource base of its enclosing context handler
|
||||
else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
|
||||
resource=_contextHandler.getBaseResource().addPath(pathInContext);
|
||||
|
||||
|
|
|
@ -67,17 +67,17 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* <PRE>
|
||||
* bufferSize The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
|
||||
* {@link IllegalArgumentException}.
|
||||
* @see java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)
|
||||
* @see java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)
|
||||
* See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
|
||||
* and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
|
||||
*
|
||||
* minGzipSize Content will only be compressed if content length is either unknown or greater
|
||||
* than <code>minGzipSize</code>.
|
||||
*
|
||||
* deflateCompressionLevel The compression level used for deflate compression. (0-9).
|
||||
* @see java.util.zip.Deflater#Deflater(int, boolean)
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
*
|
||||
* deflateNoWrap The noWrap setting for deflate compression. Defaults to true. (true/false)
|
||||
* @see java.util.zip.Deflater#Deflater(int, boolean)
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
*
|
||||
* mimeTypes Comma separated list of mime types to compress. See description above.
|
||||
*
|
||||
|
|
|
@ -173,6 +173,16 @@ public class ProxyServlet implements Servlet
|
|||
return Log.getLogger("org.eclipse.jetty.servlets." + config.getServletName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return an HttpClientInstance
|
||||
*
|
||||
* @return HttpClient
|
||||
*/
|
||||
protected HttpClient createHttpClientInstance()
|
||||
{
|
||||
return new HttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return an HttpClient based on ServletConfig
|
||||
*
|
||||
|
@ -185,7 +195,7 @@ public class ProxyServlet implements Servlet
|
|||
*/
|
||||
protected HttpClient createHttpClient(ServletConfig config) throws Exception
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
HttpClient client = createHttpClientInstance();
|
||||
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
|
||||
|
||||
String t = config.getInitParameter("maxThreads");
|
||||
|
|
|
@ -45,11 +45,8 @@ public class GzipFilterDefaultTest
|
|||
this.compressionType = compressionType;
|
||||
}
|
||||
|
||||
|
||||
public static class HttpStatusServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int _status = 204;
|
||||
|
||||
public HttpStatusServlet()
|
||||
|
@ -57,11 +54,6 @@ public class GzipFilterDefaultTest
|
|||
super();
|
||||
}
|
||||
|
||||
public void setStatus (int status)
|
||||
{
|
||||
_status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
|
@ -69,6 +61,24 @@ public class GzipFilterDefaultTest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HttpErrorServlet extends HttpServlet
|
||||
{
|
||||
private int _status = 400;
|
||||
|
||||
public HttpErrorServlet()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.getOutputStream().write("error message".getBytes());
|
||||
resp.setStatus(_status);
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestingDir testingdir = new TestingDir();
|
||||
|
||||
|
@ -153,7 +163,7 @@ public class GzipFilterDefaultTest
|
|||
try
|
||||
{
|
||||
tester.start();
|
||||
tester.assertIsResponseNotGzipCompressed(null, -1, 204);
|
||||
tester.assertIsResponseNotGzipCompressed(-1, 204);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -161,6 +171,27 @@ public class GzipFilterDefaultTest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNotGzipCompressedHttpBadRequestStatus() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
|
||||
// Test error code 400
|
||||
FilterHolder holder = tester.setContentServlet(HttpErrorServlet.class);
|
||||
holder.setInitParameter("mimeTypes","text/plain");
|
||||
|
||||
try
|
||||
{
|
||||
tester.start();
|
||||
tester.assertIsResponseNotGzipCompressedAndEqualToExpectedString("error message", -1, 400);
|
||||
}
|
||||
finally
|
||||
{
|
||||
tester.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserAgentExclusion() throws Exception
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.io.File;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.EnumSet;
|
||||
|
@ -83,7 +84,7 @@ public class GzipTester
|
|||
|
||||
// Assert the response headers
|
||||
Assert.assertThat("Response.method",response.getMethod(),nullValue());
|
||||
Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
|
||||
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
|
||||
Assert.assertThat("Response.header[Content-Length]",response.getHeader("Content-Length"),notNullValue());
|
||||
Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),containsString(compressionType));
|
||||
|
||||
|
@ -224,28 +225,60 @@ public class GzipTester
|
|||
*/
|
||||
public void assertIsResponseNotGzipCompressed(String filename, int expectedFilesize, int status) throws Exception
|
||||
{
|
||||
System.err.printf("[GzipTester] requesting /context/%s%n",filename);
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
String uri = "/context/"+filename;
|
||||
HttpTester response = executeRequest(uri);
|
||||
assertResponseHeaders(expectedFilesize,status,response);
|
||||
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setHeader("Accept-Encoding",compressionType);
|
||||
if (this.userAgent != null)
|
||||
request.setHeader("User-Agent", this.userAgent);
|
||||
if (filename == null)
|
||||
request.setURI("/context/");
|
||||
else
|
||||
request.setURI("/context/"+filename);
|
||||
// Assert that the contents are what we expect.
|
||||
if (filename != null)
|
||||
{
|
||||
File serverFile = testdir.getFile(filename);
|
||||
String expectedResponse = IO.readToString(serverFile);
|
||||
|
||||
String actual = readResponse(response);
|
||||
Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
|
||||
}
|
||||
}
|
||||
|
||||
// Issue the request
|
||||
ByteArrayBuffer reqsBuff = new ByteArrayBuffer(request.generate().getBytes());
|
||||
// Collect the response(s)
|
||||
ByteArrayBuffer respBuff = servletTester.getResponses(reqsBuff);
|
||||
response.parse(respBuff.asArray());
|
||||
/**
|
||||
* Asserts that the request results in a properly structured GzipFilter response, where the content is
|
||||
* not compressed, and the content-length is returned appropriately.
|
||||
*
|
||||
* @param expectedResponse
|
||||
* the expected response body string
|
||||
* @param expectedFilesize
|
||||
* the expected filesize to be specified on the Content-Length portion of the response headers. (note:
|
||||
* passing -1 will disable the Content-Length assertion)
|
||||
* @throws Exception
|
||||
*/
|
||||
public void assertIsResponseNotGzipCompressedAndEqualToExpectedString(String expectedResponse, int expectedFilesize, int status) throws Exception
|
||||
{
|
||||
String uri = "/context/";
|
||||
HttpTester response = executeRequest(uri);
|
||||
assertResponseHeaders(expectedFilesize,status,response);
|
||||
|
||||
// Assert the response headers
|
||||
String actual = readResponse(response);
|
||||
Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the request results in a properly structured GzipFilter response, where the content is
|
||||
* not compressed, and the content-length is returned appropriately.
|
||||
*
|
||||
* @param expectedFilesize
|
||||
* the expected filesize to be specified on the Content-Length portion of the response headers. (note:
|
||||
* passing -1 will disable the Content-Length assertion)
|
||||
* @throws Exception
|
||||
*/
|
||||
public void assertIsResponseNotGzipCompressed(int expectedFilesize, int status) throws Exception
|
||||
{
|
||||
String uri = "/context/";
|
||||
HttpTester response = executeRequest(uri);
|
||||
assertResponseHeaders(expectedFilesize,status,response);
|
||||
}
|
||||
|
||||
private void assertResponseHeaders(int expectedFilesize, int status, HttpTester response)
|
||||
{
|
||||
Assert.assertThat("Response.method",response.getMethod(),nullValue());
|
||||
Assert.assertThat("Response.status",response.getStatus(),is(status));
|
||||
if (expectedFilesize != (-1))
|
||||
|
@ -255,34 +288,51 @@ public class GzipTester
|
|||
Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize));
|
||||
}
|
||||
Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),not(containsString(compressionType)));
|
||||
|
||||
// Assert that the contents are what we expect.
|
||||
if (filename != null)
|
||||
{
|
||||
File serverFile = testdir.getFile(filename);
|
||||
String expected = IO.readToString(serverFile);
|
||||
String actual = null;
|
||||
|
||||
InputStream in = null;
|
||||
ByteArrayOutputStream out = null;
|
||||
try
|
||||
{
|
||||
in = new ByteArrayInputStream(response.getContentBytes());
|
||||
out = new ByteArrayOutputStream();
|
||||
IO.copy(in,out);
|
||||
|
||||
actual = out.toString(encoding);
|
||||
Assert.assertEquals("Server contents",expected,actual);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(out);
|
||||
IO.close(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpTester executeRequest(String uri) throws IOException, Exception
|
||||
{
|
||||
System.err.printf("[GzipTester] requesting %s%n",uri);
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setHeader("Accept-Encoding",compressionType);
|
||||
if (this.userAgent != null)
|
||||
request.setHeader("User-Agent", this.userAgent);
|
||||
|
||||
request.setURI(uri);
|
||||
|
||||
// Issue the request
|
||||
ByteArrayBuffer reqsBuff = new ByteArrayBuffer(request.generate().getBytes());
|
||||
// Collect the response(s)
|
||||
ByteArrayBuffer respBuff = servletTester.getResponses(reqsBuff);
|
||||
response.parse(respBuff.asArray());
|
||||
return response;
|
||||
}
|
||||
|
||||
private String readResponse(HttpTester response) throws IOException, UnsupportedEncodingException
|
||||
{
|
||||
String actual = null;
|
||||
InputStream in = null;
|
||||
ByteArrayOutputStream out = null;
|
||||
try
|
||||
{
|
||||
in = new ByteArrayInputStream(response.getContentBytes());
|
||||
out = new ByteArrayOutputStream();
|
||||
IO.copy(in,out);
|
||||
|
||||
actual = out.toString(encoding);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(out);
|
||||
IO.close(in);
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -147,17 +147,16 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
// frame with a compression history will come before the first compressed frame.
|
||||
int associatedStreamId = 0;
|
||||
if (synInfo instanceof PushSynInfo)
|
||||
{
|
||||
associatedStreamId = ((PushSynInfo)synInfo).getAssociatedStreamId();
|
||||
}
|
||||
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
int streamId = streamIds.getAndAdd(2);
|
||||
SynStreamFrame synStream = new SynStreamFrame(version,synInfo.getFlags(),streamId,associatedStreamId,synInfo.getPriority(),synInfo.getHeaders());
|
||||
IStream stream = createStream(synStream,listener);
|
||||
control(stream,synStream,timeout,unit,handler,stream);
|
||||
SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), synInfo.getHeaders());
|
||||
IStream stream = createStream(synStream, listener, true);
|
||||
generateAndEnqueueControlFrame(stream, synStream, timeout, unit, handler, stream);
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -209,7 +208,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
public Future<PingInfo> ping()
|
||||
{
|
||||
Promise<PingInfo> result = new Promise<>();
|
||||
ping(0,TimeUnit.MILLISECONDS,result);
|
||||
ping(0, TimeUnit.MILLISECONDS, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -231,7 +230,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
private Future<Void> goAway(SessionStatus sessionStatus)
|
||||
{
|
||||
Promise<Void> result = new Promise<>();
|
||||
goAway(sessionStatus,0,TimeUnit.MILLISECONDS,result);
|
||||
goAway(sessionStatus, 0, TimeUnit.MILLISECONDS, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -252,7 +251,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
return;
|
||||
}
|
||||
}
|
||||
complete(handler,null);
|
||||
complete(handler, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -339,7 +338,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
@Override
|
||||
public void onDataFrame(DataFrame frame, ByteBuffer data)
|
||||
{
|
||||
notifyIdle(idleListener,false);
|
||||
notifyIdle(idleListener, false);
|
||||
try
|
||||
{
|
||||
logger.debug("Processing {}, {} data bytes",frame,data.remaining());
|
||||
|
@ -386,7 +385,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
@Override
|
||||
public void onStreamException(StreamException x)
|
||||
{
|
||||
notifyOnException(listener,x);
|
||||
notifyOnException(listener, x);
|
||||
rst(new RstInfo(x.getStreamId(),x.getStreamStatus()));
|
||||
}
|
||||
|
||||
|
@ -400,21 +399,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
|
||||
private void onSyn(SynStreamFrame frame)
|
||||
{
|
||||
IStream stream = newStream(frame,null);
|
||||
stream.updateCloseState(frame.isClose(),false);
|
||||
logger.debug("Opening {}",stream);
|
||||
int streamId = stream.getId();
|
||||
IStream existing = streams.putIfAbsent(streamId,stream);
|
||||
if (existing != null)
|
||||
{
|
||||
RstInfo rstInfo = new RstInfo(streamId,StreamStatus.PROTOCOL_ERROR);
|
||||
logger.debug("Duplicate stream, {}",rstInfo);
|
||||
rst(rstInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
processSyn(listener,stream,frame);
|
||||
}
|
||||
IStream stream = createStream(frame, null, false);
|
||||
if (stream != null)
|
||||
processSyn(listener, stream, frame);
|
||||
}
|
||||
|
||||
private void processSyn(SessionFrameListener listener, IStream stream, SynStreamFrame frame)
|
||||
|
@ -429,38 +416,43 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
removeStream(stream);
|
||||
}
|
||||
|
||||
private IStream createStream(SynStreamFrame synStream, StreamFrameListener listener)
|
||||
private IStream createStream(SynStreamFrame frame, StreamFrameListener listener, boolean local)
|
||||
{
|
||||
IStream parentStream = streams.get(synStream.getAssociatedStreamId());
|
||||
|
||||
IStream stream = newStream(synStream,parentStream);
|
||||
stream.updateCloseState(synStream.isClose(),true);
|
||||
IStream stream = newStream(frame);
|
||||
stream.updateCloseState(frame.isClose(), local);
|
||||
stream.setStreamFrameListener(listener);
|
||||
|
||||
if (synStream.isUnidirectional())
|
||||
if (stream.isUnidirectional())
|
||||
{
|
||||
// unidirectional streams are implicitly half closed for the client
|
||||
stream.updateCloseState(true,false);
|
||||
// Unidirectional streams are implicitly half closed
|
||||
stream.updateCloseState(true, !local);
|
||||
if (!stream.isClosed())
|
||||
parentStream.associate(stream);
|
||||
stream.getAssociatedStream().associate(stream);
|
||||
}
|
||||
|
||||
if (streams.putIfAbsent(synStream.getStreamId(),stream) != null)
|
||||
int streamId = stream.getId();
|
||||
if (streams.putIfAbsent(streamId, stream) != null)
|
||||
{
|
||||
// If this happens we have a bug since we did not check that the peer's streamId was valid
|
||||
// (if we're on server, then the client sent an odd streamId and we did not check that)
|
||||
throw new IllegalStateException("StreamId: " + synStream.getStreamId() + " invalid.");
|
||||
if (local)
|
||||
throw new IllegalStateException("Duplicate stream id " + streamId);
|
||||
RstInfo rstInfo = new RstInfo(streamId, StreamStatus.PROTOCOL_ERROR);
|
||||
logger.debug("Duplicate stream, {}", rstInfo);
|
||||
rst(rstInfo);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("Created {}", stream);
|
||||
if (local)
|
||||
notifyStreamCreated(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
logger.debug("Created {}",stream);
|
||||
notifyStreamCreated(stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
private IStream newStream(SynStreamFrame frame, IStream parentStream)
|
||||
private IStream newStream(SynStreamFrame frame)
|
||||
{
|
||||
return new StandardStream(frame,this,windowSize,parentStream);
|
||||
IStream associatedStream = streams.get(frame.getAssociatedStreamId());
|
||||
return new StandardStream(frame, this, windowSize, associatedStream);
|
||||
}
|
||||
|
||||
private void notifyStreamCreated(IStream stream)
|
||||
|
@ -484,15 +476,13 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
private void removeStream(IStream stream)
|
||||
{
|
||||
if (stream.isUnidirectional())
|
||||
{
|
||||
stream.getAssociatedStream().disassociate(stream);
|
||||
}
|
||||
|
||||
IStream removed = streams.remove(stream.getId());
|
||||
if (removed != null)
|
||||
assert removed == stream;
|
||||
|
||||
logger.debug("Removed {}",stream);
|
||||
logger.debug("Removed {}", stream);
|
||||
notifyStreamClosed(stream);
|
||||
}
|
||||
|
||||
|
@ -545,7 +535,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
stream.process(frame);
|
||||
|
||||
RstInfo rstInfo = new RstInfo(frame.getStreamId(),StreamStatus.from(frame.getVersion(),frame.getStatusCode()));
|
||||
notifyOnRst(listener,rstInfo);
|
||||
notifyOnRst(listener, rstInfo);
|
||||
flush();
|
||||
|
||||
if (stream != null)
|
||||
|
@ -575,18 +565,18 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
if (pingId % 2 == pingIds.get() % 2)
|
||||
{
|
||||
PingInfo pingInfo = new PingInfo(frame.getPingId());
|
||||
notifyOnPing(listener,pingInfo);
|
||||
notifyOnPing(listener, pingInfo);
|
||||
flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
control(null,frame,0,TimeUnit.MILLISECONDS,null,null);
|
||||
control(null, frame, 0, TimeUnit.MILLISECONDS, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
if (goAwayReceived.compareAndSet(false,true))
|
||||
if (goAwayReceived.compareAndSet(false, true))
|
||||
{
|
||||
GoAwayInfo goAwayInfo = new GoAwayInfo(frame.getLastStreamId(),SessionStatus.from(frame.getStatusCode()));
|
||||
notifyOnGoAway(listener,goAwayInfo);
|
||||
|
@ -692,7 +682,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
if (listener != null)
|
||||
{
|
||||
logger.debug("Invoking callback with {} on listener {}",settingsInfo,listener);
|
||||
listener.onSettings(this,settingsInfo);
|
||||
listener.onSettings(this, settingsInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -708,7 +698,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
if (listener != null)
|
||||
{
|
||||
logger.debug("Invoking callback with {} on listener {}",pingInfo,listener);
|
||||
listener.onPing(this,pingInfo);
|
||||
listener.onPing(this, pingInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -724,7 +714,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
if (listener != null)
|
||||
{
|
||||
logger.debug("Invoking callback with {} on listener {}",goAwayInfo,listener);
|
||||
listener.onGoAway(this,goAwayInfo);
|
||||
listener.onGoAway(this, goAwayInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -735,6 +725,12 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
|
||||
@Override
|
||||
public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context)
|
||||
{
|
||||
generateAndEnqueueControlFrame(stream,frame,timeout,unit,handler,context);
|
||||
flush();
|
||||
}
|
||||
|
||||
private <C> void generateAndEnqueueControlFrame(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -762,12 +758,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
else
|
||||
append(frameBytes);
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
notifyHandlerFailed(handler,x);
|
||||
notifyHandlerFailed(handler, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -363,6 +363,8 @@ public class StandardStream implements IStream
|
|||
@Override
|
||||
public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler)
|
||||
{
|
||||
if (isUnidirectional())
|
||||
throw new IllegalStateException("Protocol violation: cannot send SYN_REPLY frames in unidirectional streams");
|
||||
openState = OpenState.REPLY_SENT;
|
||||
updateCloseState(replyInfo.isClose(),true);
|
||||
SynReplyFrame frame = new SynReplyFrame(session.getVersion(),replyInfo.getFlags(),getId(),replyInfo.getHeaders());
|
||||
|
@ -426,13 +428,9 @@ public class StandardStream implements IStream
|
|||
@Override
|
||||
public boolean isUnidirectional()
|
||||
{
|
||||
if (associatedStream != null)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return associatedStream != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isReset()
|
||||
{
|
||||
|
|
|
@ -114,7 +114,8 @@ public class Headers implements Iterable<Headers.Header>
|
|||
*/
|
||||
public void put(Header header)
|
||||
{
|
||||
headers.put(header.name().toLowerCase(), header);
|
||||
if (header != null)
|
||||
headers.put(header.name().toLowerCase(), header);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,7 +80,7 @@ public interface Stream
|
|||
* @return the priority of this stream
|
||||
*/
|
||||
public byte getPriority();
|
||||
|
||||
|
||||
/**
|
||||
* @return the session this stream is associated to
|
||||
*/
|
||||
|
@ -89,18 +89,18 @@ public interface Stream
|
|||
/**
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p>
|
||||
* <p>Callers may use the returned future to get the pushstream once it got created</p>
|
||||
*
|
||||
*
|
||||
* @param synInfo the metadata to send on stream creation
|
||||
* @return a future containing the stream once it got established
|
||||
* @see #syn(SynInfo, long, TimeUnit, Handler)
|
||||
*/
|
||||
public Future<Stream> syn(SynInfo synInfo);
|
||||
|
||||
|
||||
/**
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p>
|
||||
* <p>Callers may pass a non-null completion handler to be notified of when the
|
||||
* pushstream has been established.</p>
|
||||
*
|
||||
*
|
||||
* @param synInfo the metadata to send on stream creation
|
||||
* @param timeout the operation's timeout
|
||||
* @param unit the timeout's unit
|
||||
|
@ -108,7 +108,7 @@ public interface Stream
|
|||
* @see #syn(SynInfo)
|
||||
*/
|
||||
public void syn(SynInfo synInfo, long timeout, TimeUnit unit, Handler<Stream> handler);
|
||||
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p>
|
||||
* <p>Callers may use the returned future to wait for the reply to be actually sent.</p>
|
||||
|
@ -189,12 +189,12 @@ public interface Stream
|
|||
* @return whether this stream is unidirectional or not
|
||||
*/
|
||||
public boolean isUnidirectional();
|
||||
|
||||
|
||||
/**
|
||||
* @return whether this stream has been reset
|
||||
*/
|
||||
public boolean isReset();
|
||||
|
||||
|
||||
/**
|
||||
* @return whether this stream has been closed by both parties
|
||||
* @see #isHalfClosed()
|
||||
|
@ -203,8 +203,7 @@ public interface Stream
|
|||
|
||||
/**
|
||||
* @return whether this stream has been closed by one party only
|
||||
* @see #isClosed() * @param timeout the timeout for the stream creation
|
||||
* @param unit the timeout's unit
|
||||
* @see #isClosed()
|
||||
*/
|
||||
public boolean isHalfClosed();
|
||||
|
||||
|
@ -229,15 +228,15 @@ public interface Stream
|
|||
* @see #setAttribute(String, Object)
|
||||
*/
|
||||
public Object removeAttribute(String key);
|
||||
|
||||
|
||||
/**
|
||||
* @return the associated parent stream or null if this is not an associated stream
|
||||
*/
|
||||
public Stream getAssociatedStream();
|
||||
|
||||
|
||||
/**
|
||||
* @return associated child streams or an empty set if no associated streams exist
|
||||
*/
|
||||
public Set<Stream> getPushedStreams();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -131,8 +131,7 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
|
|||
if (headersBlockParser.parse(streamId, version, length, buffer))
|
||||
{
|
||||
byte flags = controlFrameParser.getFlags();
|
||||
// TODO: can it be both FIN and UNIDIRECTIONAL ?
|
||||
if (flags != 0 && flags != SynInfo.FLAG_CLOSE && flags != PushSynInfo.FLAG_UNIDIRECTIONAL)
|
||||
if (flags > (SynInfo.FLAG_CLOSE | PushSynInfo.FLAG_UNIDIRECTIONAL))
|
||||
throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.SYN_STREAM);
|
||||
|
||||
SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId, priority, new Headers(headers, true));
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
public class HTTPSPDYServerConnector extends SPDYServerConnector
|
||||
{
|
||||
private final AsyncConnectionFactory defaultConnectionFactory;
|
||||
private final PushStrategy pushStrategy = new PushStrategy.None();
|
||||
|
||||
public HTTPSPDYServerConnector()
|
||||
{
|
||||
|
@ -47,7 +48,7 @@ public class HTTPSPDYServerConnector extends SPDYServerConnector
|
|||
{
|
||||
super.doStart();
|
||||
// Override the "spdy/2" protocol by handling HTTP over SPDY
|
||||
putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this));
|
||||
putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, pushStrategy));
|
||||
// Add the "http/1.1" protocol for browsers that do not support NPN
|
||||
putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(this));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface PushStrategy
|
||||
{
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders);
|
||||
|
||||
public static class None implements PushStrategy
|
||||
{
|
||||
@Override
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* <p>A SPDY push strategy that auto-populates push metadata based on referrer URLs.</p>
|
||||
* <p>A typical request for a main resource such as <tt>index.html</tt> is immediately
|
||||
* followed by a number of requests for associated resources. Associated resource requests
|
||||
* will have a <tt>Referer</tt> HTTP header that points to <tt>index.html</tt>, which we
|
||||
* use to link the associated resource to the main resource.</p>
|
||||
* <p>However, also following a hyperlink generates a HTTP request with a <tt>Referer</tt>
|
||||
* HTTP header that points to <tt>index.html</tt>; therefore main resources and associated
|
||||
* resources must be distinguishable.</p>
|
||||
* <p>This class distinguishes associated resources by their URL path suffix.
|
||||
* CSS stylesheets, images and JavaScript files have recognizable URL path suffixes that
|
||||
* are classified as associated resources.</p>
|
||||
* <p>Note however, that CSS stylesheets may refer to images, and the CSS image request
|
||||
* will have the CSS stylesheet as referrer, so there is some degree of recursion that
|
||||
* needs to be handled.</p>
|
||||
*
|
||||
* TODO: this class is kind-of leaking since the resources map is always adding entries
|
||||
* TODO: although these entries will be limited by the number of application pages.
|
||||
* TODO: however, there is no ConcurrentLinkedHashMap yet in JDK (there is in Guava though)
|
||||
* TODO: so we cannot use the built-in LRU features of LinkedHashMap
|
||||
*
|
||||
* TODO: Wikipedia maps URLs like http://en.wikipedia.org/wiki/File:PNG-Gradient_hex.png
|
||||
* TODO: to text/html, so perhaps we need to improve isPushResource() by looking at the
|
||||
* TODO: response Content-Type header, and not only at the URL extension
|
||||
*/
|
||||
public class ReferrerPushStrategy implements PushStrategy
|
||||
{
|
||||
private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
|
||||
private final ConcurrentMap<String, Set<String>> resources = new ConcurrentHashMap<>();
|
||||
private final Set<Pattern> pushRegexps = new LinkedHashSet<>();
|
||||
private final Set<Pattern> allowedPushOrigins = new LinkedHashSet<>();
|
||||
|
||||
public ReferrerPushStrategy()
|
||||
{
|
||||
this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps)
|
||||
{
|
||||
this(pushRegexps, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps, List<String> allowedPushOrigins)
|
||||
{
|
||||
for (String pushRegexp : pushRegexps)
|
||||
this.pushRegexps.add(Pattern.compile(pushRegexp));
|
||||
for (String allowedPushOrigin : allowedPushOrigins)
|
||||
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
|
||||
{
|
||||
Set<String> result = Collections.emptySet();
|
||||
String scheme = requestHeaders.get("scheme").value();
|
||||
String host = requestHeaders.get("host").value();
|
||||
String origin = new StringBuilder(scheme).append("://").append(host).toString();
|
||||
String url = requestHeaders.get("url").value();
|
||||
String absoluteURL = new StringBuilder(origin).append(url).toString();
|
||||
logger.debug("Applying push strategy for {}", absoluteURL);
|
||||
if (isValidMethod(requestHeaders.get("method").value()))
|
||||
{
|
||||
if (isMainResource(url, responseHeaders))
|
||||
{
|
||||
result = pushResources(absoluteURL);
|
||||
}
|
||||
else if (isPushResource(url, responseHeaders))
|
||||
{
|
||||
Headers.Header referrerHeader = requestHeaders.get("referer");
|
||||
if (referrerHeader != null)
|
||||
{
|
||||
String referrer = referrerHeader.value();
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null || !pushResources.contains(url))
|
||||
buildMetadata(origin, url, referrer);
|
||||
else
|
||||
result = pushResources(absoluteURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("Push resources for {}: {}", absoluteURL, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isValidMethod(String method)
|
||||
{
|
||||
return "GET".equalsIgnoreCase(method);
|
||||
}
|
||||
|
||||
private boolean isMainResource(String url, Headers responseHeaders)
|
||||
{
|
||||
return !isPushResource(url, responseHeaders);
|
||||
}
|
||||
|
||||
private boolean isPushResource(String url, Headers responseHeaders)
|
||||
{
|
||||
for (Pattern pushRegexp : pushRegexps)
|
||||
{
|
||||
if (pushRegexp.matcher(url).matches())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> pushResources(String absoluteURL)
|
||||
{
|
||||
Set<String> pushResources = resources.get(absoluteURL);
|
||||
if (pushResources == null)
|
||||
return Collections.emptySet();
|
||||
return Collections.unmodifiableSet(pushResources);
|
||||
}
|
||||
|
||||
private void buildMetadata(String origin, String url, String referrer)
|
||||
{
|
||||
if (referrer.startsWith(origin) || isPushOriginAllowed(origin))
|
||||
{
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null)
|
||||
{
|
||||
pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
Set<String> existing = resources.putIfAbsent(referrer, pushResources);
|
||||
if (existing != null)
|
||||
pushResources = existing;
|
||||
}
|
||||
pushResources.add(url);
|
||||
logger.debug("Built push metadata for {}: {}", referrer, pushResources);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPushOriginAllowed(String origin)
|
||||
{
|
||||
for (Pattern allowedPushOrigin : allowedPushOrigins)
|
||||
{
|
||||
if (allowedPushOrigin.matcher(origin).matches())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import java.io.InterruptedIOException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -47,9 +48,13 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.spdy.SPDYAsyncConnection;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.RstInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -62,6 +67,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
private final Queue<Runnable> tasks = new LinkedList<>();
|
||||
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
|
||||
private final SPDYAsyncConnection connection;
|
||||
private final PushStrategy pushStrategy;
|
||||
private final Stream stream;
|
||||
private Headers headers; // No need for volatile, guarded by state
|
||||
private DataInfo dataInfo; // No need for volatile, guarded by state
|
||||
|
@ -69,10 +75,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
private volatile State state = State.INITIAL;
|
||||
private boolean dispatched; // Guarded by synchronization on tasks
|
||||
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, Stream stream)
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server);
|
||||
this.connection = connection;
|
||||
this.pushStrategy = pushStrategy;
|
||||
this.stream = stream;
|
||||
getParser().setPersistent(true);
|
||||
}
|
||||
|
@ -117,7 +124,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
dispatched = true;
|
||||
logger.debug("Dispatching task {}", task);
|
||||
getServer().getThreadPool().dispatch(new Runnable()
|
||||
execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
|
@ -133,6 +140,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
}
|
||||
}
|
||||
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
getServer().getThreadPool().dispatch(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection handle()
|
||||
{
|
||||
|
@ -157,7 +169,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
String m = method.value();
|
||||
String u = uri.value();
|
||||
String v = version.value();
|
||||
logger.debug("HTTP > {} {} {}", new Object[]{m, u, v});
|
||||
logger.debug("HTTP > {} {} {}", m, u, v);
|
||||
startRequest(new ByteArrayBuffer(m), new ByteArrayBuffer(u), new ByteArrayBuffer(v));
|
||||
|
||||
updateState(State.HEADERS);
|
||||
|
@ -245,10 +257,17 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
|
||||
private void respond(Stream stream, int status)
|
||||
{
|
||||
Headers headers = new Headers();
|
||||
headers.put("status", String.valueOf(status));
|
||||
headers.put("version", "HTTP/1.1");
|
||||
stream.reply(new ReplyInfo(headers, true));
|
||||
if (stream.isUnidirectional())
|
||||
{
|
||||
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.INTERNAL_ERROR));
|
||||
}
|
||||
else
|
||||
{
|
||||
Headers headers = new Headers();
|
||||
headers.put("status", String.valueOf(status));
|
||||
headers.put("version", "HTTP/1.1");
|
||||
stream.reply(new ReplyInfo(headers, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void close(Stream stream)
|
||||
|
@ -267,7 +286,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
state = newState;
|
||||
}
|
||||
|
||||
public void beginRequest(final Headers headers)
|
||||
public void beginRequest(final Headers headers, final boolean endRequest)
|
||||
{
|
||||
this.headers = headers.isEmpty() ? null : headers;
|
||||
post(new Runnable()
|
||||
|
@ -278,6 +297,8 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
if (!headers.isEmpty())
|
||||
updateState(State.REQUEST);
|
||||
handle();
|
||||
if (endRequest)
|
||||
performEndRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -337,17 +358,22 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
public void run()
|
||||
{
|
||||
if (state == State.HEADERS)
|
||||
{
|
||||
updateState(State.HEADERS_COMPLETE);
|
||||
handle();
|
||||
}
|
||||
updateState(State.FINAL);
|
||||
handle();
|
||||
performEndRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void performEndRequest()
|
||||
{
|
||||
if (state == State.HEADERS)
|
||||
{
|
||||
updateState(State.HEADERS_COMPLETE);
|
||||
handle();
|
||||
}
|
||||
updateState(State.FINAL);
|
||||
handle();
|
||||
}
|
||||
|
||||
public void async()
|
||||
{
|
||||
post(new Runnable()
|
||||
|
@ -363,6 +389,50 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
});
|
||||
}
|
||||
|
||||
protected void reply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
if (!stream.isUnidirectional())
|
||||
stream.reply(replyInfo);
|
||||
if (replyInfo.getHeaders().get("status").value().startsWith("200") && !stream.isClosed() && !isIfModifiedSinceHeaderPresent())
|
||||
{
|
||||
// We have a 200 OK with some content to send
|
||||
|
||||
Headers.Header scheme = headers.get("scheme");
|
||||
Headers.Header host = headers.get("host");
|
||||
Headers.Header url = headers.get("url");
|
||||
Set<String> pushResources = pushStrategy.apply(stream, this.headers, replyInfo.getHeaders());
|
||||
String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(url.value()).toString();
|
||||
for (String pushURL : pushResources)
|
||||
{
|
||||
final Headers pushHeaders = new Headers();
|
||||
pushHeaders.put("method", "GET");
|
||||
pushHeaders.put("url", pushURL);
|
||||
pushHeaders.put("version", "HTTP/1.1");
|
||||
pushHeaders.put(scheme);
|
||||
pushHeaders.put(host);
|
||||
pushHeaders.put("referer", referrer);
|
||||
// Remember support for gzip encoding
|
||||
pushHeaders.put(headers.get("accept-encoding"));
|
||||
stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void completed(Stream pushStream)
|
||||
{
|
||||
Synchronous pushConnection = new Synchronous(getConnector(), getEndPoint(), getServer(), connection, pushStrategy, pushStream);
|
||||
pushConnection.beginRequest(pushHeaders, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIfModifiedSinceHeaderPresent()
|
||||
{
|
||||
if (headers.get("if-modified-since") != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Buffer consumeContent(long maxIdleTime) throws IOException, InterruptedException
|
||||
{
|
||||
while (true)
|
||||
|
@ -566,7 +636,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
// We have to query the HttpGenerator and its buffers to know
|
||||
// whether there is content buffered; if so, send the data frame
|
||||
Buffer content = getContentBuffer();
|
||||
stream.reply(new ReplyInfo(headers, content == null));
|
||||
reply(stream, new ReplyInfo(headers, content == null));
|
||||
if (content != null)
|
||||
{
|
||||
closed = allContentAdded || isAllContentWritten();
|
||||
|
@ -674,4 +744,18 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Synchronous extends ServerHTTPSPDYAsyncConnection
|
||||
{
|
||||
private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server, connection, pushStrategy, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Runnable task)
|
||||
{
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,11 +42,13 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class);
|
||||
|
||||
private final Connector connector;
|
||||
private final PushStrategy pushStrategy;
|
||||
|
||||
public ServerHTTPSPDYAsyncConnectionFactory(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler, Connector connector)
|
||||
public ServerHTTPSPDYAsyncConnectionFactory(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler, Connector connector, PushStrategy pushStrategy)
|
||||
{
|
||||
super(version, bufferPool, threadPool, scheduler);
|
||||
this.connector = connector;
|
||||
this.pushStrategy = pushStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,13 +79,13 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
|
||||
HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream);
|
||||
ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector,
|
||||
asyncEndPoint, connector.getServer(),
|
||||
(SPDYAsyncConnection)endPoint.getConnection(), stream);
|
||||
asyncEndPoint, connector.getServer(), (SPDYAsyncConnection)endPoint.getConnection(),
|
||||
pushStrategy, stream);
|
||||
asyncEndPoint.setConnection(connection);
|
||||
stream.setAttribute(CONNECTION_ATTRIBUTE, connection);
|
||||
|
||||
Headers headers = synInfo.getHeaders();
|
||||
connection.beginRequest(headers);
|
||||
connection.beginRequest(headers, synInfo.isClose());
|
||||
|
||||
if (headers.isEmpty())
|
||||
{
|
||||
|
@ -93,14 +95,9 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
else
|
||||
{
|
||||
if (synInfo.isClose())
|
||||
{
|
||||
connection.endRequest();
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public abstract class AbstractHTTPSPDYTest
|
|||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this);
|
||||
return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new PushStrategy.None());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,471 @@
|
|||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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.AbstractHandler;
|
||||
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
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.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
||||
{
|
||||
@Override
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector()
|
||||
{
|
||||
return new HTTPSPDYServerConnector()
|
||||
{
|
||||
private final AsyncConnectionFactory defaultAsyncConnectionFactory =
|
||||
new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new ReferrerPushStrategy());
|
||||
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return defaultAsyncConnectionFactory;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssociatedResourceIsPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
else if (url.endsWith(".css"))
|
||||
output.print("body { background: #FFF; }");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
associatedRequestHeaders.put("url", "/style.css");
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
associatedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource, we expect the css being pushed
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedAssociatedResourceIsPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
else if (url.endsWith(".css"))
|
||||
output.print("body { background: #FFF; }");
|
||||
else if (url.endsWith(".gif"))
|
||||
output.print("\u0000");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
String associatedResource = "/style.css";
|
||||
associatedRequestHeaders.put("url", associatedResource);
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
associatedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
|
||||
Headers nestedRequestHeaders = new Headers();
|
||||
nestedRequestHeaders.put("method", "GET");
|
||||
nestedRequestHeaders.put("url", "/image.gif");
|
||||
nestedRequestHeaders.put("version", "HTTP/1.1");
|
||||
nestedRequestHeaders.put("scheme", "http");
|
||||
nestedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
nestedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + associatedResource);
|
||||
session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
nestedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(2);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainResourceWithReferrerIsNotPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
associatedRequestHeaders.put("url", "/home.html");
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
associatedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource, we expect nothing being pushed
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
pushLatch.countDown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
else if (url.endsWith(".css"))
|
||||
output.print("body { background: #FFF; }");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
associatedRequestHeaders.put("url", "/style.css");
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
associatedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource, we expect the css NOT being pushed as the main request contains an
|
||||
// if-modified-since header
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -74,6 +74,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -117,6 +118,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", uri);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -157,6 +159,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "HEAD");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -206,6 +209,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
@ -251,6 +255,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
@ -299,6 +304,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
@ -344,6 +350,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -394,6 +401,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -449,6 +457,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
|
@ -508,6 +517,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -565,6 +575,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -622,6 +633,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -684,6 +696,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -741,6 +754,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -782,6 +796,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -828,6 +843,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -874,6 +890,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
|
@ -939,6 +956,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
@ -1015,6 +1033,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
@ -1077,6 +1096,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
@ -1149,6 +1169,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch responseLatch = new CountDownLatch(2);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -53,9 +49,14 @@ import org.junit.Assert;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class ClosedStreamTest extends AbstractTest
|
||||
{
|
||||
//TODO: Right now it sends a rst as the stream is unknown to the session once it's closed. But according to the spec we probably should just ignore the data?!
|
||||
//TODO: Right now it sends a rst as the stream is unknown to the session once it's closed.
|
||||
//TODO: But according to the spec we probably should just ignore the data?!
|
||||
@Test
|
||||
public void testDataSentOnClosedStreamIsIgnored() throws Exception
|
||||
{
|
||||
|
@ -83,25 +84,25 @@ public class ClosedStreamTest extends AbstractTest
|
|||
|
||||
ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Headers()));
|
||||
channel.write(writeBuffer);
|
||||
Assert.assertThat(writeBuffer.hasRemaining(), is(false));
|
||||
|
||||
byte[] bytes = new byte[1];
|
||||
writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
|
||||
channel.write(writeBuffer);
|
||||
Assert.assertThat(writeBuffer.hasRemaining(), is(false));
|
||||
|
||||
// Write again to simulate the faulty condition
|
||||
writeBuffer.flip();
|
||||
channel.write(writeBuffer);
|
||||
Assert.assertThat(writeBuffer.hasRemaining(), is(false));
|
||||
|
||||
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();
|
||||
session.goAway().get(5, TimeUnit.SECONDS);
|
||||
|
||||
server.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSendDataOnHalfClosedStreamCausesExceptionOnServer() throws Exception
|
||||
{
|
||||
|
@ -163,19 +164,17 @@ public class ClosedStreamTest extends AbstractTest
|
|||
@Test
|
||||
public void testV2ReceiveDataOnHalfClosedStream() throws Exception
|
||||
{
|
||||
final CountDownLatch clientResetReceivedLatch = runReceiveDataOnHalfClosedStream(SPDY.V2);
|
||||
assertThat("server didn't receive data",clientResetReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
|
||||
runReceiveDataOnHalfClosedStream(SPDY.V2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore("until v3 is properly implemented")
|
||||
public void testV3ReceiveDataOnHalfClosedStream() throws Exception
|
||||
{
|
||||
final CountDownLatch clientResetReceivedLatch = runReceiveDataOnHalfClosedStream(SPDY.V3);
|
||||
assertThat("server didn't receive data",clientResetReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
|
||||
runReceiveDataOnHalfClosedStream(SPDY.V3);
|
||||
}
|
||||
|
||||
private CountDownLatch runReceiveDataOnHalfClosedStream(short version) throws Exception, IOException, InterruptedException
|
||||
private void runReceiveDataOnHalfClosedStream(short version) throws Exception
|
||||
{
|
||||
final CountDownLatch clientResetReceivedLatch = new CountDownLatch(1);
|
||||
final CountDownLatch serverReplySentLatch = new CountDownLatch(1);
|
||||
|
@ -209,10 +208,11 @@ public class ClosedStreamTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
|
||||
final SocketChannel socketChannel = SocketChannel.open(startServer);
|
||||
final Generator generator = new Generator(new StandardByteBufferPool(),new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer synData = generator.control(new SynStreamFrame(version,SynInfo.FLAG_CLOSE,1,0,(byte)0,new Headers()));
|
||||
int streamId = 1;
|
||||
ByteBuffer synData = generator.control(new SynStreamFrame(version,SynInfo.FLAG_CLOSE, streamId,0,(byte)0,new Headers()));
|
||||
|
||||
final SocketChannel socketChannel = SocketChannel.open(startServer);
|
||||
socketChannel.write(synData);
|
||||
|
||||
assertThat("server: syn reply is sent",serverReplySentLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
|
@ -257,7 +257,12 @@ public class ClosedStreamTest extends AbstractTest
|
|||
parser.parse(response);
|
||||
|
||||
assertThat("server didn't receive data",serverDataReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
|
||||
return clientResetReceivedLatch;
|
||||
}
|
||||
assertThat("client didn't receive reset",clientResetReceivedLatch.await(1,TimeUnit.SECONDS),not(true));
|
||||
|
||||
ByteBuffer buffer = generator.control(new GoAwayFrame(version, streamId, SessionStatus.OK.getCode()));
|
||||
socketChannel.write(buffer);
|
||||
Assert.assertThat(buffer.hasRemaining(), is(false));
|
||||
|
||||
socketChannel.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
@ -26,6 +23,7 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Exchanger;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
|
@ -40,13 +38,19 @@ import org.eclipse.jetty.spdy.api.SynInfo;
|
|||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class PushStreamTest extends AbstractTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void testSynPushStream() throws Exception
|
||||
{
|
||||
final CountDownLatch pushStreamSynLatch = new CountDownLatch(1);
|
||||
final AtomicReference<Stream> pushStreamRef = new AtomicReference<>();
|
||||
final CountDownLatch pushStreamLatch = new CountDownLatch(1);
|
||||
|
||||
Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
|
@ -54,22 +58,37 @@ public class PushStreamTest extends AbstractTest
|
|||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
stream.syn(new SynInfo(false));
|
||||
stream.syn(new SynInfo(true));
|
||||
return null;
|
||||
}
|
||||
}),new SessionFrameListener.Adapter()
|
||||
}), new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
pushStreamSynLatch.countDown();
|
||||
stream.reply(new ReplyInfo(false));
|
||||
return super.onSyn(stream,synInfo);
|
||||
assertThat("streamId is even", stream.getId() % 2, is(0));
|
||||
assertThat("stream is unidirectional", stream.isUnidirectional(), is(true));
|
||||
assertThat("stream is closed", stream.isClosed(), is(true));
|
||||
assertThat("stream has associated stream", stream.getAssociatedStream(), notNullValue());
|
||||
try
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
fail("Cannot reply to push streams");
|
||||
}
|
||||
catch (IllegalStateException x)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
pushStreamRef.set(stream);
|
||||
pushStreamLatch.countDown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
clientSession.syn(new SynInfo(false),null).get();
|
||||
assertThat("onSyn has been called",pushStreamSynLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
Stream stream = clientSession.syn(new SynInfo(true), null).get();
|
||||
assertThat("onSyn has been called", pushStreamLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
Stream pushStream = pushStreamRef.get();
|
||||
assertThat("main stream and associated stream are the same", stream, sameInstance(pushStream.getAssociatedStream()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -131,7 +150,6 @@ public class PushStreamTest extends AbstractTest
|
|||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
pushStreamSynLatch.countDown();
|
||||
stream.reply(new ReplyInfo(false));
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -321,7 +339,6 @@ public class PushStreamTest extends AbstractTest
|
|||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
assertStreamIdIsEven(stream);
|
||||
pushStreamIdIsEvenLatch.countDown();
|
||||
return super.onSyn(stream,synInfo);
|
||||
|
@ -347,7 +364,7 @@ public class PushStreamTest extends AbstractTest
|
|||
{
|
||||
assertThat("streamId is odd",stream.getId() % 2,is(1));
|
||||
}
|
||||
|
||||
|
||||
private void assertThatNoExceptionOccured(final CountDownLatch exceptionCountDownLatch) throws InterruptedException
|
||||
{
|
||||
assertThat("No exception occured", exceptionCountDownLatch.await(1,TimeUnit.SECONDS),is(false));
|
||||
|
|
|
@ -550,22 +550,25 @@ public class Main
|
|||
if (_exec)
|
||||
{
|
||||
CommandLineBuilder cmd = buildCommandLine(classpath,configuredXmls);
|
||||
|
||||
ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs());
|
||||
final Process process = pbuilder.start();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Config.debug("Destroying " + process);
|
||||
process.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
copyInThread(process.getErrorStream(),System.err);
|
||||
copyInThread(process.getInputStream(),System.out);
|
||||
copyInThread(System.in,process.getOutputStream());
|
||||
monitor.setProcess(process);
|
||||
process.waitFor();
|
||||
|
||||
try
|
||||
{
|
||||
copyInThread(process.getErrorStream(),System.err);
|
||||
copyInThread(process.getInputStream(),System.out);
|
||||
copyInThread(System.in,process.getOutputStream());
|
||||
monitor.setProcess(process);
|
||||
process.waitFor();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Config.debug("Destroying " + process);
|
||||
process.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -228,7 +228,11 @@ class JarFileResource extends JarResource
|
|||
public long lastModified()
|
||||
{
|
||||
if (checkConnection() && _file!=null)
|
||||
{
|
||||
if (exists() && _entry!=null)
|
||||
return _entry.getTime();
|
||||
return _file.lastModified();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.io.InputStream;
|
|||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -310,6 +311,16 @@ public class ResourceTest
|
|||
assertFalse(jarFileResource.isContainedIn(container));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Test
|
||||
public void testJarFileLastModified ()
|
||||
throws Exception
|
||||
{
|
||||
String s = "jar:"+__userURL+"TestData/test.zip!/subdir/numbers";
|
||||
Resource r = Resource.newResource(s);
|
||||
assertEquals(971425252000L,r.lastModified()); // Known date value inside zip
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Test
|
||||
public void testJarFileCopyToDirectoryTraversal () throws Exception
|
||||
|
|
Loading…
Reference in New Issue