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

This commit is contained in:
Jan Bartel 2012-05-22 17:28:40 +02:00
commit 34954bc617
55 changed files with 2310 additions and 1657 deletions

View File

@ -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"));
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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()

View File

@ -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;
}
/**
* &lt;autoInstallOSGiBundles&gt;true&lt;/autoInstallOSGiBundles&gt;
*
* @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;
}
}
}

View File

@ -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 */

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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 &quot;this.bundle.install&quot; 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();
}
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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 '\'';
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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();
}
}
/*

View File

@ -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???
}
}
}

View File

@ -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);

View File

@ -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.
*

View File

@ -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");

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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);
}
}

View File

@ -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()
{

View File

@ -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);
}
/**

View File

@ -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();
}

View File

@ -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));

View File

@ -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));
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
};
}

View File

@ -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));
}
}

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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