diff --git a/.gitignore b/.gitignore index 12ab1b6945c..815d2963057 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ target/ #maven *.versionsBackup +*.releaseBackup diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java index 86453ae9e4f..9ebba4e49fd 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java @@ -29,7 +29,11 @@ public class OneWebApp server.setConnectors(new Connector[] { connector }); - String war = args.length > 0?args[0]: "../test-jetty-webapp/target/test-jetty-webapp-" + Server.getVersion(); + + //If you're running this from inside Eclipse, then Server.getVersion will not provide + //the correct number as there is no manifest. Use the command line instead to provide the path to the + //test webapp + String war = args.length > 0?args[0]: "../test-jetty-webapp/target/test-jetty-webapp-"+Server.getVersion(); String path = args.length > 1?args[1]:"/"; System.err.println(war + " " + path); @@ -37,6 +41,15 @@ public class OneWebApp WebAppContext webapp = new WebAppContext(); webapp.setContextPath(path); webapp.setWar(war); + + //If the webapp contains security constraints, you will need to configure a LoginService + if (war.contains("test-jetty-webapp")) + { + org.eclipse.jetty.security.HashLoginService loginService = new org.eclipse.jetty.security.HashLoginService(); + loginService.setName("Test Realm"); + loginService.setConfig("src/test/resources/realm.properties"); + webapp.getSecurityHandler().setLoginService(loginService); + } server.setHandler(webapp); diff --git a/jetty-aggregate/jetty-all-server/pom.xml b/jetty-aggregate/jetty-all-server/pom.xml index cee39b8ffcf..7dfeca062dc 100644 --- a/jetty-aggregate/jetty-all-server/pom.xml +++ b/jetty-aggregate/jetty-all-server/pom.xml @@ -5,7 +5,6 @@ 8.1.3-SNAPSHOT 4.0.0 - org.eclipse.jetty.aggregate jetty-all-server Jetty :: Aggregate :: All Server @@ -80,6 +79,8 @@ !org.eclipse.jetty*, javax.annotation;version="1.0.0";resolution:=optional, javax.servlet;version="2.6.0", + javax.servlet.annotation;version="2.6.0", + javax.servlet.descriptor;version="2.6.0", javax.servlet.http;version="2.6.0", javax.mail;version="1.4.0";resolution:=optional, javax.mail.event;version="1.4.0";resolution:=optional, diff --git a/jetty-aggregate/jetty-client/pom.xml b/jetty-aggregate/jetty-client/pom.xml index 26b5d746011..d06130ff5c5 100644 --- a/jetty-aggregate/jetty-client/pom.xml +++ b/jetty-aggregate/jetty-client/pom.xml @@ -5,7 +5,6 @@ 8.1.3-SNAPSHOT 4.0.0 - org.eclipse.jetty.aggregate jetty-client Jetty :: Aggregate :: HTTP Client diff --git a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13SocketConnector.java b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13SocketConnector.java index 3632b291844..37ff45fac6c 100644 --- a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13SocketConnector.java +++ b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13SocketConnector.java @@ -36,10 +36,10 @@ public class Ajp13SocketConnector extends SocketConnector static boolean __allowShutdown = false; public Ajp13SocketConnector() { - super.setRequestHeaderSize(Ajp13Packet.MAX_DATA_SIZE); - super.setResponseHeaderSize(Ajp13Packet.MAX_DATA_SIZE); - super.setRequestBufferSize(Ajp13Packet.MAX_DATA_SIZE); - super.setResponseBufferSize(Ajp13Packet.MAX_DATA_SIZE); + super.setRequestHeaderSize(Ajp13Packet.MAX_PACKET_SIZE); + super.setResponseHeaderSize(Ajp13Packet.MAX_PACKET_SIZE); + super.setRequestBufferSize(Ajp13Packet.MAX_PACKET_SIZE); + super.setResponseBufferSize(Ajp13Packet.MAX_PACKET_SIZE); // IN AJP protocol the socket stay open, so // by default the time out is set to 0 seconds super.setMaxIdleTime(0); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 0b5581ed984..4de850fa627 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -171,7 +171,7 @@ public class HttpClient extends AggregateLifeCycle implements HttpBuffers, Attri /* ------------------------------------------------------------ */ /** Set the ThreadPool. - * The threadpool passed is added via {@link #addBean(Object)} so that + * The threadpool passed is added via {@link #addBean(Object)} so that * it's lifecycle may be managed as a {@link AggregateLifeCycle}. * @param threadPool the threadPool to set */ @@ -878,7 +878,7 @@ public class HttpClient extends AggregateLifeCycle implements HttpBuffers, Attri @Deprecated public void setProvider(String provider) { - setProvider(provider); + _sslContextFactory.setProvider(provider); } /* ------------------------------------------------------------ */ diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java index 18a197ced6a..5ea60430766 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java @@ -475,10 +475,12 @@ public class HttpDestination implements Dumpable HttpEventListener elistener = (HttpEventListener)constructor.newInstance(this, ex); ex.setEventListener(elistener); } - catch (Exception e) + catch (final Exception e) { - e.printStackTrace(); - throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass); + throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass) + { + {initCause(e);} + }; } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java index e1c282e74db..baf31b1f902 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java @@ -276,6 +276,11 @@ class SelectConnector extends AggregateLifeCycle implements HttpClient.Connector _endp.shutdownOutput(); } + public void dispatch() + { + _endp.asyncDispatch(); + } + public void asyncDispatch() { _endp.asyncDispatch(); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/security/DigestAuthentication.java b/jetty-client/src/main/java/org/eclipse/jetty/client/security/DigestAuthentication.java index 032a2aafab2..a5ba37ee740 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/security/DigestAuthentication.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/security/DigestAuthentication.java @@ -104,8 +104,7 @@ public class DigestAuthentication implements Authentication } catch(Exception e) { - e.printStackTrace(); - return null; + throw new RuntimeException(e); } } @@ -119,8 +118,7 @@ public class DigestAuthentication implements Authentication } catch(Exception e) { - e.printStackTrace(); - return null; + throw new RuntimeException(e); } } diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java index 6a90a5ad3c9..4dd86193df9 100644 --- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java +++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.continuation; import java.lang.reflect.Constructor; - import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; import javax.servlet.ServletResponse; @@ -36,7 +35,7 @@ public class ContinuationSupport static { boolean servlet3Support=false; - Constructors3cc=null; + Constructors3cc=null; try { boolean servlet3=ServletRequest.class.getMethod("startAsync")!=null; @@ -52,11 +51,11 @@ public class ContinuationSupport finally { __servlet3=servlet3Support; - __newServlet3Continuation=(Constructor)s3cc; + __newServlet3Continuation=s3cc; } - + boolean jetty6Support=false; - Constructorj6cc=null; + Constructorj6cc=null; try { Class jetty6ContinuationClass = ContinuationSupport.class.getClassLoader().loadClass("org.mortbay.util.ajax.Continuation"); @@ -73,9 +72,9 @@ public class ContinuationSupport finally { __jetty6=jetty6Support; - __newJetty6Continuation=(Constructor)j6cc; + __newJetty6Continuation=j6cc; } - + Class waiting=null; try { @@ -93,12 +92,12 @@ public class ContinuationSupport /* ------------------------------------------------------------ */ /** * Get a Continuation. The type of the Continuation returned may - * vary depending on the container in which the application is + * vary depending on the container in which the application is * deployed. It may be an implementation native to the container (eg * org.eclipse.jetty.server.AsyncContinuation) or one of the utility * implementations provided such as an internal FauxContinuation * or a real implementation like {@link org.eclipse.jetty.continuation.Servlet3Continuation}. - * @param request The request + * @param request The request * @return a Continuation instance */ public static Continuation getContinuation(ServletRequest request) @@ -106,10 +105,10 @@ public class ContinuationSupport Continuation continuation = (Continuation) request.getAttribute(Continuation.ATTRIBUTE); if (continuation!=null) return continuation; - + while (request instanceof ServletRequestWrapper) request=((ServletRequestWrapper)request).getRequest(); - + if (__servlet3 ) { try diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java index 9486b725ad8..26323372c49 100644 --- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java +++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java @@ -3,7 +3,6 @@ package org.eclipse.jetty.continuation; import java.io.IOException; import java.util.ArrayList; import java.util.List; - import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -16,7 +15,7 @@ import javax.servlet.ServletResponseWrapper; /* ------------------------------------------------------------ */ /** * This implementation of Continuation is used by {@link ContinuationSupport} - * when it detects that the application has been deployed in a non-jetty Servlet 3 + * when it detects that the application has been deployed in a non-jetty Servlet 3 * server. */ public class Servlet3Continuation implements Continuation @@ -24,11 +23,11 @@ public class Servlet3Continuation implements Continuation // Exception reused for all continuations // Turn on debug in ContinuationFilter to see real stack trace. private final static ContinuationThrowable __exception = new ContinuationThrowable(); - + private final ServletRequest _request; private ServletResponse _response; private AsyncContext _context; - private List _listeners=new ArrayList(); + private List _listeners=new ArrayList(); private volatile boolean _initial=true; private volatile boolean _resumed=false; private volatile boolean _expired=false; @@ -59,7 +58,6 @@ public class Servlet3Continuation implements Continuation public void onTimeout(AsyncEvent event) throws IOException { _initial=false; - System.err.println("Doing dispatch on timed out continuation for "+_request.getAttribute("FOO")); event.getAsyncContext().dispatch(); } }); @@ -91,7 +89,7 @@ public class Servlet3Continuation implements Continuation listener.onTimeout(Servlet3Continuation.this); } }; - + if (_context!=null) _context.addListener(wrapped); else @@ -171,7 +169,7 @@ public class Servlet3Continuation implements Continuation _expired=false; _context=_request.startAsync(); _context.setTimeout(_timeoutMs); - + for (AsyncListener listener:_listeners) _context.addListener(listener); _listeners.clear(); @@ -184,7 +182,7 @@ public class Servlet3Continuation implements Continuation _expired=false; _context=_request.startAsync(); _context.setTimeout(_timeoutMs); - + for (AsyncListener listener:_listeners) _context.addListener(listener); _listeners.clear(); diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index e93d75c3656..fb442e3963d 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -103,6 +103,16 @@ ${assembly-directory}/webapps test.war + + org.eclipse.jetty.spdy + spdy-jetty-http-webapp + ${project.version} + war + true + ** + ${assembly-directory}/webapps + spdy.war + org.eclipse.jetty jetty-start @@ -124,12 +134,24 @@ org.eclipse.jetty - org.eclipse.jetty.orbit + org.eclipse.jetty.orbit,org.eclipse.jetty.spdy jetty-all,jetty-start,jetty-monitor,jetty-jsp jar ${assembly-directory}/lib + + copy-lib-spdy-deps + generate-resources + + copy-dependencies + + + org.eclipse.jetty.spdy + jar + ${assembly-directory}/lib/spdy + + copy-orbit-servlet-api-deps generate-resources @@ -222,7 +244,7 @@ unpack-dependencies - org.eclipse.jetty + org.eclipse.jetty,org.eclipse.jetty.spdy config false META-INF/** @@ -398,6 +420,27 @@ jetty-overlay-deployer ${project.version} + + org.eclipse.jetty.spdy + spdy-core + ${project.version} + + + org.eclipse.jetty.spdy + spdy-jetty + ${project.version} + + + org.eclipse.jetty.spdy + spdy-jetty-http + ${project.version} + + + org.eclipse.jetty.spdy + spdy-jetty-http-webapp + ${project.version} + war + org.eclipse.jetty.aggregate jetty-all diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml index 5f232e3100e..51d9367641e 100644 --- a/jetty-http/pom.xml +++ b/jetty-http/pom.xml @@ -1,4 +1,5 @@ - + + jetty-project org.eclipse.jetty diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 36d38a7dea9..427ea48398b 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -295,7 +295,6 @@ public class HttpFields /* -------------------------------------------------------------- */ private final ArrayList _fields = new ArrayList(20); private final HashMap _names = new HashMap(32); - private final int _maxCookieVersion; /* ------------------------------------------------------------ */ /** @@ -303,19 +302,8 @@ public class HttpFields */ public HttpFields() { - _maxCookieVersion=1; } - /* ------------------------------------------------------------ */ - /** - * Constructor. - */ - public HttpFields(int maxCookieVersion) - { - _maxCookieVersion=maxCookieVersion; - } - - // TODO externalize this cache so it can be configurable private static ConcurrentMap __cache = new ConcurrentHashMap(); private static int __cacheSize = Integer.getInteger("org.eclipse.jetty.http.HttpFields.CACHE",2000); @@ -971,7 +959,7 @@ public class HttpFields final boolean isHttpOnly, int version) { - String delim=_maxCookieVersion==0?"":__COOKIE_DELIM; + String delim=__COOKIE_DELIM; // Check arguments if (name == null || name.length() == 0) @@ -980,29 +968,18 @@ public class HttpFields // Format value and params StringBuilder buf = new StringBuilder(128); String name_value_params; - boolean quoted = QuotedStringTokenizer.quoteIfNeeded(buf, name, delim); + QuotedStringTokenizer.quoteIfNeeded(buf, name, delim); buf.append('='); String start=buf.toString(); if (value != null && value.length() > 0) - quoted|=QuotedStringTokenizer.quoteIfNeeded(buf, value, delim); - - // upgrade to version 1 cookies if quoted. - if (quoted&&version==0 && _maxCookieVersion>=1) - version=1; + QuotedStringTokenizer.quoteIfNeeded(buf, value, delim); - if (version>_maxCookieVersion) - version=_maxCookieVersion; - - if (version > 0) + if (comment != null && comment.length() > 0) { - buf.append(";Version="); - buf.append(version); - if (comment != null && comment.length() > 0) - { - buf.append(";Comment="); - QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim); - } + buf.append(";Comment="); + QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim); } + if (path != null && path.length() > 0) { buf.append(";Path="); @@ -1019,23 +996,19 @@ public class HttpFields if (maxAge >= 0) { - // Always add the expires param as some browsers still don't handle max-age - buf.append(";Expires="); - if (maxAge == 0) - buf.append(__01Jan1970_COOKIE); - else - formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge); - + // Always add the expires param as some browsers still don't handle max-age + buf.append(";Expires="); + if (maxAge == 0) + buf.append(__01Jan1970_COOKIE); + else + formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge); + if (version >0) { buf.append(";Max-Age="); buf.append(maxAge); } } - else if (version > 0) - { - buf.append(";Discard"); - } if (isSecure) buf.append(";Secure"); diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index c7d7d39f88a..0b4053c15e3 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -260,7 +260,6 @@ public class HttpParser implements Parser { _state=STATE_END; _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } @@ -326,7 +325,6 @@ public class HttpParser implements Parser if (!isComplete() && !isIdle()) throw new EofException(); - returnBuffers(); return -1; } length=_buffer.length(); @@ -440,7 +438,6 @@ public class HttpParser implements Parser _state=STATE_SEEKING_EOF; _handler.headerComplete(); _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } break; @@ -470,7 +467,6 @@ public class HttpParser implements Parser _state=STATE_SEEKING_EOF; _handler.headerComplete(); _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } } @@ -634,7 +630,6 @@ public class HttpParser implements Parser _handler.headerComplete(); _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; default: @@ -840,7 +835,6 @@ public class HttpParser implements Parser { _state=_persistent?STATE_END:STATE_SEEKING_EOF; _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } @@ -860,7 +854,6 @@ public class HttpParser implements Parser { _state=_persistent?STATE_END:STATE_SEEKING_EOF; _handler.messageComplete(_contentPosition); - returnBuffers(); } // TODO adjust the _buffer to keep unconsumed content return 1; @@ -895,7 +888,6 @@ public class HttpParser implements Parser _eol=_buffer.get(); _state=_persistent?STATE_END:STATE_SEEKING_EOF; _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } else @@ -926,7 +918,6 @@ public class HttpParser implements Parser _eol=_buffer.get(); _state=_persistent?STATE_END:STATE_SEEKING_EOF; _handler.messageComplete(_contentPosition); - returnBuffers(); return 1; } else diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/AbstractCompressedStream.java b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/AbstractCompressedStream.java new file mode 100644 index 00000000000..77ac1fe41d0 --- /dev/null +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/AbstractCompressedStream.java @@ -0,0 +1,345 @@ +// ======================================================================== +// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.http.gzip; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.zip.DeflaterOutputStream; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.util.ByteArrayOutputStream2; + +/* ------------------------------------------------------------ */ +/** + * Skeletal implementation of a CompressedStream. This class adds compression features to a ServletOutputStream and takes care of setting response headers, etc. + * Major work and configuration is done here. Subclasses using different kinds of compression only have to implement the abstract methods doCompress() and + * setContentEncoding() using the desired compression and setting the appropriate Content-Encoding header string. + */ +public abstract class AbstractCompressedStream extends ServletOutputStream +{ + private final String _encoding; + protected HttpServletRequest _request; + protected HttpServletResponse _response; + protected OutputStream _out; + protected ByteArrayOutputStream2 _bOut; + protected DeflaterOutputStream _compressedOutputStream; + protected boolean _closed; + protected int _bufferSize; + protected int _minCompressSize; + protected long _contentLength; + protected boolean _doNotCompress; + + /** + * Instantiates a new compressed stream. + * + * @param request + * the request + * @param response + * the response + * @param contentLength + * the content length + * @param bufferSize + * the buffer size + * @param minCompressSize + * the min compress size + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public AbstractCompressedStream(String encoding,HttpServletRequest request, HttpServletResponse response, long contentLength, int bufferSize, int minCompressSize) + throws IOException + { + _encoding=encoding; + _request = request; + _response = response; + _contentLength = contentLength; + _bufferSize = bufferSize; + _minCompressSize = minCompressSize; + if (minCompressSize == 0) + doCompress(); + } + + /** + * Reset buffer. + */ + public void resetBuffer() + { + if (_response.isCommitted()) + throw new IllegalStateException("Committed"); + _closed = false; + _out = null; + _bOut = null; + if (_compressedOutputStream != null) + _response.setHeader("Content-Encoding",null); + _compressedOutputStream = null; + _doNotCompress = false; + } + + /** + * Sets the content length. + * + * @param length + * the new content length + */ + public void setContentLength(long length) + { + _contentLength = length; + if (_doNotCompress && length >= 0) + { + if (_contentLength < Integer.MAX_VALUE) + _response.setContentLength((int)_contentLength); + else + _response.setHeader("Content-Length",Long.toString(_contentLength)); + } + } + + /* ------------------------------------------------------------ */ + /** + * @see java.io.OutputStream#flush() + */ + @Override + public void flush() throws IOException + { + if (_out == null || _bOut != null) + { + if (_contentLength > 0 && _contentLength < _minCompressSize) + doNotCompress(); + else + doCompress(); + } + + _out.flush(); + } + + /* ------------------------------------------------------------ */ + /** + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException + { + if (_closed) + return; + + if (_request.getAttribute("javax.servlet.include.request_uri") != null) + flush(); + else + { + if (_bOut != null) + { + if (_contentLength < 0) + _contentLength = _bOut.getCount(); + if (_contentLength < _minCompressSize) + doNotCompress(); + else + doCompress(); + } + else if (_out == null) + { + doNotCompress(); + } + + if (_compressedOutputStream != null) + _compressedOutputStream.close(); + else + _out.close(); + _closed = true; + } + } + + /** + * Finish. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void finish() throws IOException + { + if (!_closed) + { + if (_out == null || _bOut != null) + { + if (_contentLength > 0 && _contentLength < _minCompressSize) + doNotCompress(); + else + doCompress(); + } + + if (_compressedOutputStream != null && !_closed) + { + _closed = true; + _compressedOutputStream.close(); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException + { + checkOut(1); + _out.write(b); + } + + /* ------------------------------------------------------------ */ + /** + * @see java.io.OutputStream#write(byte[]) + */ + @Override + public void write(byte b[]) throws IOException + { + checkOut(b.length); + _out.write(b); + } + + /* ------------------------------------------------------------ */ + /** + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte b[], int off, int len) throws IOException + { + checkOut(len); + _out.write(b,off,len); + } + + /** + * Do compress. + * + * @throws IOException Signals that an I/O exception has occurred. + */ + public void doCompress() throws IOException + { + if (_compressedOutputStream==null) + { + if (_response.isCommitted()) + throw new IllegalStateException(); + + setHeader("Content-Encoding", _encoding); + + if (_response.containsHeader("Content-Encoding")) + { + _out=_compressedOutputStream=createStream(); + + if (_bOut!=null) + { + _out.write(_bOut.getBuf(),0,_bOut.getCount()); + _bOut=null; + } + } + else + doNotCompress(); + } + } + + /** + * Do not compress. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void doNotCompress() throws IOException + { + if (_compressedOutputStream != null) + throw new IllegalStateException(); + if (_out == null || _bOut != null) + { + _doNotCompress = true; + + _out = _response.getOutputStream(); + setContentLength(_contentLength); + + if (_bOut != null) + _out.write(_bOut.getBuf(),0,_bOut.getCount()); + _bOut = null; + } + } + + /** + * Check out. + * + * @param length + * the length + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void checkOut(int length) throws IOException + { + if (_closed) + throw new IOException("CLOSED"); + + if (_out == null) + { + if (_response.isCommitted() || (_contentLength >= 0 && _contentLength < _minCompressSize)) + doNotCompress(); + else if (length > _minCompressSize) + doCompress(); + else + _out = _bOut = new ByteArrayOutputStream2(_bufferSize); + } + else if (_bOut != null) + { + if (_response.isCommitted() || (_contentLength >= 0 && _contentLength < _minCompressSize)) + doNotCompress(); + else if (length >= (_bOut.getBuf().length - _bOut.getCount())) + doCompress(); + } + } + + /** + * @see org.eclipse.jetty.http.gzip.CompressedStream#getOutputStream() + */ + public OutputStream getOutputStream() + { + return _out; + } + + /** + * @see org.eclipse.jetty.http.gzip.CompressedStream#isClosed() + */ + public boolean isClosed() + { + return _closed; + } + + /** + * Allows derived implementations to replace PrintWriter implementation. + */ + protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException + { + return encoding == null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding)); + } + + protected void setHeader(String name,String value) + { + _response.setHeader(name, value); + } + + /** + * Create the stream fitting to the underlying compression type. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + protected abstract DeflaterOutputStream createStream() throws IOException; + +} diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/CompressedResponseWrapper.java similarity index 63% rename from jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java rename to jetty-http/src/main/java/org/eclipse/jetty/http/gzip/CompressedResponseWrapper.java index 093e127d902..1431f3868e2 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/CompressedResponseWrapper.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) Webtide LLC +// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 @@ -11,7 +11,6 @@ // You may elect to redistribute this code under either of these licenses. // ======================================================================== - package org.eclipse.jetty.http.gzip; import java.io.IOException; @@ -28,42 +27,34 @@ import javax.servlet.http.HttpServletResponseWrapper; import org.eclipse.jetty.util.StringUtil; - -/* ------------------------------------------------------------ */ +/*------------------------------------------------------------ */ /** */ -public class GzipResponseWrapper extends HttpServletResponseWrapper +public abstract class CompressedResponseWrapper extends HttpServletResponseWrapper { - public static final int DEFAULT_BUFFER_SIZE = 8192; - public static final int DEFAULT_MIN_GZIP_SIZE = 256; - private HttpServletRequest _request; + public static final int DEFAULT_BUFFER_SIZE = 8192; + public static final int DEFAULT_MIN_COMPRESS_SIZE = 256; + private Set _mimeTypes; private int _bufferSize=DEFAULT_BUFFER_SIZE; - private int _minGzipSize=DEFAULT_MIN_GZIP_SIZE; + private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE; + protected HttpServletRequest _request; private PrintWriter _writer; - private GzipStream _gzStream; + private AbstractCompressedStream _compressedStream; private long _contentLength=-1; - private boolean _noGzip; + private boolean _noCompression; - /** - * Instantiates a new gzip response wrapper. - * - * @param request the request - * @param response the response - */ - public GzipResponseWrapper(HttpServletRequest request, HttpServletResponse response) + public CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response) { super(response); - _request=request; + _request = request; } /* ------------------------------------------------------------ */ /** - * Sets the mime types. - * - * @param mimeTypes the new mime types + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setMimeTypes(java.util.Set) */ public void setMimeTypes(Set mimeTypes) { @@ -72,8 +63,9 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper /* ------------------------------------------------------------ */ /** - * @see javax.servlet.ServletResponseWrapper#setBufferSize(int) + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setBufferSize(int) */ + @Override public void setBufferSize(int bufferSize) { _bufferSize = bufferSize; @@ -81,64 +73,66 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper /* ------------------------------------------------------------ */ /** - * Sets the min gzip size. - * - * @param minGzipSize the new min gzip size + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setMinCompressSize(int) */ - public void setMinGzipSize(int minGzipSize) + public void setMinCompressSize(int minCompressSize) { - _minGzipSize = minGzipSize; + _minCompressSize = minCompressSize; } /* ------------------------------------------------------------ */ /** - * @see javax.servlet.ServletResponseWrapper#setContentType(java.lang.String) + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setContentType(java.lang.String) */ + @Override public void setContentType(String ct) { super.setContentType(ct); - + if (ct!=null) { int colon=ct.indexOf(";"); if (colon>0) ct=ct.substring(0,colon); } - - if ((_gzStream==null || _gzStream._out==null) && + + if ((_compressedStream==null || _compressedStream.getOutputStream()==null) && (_mimeTypes==null && ct!=null && ct.contains("gzip") || _mimeTypes!=null && (ct==null||!_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))))) { - noGzip(); + noCompression(); } } /* ------------------------------------------------------------ */ /** - * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int, java.lang.String) + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String) */ + @Override public void setStatus(int sc, String sm) { super.setStatus(sc,sm); if (sc<200 || sc==204 || sc==205 || sc>=300) - noGzip(); + noCompression(); } /* ------------------------------------------------------------ */ /** - * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int) + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setStatus(int) */ + @Override public void setStatus(int sc) { super.setStatus(sc); if (sc<200 || sc==204 || sc==205 ||sc>=300) - noGzip(); + noCompression(); } /* ------------------------------------------------------------ */ /** - * @see javax.servlet.ServletResponseWrapper#setContentLength(int) + * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setContentLength(int) */ + @Override public void setContentLength(int length) { setContentLength((long)length); @@ -148,9 +142,9 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper protected void setContentLength(long length) { _contentLength=length; - if (_gzStream!=null) - _gzStream.setContentLength(length); - else if (_noGzip && _contentLength>=0) + if (_compressedStream!=null) + _compressedStream.setContentLength(length); + else if (_noCompression && _contentLength>=0) { HttpServletResponse response = (HttpServletResponse)getResponse(); if(_contentLength=0) - { - if(_contentLength0 && _contentLength<_minGzipSize) - doNotGzip(); - else - doGzip(); - } - - _out.flush(); - } - - /* ------------------------------------------------------------ */ - /** - * @see java.io.OutputStream#close() - */ - public void close() throws IOException - { - if (_closed) - return; - - if (_request.getAttribute("javax.servlet.include.request_uri")!=null) - flush(); - else - { - if (_bOut!=null) - { - if (_contentLength<0) - _contentLength=_bOut.getCount(); - if (_contentLength<_minGzipSize) - doNotGzip(); - else - doGzip(); - } - else if (_out==null) - { - doNotGzip(); - } - - if (_gzOut!=null) - _gzOut.close(); - else - _out.close(); - _closed=true; - } - } - - /** - * Finish. - * - * @throws IOException Signals that an I/O exception has occurred. - */ - public void finish() throws IOException - { - if (!_closed) - { - if (_out==null || _bOut!=null) - { - if (_contentLength>0 && _contentLength<_minGzipSize) - doNotGzip(); - else - doGzip(); - } - - if (_gzOut!=null && !_closed) - { - _closed=true; - _gzOut.close(); - } - } - } - - /* ------------------------------------------------------------ */ - /** - * @see java.io.OutputStream#write(int) - */ - public void write(int b) throws IOException - { - checkOut(1); - _out.write(b); - } - - /* ------------------------------------------------------------ */ - /** - * @see java.io.OutputStream#write(byte[]) - */ - public void write(byte b[]) throws IOException - { - checkOut(b.length); - _out.write(b); - } - - /* ------------------------------------------------------------ */ - /** - * @see java.io.OutputStream#write(byte[], int, int) - */ - public void write(byte b[], int off, int len) throws IOException - { - checkOut(len); - _out.write(b,off,len); - } - - /** - * Sets the content encoding gzip. - * - * @return true, if successful - */ - protected boolean setContentEncodingGzip() - { - _response.setHeader("Content-Encoding", "gzip"); - return _response.containsHeader("Content-Encoding"); - } - - /** - * Do gzip. - * - * @throws IOException Signals that an I/O exception has occurred. - */ - public void doGzip() throws IOException - { - if (_gzOut==null) - { - if (_response.isCommitted()) - throw new IllegalStateException(); - - if (setContentEncodingGzip()) - { - _out=_gzOut=new GZIPOutputStream(_response.getOutputStream(),_bufferSize); - - if (_bOut!=null) - { - _out.write(_bOut.getBuf(),0,_bOut.getCount()); - _bOut=null; - } - } - else - doNotGzip(); - } - } - - /** - * Do not gzip. - * - * @throws IOException Signals that an I/O exception has occurred. - */ - public void doNotGzip() throws IOException - { - if (_gzOut!=null) - throw new IllegalStateException(); - if (_out==null || _bOut!=null ) - { - _doNotGzip = true; - - _out=_response.getOutputStream(); - setContentLength(_contentLength); - - if (_bOut!=null) - _out.write(_bOut.getBuf(),0,_bOut.getCount()); - _bOut=null; - } - } - - /** - * Check out. - * - * @param length the length - * @throws IOException Signals that an I/O exception has occurred. - */ - private void checkOut(int length) throws IOException - { - if (_closed) - throw new IOException("CLOSED"); - - if (_out==null) - { - if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize)) - doNotGzip(); - else if (length>_minGzipSize) - doGzip(); - else - _out=_bOut=new ByteArrayOutputStream2(_bufferSize); - } - else if (_bOut!=null) - { - if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize)) - doNotGzip(); - else if (length>=(_bOut.getBuf().length -_bOut.getCount())) - doGzip(); - } - } - - /** - * Allows derived implementations to replace PrintWriter implementation. - */ - protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException - { - return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding)); - } -} - diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index 3ae9a409fd0..4d36e9aa3b7 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -361,28 +361,18 @@ public class HttpFieldsTest fields.clear(); fields.addSetCookie("everything","wrong","wrong","wrong",0,"to be replaced",true,true,0); fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0); - assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",fields.getStringField("Set-Cookie")); + assertEquals("everything=value;Comment=comment;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",fields.getStringField("Set-Cookie")); Enumeration e =fields.getValues("Set-Cookie"); assertTrue(e.hasMoreElements()); - assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement()); + assertEquals("everything=value;Comment=comment;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Secure;HttpOnly",e.nextElement()); assertFalse(e.hasMoreElements()); - assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires")); - + assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires")); fields.clear(); fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2); String setCookie=fields.getStringField("Set-Cookie"); - assertTrue(setCookie.startsWith("\"ev erything\"=\"va lue\";Version=1;Comment=\"co mment\";Path=\"pa th\";Domain=\"do main\";Expires=")); + assertTrue(setCookie.startsWith("\"ev erything\"=\"va lue\";Comment=\"co mment\";Path=\"pa th\";Domain=\"do main\";Expires=")); assertTrue(setCookie.endsWith("GMT;Max-Age=1;Secure;HttpOnly")); - - fields.clear(); - fields.addSetCookie("name","value",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); - assertEquals(-1,setCookie.indexOf("Version=")); - fields.clear(); - fields.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); - assertEquals(17,setCookie.indexOf("Version=1")); fields.clear(); fields.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1); @@ -401,12 +391,6 @@ public class HttpFieldsTest e=fields.getValues("Set-Cookie"); assertEquals("name=more;Domain=domain",e.nextElement()); assertEquals("foo=bob;Domain=domain",e.nextElement()); - - fields=new HttpFields(0); - fields.addSetCookie("name","value==",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); - assertEquals("name=value==",setCookie); - } private Set enum2set(Enumeration e) diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java index 46cfd4e2953..15c53bc9c08 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java @@ -13,9 +13,6 @@ package org.eclipse.jetty.http; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @@ -28,6 +25,9 @@ import org.eclipse.jetty.io.SimpleBuffers; import org.eclipse.jetty.io.View; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class HttpGeneratorClientTest { public final static String CONTENT="The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n"; @@ -52,7 +52,7 @@ public class HttpGeneratorClientTest generator.completeHeader(fields,false); - generator.addContent(new ByteArrayBuffer(content),true); + generator.addContent(new ByteArrayBuffer(content).asMutableBuffer(),true); generator.flushBuffer(); generator.complete(); generator.flushBuffer(); @@ -77,7 +77,7 @@ public class HttpGeneratorClientTest String content = "The quick brown fox jumped over the lazy dog"; - generator.addContent(new ByteArrayBuffer(content),true); + generator.addContent(new ByteArrayBuffer(content).asMutableBuffer(),true); generator.completeHeader(fields,true); generator.flushBuffer(); @@ -106,7 +106,7 @@ public class HttpGeneratorClientTest generator.completeHeader(fields,false); - generator.addContent(new ByteArrayBuffer(content),false); + generator.addContent(new ByteArrayBuffer(content).asMutableBuffer(),false); generator.flushBuffer(); generator.complete(); generator.flushBuffer(); @@ -120,7 +120,7 @@ public class HttpGeneratorClientTest * screw up the chunking by leaving out the second chunk header. */ @Test - public void testChunkedWithBackPressure() throws Exception + public void testChunkedWithBackPressure() throws Exception { final AtomicInteger availableChannelBytes = new AtomicInteger(500); ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java index 992ad7a195a..5a58160ec35 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java @@ -19,7 +19,14 @@ public interface AsyncEndPoint extends ConnectedEndPoint { /* ------------------------------------------------------------ */ /** - * Dispatch the endpoint to a thread to attend to it. + * Dispatch the endpoint if it is not already dispatched + * + */ + public void dispatch(); + + /* ------------------------------------------------------------ */ + /** + * Dispatch the endpoint. If it is already dispatched, schedule a redispatch * */ public void asyncDispatch(); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayBuffer.java index f66a52c0018..933dbf98b28 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayBuffer.java @@ -352,7 +352,8 @@ public class ByteArrayBuffer extends AbstractBuffer throws IOException { out.write(_bytes,getIndex(),length()); - clear(); + if (!isImmutable()) + clear(); } /* ------------------------------------------------------------ */ diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java index 029ed2f84ee..a0e4f8e96df 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java @@ -246,7 +246,8 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } } int len = _out.put(buffer); - buffer.skip(len); + if (!buffer.isImmutable()) + buffer.skip(len); return len; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java index a92e2e83700..0ebab0058d3 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java @@ -157,7 +157,8 @@ public class StreamEndPoint implements EndPoint int length=buffer.length(); if (length>0) buffer.writeTo(_out); - buffer.clear(); + if (!buffer.isImmutable()) + buffer.clear(); return length; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java index 1698249d438..e55d4aeb6b6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java @@ -70,10 +70,12 @@ public class StringEndPoint extends StreamEndPoint _bout.reset(); return s; } - catch(Exception e) + catch(final Exception e) { - e.printStackTrace(); - throw new IllegalStateException(_encoding+": "+e.toString()); + throw new IllegalStateException(_encoding) + { + {initCause(e);} + }; } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java index fa8a02d505a..828e38e3fcb 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java @@ -280,24 +280,17 @@ public class ChannelEndPoint implements EndPoint if (buf instanceof NIOBuffer) { final NIOBuffer nbuf = (NIOBuffer)buf; - final ByteBuffer bbuf=nbuf.getByteBuffer(); - - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized(bbuf) + final ByteBuffer bbuf=nbuf.getByteBuffer().asReadOnlyBuffer(); + try { - try - { - bbuf.position(buffer.getIndex()); - bbuf.limit(buffer.putIndex()); - len=_channel.write(bbuf); - } - finally - { - if (len>0) - buffer.skip(len); - bbuf.position(0); - bbuf.limit(bbuf.capacity()); - } + bbuf.position(buffer.getIndex()); + bbuf.limit(buffer.putIndex()); + len=_channel.write(bbuf); + } + finally + { + if (len>0) + buffer.skip(len); } } else if (buf instanceof RandomAccessFileBuffer) @@ -363,52 +356,29 @@ public class ChannelEndPoint implements EndPoint synchronized(this) { - // We must sync because buffers may be shared (eg nbuf1 is likely to be cached content). - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized(bbuf0) + // Adjust position indexs of buf0 and buf1 + bbuf0=bbuf0.asReadOnlyBuffer(); + bbuf0.position(header.getIndex()); + bbuf0.limit(header.putIndex()); + bbuf1=bbuf1.asReadOnlyBuffer(); + bbuf1.position(buffer.getIndex()); + bbuf1.limit(buffer.putIndex()); + + _gather2[0]=bbuf0; + _gather2[1]=bbuf1; + + // do the gathering write. + length=(int)((GatheringByteChannel)_channel).write(_gather2); + + int hl=header.length(); + if (length>hl) { - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized(bbuf1) - { - try - { - // Adjust position indexs of buf0 and buf1 - bbuf0.position(header.getIndex()); - bbuf0.limit(header.putIndex()); - bbuf1.position(buffer.getIndex()); - bbuf1.limit(buffer.putIndex()); - - _gather2[0]=bbuf0; - _gather2[1]=bbuf1; - - // do the gathering write. - length=(int)((GatheringByteChannel)_channel).write(_gather2); - - int hl=header.length(); - if (length>hl) - { - header.clear(); - buffer.skip(length-hl); - } - else if (length>0) - { - header.skip(length); - } - } - finally - { - // adjust buffer 0 and 1 - if (!header.isImmutable()) - header.setGetIndex(bbuf0.position()); - if (!buffer.isImmutable()) - buffer.setGetIndex(bbuf1.position()); - - bbuf0.position(0); - bbuf1.position(0); - bbuf0.limit(bbuf0.capacity()); - bbuf1.limit(bbuf1.capacity()); - } - } + header.clear(); + buffer.skip(length-hl); + } + else if (length>0) + { + header.skip(length); } } return length; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java index b24ebd8d0d9..ca182eadde6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java @@ -37,6 +37,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio"); + private final boolean WORK_AROUND_JVM_BUG_6346658 = System.getProperty("os.name").toLowerCase().contains("win"); private final SelectorManager.SelectSet _selectSet; private final SelectorManager _manager; private SelectionKey _key; @@ -208,11 +209,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { synchronized(this) { - if (_dispatched) - { - throw new IllegalStateException("dispatched"); - } - else + if (!_dispatched) { _dispatched = true; boolean dispatched = _manager.dispatch(_handler); @@ -685,15 +682,23 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo @Override public void close() throws IOException { - try + // On unix systems there is a JVM issue that if you cancel before closing, it can + // cause the selector to block waiting for a channel to close and that channel can + // block waiting for the remote end. But on windows, if you don't cancel before a + // close, then the selector can block anyway! + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=357318 + if (WORK_AROUND_JVM_BUG_6346658) { - SelectionKey key = _key; - if (key!=null) - key.cancel(); - } - catch (Throwable e) - { - LOG.ignore(e); + try + { + SelectionKey key = _key; + if (key!=null) + key.cancel(); + } + catch (Throwable e) + { + LOG.ignore(e); + } } try diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java index 4b8e0a55d1d..b4e9791b165 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java @@ -237,6 +237,9 @@ public class SslConnection extends AbstractConnection implements AsyncConnection /* ------------------------------------------------------------ */ public void onClose() { + Connection connection = _sslEndPoint.getConnection(); + if (connection != null && connection != this) + connection.onClose(); } /* ------------------------------------------------------------ */ @@ -408,8 +411,8 @@ public class SslConnection extends AbstractConnection implements AsyncConnection } // If we are reading into the temp buffer and it has some content, then we should be dispatched. - if (toFill==_unwrapBuf && _unwrapBuf.hasContent()) - _aEndp.asyncDispatch(); + if (toFill==_unwrapBuf && _unwrapBuf.hasContent() && !_connection.isSuspended()) + _aEndp.dispatch(); } finally { @@ -550,7 +553,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection break; case BUFFER_OVERFLOW: - _logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString()); + if (_logger.isDebugEnabled()) _logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString()); break; case OK: @@ -718,6 +721,11 @@ public class SslConnection extends AbstractConnection implements AsyncConnection process(null, null); } + public void dispatch() + { + _aEndp.dispatch(); + } + public void asyncDispatch() { _aEndp.asyncDispatch(); diff --git a/jetty-jmx/src/main/config/etc/jetty-jmx.xml b/jetty-jmx/src/main/config/etc/jetty-jmx.xml index a685eded058..4db0dbb473a 100644 --- a/jetty-jmx/src/main/config/etc/jetty-jmx.xml +++ b/jetty-jmx/src/main/config/etc/jetty-jmx.xml @@ -69,7 +69,7 @@ --> diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java index e4eab9e057f..085522f270a 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java @@ -245,21 +245,37 @@ public class MongoSessionManager extends NoSqlSessionManager { for (String name : attrs.keySet()) { - if ( __METADATA.equals(name) ) + if (__METADATA.equals(name)) { continue; } - + String attr = decodeName(name); Object value = decodeValue(attrs.get(name)); - session.doPutOrRemove(attr,value); - session.bindValue(attr,value); + + if (attrs.keySet().contains(name)) + { + session.doPutOrRemove(attr,value); + session.bindValue(attr,value); + } + else + { + session.doPutOrRemove(attr,value); + } + } + // cleanup, remove values from session, that don't exist in data anymore: + for (String name : session.getNames()) + { + if (!attrs.keySet().contains(name)) + { + session.doPutOrRemove(name,null); + session.unbindValue(name,session.getAttribute(name)); + } } } session.didActivate(); - - + return version; } catch (Exception e) @@ -348,7 +364,7 @@ public class MongoSessionManager extends NoSqlSessionManager BasicDBObject remove = new BasicDBObject(); BasicDBObject unsets = new BasicDBObject(); unsets.put(getContextKey(),1); - remove.put("$unsets",unsets); + remove.put("$unset",unsets); _sessions.update(key,remove); return true; diff --git a/jetty-npn/pom.xml b/jetty-npn/pom.xml index 243f4cdfa84..063dffda7a0 100644 --- a/jetty-npn/pom.xml +++ b/jetty-npn/pom.xml @@ -2,13 +2,35 @@ org.eclipse.jetty - jetty-project - 8.1.3-SNAPSHOT + jetty-parent + 19 4.0.0 org.eclipse.jetty.npn npn-api + 1.0.1-SNAPSHOT Jetty :: Next Protocol Negotiation :: API + + scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git + scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git + http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-npn + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.2.1 + + false + deploy + -Peclipse-release + clean install + + + + diff --git a/jetty-osgi/jetty-osgi-boot-jsp/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-boot-jsp/META-INF/MANIFEST.MF index 08335e141cb..3388173dcd0 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-boot-jsp/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Jetty-OSGi-Jasper integration Fragment-Host: org.eclipse.jetty.osgi.boot Bundle-SymbolicName: org.eclipse.jetty.osgi.boot.jsp -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Vendor: Mort Bay Consulting Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: com.sun.el;version="2.2.0";resolution:=optional, @@ -16,7 +16,7 @@ Import-Package: com.sun.el;version="2.2.0";resolution:=optional, javax.servlet.jsp.jstl.tlv;version="1.2.0";resolution:=optional, javax.servlet.jsp.resources;version="2.1.0", javax.servlet.jsp.tagext;version="2.1.0", - javax.servlet.resources;version="2.5.0", + javax.servlet.resources;version="2.6.0", org.apache.jasper;version="2.2.2";resolution:=optional, org.apache.jasper.compiler;version="2.2.2";resolution:=optional, org.apache.jasper.compiler.tagplugin;version="2.2.2";resolution:=optional, diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java index 3dc2f896c45..79854f2c99f 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/PluggableWebAppRegistrationCustomizerImpl.java @@ -28,98 +28,106 @@ import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; /** - * Plug bundles that contains tld files so that jasper will discover them - * and set them up in jetty. + * Plug bundles that contains tld files so that jasper will discover them and + * set them up in jetty. * - * For example: -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh - * Otherwise use an attribute to the WebAppDeployer - * <New class="org.eclipse.jetty.deploy.providers.WebAppProvider"> - * .... - * <Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles" default="" /></Set> - * <New> + * For example: + * -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet + * ,com.opensymphony.module.sitemesh Otherwise use an attribute to the + * WebAppDeployer <New + * class="org.eclipse.jetty.deploy.providers.WebAppProvider"> .... <Set + * name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles" + * default="" /></Set> <New> */ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistrationCustomizer { - /** - * To plug into jasper bundles that contain tld files - * please use a list of bundle's symbolic names: - * -Djetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh - */ - public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; - - /** - * Union of the tld bundles defined system wide and the one defines as an attribute of the AppProvider. - * @param provider - * @return - */ - private static Collection getTldBundles(OSGiAppProvider provider) - { - String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); - String att = (String)provider.getTldBundles(); - if (sysprop == null && att == null) - { - return Collections.emptySet(); - } - if (att == null) - { - att = sysprop; - } - else if (sysprop != null) - { - att = att + "," + sysprop; - } - - Collection tldbundles = new HashSet(); - StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false); - while (tokenizer.hasMoreTokens()) - { - tldbundles.add(tokenizer.nextToken()); - } - return tldbundles; - } - - /** - * @return The location of the jars that contain tld files. - * Jasper will discover them. - */ + /** + * To plug into jasper bundles that contain tld files please use a list of + * bundle's symbolic names: + * -Djetty.osgi.tldbundles=org.springframework.web.servlet + * ,com.opensymphony.module.sitemesh + */ + public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; + + /** + * Union of the tld bundles defined system wide and the one defines as an + * attribute of the AppProvider. + * + * @param provider + * @return + */ + private static Collection getTldBundles(OSGiAppProvider provider) + { + String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); + String att = (String) provider.getTldBundles(); + if (sysprop == null && att == null) { return Collections.emptySet(); } + if (att == null) + { + att = sysprop; + } + else if (sysprop != null) + { + att = att + "," + sysprop; + } + + Collection tldbundles = new HashSet(); + StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false); + while (tokenizer.hasMoreTokens()) + { + tldbundles.add(tokenizer.nextToken()); + } + return tldbundles; + } + + /** + * @return The location of the jars that contain tld files. Jasper will + * discover them. + */ public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception { - List urls = new ArrayList(); - //naive way of finding those bundles. - //lots of assumptions: for example we assume a single version of each bundle that would contain tld files. - //this is probably good enough as those tlds are loaded system-wide on jetty. - //to do better than this we need to do it on a per webapp basis. - //probably using custom properties in the ContextHandler service - //and mirroring those in the MANIFEST.MF - - Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); - Collection tldbundles = getTldBundles(provider); - for (Bundle bundle : bundles) - { - if (tldbundles.contains(bundle.getSymbolicName())) - { - registerTldBundle(locatorHelper, bundle, urls); - } - } - - return urls.toArray(new URL[urls.size()]); + List urls = new ArrayList(); + // naive way of finding those bundles. + // lots of assumptions: for example we assume a single version of each + // bundle that would contain tld files. + // this is probably good enough as those tlds are loaded system-wide on + // jetty. + // to do better than this we need to do it on a per webapp basis. + // probably using custom properties in the ContextHandler service + // and mirroring those in the MANIFEST.MF + + Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); + Collection tldbundles = getTldBundles(provider); + for (Bundle bundle : bundles) + { + if (tldbundles.contains(bundle.getSymbolicName())) + { + registerTldBundle(locatorHelper, bundle, urls); + } + } + + return urls.toArray(new URL[urls.size()]); } - + /** * Resolves the bundle that contains tld files as a set of URLs that will be - * passed to jasper as a URLClassLoader later on. - * Usually that would be a single URL per bundle. - * But we do some more work if there are jars embedded in the bundle. + * passed to jasper as a URLClassLoader later on. Usually that would be a + * single URL per bundle. But we do some more work if there are jars + * embedded in the bundle. * - * The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such - * 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 jasper TldScanner expects a URLClassloader to parse a jar for the + * /META-INF/*.tld it may contain. We place the bundles that we know contain + * such tag-libraries. Please note that it will work if and only if the + * bundle is a jar (!) Currently we just hardcode the bundle that contains + * the jstl implemenation. * - * A workaround when the tld cannot be parsed with this method is to copy and paste it inside the WEB-INF of the webapplication where it is used. + * A workaround when the tld cannot be parsed with this method is to copy + * and paste it inside the WEB-INF of the webapplication where it is used. * - * Support only 2 types of packaging for the bundle: - the bundle is a jar (recommended for runtime.) - the bundle is a folder and contain jars in the root - * and/or in the lib folder (nice for PDE developement situations) Unsupported: the bundle is a jar that embeds more jars. + * 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) + * Unsupported: the bundle is a jar that embeds more jars. * * @param locatorHelper * @param bundle @@ -152,9 +160,9 @@ public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistra } else { - urls.add(jasperLocation.toURI().toURL()); + urls.add(jasperLocation.toURI().toURL()); } } - + } \ No newline at end of file diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java index 197c7497476..8e1a9b988db 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jasper/WebappRegistrationCustomizerImpl.java @@ -36,48 +36,52 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** - * Fix various shortcomings with the way jasper parses the tld files. - * Plugs the JSTL tlds assuming that they are packaged with the bundle that contains the JSTL classes. + * Fix various shortcomings with the way jasper parses the tld files. Plugs the + * JSTL tlds assuming that they are packaged with the bundle that contains the + * JSTL classes. *

- * Pluggable tlds at the server level are handled by {@link PluggableWebAppRegistrationCustomizerImpl}. + * Pluggable tlds at the server level are handled by + * {@link PluggableWebAppRegistrationCustomizerImpl}. *

*/ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCustomizer { - - /** - * Default name of a class that belongs to the jstl bundle. - * From that class we locate the corresponding bundle and register it - * as a bundle that contains tld files. - */ - private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag"; - //used to be "org.apache.jasper.runtime.JspFactoryImpl" but now - //the standard tag library implementation are stored in a separate bundle. - - //DISABLED please use the tld bundle argument for the OSGiAppProvider -// /** -// * Default name of a class that belongs to the bundle where the Java server Faces tld files are defined. -// * This is the sun's reference implementation. -// */ -// private static String DEFAUT_JSF_IMPL_CLASS = "com.sun.faces.config.ConfigureListener"; - /** - * Default jsp factory implementation. - * Idally jasper is osgified and we can use services. - * In the mean time we statically set the jsp factory implementation. - * bug #299733 - */ - private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl"; - + /** + * Default name of a class that belongs to the jstl bundle. From that class + * we locate the corresponding bundle and register it as a bundle that + * contains tld files. + */ + private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag"; + + // used to be "org.apache.jasper.runtime.JspFactoryImpl" but now + // the standard tag library implementation are stored in a separate bundle. + + // DISABLED please use the tld bundle argument for the OSGiAppProvider + // /** + // * Default name of a class that belongs to the bundle where the Java + // server Faces tld files are defined. + // * This is the sun's reference implementation. + // */ + // private static String DEFAUT_JSF_IMPL_CLASS = + // "com.sun.faces.config.ConfigureListener"; + + /** + * Default jsp factory implementation. Idally jasper is osgified and we can + * use services. In the mean time we statically set the jsp factory + * implementation. bug #299733 + */ + private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl"; + public WebappRegistrationCustomizerImpl() { fixupDtdResolution(); - + try { - //sanity check: + // sanity check: Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet"); - //System.err.println("found the jsp servlet: " + cl.getName()); + // System.err.println("found the jsp servlet: " + cl.getName()); } catch (Exception e) { @@ -87,18 +91,18 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto } try { - //bug #299733 + // bug #299733 JspFactory fact = JspFactory.getDefaultFactory(); if (fact == null) - { //bug #299733 - //JspFactory does a simple Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") - //however its bundles does not import the jasper package - //so it fails. let's help things out: - fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader() - .loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance(); + { // bug #299733 + // JspFactory does a simple + // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") + // however its bundles does not import the jasper package + // so it fails. let's help things out: + fact = (JspFactory) JettyBootstrapActivator.class.getClassLoader().loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance(); JspFactory.setDefaultFactory(fact); } - + } catch (Exception e) { @@ -106,84 +110,90 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto e.printStackTrace(); } } - + /** - * The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such - * 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 jasper TldScanner expects a URLClassloader to parse a jar for the + * /META-INF/*.tld it may contain. We place the bundles that we know contain + * such tag-libraries. Please note that it will work if and only if the + * bundle is a jar (!) Currently we just hardcode the bundle that contains + * the jstl implemenation. * - * A workaround when the tld cannot be parsed with this method is to copy and paste it inside the WEB-INF of the webapplication where it is used. + * A workaround when the tld cannot be parsed with this method is to copy + * and paste it inside the WEB-INF of the webapplication where it is used. * - * Support only 2 types of packaging for the bundle: - the bundle is a jar (recommended for runtime.) - the bundle is a folder and contain jars in the root - * and/or in the lib folder (nice for PDE developement situations) Unsupported: the bundle is a jar that embeds more jars. + * 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) + * Unsupported: the bundle is a jar that embeds more jars. * * @return array of URLs * @throws Exception */ public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception { + HashSet> classesToAddToTheTldBundles = new HashSet>(); - //Look for the jstl bundle - //We assume the jstl's tlds are defined there. - //We assume that the jstl bundle is imported by this bundle - //So we can look for this class using this bundle's classloader: - Class jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); - - classesToAddToTheTldBundles.add(jstlClass); - + // Look for the jstl bundle + // We assume the jstl's tlds are defined there. + // We assume that the jstl bundle is imported by this bundle + // So we can look for this class using this bundle's classloader: + Class jstlClass = WebappRegistrationCustomizerImpl.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); + + classesToAddToTheTldBundles.add(jstlClass); + ArrayList urls = new ArrayList(); - for (Class cl : classesToAddToTheTldBundles) - { - Bundle tldBundle = FrameworkUtil.getBundle(cl); - File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle); - if (tldBundleLocation != null && tldBundleLocation.isDirectory()) - { - // try to find the jar files inside this folder - for (File f : tldBundleLocation.listFiles()) - { - if (f.getName().endsWith(".jar") && f.isFile()) - { - urls.add(f.toURI().toURL()); - } - else if (f.isDirectory() && f.getName().equals("lib")) - { - for (File f2 : tldBundleLocation.listFiles()) - { - if (f2.getName().endsWith(".jar") && f2.isFile()) - { - urls.add(f2.toURI().toURL()); - } - } - } - } - - } - else if (tldBundleLocation != null) - { - urls.add(tldBundleLocation.toURI().toURL()); - } - } - return urls.toArray(new URL[urls.size()]); + for (Class cl : classesToAddToTheTldBundles) + { + Bundle tldBundle = FrameworkUtil.getBundle(cl); + File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle); + if (tldBundleLocation != null && tldBundleLocation.isDirectory()) + { + // try to find the jar files inside this folder + for (File f : tldBundleLocation.listFiles()) + { + if (f.getName().endsWith(".jar") && f.isFile()) + { + urls.add(f.toURI().toURL()); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : tldBundleLocation.listFiles()) + { + if (f2.getName().endsWith(".jar") && f2.isFile()) + { + urls.add(f2.toURI().toURL()); + } + } + } + } + + } + else if (tldBundleLocation != null) + { + urls.add(tldBundleLocation.toURI().toURL()); + } + } + return urls.toArray(new URL[urls.size()]); } - - - + /** - * Jasper resolves the dtd when it parses a taglib descriptor. - * It uses this code to do that: ParserUtils.getClass().getResourceAsStream(resourcePath); where - * resourcePath is for example: /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd Unfortunately, - * the dtd file is not in the exact same classloader as - * ParserUtils class and the dtds are packaged in 2 separate bundles. - * OSGi does not look in the dependencies' classloader when a resource is searched. + * Jasper resolves the dtd when it parses a taglib descriptor. It uses this + * code to do that: + * ParserUtils.getClass().getResourceAsStream(resourcePath); where + * resourcePath is for example: + * /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd Unfortunately, the + * dtd file is not in the exact same classloader as ParserUtils class and + * the dtds are packaged in 2 separate bundles. OSGi does not look in the + * dependencies' classloader when a resource is searched. *

- * The workaround consists of setting the entity resolver. That is a patch - * added to the version of glassfish-jasper-jetty. IT is also present in the latest - * version of glassfish jasper. Could not use introspection to set new value - * on a static friendly field :( + * The workaround consists of setting the entity resolver. That is a patch + * added to the version of glassfish-jasper-jetty. IT is also present in the + * latest version of glassfish jasper. Could not use introspection to set + * new value on a static friendly field :( *

*/ - void fixupDtdResolution() + void fixupDtdResolution() { try { @@ -198,29 +208,23 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto } /** - * Instead of using the ParserUtil's classloader, we use a class that is indeed next to the resource for sure. + * Instead of using the ParserUtil's classloader, we use a class that is + * indeed next to the resource for sure. */ static class MyFixedupEntityResolver implements EntityResolver { /** * Same values than in ParserUtils... */ - static final String[] CACHED_DTD_PUBLIC_IDS = - { Constants.TAGLIB_DTD_PUBLIC_ID_11, Constants.TAGLIB_DTD_PUBLIC_ID_12, - Constants.WEBAPP_DTD_PUBLIC_ID_22, Constants.WEBAPP_DTD_PUBLIC_ID_23, }; + static final String[] CACHED_DTD_PUBLIC_IDS = { Constants.TAGLIB_DTD_PUBLIC_ID_11, Constants.TAGLIB_DTD_PUBLIC_ID_12, + Constants.WEBAPP_DTD_PUBLIC_ID_22, Constants.WEBAPP_DTD_PUBLIC_ID_23, }; - static final String[] CACHED_DTD_RESOURCE_PATHS = - { Constants.TAGLIB_DTD_RESOURCE_PATH_11, - Constants.TAGLIB_DTD_RESOURCE_PATH_12, - Constants.WEBAPP_DTD_RESOURCE_PATH_22, - Constants.WEBAPP_DTD_RESOURCE_PATH_23, }; + static final String[] CACHED_DTD_RESOURCE_PATHS = { Constants.TAGLIB_DTD_RESOURCE_PATH_11, Constants.TAGLIB_DTD_RESOURCE_PATH_12, + Constants.WEBAPP_DTD_RESOURCE_PATH_22, Constants.WEBAPP_DTD_RESOURCE_PATH_23, }; + + static final String[] CACHED_SCHEMA_RESOURCE_PATHS = { Constants.TAGLIB_SCHEMA_RESOURCE_PATH_20, Constants.TAGLIB_SCHEMA_RESOURCE_PATH_21, + Constants.WEBAPP_SCHEMA_RESOURCE_PATH_24, Constants.WEBAPP_SCHEMA_RESOURCE_PATH_25, }; - static final String[] CACHED_SCHEMA_RESOURCE_PATHS = { - Constants.TAGLIB_SCHEMA_RESOURCE_PATH_20, - Constants.TAGLIB_SCHEMA_RESOURCE_PATH_21, - Constants.WEBAPP_SCHEMA_RESOURCE_PATH_24, - Constants.WEBAPP_SCHEMA_RESOURCE_PATH_25, - }; public InputSource resolveEntity(String publicId, String systemId) throws SAXException { for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++) @@ -241,10 +245,7 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto input = this.getClass().getResourceAsStream(resourcePath); } } - if (input == null) - { - throw new SAXException(Localizer.getMessage("jsp.error.internal.filenotfound",resourcePath)); - } + if (input == null) { throw new SAXException(Localizer.getMessage("jsp.error.internal.filenotfound", resourcePath)); } InputSource isrc = new InputSource(input); return isrc; } @@ -253,5 +254,5 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto return null; } } - + } diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java index 99a567cd70d..3f8f2874e65 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/FragmentActivator.java @@ -19,15 +19,14 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; /** - * Pseudo fragment activator. - * Called by the main org.eclipse.jetty.osgi.boot bundle. - * Please note: this is not a real BundleActivator. Simply something called back by - * the host bundle. + * Pseudo fragment activator. Called by the main org.eclipse.jetty.osgi.boot + * bundle. Please note: this is not a real BundleActivator. Simply something + * called back by the host bundle. *

- * It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: - * this is because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name - * of this fragment. From that name, the PackageadminTracker will call - * this class. IN a different package it won't be called. + * It must be placed in the org.eclipse.jetty.osgi.boot.jsp package: this is + * because org.eclipse.jetty.osgi.boot.jsp is the sympbolic-name of this + * fragment. From that name, the PackageadminTracker will call this class. IN a + * different package it won't be called. *

*/ public class FragmentActivator implements BundleActivator @@ -35,8 +34,9 @@ public class FragmentActivator implements BundleActivator /** * */ - public void start(BundleContext context) throws Exception { - System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString()); + public void start(BundleContext context) throws Exception + { + System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString()); WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new WebappRegistrationCustomizerImpl()); WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS.add(new PluggableWebAppRegistrationCustomizerImpl()); } @@ -44,7 +44,8 @@ public class FragmentActivator implements BundleActivator /** * */ - public void stop(BundleContext context) throws Exception { - + public void stop(BundleContext context) throws Exception + { + } } diff --git a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java index 510261db4d0..ad31de22e7f 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot-jsp/src/main/java/org/eclipse/jetty/osgi/boot/jsp/TagLibOSGiConfiguration.java @@ -35,123 +35,120 @@ import org.osgi.util.tracker.ServiceTracker; * Replacement for {@link TagLibConfiguration} for the OSGi integration. *

*

- * In the case of a WAB, tlds can be located in OSGi bundles that are dependencies - * of the WAB. - * It is expected that each WAB lists the symbolic-names of the bundles that contain - * tld files. The list is defined as the value of the header 'Require-TldBundle' + * In the case of a WAB, tlds can be located in OSGi bundles that are + * dependencies of the WAB. It is expected that each WAB lists the + * symbolic-names of the bundles that contain tld files. The list is defined as + * the value of the header 'Require-TldBundle' *

*

- * Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971 + * Discussions about this are logged in + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971 *

*/ public class TagLibOSGiConfiguration extends TagLibConfiguration { private static final Logger LOG = Log.getLogger(TagLibOSGiConfiguration.class); - private ServiceTracker packageAdminServiceTracker = null; - - /** - * Override the preConfigure; locates the bundles that contain - * tld files according to the value of the manifest header Require-TldBundle. - *

- * Set or add to the property TldProcessor.TLDResources the list of located jars - * so that the super class will scan those. - *

- */ + private ServiceTracker packageAdminServiceTracker = null; + + /** + * Override the preConfigure; locates the bundles that contain tld files + * according to the value of the manifest header Require-TldBundle. + *

+ * Set or add to the property TldProcessor.TLDResources the list of located + * jars so that the super class will scan those. + *

+ */ public void preConfigure(WebAppContext context) throws Exception { - String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); - if (requireTldBundle != null) - { - Collection resources = getRequireTldBundleAsJettyResources(context, requireTldBundle); - if (resources != null && !resources.isEmpty()) - { - Collection previouslySet = (Collection) - context.getAttribute(TagLibConfiguration.TLD_RESOURCES); - if (previouslySet != null) - { - resources.addAll(previouslySet); - } - context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources); - } - } - super.preConfigure(context); + String requireTldBundle = (String) context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + if (requireTldBundle != null) + { + Collection resources = getRequireTldBundleAsJettyResources(context, requireTldBundle); + if (resources != null && !resources.isEmpty()) + { + Collection previouslySet = (Collection) context.getAttribute(TagLibConfiguration.TLD_RESOURCES); + if (previouslySet != null) + { + resources.addAll(previouslySet); + } + context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources); + } + } + super.preConfigure(context); } /** - * @param requireTldBundle The comma separated list of bundles' symbolic names - * that contain tld for this osgi webapp. + * @param requireTldBundle The comma separated list of bundles' symbolic + * names that contain tld for this osgi webapp. * @return The collection of jars or folders that match those bundles. */ - private Collection getRequireTldBundleAsJettyResources( - WebAppContext context, String requireTldBundle) + private Collection getRequireTldBundleAsJettyResources(WebAppContext context, String requireTldBundle) { - Bundle bundle = (Bundle) - context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); - PackageAdmin packAdmin = getBundleAdmin(); - String[] symbNames = requireTldBundle.split(", "); - Collection tlds = new LinkedHashSet(); - for (String symbName : symbNames) - { - Bundle[] bs = packAdmin.getBundles(symbName, null); - if (bs == null || bs.length == 0) - { - throw new IllegalArgumentException("Unable to locate the bundle '" - + symbName + "' specified in the " - + OSGiWebappConstants.REQUIRE_TLD_BUNDLE - + " of the manifest of " - + bundle.getSymbolicName()); - } - //take the first one as it is the most recent version? - Enumeration en = bs[0].findEntries("META-INF", "*.tld", false); - boolean atLeastOneTldFound = false; - while (en.hasMoreElements()) - { - atLeastOneTldFound = true; - URL oriUrl = en.nextElement(); - URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl); - Resource tldResource; - try - { - tldResource = Resource.newResource(url); - } - catch (IOException e) - { - throw new IllegalArgumentException("Unable to locate the " - + "tld resource in '" - + url.toString() - + "' in the bundle '" + bs[0].getSymbolicName() - + "' while registering the " - + OSGiWebappConstants.REQUIRE_TLD_BUNDLE - + " of the manifest of " - + bundle.getSymbolicName(), e); - } - tlds.add(tldResource); - } - if (!atLeastOneTldFound) - { - LOG.warn("No '/META-INF/*.tld' resources were found " - + " in the bundle '" + bs[0].getSymbolicName() - + "' while registering the " - + OSGiWebappConstants.REQUIRE_TLD_BUNDLE - + " of the manifest of " - + bundle.getSymbolicName()); - } - } - return tlds; + Bundle bundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); + PackageAdmin packAdmin = getBundleAdmin(); + String[] symbNames = requireTldBundle.split(", "); + Collection tlds = new LinkedHashSet(); + for (String symbName : symbNames) + { + Bundle[] bs = packAdmin.getBundles(symbName, null); + if (bs == null || bs.length == 0) + { + throw new IllegalArgumentException("Unable to locate the bundle '" + symbName + + "' specified in the " + + OSGiWebappConstants.REQUIRE_TLD_BUNDLE + + " of the manifest of " + + bundle.getSymbolicName()); + } + // take the first one as it is the most recent version? + Enumeration en = bs[0].findEntries("META-INF", "*.tld", false); + boolean atLeastOneTldFound = false; + while (en.hasMoreElements()) + { + atLeastOneTldFound = true; + URL oriUrl = en.nextElement(); + URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl); + Resource tldResource; + try + { + tldResource = Resource.newResource(url); + } + catch (IOException e) + { + throw new IllegalArgumentException("Unable to locate the " + "tld resource in '" + + url.toString() + + "' in the bundle '" + + bs[0].getSymbolicName() + + "' while registering the " + + OSGiWebappConstants.REQUIRE_TLD_BUNDLE + + " of the manifest of " + + bundle.getSymbolicName(), e); + } + tlds.add(tldResource); + } + if (!atLeastOneTldFound) + { + LOG.warn("No '/META-INF/*.tld' resources were found " + " in the bundle '" + + bs[0].getSymbolicName() + + "' while registering the " + + OSGiWebappConstants.REQUIRE_TLD_BUNDLE + + " of the manifest of " + + bundle.getSymbolicName()); + } + } + return tlds; } - - private PackageAdmin getBundleAdmin() - { - if (packageAdminServiceTracker == null) - { - Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle(); - packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(), - PackageAdmin.class.getName(), null); - packageAdminServiceTracker.open(); - } - return (PackageAdmin) packageAdminServiceTracker.getService(); - } - + + private PackageAdmin getBundleAdmin() + { + if (packageAdminServiceTracker == null) + { + Bundle bootBundle = ((BundleReference) OSGiWebappConstants.class.getClassLoader()).getBundle(); + packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(), PackageAdmin.class.getName(), null); + packageAdminServiceTracker.open(); + } + return (PackageAdmin) packageAdminServiceTracker.getService(); + } + } diff --git a/jetty-osgi/jetty-osgi-boot-logback/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-boot-logback/META-INF/MANIFEST.MF index 0400545f14d..39e193236cd 100644 --- a/jetty-osgi/jetty-osgi-boot-logback/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-boot-logback/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Jetty-OSGi-Logback integration Fragment-Host: org.eclipse.jetty.osgi.boot Bundle-SymbolicName: org.eclipse.jetty.osgi.boot.logback;singleton:=true -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Vendor: Mort Bay Consulting Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: ch.qos.logback.classic, diff --git a/jetty-osgi/jetty-osgi-boot-warurl/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-boot-warurl/META-INF/MANIFEST.MF index c8f2dcf1bd6..64712ef5f7e 100644 --- a/jetty-osgi/jetty-osgi-boot-warurl/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-boot-warurl/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Support for rfc66 war url scheme Bundle-SymbolicName: org.eclipse.jetty.osgi.boot.warurl;singleton:=true -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Activator: org.eclipse.jetty.osgi.boot.warurl.WarUrlActivator Bundle-Vendor: Mort Bay Consulting Bundle-RequiredExecutionEnvironment: JavaSE-1.6 diff --git a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/WarUrlStreamHandler.java b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/WarUrlStreamHandler.java index 654fb9b078f..0f861629a81 100644 --- a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/WarUrlStreamHandler.java +++ b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/WarUrlStreamHandler.java @@ -24,12 +24,13 @@ import java.util.jar.Manifest; import org.eclipse.jetty.osgi.boot.warurl.internal.WarBundleManifestGenerator; import org.eclipse.jetty.osgi.boot.warurl.internal.WarURLConnection; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; import org.osgi.service.url.AbstractURLStreamHandlerService; /** - * RFC-66: support for the "war" protocol - * We are reusing the parsing of the query string from jetty. - * If we wanted to not depend on jetty at all we could duplicate that method here + * RFC-66: support for the "war" protocol We are reusing the parsing of the + * query string from jetty. If we wanted to not depend on jetty at all we could + * duplicate that method here */ public class WarUrlStreamHandler extends AbstractURLStreamHandlerService { @@ -40,11 +41,11 @@ public class WarUrlStreamHandler extends AbstractURLStreamHandlerService @Override public URLConnection openConnection(URL url) throws IOException { - //remove the war scheme. + // remove the war scheme. URL actual = new URL(url.toString().substring("war:".length())); - - //let's do some basic tests: see if this is a folder or not. - //if it is a folder. we will try to support it. + + // let's do some basic tests: see if this is a folder or not. + // if it is a folder. we will try to support it. if (actual.getProtocol().equals("file")) { File file = new File(URIUtil.encodePath(actual.getPath())); @@ -52,34 +53,48 @@ public class WarUrlStreamHandler extends AbstractURLStreamHandlerService { if (file.isDirectory()) { - //TODO (not mandatory for rfc66 though) + // TODO (not mandatory for rfc66 though) } } } - - // if (actual.toString().startsWith("file:/") && ! actual.to) + + // if (actual.toString().startsWith("file:/") && ! actual.to) URLConnection ori = (URLConnection) actual.openConnection(); + ori.setDefaultUseCaches(Resource.getDefaultUseCaches()); JarURLConnection jarOri = null; - try { + try + { if (ori instanceof JarURLConnection) { - jarOri = (JarURLConnection)ori; + jarOri = (JarURLConnection) ori; } else { - jarOri = (JarURLConnection) new URL("jar:"+actual.toString() + "!/").openConnection(); + jarOri = (JarURLConnection) new URL("jar:" + actual.toString() + "!/").openConnection(); + jarOri.setDefaultUseCaches(Resource.getDefaultUseCaches()); } - Manifest mf = WarBundleManifestGenerator.createBundleManifest( - jarOri.getManifest(), url, jarOri.getJarFile()); - try { jarOri.getJarFile().close(); jarOri = null; } catch (Throwable t) {} - return new WarURLConnection(actual,mf); + Manifest mf = WarBundleManifestGenerator.createBundleManifest(jarOri.getManifest(), url, jarOri.getJarFile()); + try + { + jarOri.getJarFile().close(); + jarOri = null; + } + catch (Throwable t) + { + } + return new WarURLConnection(actual, mf); } finally { - if (jarOri != null) try { jarOri.getJarFile().close(); } catch (Throwable t) {} + if (jarOri != null) try + { + jarOri.getJarFile().close(); + } + catch (Throwable t) + { + } } - + } - } diff --git a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarURLConnection.java b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarURLConnection.java index 744a43c2ecc..c19a2fa6bfe 100644 --- a/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarURLConnection.java +++ b/jetty-osgi/jetty-osgi-boot-warurl/src/main/java/org/eclipse/jetty/osgi/boot/warurl/internal/WarURLConnection.java @@ -31,6 +31,7 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.Resource; /** * Facade for a URLConnection that will read a jar and substitute its @@ -106,6 +107,7 @@ public class WarURLConnection extends URLConnection { super(url); _conn = url.openConnection(); + _conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); _mf = mf; } @Override diff --git a/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF index b5390842caf..4cf661c47f0 100644 --- a/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-boot/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Jetty OSGi bootstrap Bundle-SymbolicName: org.eclipse.jetty.osgi.boot Bundle-Vendor: Mort Bay Consulting -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Activator: org.eclipse.jetty.osgi.boot.JettyBootstrapActivator Import-Package: javax.mail;version="1.4.0";resolution:=optional, javax.mail.event;version="1.4.0";resolution:=optional, @@ -14,20 +14,20 @@ Import-Package: javax.mail;version="1.4.0";resolution:=optional, javax.servlet.http;version="2.6", javax.transaction;version="1.1.0";resolution:=optional, javax.transaction.xa;version="1.1.0";resolution:=optional, - org.eclipse.jetty.annotations;version="8.1.2";resolution:=optional, - org.eclipse.jetty.deploy;version="8.1.2", - org.eclipse.jetty.deploy.providers;version="8.1.2", - org.eclipse.jetty.http;version="8.1.2", - org.eclipse.jetty.nested;version="8.1.2";resolution:=optional, - org.eclipse.jetty.server;version="8.1.2", - org.eclipse.jetty.server.handler;version="8.1.2", - org.eclipse.jetty.servlet;version="8.1.2", - org.eclipse.jetty.util;version="8.1.2", - org.eclipse.jetty.util.component;version="8.1.2", - org.eclipse.jetty.util.log;version="8.1.2", - org.eclipse.jetty.util.resource;version="8.1.2", - org.eclipse.jetty.webapp;version="8.1.2", - org.eclipse.jetty.xml;version="8.1.2", + org.eclipse.jetty.annotations;version="8.1.3";resolution:=optional, + org.eclipse.jetty.deploy;version="8.1.3", + org.eclipse.jetty.deploy.providers;version="8.1.3", + org.eclipse.jetty.http;version="8.1.3", + org.eclipse.jetty.nested;version="8.1.3";resolution:=optional, + org.eclipse.jetty.server;version="8.1.3", + org.eclipse.jetty.server.handler;version="8.1.3", + org.eclipse.jetty.servlet;version="8.1.3", + org.eclipse.jetty.util;version="8.1.3", + org.eclipse.jetty.util.component;version="8.1.3", + org.eclipse.jetty.util.log;version="8.1.3", + org.eclipse.jetty.util.resource;version="8.1.3", + org.eclipse.jetty.webapp;version="8.1.3", + org.eclipse.jetty.xml;version="8.1.3", org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, @@ -41,8 +41,8 @@ Import-Package: javax.mail;version="1.4.0";resolution:=optional, org.xml.sax.helpers Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Classpath: . -Export-Package: org.eclipse.jetty.osgi.boot;version="8.1.2", - org.eclipse.jetty.osgi.nested;version="8.1.2", - org.eclipse.jetty.osgi.boot.utils;version="8.1.2", - org.eclipse.jetty.osgi.annotations;version="8.1.2" +Export-Package: org.eclipse.jetty.osgi.boot;version="8.1.3", + org.eclipse.jetty.osgi.nested;version="8.1.3", + org.eclipse.jetty.osgi.boot.utils;version="8.1.3", + org.eclipse.jetty.osgi.annotations;version="8.1.3" DynamicImport-Package: org.eclipse.jetty.*;version="[8.1,9)" diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-default.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-default.xml index 1202ae7023c..f8d1b680645 100644 --- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-default.xml +++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-default.xml @@ -21,6 +21,7 @@ 10 200 + false @@ -45,19 +46,6 @@ - - - - - - - - - - - - - @@ -82,10 +70,6 @@ - - - - @@ -95,7 +79,7 @@ org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern - .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ + .*/servlet-api-[^/]*\.jar$ @@ -126,8 +110,12 @@ true true 1000 + false + false - + + + java.naming.factory.initial diff --git a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml index 67d84e9df8f..b5eeaf3ab06 100644 --- a/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml +++ b/jetty-osgi/jetty-osgi-boot/jettyhome/etc/jetty-osgi-nested-default.xml @@ -54,10 +54,6 @@ - - - - @@ -67,7 +63,7 @@ org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern - .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ + .*/servlet-api-[^/]*\.jar$ @@ -82,7 +78,7 @@ 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 --> - @@ -99,8 +95,12 @@ true true 1000 + false + false - + + + java.naming.factory.initial diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java index e3c0f9040db..78d27131836 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java @@ -66,15 +66,19 @@ public class JettyBootstrapActivator implements BundleActivator } private ServiceRegistration _registeredServer; + private Server _server; + private JettyContextHandlerServiceTracker _jettyContextHandlerTracker; + private PackageAdminServiceTracker _packageAdminServiceTracker; + private BundleTracker _webBundleTracker; + private BundleContext _bundleContext; - -// private ServiceRegistration _jettyServerFactoryService; + + // private ServiceRegistration _jettyServerFactoryService; private JettyServerServiceTracker _jettyServerServiceTracker; - /** * Setup a new jetty Server, registers it as a service. Setup the Service @@ -93,30 +97,31 @@ public class JettyBootstrapActivator implements BundleActivator // should activate. _packageAdminServiceTracker = new PackageAdminServiceTracker(context); - _jettyServerServiceTracker = new JettyServerServiceTracker(); - context.addServiceListener(_jettyServerServiceTracker,"(objectclass=" + Server.class.getName() + ")"); + _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); - //Register the Jetty Server Factory as a ManagedServiceFactory: -// Properties jettyServerMgdFactoryServiceProps = new Properties(); -// jettyServerMgdFactoryServiceProps.put("pid", OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID); -// _jettyServerFactoryService = context.registerService( -// ManagedServiceFactory.class.getName(), new JettyServersManagedFactory(), -// jettyServerMgdFactoryServiceProps); - _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker); // the tracker in charge of the actual deployment // and that will configure and start the jetty server. - context.addServiceListener(_jettyContextHandlerTracker,"(objectclass=" + ContextHandler.class.getName() + ")"); + context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")"); - //see if we shoult start a default jetty instance right now. + // see if we shoult start a default jetty instance right now. DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context); - - // now ready to support the Extender pattern: - _webBundleTracker = new BundleTracker(context, - Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer()); + + // now ready to support the Extender pattern: + _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer()); _webBundleTracker.open(); - + } /* @@ -129,12 +134,12 @@ public class JettyBootstrapActivator implements BundleActivator { try { - - if (_webBundleTracker != null) - { - _webBundleTracker.close(); - _webBundleTracker = null; - } + + if (_webBundleTracker != null) + { + _webBundleTracker.close(); + _webBundleTracker = null; + } if (_jettyContextHandlerTracker != null) { _jettyContextHandlerTracker.stop(); @@ -143,7 +148,7 @@ public class JettyBootstrapActivator implements BundleActivator } if (_jettyServerServiceTracker != null) { - _jettyServerServiceTracker.stop(); + _jettyServerServiceTracker.stop(); context.removeServiceListener(_jettyServerServiceTracker); _jettyServerServiceTracker = null; } @@ -165,31 +170,31 @@ public class JettyBootstrapActivator implements BundleActivator } finally { - _registeredServer = null; + _registeredServer = null; } } -// if (_jettyServerFactoryService != null) -// { -// try -// { -// _jettyServerFactoryService.unregister(); -// } -// catch (IllegalArgumentException ill) -// { -// // already unregistered. -// } -// finally -// { -// _jettyServerFactoryService = null; -// } -// } + // if (_jettyServerFactoryService != null) + // { + // try + // { + // _jettyServerFactoryService.unregister(); + // } + // catch (IllegalArgumentException ill) + // { + // // already unregistered. + // } + // finally + // { + // _jettyServerFactoryService = null; + // } + // } } finally { if (_server != null) { - _server.stop(); + _server.stop(); } INSTANCE = null; } @@ -200,27 +205,25 @@ public class JettyBootstrapActivator implements BundleActivator * registers it as an OSGi service. The tracker * {@link JettyContextHandlerServiceTracker} will do the actual deployment. * - * @param contributor - * 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 contributor 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 "/" * @throws Exception */ public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception { - checkBundleActivated(); - WebAppContext contextHandler = new WebAppContext(); + checkBundleActivated(); + WebAppContext contextHandler = new WebAppContext(); Dictionary dic = new Hashtable(); - 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); - if (requireTldBundle != null) { - dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle); + 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); + if (requireTldBundle != null) + { + dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle); } - contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); + contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic); } /** @@ -228,24 +231,20 @@ public class JettyBootstrapActivator implements BundleActivator * registers it as an OSGi service. The tracker * {@link JettyContextHandlerServiceTracker} will do the actual deployment. * - * @param contributor - * 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 dic - * TODO: parameter description + * @param contributor 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 dic TODO: parameter description * @throws Exception */ public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary dic) throws Exception { - checkBundleActivated(); + checkBundleActivated(); WebAppContext contextHandler = new WebAppContext(); - dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath); - dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath); - contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); + dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath); + dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath); + contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic); } /** @@ -253,16 +252,14 @@ public class JettyBootstrapActivator implements BundleActivator * registers it as an OSGi service. The tracker * {@link JettyContextHandlerServiceTracker} will do the actual deployment. * - * @param contributor - * The bundle that registers a new context - * @param contextFilePath - * The path to the file inside the bundle that defines the - * context. + * @param contributor The bundle that registers a new context + * @param contextFilePath The path to the file inside the bundle that + * defines the context. * @throws Exception */ public static void registerContext(Bundle contributor, String contextFilePath) throws Exception { - registerContext(contributor,contextFilePath,new Hashtable()); + registerContext(contributor, contextFilePath, new Hashtable()); } /** @@ -270,33 +267,30 @@ public class JettyBootstrapActivator implements BundleActivator * registers it as an OSGi service. The tracker * {@link JettyContextHandlerServiceTracker} will do the actual deployment. * - * @param contributor - * The bundle that registers a new context - * @param contextFilePath - * The path to the file inside the bundle that defines the - * context. - * @param dic - * TODO: parameter description + * @param contributor The bundle that registers a new context + * @param contextFilePath The path to the file inside the bundle that + * defines the context. + * @param dic TODO: parameter description * @throws Exception */ public static void registerContext(Bundle contributor, String contextFilePath, Dictionary dic) throws Exception { - checkBundleActivated(); + checkBundleActivated(); ContextHandler contextHandler = new ContextHandler(); - dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH,contextFilePath); - dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE,Boolean.TRUE.toString()); - contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic); + dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath); + dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString()); + contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic); } public static void unregister(String contextPath) { // todo } - + /** * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy - * when one fo the static methods to register a webapp is called we should make sure that - * the bundle is started. + * when one fo the static methods to register a webapp is called we should + * make sure that the bundle is started. */ private static void checkBundleActivated() { @@ -313,7 +307,7 @@ public class JettyBootstrapActivator implements BundleActivator } } } - + /** * @return The bundle context for this bundle. */ @@ -322,6 +316,5 @@ public class JettyBootstrapActivator implements BundleActivator checkBundleActivated(); return INSTANCE._bundleContext; } - } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java index 2debdc43418..5df56950d0e 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiAppProvider.java @@ -348,7 +348,7 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider } catch (IOException e) { - e.printStackTrace(); + LOG.warn(e); return null; } } @@ -369,11 +369,11 @@ public class OSGiAppProvider extends ScanningAppProvider implements AppProvider } catch (IOException e) { - e.printStackTrace(); + LOG.warn(e); return null; } } - + public boolean isExtract() { return _extractWars; diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java index c07ab027131..17ea8d22cc7 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java @@ -39,7 +39,7 @@ import org.osgi.framework.BundleContext; 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 @@ -69,7 +69,7 @@ public class DefaultJettyAtJettyHomeHelper { * 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, @@ -88,81 +88,76 @@ public class DefaultJettyAtJettyHomeHelper { * that might use them as part of their properties. *

*/ - public static void startJettyAtJettyHome(BundleContext bundleContext) + public static void startJettyAtJettyHome(BundleContext bundleContext) throws Exception { - String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME); - String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE); - File jettyHome = null; - Bundle jettyHomeBundle = null; - if (jettyHomeSysProp != null) - { - jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp); - //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."); - } - jettyHome = new File(jettyHomeSysProp); - if (!jettyHome.exists() || !jettyHome.isDirectory()) - { - LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp); - return; - } - } - else if (jettyHomeBundleSysProp != null) - { - jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp); - for (Bundle b : bundleContext.getBundles()) - { - if (b.getSymbolicName().equals(jettyHomeBundleSysProp)) - { - jettyHomeBundle = b; - break; - } - } - if (jettyHomeBundle == null) - { - LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp); - return; - } - - } - if (jettyHome == null && jettyHomeBundle == null) - { - LOG.warn("No default jetty started."); - return; - } - try - { - Server server = new Server(); - Dictionary properties = new Hashtable(); - properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME); - - String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle); - properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, configURLs); + String jettyHomeSysProp = System.getProperty(SYS_PROP_JETTY_HOME); + String jettyHomeBundleSysProp = System.getProperty(SYS_PROP_JETTY_HOME_BUNDLE); + File jettyHome = null; + Bundle jettyHomeBundle = null; + if (jettyHomeSysProp != null) + { + jettyHomeSysProp = resolvePropertyValue(jettyHomeSysProp); + //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."); + } + jettyHome = new File(jettyHomeSysProp); + if (!jettyHome.exists() || !jettyHome.isDirectory()) + { + LOG.warn("Unable to locate the jetty.home folder " + jettyHomeSysProp); + return; + } + } + else if (jettyHomeBundleSysProp != null) + { + jettyHomeBundleSysProp = resolvePropertyValue(jettyHomeBundleSysProp); + for (Bundle b : bundleContext.getBundles()) + { + if (b.getSymbolicName().equals(jettyHomeBundleSysProp)) + { + jettyHomeBundle = b; + break; + } + } + if (jettyHomeBundle == null) + { + LOG.warn("Unable to find the jetty.home.bundle named " + jettyHomeSysProp); + return; + } - 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)); + } + if (jettyHome == null && jettyHomeBundle == null) + { + LOG.warn("No default jetty started."); + return; + } + + Server server = new Server(); + Dictionary properties = new Hashtable(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME); + + String configURLs = jettyHome != null ? getJettyConfigurationURLs(jettyHome) : getJettyConfigurationURLs(jettyHomeBundle); + properties.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS, configURLs); + + 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)); + + bundleContext.registerService(Server.class.getName(), server, properties); + // hookNestedConnectorToBridgeServlet(server); - bundleContext.registerService(Server.class.getName(), server, properties); -// hookNestedConnectorToBridgeServlet(server); - } - catch (Throwable t) - { - t.printStackTrace(); - } } - + /** * 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 @@ -172,22 +167,25 @@ public class DefaultJettyAtJettyHomeHelper { */ private static String getJettyConfigurationURLs(File jettyhome) { - String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml"); - StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false); + String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES, "etc/jetty.xml"); + StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false); StringBuilder res = new StringBuilder(); while (tokenizer.hasMoreTokens()) { - String next = tokenizer.nextToken().trim(); - if (!next.startsWith("/") && next.indexOf(':') == -1) - { - try { - next = new File(jettyhome, next).toURI().toURL().toString(); - } catch (MalformedURLException e) { - e.printStackTrace(); - continue; - } - } - appendToCommaSeparatedList(res, next); + String next = tokenizer.nextToken().trim(); + if (!next.startsWith("/") && next.indexOf(':') == -1) + { + try + { + next = new File(jettyhome, next).toURI().toURL().toString(); + } + catch (MalformedURLException e) + { + LOG.warn(e); + continue; + } + } + appendToCommaSeparatedList(res, next); } return res.toString(); } @@ -202,108 +200,108 @@ public class DefaultJettyAtJettyHomeHelper { */ private static String getJettyConfigurationURLs(Bundle configurationBundle) { - String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml"); + String jettyetc = System.getProperty(SYS_PROP_JETTY_ETC_FILES,"etc/jetty.xml"); StringTokenizer tokenizer = new StringTokenizer(jettyetc,";,", false); StringBuilder res = new StringBuilder(); - + while (tokenizer.hasMoreTokens()) { String etcFile = tokenizer.nextToken().trim(); if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1) { - appendToCommaSeparatedList(res, etcFile); + appendToCommaSeparatedList(res, etcFile); } else { - Enumeration 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")) - { - 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"); - } - if (enUrls == null || !enUrls.hasMoreElements()) - { - System.err.println("Unable to locate a jetty configuration file for " + etcFile); - } - if (enUrls != null) - { - while (enUrls.hasMoreElements()) - { - appendToCommaSeparatedList(res, enUrls.nextElement().toString()); - } - } + Enumeration 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")) + { + 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"); + } + if (enUrls == null || !enUrls.hasMoreElements()) + { + LOG.warn("Unable to locate a jetty configuration file for " + etcFile); + } + if (enUrls != null) + { + while (enUrls.hasMoreElements()) + { + appendToCommaSeparatedList(res, enUrls.nextElement().toString()); + } + } } } return res.toString(); } - - private static void appendToCommaSeparatedList(StringBuilder buffer, String value) - { - if (buffer.length() != 0) - { - buffer.append(","); - } - buffer.append(value); - } - - private static void setProperty(Dictionary properties, String key, String value) - { - if (value != null) - { - properties.put(key, value); - } - } - - /** - * 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. - * - * @param value - * @return - */ - public static String resolvePropertyValue(String value) - { - int ind = value.indexOf("${"); - if (ind == -1) { - return value; - } - int ind2 = value.indexOf('}', ind); - 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()) - { - defaultValue = sysprop.substring(comma+1); - defaultValue = resolvePropertyValue(defaultValue); - sysprop = sysprop.substring(0,comma); - } - else - { - defaultValue = "${" + sysprop + "}"; - } - - String v = System.getProperty(sysprop); - - String reminder = value.length() > ind2 + 1 ? value.substring(ind2+1) : ""; - reminder = resolvePropertyValue(reminder); - if (v != null) - { - return value.substring(0, ind) + v + reminder; - } - else - { - return value.substring(0, ind) + defaultValue + reminder; - } - } + + private static void appendToCommaSeparatedList(StringBuilder buffer, String value) + { + if (buffer.length() != 0) + { + buffer.append(","); + } + buffer.append(value); + } + + private static void setProperty(Dictionary properties, String key, String value) + { + if (value != null) + { + properties.put(key, value); + } + } + + /** + * 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. + * + * @param value + * @return + */ + public static String resolvePropertyValue(String value) + { + int ind = value.indexOf("${"); + if (ind == -1) { + return value; + } + int ind2 = value.indexOf('}', ind); + 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()) + { + defaultValue = sysprop.substring(comma+1); + defaultValue = resolvePropertyValue(defaultValue); + sysprop = sysprop.substring(0,comma); + } + else + { + defaultValue = "${" + sysprop + "}"; + } + + String v = System.getProperty(sysprop); + + String reminder = value.length() > ind2 + 1 ? value.substring(ind2+1) : ""; + reminder = resolvePropertyValue(reminder); + if (v != null) + { + return value.substring(0, ind) + v + reminder; + } + else + { + return value.substring(0, ind) + defaultValue + reminder; + } + } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java index 200647cecc9..88f1d7f4f5c 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java @@ -18,50 +18,53 @@ import java.util.Properties; import org.eclipse.jetty.osgi.boot.OSGiServerConstants; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; /** - * Deploy the jetty server instances when they are registered as an OSGi service. + * Deploy the jetty server instances when they are registered as an OSGi + * service. */ 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. + * Servers indexed by PIDs. PIDs are generated by the ConfigurationAdmin + * service. */ private Map _serversIndexedByName = new HashMap(); + /** The context-handler to deactivate indexed by ServerInstanceWrapper */ private Map _indexByServiceReference = new HashMap(); - /** * Stops each one of the registered servers. */ public void stop() { - //not sure that this is really useful but here we go. - for (ServerInstanceWrapper wrapper : _serversIndexedByName.values()) - { - try - { - wrapper.stop(); - } - catch (Throwable t) - { - - } - } + // not sure that this is really useful but here we go. + for (ServerInstanceWrapper wrapper : _serversIndexedByName.values()) + { + try + { + wrapper.stop(); + } + catch (Throwable t) + { + LOG.warn(t); + } + } } - /** * Receives notification that a service has had a lifecycle change. * - * @param ev - * The ServiceEvent object. + * @param ev The ServiceEvent object. */ public void serviceChanged(ServiceEvent ev) { @@ -71,17 +74,16 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty case ServiceEvent.MODIFIED: case ServiceEvent.UNREGISTERING: { - ServerInstanceWrapper instance = unregisterInIndex(ev.getServiceReference()); + ServerInstanceWrapper instance = unregisterInIndex(ev.getServiceReference()); if (instance != null) { try { - instance.stop(); + instance.stop(); } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOG.warn(e); } } } @@ -96,32 +98,35 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty } case ServiceEvent.REGISTERED: { - Bundle contributor = sr.getBundle(); - Server server = (Server)contributor.getBundleContext().getService(sr); - ServerInstanceWrapper wrapper = registerInIndex(server, sr); - Properties props = new Properties(); - for (String key : sr.getPropertyKeys()) - { - Object value = sr.getProperty(key); - props.put(key, value); - } - wrapper.start(server, props); - break; + try + { + Bundle contributor = sr.getBundle(); + Server server = (Server) contributor.getBundleContext().getService(sr); + ServerInstanceWrapper wrapper = registerInIndex(server, sr); + Properties props = new Properties(); + for (String key : sr.getPropertyKeys()) + { + Object value = sr.getProperty(key); + props.put(key, value); + } + wrapper.start(server, props); + } + catch (Exception e) + { + LOG.warn(e); + } + break; } } } private ServerInstanceWrapper registerInIndex(Server server, ServiceReference sr) { - String name = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); - if (name == null) - { - throw new IllegalArgumentException("The property " + - OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory"); - } + String name = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + if (name == null) { throw new IllegalArgumentException("The property " + OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory"); } ServerInstanceWrapper wrapper = new ServerInstanceWrapper(name); - _indexByServiceReference.put(sr,wrapper); - _serversIndexedByName.put(name,wrapper); + _indexByServiceReference.put(sr, wrapper); + _serversIndexedByName.put(name, wrapper); return wrapper; } @@ -133,7 +138,7 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty */ private ServerInstanceWrapper unregisterInIndex(ServiceReference sr) { - ServerInstanceWrapper handler = _indexByServiceReference.remove(sr); + ServerInstanceWrapper handler = _indexByServiceReference.remove(sr); if (handler == null) { // a warning? @@ -149,13 +154,12 @@ public class JettyServerServiceTracker implements ServiceListener, IManagedJetty /** * @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) { - return _serversIndexedByName.get(managedServerName == null - ? OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME : managedServerName); + return _serversIndexedByName.get(managedServerName == null ? OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME : managedServerName); } - - + } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java index 31ad22534b2..3872fad70ff 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/JettyServersManagedFactory.java @@ -99,14 +99,21 @@ public class JettyServersManagedFactory implements ManagedServiceFactory, IManag 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"); + 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); - serverInstanceWrapper.start(new Server(), properties); + 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) diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java index 21d0d6adb7d..bb274fe313e 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java @@ -44,28 +44,29 @@ import org.eclipse.jetty.util.resource.Resource; 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 + * Configure and start it. Can also be used from the ManagedServiceFactory */ -public class ServerInstanceWrapper { +public class ServerInstanceWrapper +{ - /** The value of this property points to the parent director of - * the jetty.xml configuration file currently executed. - * Everything is passed as a URL to support the - * case where the bundle is zipped. */ + /** + * The value of this property points to the parent director of the jetty.xml + * configuration file currently executed. Everything is passed as a URL to + * support the case where the bundle is zipped. + */ public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url"; - private static Logger __logger = Log.getLogger(ServerInstanceWrapper.class.getName()); - + private static Logger LOG = Log.getLogger(ServerInstanceWrapper.class.getName()); + private final String _managedServerName; - + /** * The managed jetty server */ private Server _server; + private ContextHandlerCollection _ctxtHandler; /** @@ -74,32 +75,34 @@ public class ServerInstanceWrapper { * let the TldScanner find the jars where the tld files are. */ private ClassLoader _commonParentClassLoaderForWebapps; + private DeploymentManager _deploymentManager; + private OSGiAppProvider _provider; - + private WebBundleDeployerHelper _webBundleDeployerHelper; - - + public ServerInstanceWrapper(String managedServerName) { _managedServerName = managedServerName; } - + public String getManagedServerName() { return _managedServerName; } - + /** - * The classloader that should be the parent classloader for - * each webapp deployed on this server. + * The classloader that should be the parent classloader for each webapp + * deployed on this server. + * * @return */ public ClassLoader getParentClassLoaderForWebapps() { return _commonParentClassLoaderForWebapps; } - + /** * @return The deployment manager registered on this server. */ @@ -107,7 +110,7 @@ public class ServerInstanceWrapper { { return _deploymentManager; } - + /** * @return The app provider registered on this server. */ @@ -115,19 +118,17 @@ public class ServerInstanceWrapper { { return _provider; } - - + public Server getServer() { return _server; } - - + public WebBundleDeployerHelper getWebBundleDeployerHelp() { return _webBundleDeployerHelper; } - + /** * @return The collection of context handlers */ @@ -136,8 +137,7 @@ public class ServerInstanceWrapper { return _ctxtHandler; } - - public void start(Server server, Dictionary props) + public void start(Server server, Dictionary props) throws Exception { _server = server; ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); @@ -146,62 +146,61 @@ public class ServerInstanceWrapper { // passing this bundle's classloader as the context classlaoder // 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); - try - { - List shared = sharedURLs != null ? extractFiles(sharedURLs) : null; - libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader( - shared, null, server, JettyBootstrapActivator.class.getClassLoader()); - } - catch (MalformedURLException e) - { - e.printStackTrace(); - } + String sharedURLs = (String) props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS); + + List shared = sharedURLs != null ? extractFiles(sharedURLs) : null; + libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(shared, null, server, JettyBootstrapActivator.class.getClassLoader()); Thread.currentThread().setContextClassLoader(libExtClassLoader); - + configure(server, props); init(); - //now that we have an app provider we can call the registration customizer. - try - { - URL[] jarsWithTlds = getJarsWithTlds(); - _commonParentClassLoaderForWebapps = jarsWithTlds == null - ? libExtClassLoader - :new TldLocatableURLClassloader(libExtClassLoader,jarsWithTlds); - } - catch (MalformedURLException e) - { - e.printStackTrace(); - } + // now that we have an app provider we can call the registration + // customizer. + + URL[] jarsWithTlds = getJarsWithTlds(); + _commonParentClassLoaderForWebapps = jarsWithTlds == null ? libExtClassLoader : new TldLocatableURLClassloader(libExtClassLoader, jarsWithTlds); - server.start(); + _webBundleDeployerHelper = new WebBundleDeployerHelper(this); } - catch (Throwable t) + catch (Exception e) { - t.printStackTrace(); + if (server != null) + { + try + { + server.stop(); + } + catch (Exception x) + { + LOG.ignore(x); + } + } + throw e; } finally { Thread.currentThread().setContextClassLoader(contextCl); } - _webBundleDeployerHelper = new WebBundleDeployerHelper(this); + + } - - + public void stop() { - try { + try + { if (_server.isRunning()) { _server.stop(); } - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } + catch (Exception e) + { + LOG.warn(e); } } @@ -229,14 +228,13 @@ public class ServerInstanceWrapper { private URL[] getJarsWithTlds() throws Exception { ArrayList res = new ArrayList(); - WebBundleDeployerHelper.staticInit();//that is not looking great. + WebBundleDeployerHelper.staticInit();// that is not looking great. for (WebappRegistrationCustomizer regCustomizer : WebBundleDeployerHelper.JSP_REGISTRATION_HELPERS) { URL[] urls = regCustomizer.getJarsWithTlds(_provider, WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER); for (URL url : urls) { - if (!res.contains(url)) - res.add(url); + if (!res.contains(url)) res.add(url); } } if (!res.isEmpty()) @@ -244,19 +242,15 @@ public class ServerInstanceWrapper { else return null; } - + private void configure(Server server, Dictionary props) throws Exception { String jettyConfigurationUrls = (String) props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS); - List jettyConfigurations = jettyConfigurationUrls != null - ? extractResources(jettyConfigurationUrls) : null; - if (jettyConfigurations == null || jettyConfigurations.isEmpty()) - { - return; - } - Map id_map = new HashMap(); - id_map.put("Server",server); - Map properties = new HashMap(); + List jettyConfigurations = jettyConfigurationUrls != null ? extractResources(jettyConfigurationUrls) : null; + if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; } + Map id_map = new HashMap(); + id_map.put("Server", server); + Map properties = new HashMap(); Enumeration en = props.keys(); while (en.hasMoreElements()) { @@ -275,15 +269,17 @@ public class ServerInstanceWrapper { is = r.getInputStream(); XmlConfiguration config = new XmlConfiguration(is); config.getIdMap().putAll(id_map); - - //#334062 compute the URL of the folder that contains the jetty.xml conf file - //and set it as a property so we can compute relative paths from it. + + // #334062 compute the URL of the folder that contains the + // jetty.xml conf file + // and set it as a property so we can compute relative paths + // from it. String urlPath = jettyConfiguration.toString(); int lastSlash = urlPath.lastIndexOf('/'); if (lastSlash > 4) { urlPath = urlPath.substring(0, lastSlash); - Map properties2 = new HashMap(properties); + Map properties2 = new HashMap(properties); properties2.put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath); config.getProperties().putAll(properties2); } @@ -292,11 +288,11 @@ public class ServerInstanceWrapper { config.getProperties().putAll(properties); } config.configure(); - id_map=config.getIdMap(); + id_map = config.getIdMap(); } catch (SAXParseException saxparse) { - __logger.warn("Unable to configure the jetty/etc file " + jettyConfiguration,saxparse); + LOG.warn("Unable to configure the jetty/etc file " + jettyConfiguration, saxparse); throw saxparse; } finally @@ -306,8 +302,7 @@ public class ServerInstanceWrapper { } } - - + /** * Must be called after the server is configured. * @@ -319,50 +314,48 @@ public class ServerInstanceWrapper { private void init() { // Get the context handler - _ctxtHandler = (ContextHandlerCollection)_server.getChildHandlerByClass(ContextHandlerCollection.class); - + _ctxtHandler = (ContextHandlerCollection) _server.getChildHandlerByClass(ContextHandlerCollection.class); + // get a deployerManager List deployers = _server.getBeans(DeploymentManager.class); if (deployers != null && !deployers.isEmpty()) { _deploymentManager = deployers.get(0); - + for (AppProvider provider : _deploymentManager.getAppProviders()) { if (provider instanceof OSGiAppProvider) { - _provider=(OSGiAppProvider)provider; + _provider = (OSGiAppProvider) provider; break; } } if (_provider == null) { - //create it on the fly with reasonable default values. + // create it on the fly with reasonable default values. try { _provider = new OSGiAppProvider(); - _provider.setMonitoredDirResource( - Resource.newResource(getDefaultOSGiContextsHome( - new File(System.getProperty("jetty.home"))).toURI())); - } catch (IOException e) { - e.printStackTrace(); + _provider.setMonitoredDirResource(Resource.newResource(getDefaultOSGiContextsHome(new File(System.getProperty("jetty.home"))).toURI())); + } + catch (IOException e) + { + LOG.warn(e); } _deploymentManager.addAppProvider(_provider); } } - if (_ctxtHandler == null || _provider==null) - throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured"); - + if (_ctxtHandler == null || _provider == null) throw new IllegalStateException("ERROR: No ContextHandlerCollection or OSGiAppProvider configured"); } - + /** * @return The default folder in which the context files of the osgi bundles * are located and watched. Or null when the system property - * "jetty.osgi.contexts.home" is not defined. - * If the configuration file defines the OSGiAppProvider's context. - * This will not be taken into account. + * "jetty.osgi.contexts.home" is not defined. If the configuration + * file defines the OSGiAppProvider's context. This will not be + * taken into account. */ File getDefaultOSGiContextsHome(File jettyHome) { @@ -370,20 +363,19 @@ 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"); } - + File getOSGiContextsHome() { return _provider.getContextXmlDirAsFile(); } - + /** * @return the urls in this string. */ @@ -396,19 +388,19 @@ public class ServerInstanceWrapper { String tok = tokenizer.nextToken(); try { - urls.add(((DefaultFileLocatorHelper) WebBundleDeployerHelper - .BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok))); + urls.add(((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getLocalURL(new URL(tok))); } catch (Throwable mfe) { - + LOG.warn(mfe); } } return urls; } - + /** - * Get the folders that might contain jars for the legacy J2EE shared libraries + * Get the folders that might contain jars for the legacy J2EE shared + * libraries */ private List extractFiles(String propertyValue) { @@ -420,8 +412,7 @@ public class ServerInstanceWrapper { try { URL url = new URL(tok); - url = ((DefaultFileLocatorHelper) WebBundleDeployerHelper - .BUNDLE_FILE_LOCATOR_HELPER).getFileURL(url); + url = ((DefaultFileLocatorHelper) WebBundleDeployerHelper.BUNDLE_FILE_LOCATOR_HELPER).getFileURL(url); if (url.getProtocol().equals("file")) { Resource res = Resource.newResource(url); @@ -434,11 +425,10 @@ public class ServerInstanceWrapper { } catch (Throwable mfe) { - + LOG.warn(mfe); } } return files; } - } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java index 021c292f862..0838a4850ab 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/JettyContextHandlerServiceTracker.java @@ -24,6 +24,8 @@ import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHom import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry; import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.webapp.WebAppContext; import org.osgi.framework.Bundle; @@ -52,15 +54,17 @@ import org.osgi.framework.ServiceReference; */ public class JettyContextHandlerServiceTracker implements ServiceListener { - - /** New style: ability to manage multiple jetty instances */ - private final IManagedJettyServerRegistry _registry; + private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName()); + + /** New style: ability to manage multiple jetty instances */ + private final IManagedJettyServerRegistry _registry; /** The context-handler to deactivate indexed by context handler */ private Map _indexByServiceReference = new HashMap(); /** - * The index is the bundle-symbolic-name/path/to/context/file when there is such thing + * The index is the bundle-symbolic-name/path/to/context/file when there is + * such thing */ private Map _indexByContextFile = new HashMap(); @@ -72,7 +76,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener */ public JettyContextHandlerServiceTracker(IManagedJettyServerRegistry registry) throws Exception { - _registry = registry; + _registry = registry; } public void stop() throws Exception @@ -85,19 +89,16 @@ public class JettyContextHandlerServiceTracker implements ServiceListener // nothing to stop in the WebappRegistrationHelper } - + /** - * @param contextHome Parent folder where the context files can override the context files - * defined in the web bundles: equivalent to the contexts folder in a traditional - * jetty installation. - * when null, just do nothing. + * @param contextHome Parent folder where the context files can override the + * context files defined in the web bundles: equivalent to the + * contexts folder in a traditional jetty installation. when + * null, just do nothing. */ protected void setupContextHomeScanner(File contextHome) throws IOException { - if (contextHome == null) - { - return; - } + if (contextHome == null) { return; } final String osgiContextHomeFolderCanonicalPath = contextHome.getCanonicalPath(); _scanner = new Scanner(); _scanner.setRecursive(true); @@ -132,8 +133,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener /** * Receives notification that a service has had a lifecycle change. * - * @param ev - * The ServiceEvent object. + * @param ev The ServiceEvent object. */ public void serviceChanged(ServiceEvent ev) { @@ -148,12 +148,11 @@ public class JettyContextHandlerServiceTracker implements ServiceListener { try { - getWebBundleDeployerHelp(sr).unregister(ctxtHandler); + getWebBundleDeployerHelp(sr).unregister(ctxtHandler); } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + __logger.warn(e); } } } @@ -170,30 +169,32 @@ public class JettyContextHandlerServiceTracker implements ServiceListener { Bundle contributor = sr.getBundle(); BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext(); - ContextHandler contextHandler = (ContextHandler)context.getService(sr); + ContextHandler contextHandler = (ContextHandler) context.getService(sr); if (contextHandler.getServer() != null) { // is configured elsewhere. return; } - String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); + String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); if (contextHandler instanceof WebAppContext && contextFilePath == null) - //it could be a web-application that will in fact be configured via a context file. - //that case is identified by the fact that the contextFilePath is not null - //in that case we must use the register context methods. + // it could be a web-application that will in fact be configured + // via a context file. + // that case is identified by the fact that the contextFilePath + // is not null + // in that case we must use the register context methods. { - WebAppContext webapp = (WebAppContext)contextHandler; - String contextPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH); + WebAppContext webapp = (WebAppContext) contextHandler; + String contextPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH); if (contextPath == null) { contextPath = webapp.getContextPath(); } - String webXmlPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH); + String webXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_WEB_XML_PATH); if (webXmlPath == null) { webXmlPath = webapp.getDescriptor(); } - String defaultWebXmlPath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH); + String defaultWebXmlPath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_DEFAULT_WEB_XML_PATH); if (defaultWebXmlPath == null) { String jettyHome = System.getProperty(DefaultJettyAtJettyHomeHelper.SYS_PROP_JETTY_HOME); @@ -202,7 +203,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener File etc = new File(jettyHome, "etc"); if (etc.exists() && etc.isDirectory()) { - File webDefault = new File (etc, "webdefault.xml"); + File webDefault = new File(etc, "webdefault.xml"); if (webDefault.exists()) defaultWebXmlPath = webDefault.getAbsolutePath(); else @@ -212,83 +213,80 @@ public class JettyContextHandlerServiceTracker implements ServiceListener defaultWebXmlPath = webapp.getDefaultsDescriptor(); } } - String war = (String)sr.getProperty("war"); + String war = (String) sr.getProperty("war"); try { - IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); - if (deployerHelper == null) - { - - } - else - { - WebAppContext handler = deployerHelper - .registerWebapplication(contributor,war,contextPath, - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), - webXmlPath,defaultWebXmlPath,webapp); + IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); + if (deployerHelper == null) + { + + } + else + { + WebAppContext handler = deployerHelper.registerWebapplication(contributor, + war, + contextPath, + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), + webXmlPath, defaultWebXmlPath, webapp); if (handler != null) { - registerInIndex(handler,sr); + registerInIndex(handler, sr); } - } + } } catch (Throwable e) { - e.printStackTrace(); + __logger.warn(e); } } else { // consider this just an empty skeleton: - if (contextFilePath == null) - { - throw new IllegalArgumentException("the property contextFilePath is required"); - } + if (contextFilePath == null) { throw new IllegalArgumentException("the property contextFilePath is required"); } try { - IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); - if (deployerHelper == null) - { - //more warnings? - } - else - { - if (Boolean.TRUE.toString().equals(sr.getProperty( - IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE))) - { - contextHandler = null; - } - ContextHandler handler = deployerHelper.registerContext(contributor,contextFilePath, - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), - (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), - contextHandler); - if (handler != null) - { - registerInIndex(handler,sr); - } - } + IWebBundleDeployerHelper deployerHelper = getWebBundleDeployerHelp(sr); + if (deployerHelper == null) + { + // more warnings? + } + else + { + if (Boolean.TRUE.toString().equals(sr.getProperty(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE))) + { + contextHandler = null; + } + ContextHandler handler = deployerHelper.registerContext(contributor, + contextFilePath, + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_EXTRA_CLASSPATH), + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_BUNDLE_INSTALL_LOCATION_OVERRIDE), + (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE), + contextHandler); + if (handler != null) + { + registerInIndex(handler, sr); + } + } } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); + __logger.warn(e); } } } - break; + break; } } private void registerInIndex(ContextHandler handler, ServiceReference sr) { - _indexByServiceReference.put(sr,handler); + _indexByServiceReference.put(sr, handler); String key = getSymbolicNameAndContextFileKey(sr); if (key != null) { - _indexByContextFile.put(key,sr); + _indexByContextFile.put(key, sr); } } @@ -320,11 +318,8 @@ public class JettyContextHandlerServiceTracker implements ServiceListener */ private String getSymbolicNameAndContextFileKey(ServiceReference sr) { - String contextFilePath = (String)sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); - if (contextFilePath != null) - { - return sr.getBundle().getSymbolicName() + "/" + contextFilePath; - } + String contextFilePath = (String) sr.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); + if (contextFilePath != null) { return sr.getBundle().getSymbolicName() + "/" + contextFilePath; } return null; } @@ -336,17 +331,14 @@ public class JettyContextHandlerServiceTracker implements ServiceListener public void reloadJettyContextHandler(String canonicalNameOfFileChanged, String osgiContextHomeFolderCanonicalPath) { String key = getNormalizedRelativePath(canonicalNameOfFileChanged, osgiContextHomeFolderCanonicalPath); - if (key == null) - { - return; - } + if (key == null) { return; } ServiceReference sr = _indexByContextFile.get(key); if (sr == null) { // nothing to do? return; } - serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED,sr)); + serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr)); } /** @@ -362,36 +354,31 @@ public class JettyContextHandlerServiceTracker implements ServiceListener // warning? return null; } - return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\','/'); + return canFilename.substring(osgiContextHomeFolderCanonicalPath.length()).replace('\\', '/'); } - + /** * @return The server on which this webapp is meant to be deployed */ private ServerInstanceWrapper getServerInstanceWrapper(String managedServerName) { - if (_registry == null) - { - return null; - } - if (managedServerName == null) - { - managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME; - } - ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName); - //System.err.println("Returning " + managedServerName + " = " + wrapper); - return wrapper; + if (_registry == null) { return null; } + if (managedServerName == null) + { + managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME; + } + ServerInstanceWrapper wrapper = _registry.getServerInstanceWrapper(managedServerName); + // System.err.println("Returning " + managedServerName + " = " + + // wrapper); + return wrapper; } - + private IWebBundleDeployerHelper getWebBundleDeployerHelp(ServiceReference sr) { - if (_registry == null) - { - return null; - } - String managedServerName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); - ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName); - return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null; + if (_registry == null) { return null; } + String managedServerName = (String) sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + ServerInstanceWrapper wrapper = getServerInstanceWrapper(managedServerName); + return wrapper != null ? wrapper.getWebBundleDeployerHelp() : null; } - + } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java index 4a7dcf96e90..c662d2cae02 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java @@ -247,7 +247,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe catch (IOException e) { // nevermind. just trying our best - e.printStackTrace(); + __logger.ignore(e); } return true; } @@ -279,7 +279,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe catch (Throwable t) { // humf that will hurt if it does not work. - t.printStackTrace(); + __logger.warn("Unable to set webappcontext", t); } } } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java index 26d3c969d95..9cf2fd461a1 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java @@ -78,13 +78,14 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper private static Logger __logger = Log.getLogger(WebBundleDeployerHelper.class.getName()); private static boolean INITIALIZED = false; - + /** * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports * equinox and apache-felix fragment bundles that are specific to an OSGi * implementation should set a different implementation. */ public static BundleClassLoaderHelper BUNDLE_CLASS_LOADER_HELPER = null; + /** * By default set to: {@link DefaultBundleClassLoaderHelper}. It supports * equinox and apache-felix fragment bundles that are specific to an OSGi @@ -97,8 +98,9 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper * equinox and apache-felix fragment bundles that are specific to an OSGi * implementation should set a different implementation. *

- * Several of those objects can be added here: For example we could have an optional fragment that setups - * a specific implementation of JSF for the whole of jetty-osgi. + * Several of those objects can be added here: For example we could have an + * optional fragment that setups a specific implementation of JSF for the + * whole of jetty-osgi. *

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

+ * This method is called before a bundle which matched the search parameters + * of the BundleTracker is added to the + * BundleTracker. This method should return the object to be + * tracked for the specified Bundle. The returned object is + * stored in the BundleTracker and is available from the + * {@link BundleTracker#getObject(Bundle) getObject} method. + * + * @param bundle The Bundle being added to the + * BundleTracker. + * @param event The bundle event which caused this customizer method to be + * called or null if there is no bundle event + * associated with the call to this method. + * @return The object to be tracked for the specified Bundle + * object or null if the specified Bundle + * object should not be tracked. + */ + public Object addingBundle(Bundle bundle, BundleEvent event) + { + if (bundle.getState() == Bundle.ACTIVE) + { + boolean isWebBundle = register(bundle); + return isWebBundle ? bundle : null; + } + else if (bundle.getState() == Bundle.STOPPING) + { + unregister(bundle); + } + else + { + // we should not be called in that state as + // we are registered only for ACTIVE and STOPPING + } + return null; + } - /** - * A bundle is being added to the BundleTracker. - * - *

- * This method is called before a bundle which matched the search parameters - * of the BundleTracker is added to the - * BundleTracker. This method should return the object to be - * tracked for the specified Bundle. The returned object is - * stored in the BundleTracker and is available from the - * {@link BundleTracker#getObject(Bundle) getObject} method. - * - * @param bundle The Bundle being added to the - * BundleTracker. - * @param event The bundle event which caused this customizer method to be - * called or null if there is no bundle event associated - * with the call to this method. - * @return The object to be tracked for the specified Bundle - * object or null if the specified Bundle - * object should not be tracked. - */ - public Object addingBundle(Bundle bundle, BundleEvent event) - { - if (bundle.getState() == Bundle.ACTIVE) - { - boolean isWebBundle = register(bundle); - return isWebBundle ? bundle : null; - } - else if (bundle.getState() == Bundle.STOPPING) - { - unregister(bundle); - } - else - { - //we should not be called in that state as - //we are registered only for ACTIVE and STOPPING - } - return null; - } + /** + * A bundle tracked by the BundleTracker has been modified. + * + *

+ * This method is called when a bundle being tracked by the + * BundleTracker has had its state modified. + * + * @param bundle The Bundle whose state has been modified. + * @param event The bundle event which caused this customizer method to be + * called or null if there is no bundle event + * associated with the call to this method. + * @param object The tracked object for the specified bundle. + */ + public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) + { + // nothing the web-bundle was already track. something changed. + // we only reload the webapps if the bundle is stopped and restarted. + if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE) + { + unregister(bundle); + } + if (bundle.getState() == Bundle.ACTIVE) + { + register(bundle); + } + } - /** - * A bundle tracked by the BundleTracker has been modified. - * - *

- * This method is called when a bundle being tracked by the - * BundleTracker has had its state modified. - * - * @param bundle The Bundle whose state has been modified. - * @param event The bundle event which caused this customizer method to be - * called or null if there is no bundle event associated - * with the call to this method. - * @param object The tracked object for the specified bundle. - */ - public void modifiedBundle(Bundle bundle, BundleEvent event, - Object object) - { - //nothing the web-bundle was already track. something changed. - //we only reload the webapps if the bundle is stopped and restarted. -// System.err.println(bundle.getSymbolicName()); - if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE) - { - unregister(bundle); - } - if (bundle.getState() == Bundle.ACTIVE) - { - register(bundle); - } - } + /** + * A bundle tracked by the BundleTracker has been removed. + * + *

+ * This method is called after a bundle is no longer being tracked by the + * BundleTracker. + * + * @param bundle The Bundle that has been removed. + * @param event The bundle event which caused this customizer method to be + * called or null if there is no bundle event + * associated with the call to this method. + * @param object The tracked object for the specified bundle. + */ + public void removedBundle(Bundle bundle, BundleEvent event, Object object) + { + unregister(bundle); + } - /** - * A bundle tracked by the BundleTracker has been removed. - * - *

- * This method is called after a bundle is no longer being tracked by the - * BundleTracker. - * - * @param bundle The Bundle that has been removed. - * @param event The bundle event which caused this customizer method to be - * called or null if there is no bundle event associated - * with the call to this method. - * @param object The tracked object for the specified bundle. - */ - public void removedBundle(Bundle bundle, BundleEvent event, - Object object) - { - unregister(bundle); - } - - - /** - * @param bundle - * @return true if this bundle in indeed a web-bundle. - */ + /** + * @param bundle + * @return true if this bundle in indeed a web-bundle. + */ private boolean register(Bundle bundle) { Dictionary dic = bundle.getHeaders(); - String warFolderRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH); + String warFolderRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_WAR_FOLDER_PATH); if (warFolderRelativePath != null) { - String contextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + String contextPath = getWebContextPath(bundle, dic, false);// (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); if (contextPath == null || !contextPath.startsWith("/")) { - LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH + - ": " + warFolderRelativePath + "' in the bundle " + bundle.getSymbolicName() + - " is not valid: there is no Web-ContextPath defined in the manifest."); - return false; + LOG.warn("The manifest header '" + OSGiWebappConstants.JETTY_WAR_FOLDER_PATH + + ": " + + warFolderRelativePath + + "' in the bundle " + + bundle.getSymbolicName() + + " is not valid: there is no Web-ContextPath defined in the manifest."); + return false; } // create the corresponding service and publish it in the context of // the contributor bundle. try { - JettyBootstrapActivator.registerWebapplication(bundle,warFolderRelativePath,contextPath); + JettyBootstrapActivator.registerWebapplication(bundle, warFolderRelativePath, contextPath); return true; } catch (Throwable e) { - LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e); - return true;//maybe it did not work maybe it did. safer to track this bundle. + LOG.warn("Starting the web-bundle " + bundle.getSymbolicName() + " threw an exception.", e); + return true;// maybe it did not work maybe it did. safer to track this bundle. } } else if (dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH) != null) { - String contextFileRelativePath = (String)dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String contextFileRelativePath = (String) dic.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (contextFileRelativePath == null) { // nothing to register here. @@ -187,12 +184,11 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer { { try { - JettyBootstrapActivator.registerContext(bundle,path.trim()); + JettyBootstrapActivator.registerContext(bundle, path.trim()); } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOG.warn(e); } } return true; @@ -203,8 +199,8 @@ 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 } // this is risky: should we make sure that there is no classes and @@ -215,39 +211,39 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer { // pointing to files and folders inside WEB-INF. We should // filter-out // META-INF too - String rfc66ContextPath = getWebContextPath(bundle,dic); + String rfc66ContextPath = getWebContextPath(bundle, dic, rfc66Webxml == null); try { - JettyBootstrapActivator.registerWebapplication(bundle,".",rfc66ContextPath); + JettyBootstrapActivator.registerWebapplication(bundle, ".", rfc66ContextPath); return true; } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return true;//maybe it did not work maybe it did. safer to track this bundle. + LOG.warn(e); + return true;// maybe it did not work maybe it did. safer to track this bundle. } } } - private String getWebContextPath(Bundle bundle, Dictionary dic) + private String getWebContextPath(Bundle bundle, Dictionary dic, boolean webinfWebxmlExists) { - String rfc66ContextPath = (String)dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + String rfc66ContextPath = (String) dic.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); if (rfc66ContextPath == null) { + if (!webinfWebxmlExists) { return null; } // extract from the last token of the bundle's location: // (really ? // could consider processing the symbolic name as an alternative // the location will often reflect the version. // maybe this is relevant when the file is a war) String location = bundle.getLocation(); - String toks[] = location.replace('\\','/').split("/"); + String toks[] = location.replace('\\', '/').split("/"); rfc66ContextPath = toks[toks.length - 1]; // remove .jar, .war etc: int lastDot = rfc66ContextPath.lastIndexOf('.'); if (lastDot != -1) { - rfc66ContextPath = rfc66ContextPath.substring(0,lastDot); + rfc66ContextPath = rfc66ContextPath.substring(0, lastDot); } } if (!rfc66ContextPath.startsWith("/")) @@ -265,7 +261,4 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer { // webapps registered in that bundle. } - - - } diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java index 33344060fa1..0e0ff3fbdae 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java @@ -25,6 +25,7 @@ import java.util.zip.ZipFile; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.FileResource; import org.osgi.framework.Bundle; @@ -43,11 +44,13 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper // The url nuxeo and felix return is created directly from the File so it // should work. private static Field BUNDLE_ENTRY_FIELD = null; + private static Field FILE_FIELD = null; private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null;// ZipBundleFile - // inside - // DirZipBundleEntry + + // inside + // DirZipBundleEntry private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile @@ -56,8 +59,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper * spirit of OSGi but quite necessary to support self-contained webapps and * other situations. * - * @param bundle - * The bundle + * @param bundle The bundle * @return Its installation location as a file. * @throws Exception */ @@ -67,11 +69,12 @@ 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"); -// System.err.println(url.toString() + " " + url.toURI() + " " + url.getProtocol()); + if (url.getProtocol().equals("file")) { // some osgi frameworks do use the file protocole directly in some - // situations. Do use the FileResource to transform the URL into a File: URL#toURI is broken + // situations. Do use the FileResource to transform the URL into a + // File: URL#toURI is broken return new FileResource(url).getFile().getParentFile().getParentFile(); } else if (url.getProtocol().equals("bundleentry")) @@ -79,7 +82,10 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper // say hello to equinox who has its own protocol. // we use introspection like there is no tomorrow to get access to // the File + URLConnection con = url.openConnection(); + con.setUseCaches(Resource.getDefaultUseCaches()); //work around problems where url connections cache references to jars + if (BUNDLE_ENTRY_FIELD == null) { BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry"); @@ -93,13 +99,16 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper FILE_FIELD = bundleEntry.getClass().getDeclaredField("file"); FILE_FIELD.setAccessible(true); } - File f = (File)FILE_FIELD.get(bundleEntry); + File f = (File) FILE_FIELD.get(bundleEntry); return f.getParentFile().getParentFile(); } 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"); @@ -117,7 +126,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile"); ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true); } - ZipFile zipFile = (ZipFile)ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile); + ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile); return new File(zipFile.getName()); } else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry")) @@ -130,7 +139,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper { // observed this on felix-2.0.0 String location = bundle.getLocation(); -// System.err.println("location " + location); + // System.err.println("location " + location); if (location.startsWith("file:/")) { URI uri = new URI(URIUtil.encodePath(location)); @@ -138,27 +147,32 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper } else if (location.startsWith("file:")) { - //location defined in the BundleArchive m_bundleArchive - //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())); - } - return res; + // location defined in the BundleArchive m_bundleArchive + // 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())); + } + return res; } else if (location.startsWith("reference:file:")) { - location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8"); - File file = new File(location.substring("file:".length())); - return file; + location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8"); + File file = new File(location.substring("file:".length())); + return file; } } return null; @@ -179,37 +193,40 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper path = path.substring(1); } 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 ") + ")"); + 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 ") + + ")"); } return webapp; } - + /** - * Helper method equivalent to Bundle#getEntry(String entryPath) except that - * it searches for entries in the fragments by using the Bundle#findEntries method. - * - * @param bundle - * @param entryPath - * @return null or all the entries found for that path. - */ - public Enumeration findEntries(Bundle bundle, String entryPath) - { - int last = entryPath.lastIndexOf('/'); - String path = last != -1 && last < entryPath.length() -2 - ? entryPath.substring(0, last) : "/"; - if (!path.startsWith("/")) - { - path = "/" + path; - } - String pattern = last != -1 && last < entryPath.length() -2 - ? entryPath.substring(last+1) : entryPath; - Enumeration enUrls = bundle.findEntries(path, pattern, false); - return enUrls; - } + * Helper method equivalent to Bundle#getEntry(String entryPath) except that + * it searches for entries in the fragments by using the Bundle#findEntries + * method. + * + * @param bundle + * @param entryPath + * @return null or all the entries found for that path. + */ + public Enumeration findEntries(Bundle bundle, String entryPath) + { + int last = entryPath.lastIndexOf('/'); + String path = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(0, last) : "/"; + if (!path.startsWith("/")) + { + path = "/" + path; + } + String pattern = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(last + 1) : entryPath; + Enumeration enUrls = bundle.findEntries(path, pattern, false); + return enUrls; + } /** * If the bundle is a jar, returns the jar. If the bundle is a folder, look @@ -256,76 +273,84 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper return new File[] { jasperLocation }; } } - - //introspection on equinox to invoke the getLocalURL method on BundleURLConnection - //equivalent to using the FileLocator without depending on an equinox class. - private static Method BUNDLE_URL_CONNECTION_getLocalURL = null; - private static Method BUNDLE_URL_CONNECTION_getFileURL = null; - /** - * Only useful for equinox: on felix we get the file:// or jar:// url already. - * Other OSGi implementations have not been tested - *

- * Get a URL to the bundle entry that uses a common protocol (i.e. file: - * jar: or http: etc.). - *

- * @return a URL to the bundle entry that uses a common protocol - */ - public static URL getLocalURL(URL url) { - if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) { - try { - URLConnection conn = url.openConnection(); - 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); - } - if (BUNDLE_URL_CONNECTION_getLocalURL != null) { - return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); - } - } catch (Throwable t) { - System.err.println("Unable to locate the OSGi url: '" + url + "'."); - t.printStackTrace(); - } - } - return url; - } - /** - * Only useful for equinox: on felix we get the file:// url already. - * Other OSGi implementations have not been tested - *

- * Get a URL to the content of the bundle entry that uses the file: protocol. - * The content of the bundle entry may be downloaded or extracted to the local - * file system in order to create a file: URL. - * @return a URL to the content of the bundle entry that uses the file: protocol - *

- */ - public static URL getFileURL(URL url) - { - if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) - { - try - { - URLConnection conn = url.openConnection(); - 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); - } - if (BUNDLE_URL_CONNECTION_getFileURL != null) - { - return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); - } - } - catch (Throwable t) - { - t.printStackTrace(); - } - } - return url; - } + // introspection on equinox to invoke the getLocalURL method on + // BundleURLConnection + // equivalent to using the FileLocator without depending on an equinox + // class. + private static Method BUNDLE_URL_CONNECTION_getLocalURL = null; + + private static Method BUNDLE_URL_CONNECTION_getFileURL = null; + + /** + * Only useful for equinox: on felix we get the file:// or jar:// url + * already. Other OSGi implementations have not been tested + *

+ * Get a URL to the bundle entry that uses a common protocol (i.e. file: + * jar: or http: etc.). + *

+ * + * @return a URL to the bundle entry that uses a common protocol + */ + public static URL getLocalURL(URL url) + { + if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) + { + try + { + 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")) + { + BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null); + BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true); + } + if (BUNDLE_URL_CONNECTION_getLocalURL != null) { return (URL) BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn, null); } + } + catch (Throwable t) + { + System.err.println("Unable to locate the OSGi url: '" + url + "'."); + t.printStackTrace(); + } + } + return url; + } + + /** + * Only useful for equinox: on felix we get the file:// url already. Other + * OSGi implementations have not been tested + *

+ * Get a URL to the content of the bundle entry that uses the file: + * protocol. The content of the bundle entry may be downloaded or extracted + * to the local file system in order to create a file: URL. + * + * @return a URL to the content of the bundle entry that uses the file: + * protocol + *

+ */ + public static URL getFileURL(URL url) + { + if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) + { + try + { + 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")) + { + BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null); + BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true); + } + if (BUNDLE_URL_CONNECTION_getFileURL != null) { return (URL) BUNDLE_URL_CONNECTION_getFileURL.invoke(conn, null); } + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + return url; + } } diff --git a/jetty-osgi/jetty-osgi-equinoxtools/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-equinoxtools/META-INF/MANIFEST.MF index d3f9bfb5433..ec9ca0d2b7f 100644 --- a/jetty-osgi/jetty-osgi-equinoxtools/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-equinoxtools/META-INF/MANIFEST.MF @@ -3,20 +3,20 @@ Bundle-ManifestVersion: 2 Bundle-Name: Console Bundle-SymbolicName: org.eclipse.jetty.osgi.equinoxtools Bundle-Description: Example application: equinox console accesssible on the web -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Activator: org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator Import-Package: javax.servlet;version="2.5.0", javax.servlet.http;version="2.5.0", - org.eclipse.jetty.continuation;version="8.1.2", - org.eclipse.jetty.io;version="8.1.2", - org.eclipse.jetty.util;version="8.1.2", - org.eclipse.jetty.util.log;version="8.1.2", - org.eclipse.jetty.websocket;version="8.1.2", + org.eclipse.jetty.continuation;version="8.1.3", + org.eclipse.jetty.io;version="8.1.3", + org.eclipse.jetty.util;version="8.1.3", + org.eclipse.jetty.util.log;version="8.1.3", + org.eclipse.jetty.websocket;version="8.1.3", org.eclipse.osgi.framework.console;version="1.1.0", org.osgi.framework;version="1.3.0", org.osgi.service.http;version="1.2.0", org.osgi.util.tracker;version="1.3.0" -Export-Package: org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="8.1.2", - org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="8.1.2" -Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Export-Package: org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="8.1.3", + org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="8.1.3" +Bundle-RequiredExecutionEnvironment: J2SE-1.6 diff --git a/jetty-osgi/jetty-osgi-httpservice/META-INF/MANIFEST.MF b/jetty-osgi/jetty-osgi-httpservice/META-INF/MANIFEST.MF index a1efdb9faca..a4174af433a 100644 --- a/jetty-osgi/jetty-osgi-httpservice/META-INF/MANIFEST.MF +++ b/jetty-osgi/jetty-osgi-httpservice/META-INF/MANIFEST.MF @@ -2,15 +2,16 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: OSGi HttpService provided by equinox HttpServiceServlet deployed on jetty Bundle-SymbolicName: org.eclipse.jetty.osgi.httpservice -Bundle-Version: 8.1.2.qualifier +Bundle-Version: 8.1.3.qualifier Bundle-Vendor: Mort Bay Consulting Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Jetty-ContextFilePath: contexts/httpservice.xml Import-Package: javax.servlet;version="2.6.0", javax.servlet.http;version="2.6.0", org.eclipse.equinox.http.servlet, - org.eclipse.jetty.server;version="8.1.2", - org.eclipse.jetty.server.handler;version="8.1.2", - org.eclipse.jetty.servlet;version="8.1.2", - org.eclipse.jetty.util.component;version="8.1.2" -Export-Package: org.eclipse.jetty.osgi.httpservice;version="8.1.2" + org.eclipse.jetty.server;version="8.1.3", + org.eclipse.jetty.server.handler;version="8.1.3", + org.eclipse.jetty.servlet;version="8.1.3", + org.eclipse.jetty.util.component;version="8.1.3" +Export-Package: org.eclipse.jetty.osgi.httpservice;version="8.1.3" + diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandler.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandler.java deleted file mode 100644 index 736f8245f9f..00000000000 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandler.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.eclipse.jetty.rewrite.handler; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.server.handler.ContextHandler.Context; -import org.eclipse.jetty.server.handler.ScopedHandler; -import org.eclipse.jetty.util.component.AggregateLifeCycle; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - - -/* ------------------------------------------------------------ */ -/** A handle that uses regular expressions to select the target. - *

- * This handler applies a list of regex to target name mappings to the URIs of requests. - * If the regex matches the URI, then the mapped target name is used in the nested - * call to {@link #doScope(String, Request, HttpServletRequest, HttpServletResponse)}. - *

- * This handler should be installed as the first handler in a Context. It can be configured - * either with direct calls to {@link #addPatternTarget(String, String)} or by setting - * the context init parameters "org.eclipse.jetty.rewrite.handler.REGEX_MAPPINGS" to a comma - * separated list of strings in the format regex==target. - */ -public class RegexTargetHandler extends ScopedHandler -{ - private final static Logger LOG = Log.getLogger(RegexTargetHandler.class); - public final static String REGEX_MAPPINGS="org.eclipse.jetty.rewrite.handler.REGEX_MAPPINGS"; - static class RegexMapping - { - RegexMapping(String regex,String target) - { - _pattern=Pattern.compile(regex); - _target=target; - } - final Pattern _pattern; - final String _target; - - public String toString() - { - return _pattern+"=="+_target; - } - } - - final private List _patterns = new CopyOnWriteArrayList(); - - /* ------------------------------------------------------------ */ - /** Add a pattern to target mapping. - * @param pattern The regular expression pattern to match. - * @param target The target (normally servlet name) to handle the request - */ - public void addPatternTarget(String pattern,String target) - { - _patterns.add(new RegexMapping(pattern,target)); - } - - /* ------------------------------------------------------------ */ - @Override - protected void doStart() throws Exception - { - super.doStart(); - - Context context = ContextHandler.getCurrentContext(); - if (context!=null) - { - String config=context.getInitParameter(REGEX_MAPPINGS); - LOG.debug("{}={}",REGEX_MAPPINGS,config); - String[] mappings=config.split("\\s*,\\s*"); - for (String mapping : mappings) - { - mapping=mapping.trim(); - String[] parts=mapping.split("\\s*==\\s*"); - if (parts.length==2) - { - String pattern=parts[0]; - String target=parts[1]; - addPatternTarget(pattern,target); - } - else - LOG.warn("Bad regex mapping: "+mapping); - } - } - } - - /* ------------------------------------------------------------ */ - @Override - public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - for (RegexTargetHandler.RegexMapping rm : _patterns) - { - Matcher m=rm._pattern.matcher(target); - if (m.matches()) - { - String new_target = rm._target; - final String sp; - final String pi; - - if (m.groupCount()==1&&target.endsWith(m.group(1))) - { - pi=m.group(1); - sp=target.substring(0,target.length()-pi.length()); - } - else - { - sp=target; - pi=null; - } - baseRequest.setServletPath(sp); - baseRequest.setPathInfo(pi); - baseRequest.setAttribute("org.eclipse.jetty.servlet.REGEX_PATH",target); - super.nextScope(new_target,baseRequest,request,response); - return; - } - } - super.nextScope(target,baseRequest,request,response); - } - - /* ------------------------------------------------------------ */ - @Override - public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - String path=(String)baseRequest.getAttribute("org.eclipse.jetty.servlet.REGEX_PATH"); - if (path==null) - path=target; - else - baseRequest.setAttribute("org.eclipse.jetty.servlet.REGEX_PATH",null); - - super.nextHandle(path,baseRequest,request,response); - } - - /* ------------------------------------------------------------ */ - public void dump(Appendable out, String indent) throws IOException - { - AggregateLifeCycle.dumpObject(out,this); - AggregateLifeCycle.dump(out,indent,_patterns,Collections.singletonList(getHandler())); - } - - -} \ No newline at end of file diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java index 6c57e29bdf6..6fa22b52238 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java @@ -18,18 +18,22 @@ import java.util.regex.Matcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.server.Request; /** * Rewrite the URI by matching with a regular expression. * The replacement string may use $n" to replace the nth capture group. * If the replacement string contains ? character, then it is split into a path - * and query string component. The returned target contains only the path. + * and query string component. The replacement query string may also contain $Q, which + * is replaced with the original query string. + * The returned target contains only the path. */ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI { private String _replacement; private String _query; + private boolean _queryGroup; /* ------------------------------------------------------------ */ public RewriteRegexRule() @@ -49,6 +53,7 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI String[] split=replacement.split("\\?",2); _replacement = split[0]; _query=split.length==2?split[1]:null; + _queryGroup=_query!=null && _query.contains("$Q"); } @@ -73,21 +78,32 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI } if (query!=null) + { + if (_queryGroup) + query=query.replace("$Q",request.getQueryString()==null?"":request.getQueryString()); request.setAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q",query); + } + return target; } /* ------------------------------------------------------------ */ public void applyURI(Request request, String oldTarget, String newTarget) throws IOException { - request.setRequestURI(newTarget); - if (_query!=null) + if (_query==null) + { + request.setRequestURI(newTarget); + } + else { String query=(String)request.getAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q"); - if (request.getQueryString()==null) - request.setQueryString(query); - else - request.setQueryString(request.getQueryString()+"&"+query); + + if (!_queryGroup && request.getQueryString()!=null) + query=request.getQueryString()+"&"+query; + HttpURI uri=new HttpURI(newTarget+"?"+query); + request.setUri(uri); + request.setRequestURI(newTarget); + request.setQueryString(query); } } diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java index 0321859b27b..3ac926f8815 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java @@ -197,28 +197,25 @@ public class RuleContainer extends Rule if (applied!=null) { LOG.debug("applied {}",rule); - if (!target.equals(applied)) - { - LOG.debug("rewrote {} to {}",target,applied); - if (!original_set) - { - original_set=true; - request.setAttribute(_originalPathAttribute, target); - } + LOG.debug("rewrote {} to {}",target,applied); + if (!original_set) + { + original_set=true; + request.setAttribute(_originalPathAttribute, target); + } - if (_rewriteRequestURI) - { - if (rule instanceof Rule.ApplyURI && !target.equals(request.getRequestURI())) - ((Rule.ApplyURI)rule).applyURI((Request)request, target, applied); - else - ((Request)request).setRequestURI(applied); - } - - if (_rewritePathInfo) - ((Request)request).setPathInfo(applied); - - target=applied; + if (_rewriteRequestURI) + { + if (rule instanceof Rule.ApplyURI) + ((Rule.ApplyURI)rule).applyURI((Request)request, target, applied); + else + ((Request)request).setRequestURI(applied); } + + if (_rewritePathInfo) + ((Request)request).setPathInfo(applied); + + target=applied; if (rule.isHandling()) { diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandlerTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandlerTest.java deleted file mode 100644 index c47937621df..00000000000 --- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RegexTargetHandlerTest.java +++ /dev/null @@ -1,214 +0,0 @@ -// ======================================================================== -// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== - -package org.eclipse.jetty.rewrite.handler; - -import static junit.framework.Assert.assertEquals; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.Socket; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.ServletRequestWrapper; -import javax.servlet.ServletResponseWrapper; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; - -import junit.framework.Assert; - -import org.eclipse.jetty.rewrite.handler.RegexTargetHandler; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -public class RegexTargetHandlerTest -{ - private static Server __server = new Server(0); - private static int __port; - - @BeforeClass - public static void setup() throws Exception - { - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/"); - __server.setHandler(context); - - // Serve some hello world servlets - context.addServlet(DispatchServletServlet.class,"/dispatch/*"); - context.addServlet(new ServletHolder("HelloAll",new HelloServlet("Hello World")),"/*"); - context.addServlet(new ServletHolder("Italian",new HelloServlet("Buongiorno Mondo")),"/it/*"); - context.addServlet(new ServletHolder("French", new HelloServlet("Bonjour le Monde")),"/fr/*"); - - RegexTargetHandler regexHandler=new RegexTargetHandler(); - regexHandler.setHandler(context.getHandler()); - context.setHandler(regexHandler); - - context.getInitParams().put(RegexTargetHandler.REGEX_MAPPINGS, - " .*\\.fr==French, \n"+ - "/ciao(/.*)$==Italian"); - - __server.start(); - - __port=__server.getConnectors()[0].getLocalPort(); - } - - @AfterClass - public static void shutdown() throws Exception - { - __server.stop(); - } - - - @Test - public void testNormal() throws Exception - { - String[] response=getResponse("/normal"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Hello World",response[1]); - assertEquals("",response[2]); - assertEquals("/normal",response[3]); - - response=getResponse("/it/info"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Buongiorno Mondo",response[1]); - assertEquals("/it",response[2]); - assertEquals("/info",response[3]); - } - - @Test - public void testFullMatch() throws Exception - { - String[] response=getResponse("/some/thing.fr"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Bonjour le Monde",response[1]); - assertEquals("/some/thing.fr",response[2]); - assertEquals("null",response[3]); - } - - @Test - public void testCaptureMatch() throws Exception - { - String[] response=getResponse("/ciao/info"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Buongiorno Mondo",response[1]); - assertEquals("/ciao",response[2]); - assertEquals("/info",response[3]); - } - - @Test - public void testDispatchFullMatch() throws Exception - { - String[] response=getResponse("/dispatch/xxx?forward=/some/thing.fr"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Bonjour le Monde",response[1]); - assertEquals("/some/thing.fr",response[2]); - assertEquals("null",response[3]); - } - - @Test - public void testDispatchCaptureMatch() throws Exception - { - String[] response=getResponse("/dispatch/xxx?forward=/ciao/info"); - assertEquals("HTTP/1.1 200 OK",response[0]); - assertEquals("Buongiorno Mondo",response[1]); - assertEquals("/ciao",response[2]); - assertEquals("/info",response[3]); - } - - - private String[] getResponse(String uri) throws Exception - { - Socket socket = new Socket("127.0.0.1",__port); - - PrintWriter out = new PrintWriter(socket.getOutputStream()); - out.print("GET "+uri+" HTTP/1.0\r\n\r\n"); - out.flush(); - - BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); - - String[] response=new String[4]; - response[0]=in.readLine(); - //System.err.println(response[0]); - - String line=in.readLine(); - while(line.length()>0) - line=in.readLine(); - - response[1]=in.readLine(); - //System.err.println(response[1]); - response[2]=in.readLine(); - //System.err.println(response[2]); - response[3]=in.readLine(); - //System.err.println(response[3]); - - socket.close(); - return response; - } - - - public static class HelloServlet extends HttpServlet implements Servlet - { - final String _hello; - - public HelloServlet(String hello) - { - _hello=hello; - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - response.setStatus(200); - response.getWriter().println(_hello); - response.getWriter().println(request.getServletPath()); - response.getWriter().println(request.getPathInfo()); - } - } - - - public static class DispatchServletServlet extends HttpServlet implements Servlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - RequestDispatcher dispatcher = null; - - if(request.getParameter("include")!=null) - { - dispatcher = getServletContext().getRequestDispatcher(request.getParameter("include")); - dispatcher.include(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response)); - } - else if(request.getParameter("forward")!=null) - { - dispatcher = getServletContext().getRequestDispatcher(request.getParameter("forward")); - dispatcher.forward(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response)); - } - - } - } -} diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java index 1464f67ab9c..adb8777f12f 100644 --- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java +++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java @@ -14,6 +14,10 @@ package org.eclipse.jetty.rewrite.handler; import java.io.IOException; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.UrlEncoded; import org.junit.Before; import org.junit.Test; @@ -23,13 +27,21 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase { private String[][] _tests= { - {"/foo/bar",".*","/replace","/replace",null}, - {"/foo/bar","/xxx.*","/replace",null,null}, - {"/foo/bar","/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx",null}, - {"/foo/bar","/(foo)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo/xxx",null}, - {"/foo/$bar",".*","/$replace","/$replace",null}, - {"/foo/$bar","/foo/(.*)","/$1/replace","/$bar/replace",null}, - {"/foo/bar/info","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"}, + {"/foo0/bar",null,".*","/replace","/replace",null}, + {"/foo1/bar","n=v",".*","/replace","/replace","n=v"}, + {"/foo2/bar",null,"/xxx.*","/replace",null,null}, + {"/foo3/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo3/xxx",null}, + {"/foo4/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo4"}, + {"/foo5/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo5"}, + {"/foo6/bar",null,"/(.*)/(.*)","/foo6/bar?p2=$2&p1=$1","/foo6/bar","p2=bar&p1=foo6"}, + {"/foo7/bar","n=v","/(.*)/(.*)","/foo7/bar?p2=$2&p1=$1","/foo7/bar","n=v&p2=bar&p1=foo7"}, + {"/foo8/bar",null,"/(foo8)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo8/xxx",null}, + {"/foo9/$bar",null,".*","/$replace","/$replace",null}, + {"/fooA/$bar",null,"/fooA/(.*)","/$1/replace","/$bar/replace",null}, + {"/fooB/bar/info",null,"/fooB/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"}, + {"/fooC/bar/info",null,"/fooC/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"}, + {"/fooD/bar/info","n=v","/fooD/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"}, + {"/fooE/bar/info","n=v","/fooE/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&p1=bar"}, }; private RewriteRegexRule _rule; @@ -45,17 +57,59 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase { for (String[] test : _tests) { - _rule.setRegex(test[1]); - _rule.setReplacement(test[2]); - String result = _rule.matchAndApply(test[0], _request, _response); - assertEquals(test[1], test[3], result); + reset(); + _request.setRequestURI(null); - _request.setRequestURI(test[0]); - _request.setQueryString(null); + String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3]; + _rule.setRegex(test[2]); + _rule.setReplacement(test[3]); + + _request.setUri(new HttpURI(test[0]+(test[1]==null?"":("?"+test[1])))); + _request.getRequestURI(); + + + String result = _rule.matchAndApply(test[0], _request, _response); + assertEquals(t, test[4], result); _rule.applyURI(_request,test[0],result); - assertEquals(test[3], _request.getRequestURI()); - assertEquals(test[4], _request.getQueryString()); + if (result!=null) + { + assertEquals(t,test[4], _request.getRequestURI()); + assertEquals(t,test[5], _request.getQueryString()); + } + + if (test[5]!=null) + { + MultiMap params=new MultiMap(); + UrlEncoded.decodeTo(test[5],params,StringUtil.__UTF8); + + for (String n:params.keySet()) + assertEquals(params.getString(n),_request.getParameter(n)); + } + } + } + + @Test + public void testContainedRequestUriEnabled() throws IOException + { + RuleContainer container = new RuleContainer(); + container.setRewriteRequestURI(true); + container.addRule(_rule); + for (String[] test : _tests) + { + reset(); + String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3]; + _rule.setRegex(test[2]); + _rule.setReplacement(test[3]); + + _request.setRequestURI(test[0]); + _request.setQueryString(test[1]); + _request.getAttributes().clearAttributes(); + + String result = container.apply(test[0],_request,_response); + assertEquals(t,test[4]==null?test[0]:test[4], result); + assertEquals(t,test[4]==null?test[0]:test[4], _request.getRequestURI()); + assertEquals(t,test[5], _request.getQueryString()); } } } diff --git a/jetty-security/src/main/config/etc/README.spnego b/jetty-security/src/main/config/etc/README.spnego index 6bc2123290e..0b4cc5b7ab5 100644 --- a/jetty-security/src/main/config/etc/README.spnego +++ b/jetty-security/src/main/config/etc/README.spnego @@ -12,7 +12,7 @@ The easiest place to put these lines are in the start.ini file. For debugging the spengo authentication the following options are helpful: -Dorg.eclipse.jetty.LEVEL=debug --Dsun.security.spnego.debug=all +-Dsun.security.spnego.debug=true Spengo Authentication is enabled in the webapp with the following setup. @@ -59,7 +59,4 @@ embedded, via the jetty.xml or in a context file for the webapp. true - - - -8 \ No newline at end of file + \ No newline at end of file diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java index 51833fad789..d762ebefbcf 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -254,7 +254,7 @@ public class DigestAuthenticator extends LoginAuthenticator Nonce nonce=_nonceQueue.peek(); while (nonce!=null && nonce._ts=0) value = hdr.substring(tokenstart, tokenend+1); else @@ -239,7 +239,7 @@ public class CookieCutter continue; case ';': - case ',': + // case ',': if (tokenstart>=0) { name = hdr.substring(tokenstart, tokenend+1); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index dd2476e0dd8..a194754dcf0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -65,6 +65,7 @@ import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferUtil; +import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.nio.DirectNIOBuffer; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; @@ -1943,12 +1944,28 @@ public class Request implements HttpServletRequest { if (getContentType() == null || !getContentType().startsWith("multipart/form-data")) return null; - + if (_multiPartInputStream == null) { _multiPartInputStream = new MultiPartInputStream(getInputStream(), getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT), (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null)); + Collection parts = _multiPartInputStream.getParts(); //causes parsing + for (Part p:parts) + { + MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p; + if (mp.getContentDispositionFilename() == null && mp.getFile() == null) + { + //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params + String charset = null; + if (mp.getContentType() != null) + charset = MimeTypes.getCharsetFromContentType(new ByteArrayBuffer(mp.getContentType())); + + String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); + getParameter(""); //cause params to be evaluated + getParameters().add(mp.getName(), content); + } + } } return _multiPartInputStream.getPart(name); } @@ -1964,6 +1981,22 @@ public class Request implements HttpServletRequest _multiPartInputStream = new MultiPartInputStream(getInputStream(), getContentType(),(MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT), (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null)); + Collection parts = _multiPartInputStream.getParts(); //causes parsing + for (Part p:parts) + { + MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p; + if (mp.getContentDispositionFilename() == null && mp.getFile() == null) + { + //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params + String charset = null; + if (mp.getContentType() != null) + charset = MimeTypes.getCharsetFromContentType(new ByteArrayBuffer(mp.getContentType())); + + String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); + getParameter(""); //cause params to be evaluated + getParameters().add(mp.getName(), content); + } + } } return _multiPartInputStream.getParts(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index cd89923c556..6bbb17da2ed 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -137,7 +137,7 @@ public class Response implements HttpServletResponse if (i>=0) { http_only=true; - comment=comment.substring(i,i+HTTP_ONLY_COMMENT.length()).trim(); + comment=comment.replace(HTTP_ONLY_COMMENT,"").trim(); if (comment.length()==0) comment=null; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 76c77f072d5..5c9c2076efd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -74,7 +74,6 @@ public class Server extends HandlerWrapper implements Attributes private boolean _sendDateHeader = false; //send Date: header private int _graceful=0; private boolean _stopAtShutdown; - private int _maxCookieVersion=1; private boolean _dumpAfterStart=false; private boolean _dumpBeforeStop=false; private boolean _uncheckedPrintWriter=false; @@ -267,7 +266,7 @@ public class Server extends HandlerWrapper implements Attributes mex.add(e); } - if (_connectors!=null) + if (_connectors!=null && mex.size()==0) { for (int i=0;i<_connectors.length;i++) { @@ -452,21 +451,20 @@ public class Server extends HandlerWrapper implements Attributes } /* ------------------------------------------------------------ */ - /** Get the maximum cookie version. - * @return the maximum set-cookie version sent by this server + /** */ + @Deprecated public int getMaxCookieVersion() { - return _maxCookieVersion; + return 1; } /* ------------------------------------------------------------ */ - /** Set the maximum cookie version. - * @param maxCookieVersion the maximum set-cookie version sent by this server + /** */ + @Deprecated public void setMaxCookieVersion(int maxCookieVersion) { - _maxCookieVersion = maxCookieVersion; } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java index 81198f5299d..72af6011c0e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java @@ -21,6 +21,8 @@ import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -30,7 +32,8 @@ import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.gzip.GzipResponseWrapper; +import org.eclipse.jetty.http.gzip.CompressedResponseWrapper; +import org.eclipse.jetty.http.gzip.AbstractCompressedStream; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -222,7 +225,7 @@ public class GzipHandler extends HandlerWrapper } } - final GzipResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response); + final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response); boolean exceptional=true; try @@ -256,7 +259,7 @@ public class GzipHandler extends HandlerWrapper else if (exceptional && !response.isCommitted()) { wrappedResponse.resetBuffer(); - wrappedResponse.noGzip(); + wrappedResponse.noCompression(); } else wrappedResponse.finish(); @@ -276,14 +279,27 @@ public class GzipHandler extends HandlerWrapper * @param response the response * @return the gzip response wrapper */ - protected GzipResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response) + protected CompressedResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response) { - return new GzipResponseWrapper(request, response) + return new CompressedResponseWrapper(request,response) { { super.setMimeTypes(GzipHandler.this._mimeTypes); super.setBufferSize(GzipHandler.this._bufferSize); - super.setMinGzipSize(GzipHandler.this._minGzipSize); + super.setMinCompressSize(GzipHandler.this._minGzipSize); + } + + @Override + protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minCompressSize) throws IOException + { + return new AbstractCompressedStream("gzip",request,response,contentLength,bufferSize,minCompressSize) + { + @Override + protected DeflaterOutputStream createStream() throws IOException + { + return new GZIPOutputStream(_response.getOutputStream(),_bufferSize); + } + }; } @Override diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index de3cc399594..6a872a86f3e 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -32,7 +32,6 @@ import javax.servlet.http.HttpServletResponse; import junit.framework.Assert; -import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; @@ -1280,4 +1279,51 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture response.setStatus(200); } } + + + @Test + public void testSuspendedPipeline() throws Exception + { + SuspendHandler suspend = new SuspendHandler(); + suspend.setSuspendFor(30000); + suspend.setResumeAfter(1000); + configureServer(suspend); + + long start=System.currentTimeMillis(); + Socket client=newSocket(HOST,_connector.getLocalPort()); + try + { + OutputStream os=client.getOutputStream(); + InputStream is=client.getInputStream(); + + // write an initial request + os.write(( + "GET / HTTP/1.1\r\n"+ + "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ + "\r\n" + ).getBytes()); + os.flush(); + + Thread.sleep(200); + + // write an pipelined request + os.write(( + "GET / HTTP/1.1\r\n"+ + "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ + "connection: close\r\n"+ + "\r\n" + ).getBytes()); + os.flush(); + + String response=readResponse(client); + assertThat(response,JUnitMatchers.containsString("RESUMEDHTTP/1.1 200 OK")); + assertThat((System.currentTimeMillis()-start),greaterThanOrEqualTo(1999L)); + + // TODO This test should also check that that the CPU did not spin during the suspend. + } + finally + { + client.close(); + } + } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java index e61cae38672..170060a5c65 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java @@ -13,9 +13,6 @@ package org.eclipse.jetty.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicInteger; @@ -27,14 +24,15 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.continuation.Continuation; -import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.session.SessionHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class LocalAsyncContextTest { protected Server _server = new Server(); @@ -53,7 +51,7 @@ public class LocalAsyncContextTest _server.setHandler(session); _server.start(); } - + protected Connector initConnector() { return new LocalConnector(); @@ -112,6 +110,7 @@ public class LocalAsyncContextTest _handler.setResumeAfter(100); _handler.setCompleteAfter(-1); + response=process("wibble"); check(response,"DISPATCHED"); _handler.setResumeAfter(-1); @@ -147,13 +146,14 @@ public class LocalAsyncContextTest check(response,"COMPLETED"); } + @Test public void testTwoCycles() throws Exception { String response; __completed.set(0); __completed1.set(0); - + _handler.setRead(0); _handler.setSuspendFor(1000); _handler.setResumeAfter(100); @@ -178,12 +178,12 @@ public class LocalAsyncContextTest assertTrue(i>=0); i+=m.length(); } - + } private synchronized String process(String content) throws Exception { - String request = "GET / HTTP/1.1\r\n" + + String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n"+ "Connection: close\r\n"; @@ -194,18 +194,294 @@ public class LocalAsyncContextTest return getResponse(request); } - + protected String getResponse(String request) throws Exception { return ((LocalConnector)_connector).getResponses(request); } + private static class SuspendHandler extends HandlerWrapper + { + private int _read; + private long _suspendFor=-1; + private long _resumeAfter=-1; + private long _completeAfter=-1; + private long _suspendFor2=-1; + private long _resumeAfter2=-1; + private long _completeAfter2=-1; + public SuspendHandler() + { + } + + public int getRead() + { + return _read; + } + + public void setRead(int read) + { + _read = read; + } + + public long getSuspendFor() + { + return _suspendFor; + } + + public void setSuspendFor(long suspendFor) + { + _suspendFor = suspendFor; + } + + public long getResumeAfter() + { + return _resumeAfter; + } + + public void setResumeAfter(long resumeAfter) + { + _resumeAfter = resumeAfter; + } + + public long getCompleteAfter() + { + return _completeAfter; + } + + public void setCompleteAfter(long completeAfter) + { + _completeAfter = completeAfter; + } + + + + /* ------------------------------------------------------------ */ + /** Get the suspendFor2. + * @return the suspendFor2 + */ + public long getSuspendFor2() + { + return _suspendFor2; + } + + + /* ------------------------------------------------------------ */ + /** Set the suspendFor2. + * @param suspendFor2 the suspendFor2 to set + */ + public void setSuspendFor2(long suspendFor2) + { + _suspendFor2 = suspendFor2; + } + + + /* ------------------------------------------------------------ */ + /** Get the resumeAfter2. + * @return the resumeAfter2 + */ + public long getResumeAfter2() + { + return _resumeAfter2; + } + + + /* ------------------------------------------------------------ */ + /** Set the resumeAfter2. + * @param resumeAfter2 the resumeAfter2 to set + */ + public void setResumeAfter2(long resumeAfter2) + { + _resumeAfter2 = resumeAfter2; + } + + + /* ------------------------------------------------------------ */ + /** Get the completeAfter2. + * @return the completeAfter2 + */ + public long getCompleteAfter2() + { + return _completeAfter2; + } + + + /* ------------------------------------------------------------ */ + /** Set the completeAfter2. + * @param completeAfter2 the completeAfter2 to set + */ + public void setCompleteAfter2(long completeAfter2) + { + _completeAfter2 = completeAfter2; + } + + + @Override + public void handle(String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException + { + try + { + if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) + { + if (_read>0) + { + byte[] buf=new byte[_read]; + request.getInputStream().read(buf); + } + else if (_read<0) + { + InputStream in = request.getInputStream(); + int b=in.read(); + while(b!=-1) + b=in.read(); + } + + + final AsyncContext asyncContext = baseRequest.startAsync(); + response.getOutputStream().println("STARTASYNC"); + asyncContext.addListener(__asyncListener); + asyncContext.addListener(__asyncListener1); + if (_suspendFor>0) + asyncContext.setTimeout(_suspendFor); + + + if (_completeAfter>0) + { + new Thread() { + @Override + public void run() + { + try + { + Thread.sleep(_completeAfter); + response.getOutputStream().println("COMPLETED"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }.start(); + } + else if (_completeAfter==0) + { + response.getOutputStream().println("COMPLETED"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } + + if (_resumeAfter>0) + { + new Thread() { + @Override + public void run() + { + try + { + Thread.sleep(_resumeAfter); + if(((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId()!=null) + asyncContext.dispatch(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }.start(); + } + else if (_resumeAfter==0) + { + asyncContext.dispatch(); + } + } + else + { + if (request.getAttribute("TIMEOUT")!=null) + response.getOutputStream().println("TIMEOUT"); + else + response.getOutputStream().println("DISPATCHED"); + + if (_suspendFor2>=0) + { + final AsyncContext asyncContext = baseRequest.startAsync(); + response.getOutputStream().println("STARTASYNC2"); + if (_suspendFor2>0) + asyncContext.setTimeout(_suspendFor2); + _suspendFor2=-1; + + if (_completeAfter2>0) + { + new Thread() { + @Override + public void run() + { + try + { + Thread.sleep(_completeAfter2); + response.getOutputStream().println("COMPLETED2"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }.start(); + } + else if (_completeAfter2==0) + { + response.getOutputStream().println("COMPLETED2"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } + + if (_resumeAfter2>0) + { + new Thread() { + @Override + public void run() + { + try + { + Thread.sleep(_resumeAfter2); + asyncContext.dispatch(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }.start(); + } + else if (_resumeAfter2==0) + { + asyncContext.dispatch(); + } + } + else + { + response.setStatus(200); + baseRequest.setHandled(true); + } + } + } + finally + { + } + } + } static AtomicInteger __completed = new AtomicInteger(); static AtomicInteger __completed1 = new AtomicInteger(); - - static AsyncListener __asyncListener = new AsyncListener() + + private static AsyncListener __asyncListener = new AsyncListener() { @Override @@ -235,8 +511,8 @@ public class LocalAsyncContextTest } }; - - static AsyncListener __asyncListener1 = new AsyncListener() + + private static AsyncListener __asyncListener1 = new AsyncListener() { @Override diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index 5496c028dce..565744dcd4b 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; import java.io.BufferedReader; import java.io.File; @@ -35,6 +36,7 @@ import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; import junit.framework.Assert; @@ -122,6 +124,51 @@ public class RequestTest assertTrue(responses.startsWith("HTTP/1.1 200")); } + + @Test + public void testMultiPart() throws Exception + { + _handler._checker = new RequestTester() + { + public boolean check(HttpServletRequest request,HttpServletResponse response) + { + try + { + Part foo = request.getPart("stuff"); + assertNotNull(foo); + String value = request.getParameter("stuff"); + byte[] expected = "000000000000000000000000000000000000000000000000000".getBytes("ISO-8859-1"); + return value.equals(new String(expected, "ISO-8859-1")); + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } + } + }; + + String multipart = "--AaB03x\r\n"+ + "content-disposition: form-data; name=\"field1\"\r\n"+ + "\r\n"+ + "Joe Blow\r\n"+ + "--AaB03x\r\n"+ + "content-disposition: form-data; name=\"stuff\"\r\n"+ + "Content-Type: text/plain;charset=ISO-8859-1\r\n"+ + "\r\n"+ + "000000000000000000000000000000000000000000000000000\r\n"+ + "--AaB03x--\r\n"; + + String request="GET / HTTP/1.1\r\n"+ + "Host: whatever\r\n"+ + "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+ + "Content-Length: "+multipart.getBytes().length+"\r\n"+ + "\r\n"+ + multipart; + + String responses=_connector.getResponses(request); + assertTrue(responses.startsWith("HTTP/1.1 200")); + } @Test public void testBadUtf8ParamExtraction() throws Exception @@ -671,7 +718,7 @@ public class RequestTest "POST / HTTP/1.1\r\n"+ "Host: whatever\r\n"+ "Cookie: name0=value0; name1 = value1 ; \"name2\" = \"\\\"value2\\\"\" \n" + - "Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080, name4=; name5 = ; name6\n" + + "Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080; name4=; name5 = ; name6\n" + "Cookie: name7=value7;\n" + "Connection: close\r\n"+ "\r\n"); @@ -696,6 +743,20 @@ public class RequestTest assertEquals("", cookies.get(6).getValue()); assertEquals("name7", cookies.get(7).getName()); assertEquals("value7", cookies.get(7).getValue()); + + cookies.clear(); + response=_connector.getResponses( + "GET /other HTTP/1.1\n"+ + "Host: whatever\n"+ + "Other: header\n"+ + "Cookie: __utmz=14316.133020.1.1.utr=gna.de|ucn=(real)|utd=reral|utct=/games/hen-one,gnt-50-ba-keys:key,2072262.html\n"+ + "\n" + ); + assertTrue(response.startsWith("HTTP/1.1 200 OK")); + assertEquals(1,cookies.size()); + assertEquals("__utmz", cookies.get(0).getName()); + assertEquals("14316.133020.1.1.utr=gna.de|ucn=(real)|utd=reral|utct=/games/hen-one,gnt-50-ba-keys:key,2072262.html", cookies.get(0).getValue()); + } @Test @@ -833,7 +894,9 @@ public class RequestTest { ((Request)request).setHandled(true); - if (request.getContentLength()>0 && !MimeTypes.FORM_ENCODED.equals(request.getContentType())) + if (request.getContentLength()>0 + && !MimeTypes.FORM_ENCODED.equals(request.getContentType()) + && !request.getContentType().startsWith("multipart/form-data")) _content=IO.toString(request.getInputStream()); if (_checker!=null && _checker.check(request,response)) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index 57820dbb56a..426ffcf8ac5 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -385,7 +385,7 @@ public class ResponseTest { String[][] tests={ {"/other/location?name=value","http://myhost:8888/other/location;jsessionid=12345?name=value"}, - {"/other/location","http://myhost:8888/other/location"}, + /* {"/other/location","http://myhost:8888/other/location"}, {"/other/l%20cation","http://myhost:8888/other/l%20cation"}, {"location","http://myhost:8888/path/location"}, {"./location","http://myhost:8888/path/location"}, @@ -393,7 +393,8 @@ public class ResponseTest {"/other/l%20cation","http://myhost:8888/other/l%20cation"}, {"l%20cation","http://myhost:8888/path/l%20cation"}, {"./l%20cation","http://myhost:8888/path/l%20cation"}, - {"../l%20cation","http://myhost:8888/l%20cation"}, + {"../l%20cation","http://myhost:8888/l%20cation"},*/ + {"../locati%C3%abn","http://myhost:8888/locati%C3%ABn"}, }; for (int i=1;i0) { - if (_read>0) - { - byte[] buf=new byte[_read]; - request.getInputStream().read(buf); - } - else if (_read<0) - { - InputStream in = request.getInputStream(); - int b=in.read(); - while(b!=-1) - b=in.read(); - } - - - final AsyncContext asyncContext = baseRequest.startAsync(); - response.getOutputStream().println("STARTASYNC"); - asyncContext.addListener(LocalAsyncContextTest.__asyncListener); - asyncContext.addListener(LocalAsyncContextTest.__asyncListener1); - if (_suspendFor>0) - asyncContext.setTimeout(_suspendFor); - - - if (_completeAfter>0) - { - new Thread() { - @Override - public void run() - { - try - { - Thread.sleep(_completeAfter); - response.getOutputStream().println("COMPLETED"); - response.setStatus(200); - baseRequest.setHandled(true); - asyncContext.complete(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }.start(); - } - else if (_completeAfter==0) - { - response.getOutputStream().println("COMPLETED"); - response.setStatus(200); - baseRequest.setHandled(true); - asyncContext.complete(); - } - - if (_resumeAfter>0) - { - new Thread() { - @Override - public void run() - { - try - { - Thread.sleep(_resumeAfter); - if(((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId()!=null) - asyncContext.dispatch(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }.start(); - } - else if (_resumeAfter==0) - { - asyncContext.dispatch(); - } + byte[] buf=new byte[_read]; + request.getInputStream().read(buf); } - else + else if (_read<0) { - if (request.getAttribute("TIMEOUT")!=null) - response.getOutputStream().println("TIMEOUT"); - else - response.getOutputStream().println("DISPATCHED"); + InputStream in = request.getInputStream(); + int b=in.read(); + while(b!=-1) + b=in.read(); + } - if (_suspendFor2>=0) - { - final AsyncContext asyncContext = baseRequest.startAsync(); - response.getOutputStream().println("STARTASYNC2"); - if (_suspendFor2>0) - asyncContext.setTimeout(_suspendFor2); - _suspendFor2=-1; + final AsyncContext asyncContext = baseRequest.startAsync(); + asyncContext.addListener(this); + if (_suspendFor>0) + asyncContext.setTimeout(_suspendFor); - if (_completeAfter2>0) + if (_completeAfter>0) + { + new Thread() { + @Override + public void run() { - new Thread() { - @Override - public void run() - { - try - { - Thread.sleep(_completeAfter2); - response.getOutputStream().println("COMPLETED2"); - response.setStatus(200); - baseRequest.setHandled(true); - asyncContext.complete(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }.start(); - } - else if (_completeAfter2==0) - { - response.getOutputStream().println("COMPLETED2"); - response.setStatus(200); - baseRequest.setHandled(true); - asyncContext.complete(); + try + { + Thread.sleep(_completeAfter); + response.getOutputStream().println("COMPLETED"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } + catch(Exception e) + { + e.printStackTrace(); + } } + }.start(); + } + else if (_completeAfter==0) + { + response.getOutputStream().println("COMPLETED"); + response.setStatus(200); + baseRequest.setHandled(true); + asyncContext.complete(); + } - if (_resumeAfter2>0) + if (_resumeAfter>0) + { + new Thread() { + @Override + public void run() { - new Thread() { - @Override - public void run() - { - try - { - Thread.sleep(_resumeAfter2); - asyncContext.dispatch(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }.start(); + try + { + Thread.sleep(_resumeAfter); + asyncContext.dispatch(); + } + catch(Exception e) + { + e.printStackTrace(); + } } - else if (_resumeAfter2==0) - { - asyncContext.dispatch(); - } - } - else - { - response.setStatus(200); - baseRequest.setHandled(true); - } + }.start(); + } + else if (_resumeAfter==0) + { + asyncContext.dispatch(); } } - finally + else if (request.getAttribute("TIMEOUT")!=null) { + response.setStatus(200); + response.getOutputStream().print("TIMEOUT"); + baseRequest.setHandled(true); + } + else + { + response.setStatus(200); + response.getOutputStream().print("RESUMED"); + baseRequest.setHandled(true); } } -} \ No newline at end of file + + @Override + public void onComplete(AsyncEvent asyncEvent) throws IOException + { + } + + @Override + public void onTimeout(AsyncEvent asyncEvent) throws IOException + { + asyncEvent.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE); + asyncEvent.getAsyncContext().dispatch(); + } + + @Override + public void onError(AsyncEvent asyncEvent) throws IOException + { + } + + @Override + public void onStartAsync(AsyncEvent asyncEvent) throws IOException + { + } +} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java index 2fcb497d05f..df08d35508c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java @@ -99,8 +99,6 @@ public class SSLEngineTest connector.setRequestHeaderSize(512); server.setConnectors(new Connector[]{connector }); - server.setHandler(new HelloWorldHandler()); - server.start(); } @AfterClass @@ -109,10 +107,15 @@ public class SSLEngineTest server.stop(); server.join(); } + @Test public void testBigResponse() throws Exception { + server.stop(); + server.setHandler(new HelloWorldHandler()); + server.start(); + SSLContext ctx=SSLContext.getInstance("TLS"); ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom()); @@ -123,7 +126,7 @@ public class SSLEngineTest String request = "GET /?dump=102400 HTTP/1.1\r\n"+ - "Host: localhost:8080\r\n"+ + "Host: localhost:"+port+"\r\n"+ "Connection: close\r\n"+ "\r\n"; @@ -138,6 +141,10 @@ public class SSLEngineTest @Test public void testRequestJettyHttps() throws Exception { + server.stop(); + server.setHandler(new HelloWorldHandler()); + server.start(); + final int loops=10; final int numConns=10; @@ -204,8 +211,7 @@ public class SSLEngineTest @Test public void testServletPost() throws Exception { - stopServer(); - + server.stop(); StreamHandler handler = new StreamHandler(); server.setHandler(handler); server.start(); @@ -308,6 +314,7 @@ public class SSLEngineTest { ServletOutputStream out=response.getOutputStream(); byte[] buf = new byte[Integer.valueOf(request.getParameter("dump"))]; + // System.err.println("DUMP "+buf.length); for (int i=0;i implements UserIdentity.Scope // Handle run as if (_identityService!=null) old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken); - + if (!isAsyncSupported()) baseRequest.setAsyncSupported(false); - + + MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig(); + if (mpce != null) + request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce); + servlet.service(request,response); servlet_error=false; } diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java index c3d7f3769a0..925c409493a 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java @@ -3,6 +3,7 @@ package org.eclipse.jetty.servlet; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.BufferedReader; import java.io.IOException; @@ -14,6 +15,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; import junit.framework.Assert; @@ -222,17 +224,20 @@ public class AsyncContextTest } else { + boolean wrapped = false; final AsyncContext asyncContext; if (request.getParameter("dispatchRequestResponse") != null) { - asyncContext = request.startAsync(request,response); + wrapped = true; + asyncContext = request.startAsync(request, new Wrapper(response)); } else { asyncContext = request.startAsync(); } - new Thread(new DispatchingRunnable(asyncContext)).start(); + + new Thread(new DispatchingRunnable(asyncContext, wrapped)).start(); } } } @@ -240,14 +245,18 @@ public class AsyncContextTest private class DispatchingRunnable implements Runnable { private AsyncContext asyncContext; + private boolean wrapped; - public DispatchingRunnable(AsyncContext asyncContext) + public DispatchingRunnable(AsyncContext asyncContext, boolean wrapped) { this.asyncContext = asyncContext; + this.wrapped = wrapped; } public void run() { + if (wrapped) + assertTrue(asyncContext.getResponse() instanceof Wrapper); asyncContext.dispatch(); } } @@ -329,4 +338,13 @@ public class AsyncContextTest } } + private class Wrapper extends HttpServletResponseWrapper + { + public Wrapper (HttpServletResponse response) + { + super(response); + } + } + + } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java index a0df38968f7..61c43f3f4a7 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java @@ -1007,7 +1007,7 @@ public class DoSFilter implements Filter if (_rateTrackers != null && _trackerTimeoutQ != null) { long now = _trackerTimeoutQ.getNow(); - int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length; + int latestIndex = _next == 0 ? (_timestamps.length-1) : (_next - 1 ); long last=_timestamps[latestIndex]; boolean hasRecentRequest = last != 0 && (now-last)<1000L; diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java index 1b993e6eedf..9b81e18812c 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java @@ -13,14 +13,13 @@ package org.eclipse.jetty.servlets; import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Pattern; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -34,14 +33,16 @@ import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.gzip.GzipResponseWrapper; +import org.eclipse.jetty.http.gzip.CompressedResponseWrapper; +import org.eclipse.jetty.http.gzip.AbstractCompressedStream; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** GZIP Filter - * This filter will gzip the content of a response iff:

    + * This filter will gzip or deflate the content of a response if:
      *
    • The filter is mapped to a matching path
    • + *
    • accept-encoding header is set to either gzip, deflate or a combination of those
    • *
    • The response status code is >=200 and <300 *
    • The content length is unknown or more than the minGzipSize initParameter or the minGzipSize is 0(default)
    • *
    • The content-type is in the comma separated list of mimeTypes set in the mimeTypes initParameter or @@ -50,8 +51,11 @@ import org.eclipse.jetty.util.log.Logger; *
    * *

    + * If both gzip and deflate are specified in the accept-encoding header, then gzip will be used. + *

    + *

    * Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and - * CPU cycles. If this filter is mapped for static content, then use of efficient direct NIO may be + * CPU cycles. If this filter is mapped for static content, then use of efficient direct NIO may be * prevented, thus use of the gzip mechanism of the {@link org.eclipse.jetty.servlet.DefaultServlet} is * advised instead. *

    @@ -59,15 +63,49 @@ import org.eclipse.jetty.util.log.Logger; * This filter extends {@link UserAgentFilter} and if the the initParameter excludedAgents * is set to a comma separated list of user agents, then these agents will be excluded from gzip content. *

    + *

    Init Parameters:

    + *
    + * 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)
    + *                      
    + * minGzipSize                Content will only be compressed if content length is either unknown or greater
    + *                            than minGzipSize.
    + *                      
    + * deflateCompressionLevel    The compression level used for deflate compression. (0-9).
    + *                            @see 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)
      *
    + * mimeTypes                  Comma separated list of mime types to compress. See description above.
    + * 
    + * excludedAgents             Comma separated list of user agents to exclude from compression. Does a 
    + *                            {@link String#contains(CharSequence)} to check if the excluded agent occurs
    + *                            in the user-agent header. If it does -> no compression
    + *                            
    + * excludeAgentPatterns       Same as excludedAgents, but accepts regex patterns for more complex matching.
    + * 
    + * excludePaths               Comma separated list of paths to exclude from compression. 
    + *                            Does a {@link String#startsWith(String)} comparison to check if the path matches.
    + *                            If it does match -> no compression. To match subpaths use excludePathPatterns
    + *                            instead.
    + * 
    + * excludePathPatterns        Same as excludePath, but accepts regex patterns for more complex matching.
    + * 
    */ public class GzipFilter extends UserAgentFilter { private static final Logger LOG = Log.getLogger(GzipFilter.class); + public final static String GZIP="gzip"; + public final static String DEFLATE="deflate"; protected Set _mimeTypes; protected int _bufferSize=8192; protected int _minGzipSize=256; + protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION; + protected boolean _deflateNoWrap = true; protected Set _excludedAgents; protected Set _excludedAgentPatterns; protected Set _excludedPaths; @@ -91,6 +129,14 @@ public class GzipFilter extends UserAgentFilter if (tmp!=null) _minGzipSize=Integer.parseInt(tmp); + tmp=filterConfig.getInitParameter("deflateCompressionLevel"); + if (tmp!=null) + _deflateCompressionLevel=Integer.parseInt(tmp); + + tmp=filterConfig.getInitParameter("deflateNoWrap"); + if (tmp!=null) + _deflateNoWrap=Boolean.parseBoolean(tmp); + tmp=filterConfig.getInitParameter("mimeTypes"); if (tmp!=null) { @@ -144,7 +190,7 @@ public class GzipFilter extends UserAgentFilter public void destroy() { } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) @@ -156,9 +202,8 @@ public class GzipFilter extends UserAgentFilter HttpServletRequest request=(HttpServletRequest)req; HttpServletResponse response=(HttpServletResponse)res; - String ae = request.getHeader("accept-encoding"); - if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding") - && !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod())) + String compressionType = selectCompression(request.getHeader("accept-encoding")); + if (compressionType!=null && !response.containsHeader("Content-Encoding") && !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod())) { String ua = getUserAgent(request); if (isExcludedAgent(ua)) @@ -172,8 +217,8 @@ public class GzipFilter extends UserAgentFilter super.doFilter(request,response,chain); return; } - - final GzipResponseWrapper wrappedResponse=newGzipResponseWrapper(request,response); + + CompressedResponseWrapper wrappedResponse = createWrappedResponse(request,response,compressionType); boolean exceptional=true; try @@ -186,28 +231,12 @@ public class GzipFilter extends UserAgentFilter Continuation continuation = ContinuationSupport.getContinuation(request); if (continuation.isSuspended() && continuation.isResponseWrapped()) { - continuation.addContinuationListener(new ContinuationListener() - { - public void onComplete(Continuation continuation) - { - try - { - wrappedResponse.finish(); - } - catch(IOException e) - { - LOG.warn(e); - } - } - - public void onTimeout(Continuation continuation) - {} - }); + continuation.addContinuationListener(new ContinuationListenerWaitingForWrappedResponseToFinish(wrappedResponse)); } else if (exceptional && !response.isCommitted()) { wrappedResponse.resetBuffer(); - wrappedResponse.noGzip(); + wrappedResponse.noCompression(); } else wrappedResponse.finish(); @@ -218,9 +247,104 @@ public class GzipFilter extends UserAgentFilter super.doFilter(request,response,chain); } } + + /* ------------------------------------------------------------ */ + private String selectCompression(String encodingHeader) + { + // TODO, this could be a little more robust. + // prefer gzip over deflate + if (encodingHeader!=null) + { + if (encodingHeader.toLowerCase().contains(GZIP)) + return GZIP; + else if (encodingHeader.toLowerCase().contains(DEFLATE)) + return DEFLATE; + } + return null; + } + + protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType) + { + CompressedResponseWrapper wrappedResponse = null; + if (compressionType.equals(GZIP)) + { + wrappedResponse = new CompressedResponseWrapper(request,response) + { + @Override + protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minCompressSize) throws IOException + { + return new AbstractCompressedStream(compressionType,request,response,contentLength,bufferSize,minCompressSize) + { + @Override + protected DeflaterOutputStream createStream() throws IOException + { + return new GZIPOutputStream(_response.getOutputStream(),_bufferSize); + } + }; + } + }; + } + else if (compressionType.equals(DEFLATE)) + { + wrappedResponse = new CompressedResponseWrapper(request,response) + { + @Override + protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minCompressSize) throws IOException + { + return new AbstractCompressedStream(compressionType,request,response,contentLength,bufferSize,minCompressSize) + { + @Override + protected DeflaterOutputStream createStream() throws IOException + { + return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel,_deflateNoWrap)); + } + }; + } + }; + } + else + { + throw new IllegalStateException(compressionType + " not supported"); + } + configureWrappedResponse(wrappedResponse); + return wrappedResponse; + } + + protected void configureWrappedResponse(CompressedResponseWrapper wrappedResponse) + { + wrappedResponse.setMimeTypes(_mimeTypes); + wrappedResponse.setBufferSize(_bufferSize); + wrappedResponse.setMinCompressSize(_minGzipSize); + } + private class ContinuationListenerWaitingForWrappedResponseToFinish implements ContinuationListener{ + + private CompressedResponseWrapper wrappedResponse; + + public ContinuationListenerWaitingForWrappedResponseToFinish(CompressedResponseWrapper wrappedResponse) + { + this.wrappedResponse = wrappedResponse; + } + + public void onComplete(Continuation continuation) + { + try + { + wrappedResponse.finish(); + } + catch (IOException e) + { + LOG.warn(e); + } + } + + public void onTimeout(Continuation continuation) + { + } + } + /** - * Checks to see if the UserAgent is excluded + * Checks to see if the userAgent is excluded * * @param ua * the user agent @@ -238,7 +362,7 @@ public class GzipFilter extends UserAgentFilter return true; } } - else if (_excludedAgentPatterns != null) + if (_excludedAgentPatterns != null) { for (Pattern pattern : _excludedAgentPatterns) { @@ -253,9 +377,9 @@ public class GzipFilter extends UserAgentFilter } /** - * Checks to see if the Path is excluded + * Checks to see if the path is excluded * - * @param ua + * @param requestURI * the request uri * @return boolean true if excluded */ @@ -263,6 +387,16 @@ public class GzipFilter extends UserAgentFilter { if (requestURI == null) return false; + if (_excludedPaths != null) + { + for (String excludedPath : _excludedPaths) + { + if (requestURI.startsWith(excludedPath)) + { + return true; + } + } + } if (_excludedPathPatterns != null) { for (Pattern pattern : _excludedPathPatterns) @@ -275,42 +409,4 @@ public class GzipFilter extends UserAgentFilter } return false; } - - /** - * Allows derived implementations to replace ResponseWrapper implementation. - * - * @param request the request - * @param response the response - * @return the gzip response wrapper - */ - protected GzipResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response) - { - return new GzipResponseWrapper(request, response) - { - { - setMimeTypes(GzipFilter.this._mimeTypes); - setBufferSize(GzipFilter.this._bufferSize); - setMinGzipSize(GzipFilter.this._minGzipSize); - } - - @Override - protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException - { - return GzipFilter.this.newWriter(out,encoding); - } - }; - } - - /** - * Allows derived implementations to replace PrintWriter implementation. - * - * @param out the out - * @param encoding the encoding - * @return the prints the writer - * @throws UnsupportedEncodingException - */ - protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException - { - return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding)); - } } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java index 7d9a24ac58a..50ee23ae346 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java @@ -18,18 +18,19 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.gzip.GzipResponseWrapper; -import org.eclipse.jetty.http.gzip.GzipStream; +import org.eclipse.jetty.http.gzip.CompressedResponseWrapper; +import org.eclipse.jetty.http.gzip.AbstractCompressedStream; import org.eclipse.jetty.io.UncheckedPrintWriter; - - /* ------------------------------------------------------------ */ /** Includable GZip Filter. * This extension to the {@link GzipFilter} that uses Jetty features to allow @@ -56,61 +57,82 @@ public class IncludableGzipFilter extends GzipFilter _uncheckedPrintWriter=Boolean.valueOf(tmp).booleanValue(); } + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlets.GzipFilter#createWrappedResponse(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) + */ @Override - protected GzipResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response) + protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType) { - return new IncludableResponseWrapper(request,response); + CompressedResponseWrapper wrappedResponse = null; + if (compressionType.equals(GZIP)) + { + wrappedResponse = new IncludableResponseWrapper(request,response) + { + @Override + protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minCompressSize) throws IOException + { + return new AbstractCompressedStream(compressionType,request,response,contentLength,bufferSize,minCompressSize) + { + @Override + protected DeflaterOutputStream createStream() throws IOException + { + return new GZIPOutputStream(_response.getOutputStream(),_bufferSize); + } + }; + } + }; + } + else if (compressionType.equals(DEFLATE)) + { + wrappedResponse = new IncludableResponseWrapper(request,response) + { + @Override + protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minCompressSize) throws IOException + { + return new AbstractCompressedStream(compressionType,request,response,contentLength,bufferSize,minCompressSize) + { + @Override + protected DeflaterOutputStream createStream() throws IOException + { + return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap)); + } + }; + } + }; + } + else + { + throw new IllegalStateException(compressionType + " not supported"); + } + configureWrappedResponse(wrappedResponse); + return wrappedResponse; } - - public class IncludableResponseWrapper extends GzipResponseWrapper + + + // Extend CompressedResponseWrapper to be able to set headers during include and to create unchecked printwriters + private abstract class IncludableResponseWrapper extends CompressedResponseWrapper { public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response) { super(request,response); - - super.setMimeTypes(IncludableGzipFilter.this._mimeTypes); - super.setBufferSize(IncludableGzipFilter.this._bufferSize); - super.setMinGzipSize(IncludableGzipFilter.this._minGzipSize); } @Override - protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException + public void setHeader(String name,String value) { - return new IncludableGzipStream(request,response,contentLength,bufferSize,minGzipSize); + super.setHeader(name,value); + HttpServletResponse response = (HttpServletResponse)getResponse(); + if (!response.containsHeader(name)) + response.setHeader("org.eclipse.jetty.server.include."+name,value);; } - @Override - protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException + protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException { - return IncludableGzipFilter.this.newWriter(out,encoding); - } - } - - public class IncludableGzipStream extends GzipStream - { - public IncludableGzipStream(HttpServletRequest request, HttpServletResponse response, long contentLength, int bufferSize, int minGzipSize) - throws IOException - { - super(request,response,contentLength,bufferSize,minGzipSize); - } - - @Override - protected boolean setContentEncodingGzip() - { - if (_request.getAttribute("javax.servlet.include.request_uri")!=null) - _response.setHeader("org.eclipse.jetty.server.include.Content-Encoding", "gzip"); - else - _response.setHeader("Content-Encoding", "gzip"); - - return _response.containsHeader("Content-Encoding"); + if (_uncheckedPrintWriter) + return encoding == null?new UncheckedPrintWriter(out):new UncheckedPrintWriter(new OutputStreamWriter(out,encoding)); + return super.newWriter(out,encoding); } } - @Override - protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException - { - if (_uncheckedPrintWriter) - return encoding==null?new UncheckedPrintWriter(out):new UncheckedPrintWriter(new OutputStreamWriter(out,encoding)); - return super.newWriter(out,encoding); - } } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java index d58811f9a26..f4c16883faa 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java @@ -159,7 +159,11 @@ public class MultiPartFilter implements Filter { request.setAttribute(mp.getName(),mp.getFile()); if (mp.getContentDispositionFilename() != null) + { params.add(mp.getName(), mp.getContentDispositionFilename()); + if (mp.getContentType() != null) + params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType()); + } if (_deleteFiles) { mp.getFile().deleteOnExit(); @@ -178,6 +182,8 @@ public class MultiPartFilter implements Filter ByteArrayOutputStream bytes = new ByteArrayOutputStream(); IO.copy(p.getInputStream(), bytes); params.add(p.getName(), bytes.toByteArray()); + if (p.getContentType() != null) + params.add(p.getName()+CONTENT_TYPE_SUFFIX, p.getContentType()); } } } @@ -330,7 +336,7 @@ public class MultiPartFilter implements Filter } catch(Exception e) { - e.printStackTrace(); + throw new RuntimeException(e); } } else if (o instanceof String) diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java index 445a1bf9150..afae2ab946e 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java @@ -414,7 +414,7 @@ public class ProxyServlet implements Servlet if (request.getQueryString() != null) uri += "?" + request.getQueryString(); - HttpURI url = proxyHttpURI(request.getScheme(),request.getServerName(),request.getServerPort(),uri); + HttpURI url = proxyHttpURI(request,uri); if (debug != 0) _log.debug(debug + " proxy " + uri + "-->" + url); @@ -677,6 +677,11 @@ public class ProxyServlet implements Servlet } /* ------------------------------------------------------------ */ + protected HttpURI proxyHttpURI(HttpServletRequest request, String uri) throws MalformedURLException + { + return proxyHttpURI(request.getScheme(), request.getServerName(), request.getServerPort(), uri); + } + protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri) throws MalformedURLException { if (!validateDestination(serverName,uri)) diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java index 109fe2675f2..c752591bc25 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java @@ -6,7 +6,7 @@ import java.util.List; import javax.servlet.Servlet; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.gzip.GzipResponseWrapper; +import org.eclipse.jetty.http.gzip.CompressedResponseWrapper; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlets.gzip.GzipTester; import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite; @@ -48,32 +48,42 @@ public class GzipFilterContentLengthTest { return Arrays.asList(new Object[][] { - { TestServletLengthStreamTypeWrite.class }, - { TestServletLengthTypeStreamWrite.class }, - { TestServletStreamLengthTypeWrite.class }, - { TestServletStreamTypeLengthWrite.class }, - { TestServletTypeLengthStreamWrite.class }, - { TestServletTypeStreamLengthWrite.class } }); + { TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP }, + { TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP }, + { TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP }, + { TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP }, + { TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP }, + { TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP }, + { TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE }, + { TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE }, + { TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE }, + { TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE }, + { TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE }, + { TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE } + }); } - private static final int LARGE = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 8; - private static final int MEDIUM = GzipResponseWrapper.DEFAULT_BUFFER_SIZE; - private static final int SMALL = GzipResponseWrapper.DEFAULT_BUFFER_SIZE / 4; - private static final int TINY = GzipResponseWrapper.DEFAULT_MIN_GZIP_SIZE / 2; + private static final int LARGE = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 8; + private static final int MEDIUM = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE; + private static final int SMALL = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4; + private static final int TINY = CompressedResponseWrapper.DEFAULT_MIN_COMPRESS_SIZE/ 2; + + private String compressionType; + public GzipFilterContentLengthTest(Class testServlet, String compressionType) + { + this.testServlet = testServlet; + this.compressionType = compressionType; + } + @Rule public TestingDir testingdir = new TestingDir(); private Class testServlet; - public GzipFilterContentLengthTest(Class testServlet) - { - this.testServlet = testServlet; - } - private void assertIsGzipCompressed(String filename, int filesize) throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize); @@ -93,7 +103,7 @@ public class GzipFilterContentLengthTest private void assertIsNotGzipCompressed(String filename, int filesize) throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java index ec5aab410be..a890b96b628 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java @@ -31,22 +31,41 @@ public class GzipFilterDefaultNoRecompressTest return Arrays.asList(new Object[][] { // Some already compressed files - { "test_quotes.gz", "application/gzip" }, - { "test_quotes.bz2", "application/bzip2" }, - { "test_quotes.zip", "application/zip" }, - { "test_quotes.rar", "application/octet-stream" }, + { "test_quotes.gz", "application/gzip", GzipFilter.GZIP }, + { "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP }, + { "test_quotes.zip", "application/zip", GzipFilter.GZIP }, + { "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP }, // Some images (common first) - { "jetty_logo.png", "image/png" }, - { "jetty_logo.gif", "image/gif" }, - { "jetty_logo.jpeg", "image/jpeg" }, - { "jetty_logo.jpg", "image/jpeg" }, + { "jetty_logo.png", "image/png", GzipFilter.GZIP }, + { "jetty_logo.gif", "image/gif", GzipFilter.GZIP }, + { "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP }, + { "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP }, // Lesser encountered images (usually found being requested from non-browser clients) - { "jetty_logo.bmp", "image/bmp" }, - { "jetty_logo.tga", "application/tga" }, - { "jetty_logo.tif", "image/tiff" }, - { "jetty_logo.tiff", "image/tiff" }, - { "jetty_logo.xcf", "image/xcf" }, - { "jetty_logo.jp2", "image/jpeg2000" } }); + { "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP }, + { "jetty_logo.tga", "application/tga", GzipFilter.GZIP }, + { "jetty_logo.tif", "image/tiff", GzipFilter.GZIP }, + { "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP }, + { "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP }, + { "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP }, + + // Same tests again for deflate + // Some already compressed files + { "test_quotes.gz", "application/gzip", GzipFilter.DEFLATE }, + { "test_quotes.bz2", "application/bzip2", GzipFilter.DEFLATE }, + { "test_quotes.zip", "application/zip", GzipFilter.DEFLATE }, + { "test_quotes.rar", "application/octet-stream", GzipFilter.DEFLATE }, + // Some images (common first) + { "jetty_logo.png", "image/png", GzipFilter.DEFLATE }, + { "jetty_logo.gif", "image/gif", GzipFilter.DEFLATE }, + { "jetty_logo.jpeg", "image/jpeg", GzipFilter.DEFLATE }, + { "jetty_logo.jpg", "image/jpeg", GzipFilter.DEFLATE }, + // Lesser encountered images (usually found being requested from non-browser clients) + { "jetty_logo.bmp", "image/bmp", GzipFilter.DEFLATE }, + { "jetty_logo.tga", "application/tga", GzipFilter.DEFLATE }, + { "jetty_logo.tif", "image/tiff", GzipFilter.DEFLATE }, + { "jetty_logo.tiff", "image/tiff", GzipFilter.DEFLATE }, + { "jetty_logo.xcf", "image/xcf", GzipFilter.DEFLATE }, + { "jetty_logo.jp2", "image/jpeg2000", GzipFilter.DEFLATE } }); } @Rule @@ -54,17 +73,19 @@ public class GzipFilterDefaultNoRecompressTest private String alreadyCompressedFilename; private String expectedContentType; + private String compressionType; - public GzipFilterDefaultNoRecompressTest(String testFilename, String expectedContentType) + public GzipFilterDefaultNoRecompressTest(String testFilename, String expectedContentType, String compressionType) { this.alreadyCompressedFilename = testFilename; this.expectedContentType = expectedContentType; + this.compressionType = compressionType; } @Test public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); copyTestFileToServer(alreadyCompressedFilename); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java index 82fd54dc11f..05f53c97e3e 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java @@ -1,6 +1,8 @@ package org.eclipse.jetty.servlets; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -8,23 +10,46 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.gzip.GzipResponseWrapper; +import org.eclipse.jetty.http.gzip.CompressedResponseWrapper; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlets.gzip.GzipTester; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * Test the GzipFilter support built into the {@link DefaultServlet} */ +@RunWith(Parameterized.class) public class GzipFilterDefaultTest { + @Parameters + public static Collection data() + { + String[][] data = new String[][] + { + { GzipFilter.GZIP }, + { GzipFilter.DEFLATE } }; + + return Arrays.asList(data); + } + + private String compressionType; + + public GzipFilterDefaultTest(String compressionType) + { + this.compressionType = compressionType; + } public static class HttpStatusServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + private int _status = 204; public HttpStatusServlet() @@ -50,10 +75,10 @@ public class GzipFilterDefaultTest @Test public void testIsGzipCompressedTiny() throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); // Test content that is smaller than the buffer. - int filesize = GzipResponseWrapper.DEFAULT_BUFFER_SIZE / 4; + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4; tester.prepareServerFile("file.txt",filesize); FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class); @@ -73,10 +98,10 @@ public class GzipFilterDefaultTest @Test public void testIsGzipCompressedLarge() throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); // Test content that is smaller than the buffer. - int filesize = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; tester.prepareServerFile("file.txt",filesize); FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class); @@ -96,10 +121,10 @@ public class GzipFilterDefaultTest @Test public void testIsNotGzipCompressed() throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); // Test content that is smaller than the buffer. - int filesize = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; tester.prepareServerFile("file.mp3",filesize); FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class); @@ -119,7 +144,7 @@ public class GzipFilterDefaultTest @Test public void testIsNotGzipCompressedHttpStatus() throws Exception { - GzipTester tester = new GzipTester(testingdir); + GzipTester tester = new GzipTester(testingdir, compressionType); // Test error code 204 FilterHolder holder = tester.setContentServlet(HttpStatusServlet.class); @@ -140,20 +165,88 @@ public class GzipFilterDefaultTest @Test public void testUserAgentExclusion() throws Exception { - GzipTester tester = new GzipTester(testingdir); - + GzipTester tester = new GzipTester(testingdir,compressionType); + FilterHolder holder = tester.setContentServlet(DefaultServlet.class); - holder.setInitParameter("excludedAgents", "foo"); + holder.setInitParameter("excludedAgents","foo"); tester.setUserAgent("foo"); - - int filesize = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; tester.prepareServerFile("file.txt",filesize); - + try { tester.start(); - tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200); - } + tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200); + } + finally + { + tester.stop(); + } + } + + @Test + public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception + { + GzipTester tester = new GzipTester(testingdir,compressionType); + + FilterHolder holder = tester.setContentServlet(DefaultServlet.class); + holder.setInitParameter("excludedAgents","bar"); + holder.setInitParameter("excludeAgentPatterns","fo.*"); + tester.setUserAgent("foo"); + + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + tester.prepareServerFile("file.txt",filesize); + + try + { + tester.start(); + tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200); + } + finally + { + tester.stop(); + } + } + + @Test + public void testExcludePaths() throws Exception + { + GzipTester tester = new GzipTester(testingdir,compressionType); + + FilterHolder holder = tester.setContentServlet(DefaultServlet.class); + holder.setInitParameter("excludePaths","/context/"); + + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + tester.prepareServerFile("file.txt",filesize); + + try + { + tester.start(); + tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200); + } + finally + { + tester.stop(); + } + } + + @Test + public void testExcludePathPatterns() throws Exception + { + GzipTester tester = new GzipTester(testingdir,compressionType); + + FilterHolder holder = tester.setContentServlet(DefaultServlet.class); + holder.setInitParameter("excludePathPatterns","/cont.*"); + + int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4; + tester.prepareServerFile("file.txt",filesize); + + try + { + tester.start(); + tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200); + } finally { tester.stop(); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java index 485df242df2..631864af8d7 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java @@ -1,18 +1,29 @@ package org.eclipse.jetty.servlets; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.util.EnumSet; +import java.util.Arrays; +import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; import javax.servlet.DispatcherType; @@ -28,21 +39,46 @@ import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * Test the effects of Gzip filtering when in the context of HTTP/1.1 Pipelining. */ +@RunWith(Parameterized.class) public class GzipWithPipeliningTest { + @Parameters + public static Collection data() + { + // Test different Content-Encoding header combinations. So implicitly testing that gzip is preferred oder deflate + String[][] data = new String[][] + { + { GzipFilter.GZIP }, + { GzipFilter.DEFLATE + ", " + GzipFilter.GZIP }, + { GzipFilter.GZIP + ", " + GzipFilter.DEFLATE }, + { GzipFilter.DEFLATE } + }; + + return Arrays.asList(data); + } + @Rule public TestingDir testingdir = new TestingDir(); private Server server; private URI serverUri; + private String encodingHeader; + + + public GzipWithPipeliningTest(String encodingHeader) + { + this.encodingHeader = encodingHeader; + } @Before public void startServer() throws Exception @@ -89,7 +125,7 @@ public class GzipWithPipeliningTest testingdir.ensureEmpty(); File outputDir = testingdir.getDir(); - PipelineHelper client = new PipelineHelper(serverUri); + PipelineHelper client = new PipelineHelper(serverUri, encodingHeader); try { @@ -98,7 +134,7 @@ public class GzipWithPipeliningTest // Size of content, as it exists on disk, without gzip compression. long rawsize = txtFile.length() + pngFile.length(); - Assert.assertThat("Ensure that we have sufficient file size to trigger chunking",rawsize,greaterThan(300000L)); + assertThat("Ensure that we have sufficient file size to trigger chunking",rawsize,greaterThan(300000L)); String respHeader; @@ -109,8 +145,9 @@ public class GzipWithPipeliningTest respHeader = client.readResponseHeader(); System.out.println("Response Header #1 --\n" + respHeader); - Assert.assertThat("Content-Encoding should be gzipped",respHeader,containsString("Content-Encoding: gzip\r\n")); - Assert.assertThat("Transfer-Encoding should be chunked",respHeader,containsString("Transfer-Encoding: chunked\r\n")); + String expectedEncodingHeader = encodingHeader.equals(GzipFilter.DEFLATE) ? GzipFilter.DEFLATE : GzipFilter.GZIP; + assertThat("Content-Encoding should be gzipped",respHeader,containsString("Content-Encoding: " + expectedEncodingHeader + "\r\n")); + assertThat("Transfer-Encoding should be chunked",respHeader,containsString("Transfer-Encoding: chunked\r\n")); // Raw output / gzipped, writted to disk (checked for sha1sum later) File rawOutputFile = new File(outputDir, "response-1.gz"); @@ -121,7 +158,7 @@ public class GzipWithPipeliningTest // Read only 20% - intentionally a partial read. System.out.println("Attempting to read partial content ..."); - int readBytes = client.readBody(rawOutputStream,(int)((float)chunkSize * 0.20f)); + int readBytes = client.readBody(rawOutputStream,(int)(chunkSize * 0.20f)); System.out.printf("Read %,d bytes%n",readBytes); // Issue another request @@ -136,14 +173,14 @@ public class GzipWithPipeliningTest readBytes = client.readBody(rawOutputStream,(int)chunkSize); System.out.printf("Read %,d bytes%n",readBytes); line = client.readLine(); - Assert.assertThat("Chunk delim should be an empty line with CR+LF",line,is("")); + assertThat("Chunk delim should be an empty line with CR+LF",line,is("")); chunkSize = client.readChunkSize(); System.out.printf("Next Chunk: (0x%X) %,d bytes%n",chunkSize,chunkSize); } // Inter-pipeline delim line = client.readLine(); - Assert.assertThat("Inter-pipeline delim should be an empty line with CR+LF",line,is("")); + assertThat("Inter-pipeline delim should be an empty line with CR+LF",line,is("")); // Sha1tracking for 1st Request MessageDigest digestTxt = MessageDigest.getInstance("SHA1"); @@ -152,14 +189,23 @@ public class GzipWithPipeliningTest // Decompress 1st request and calculate sha1sum IO.close(rawOutputStream); FileInputStream rawInputStream = new FileInputStream(rawOutputFile); - GZIPInputStream ungzipStream = new GZIPInputStream(rawInputStream); - IO.copy(ungzipStream, digesterTxt); + InputStream uncompressedStream = null; + if (GzipFilter.DEFLATE.equals(encodingHeader)) + { + uncompressedStream = new InflaterInputStream(rawInputStream, new Inflater(true)); + } + else + { + uncompressedStream = new GZIPInputStream(rawInputStream); + } + + IO.copy(uncompressedStream, digesterTxt); // Read 2nd request http response header respHeader = client.readResponseHeader(); System.out.println("Response Header #2 --\n" + respHeader); - Assert.assertThat("Content-Encoding should NOT be gzipped",respHeader,not(containsString("Content-Encoding: gzip\r\n"))); - Assert.assertThat("Transfer-Encoding should NOT be chunked",respHeader,not(containsString("Transfer-Encoding: chunked\r\n"))); + assertThat("Content-Encoding should NOT be gzipped",respHeader,not(containsString("Content-Encoding: gzip\r\n"))); + assertThat("Transfer-Encoding should NOT be chunked",respHeader,not(containsString("Transfer-Encoding: chunked\r\n"))); // Sha1tracking for 2nd Request MessageDigest digestImg = MessageDigest.getInstance("SHA1"); @@ -167,7 +213,7 @@ public class GzipWithPipeliningTest // Read 2nd request body int contentLength = client.getContentLength(respHeader); - Assert.assertThat("Image Content Length",(long)contentLength,is(pngFile.length())); + assertThat("Image Content Length",(long)contentLength,is(pngFile.length())); client.readBody(digesterImg,contentLength); // Validate checksums @@ -186,7 +232,7 @@ public class GzipWithPipeliningTest { String expectedSha1 = loadSha1sum(testResourceFile + ".sha1"); String actualSha1 = Hex.asHex(digest.digest()); - Assert.assertEquals(testResourceFile + " / SHA1Sum of content",expectedSha1,actualSha1); + assertEquals(testResourceFile + " / SHA1Sum of content",expectedSha1,actualSha1); } private String loadSha1sum(String testResourceSha1Sum) throws IOException @@ -195,7 +241,7 @@ public class GzipWithPipeliningTest String contents = IO.readToString(sha1File); Pattern pat = Pattern.compile("^[0-9A-Fa-f]*"); Matcher mat = pat.matcher(contents); - Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find()); + assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find()); return mat.group(); } diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java index 6e37eb311fb..6d37b0f1e6d 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.servlets; +import java.util.Arrays; +import java.util.Collection; + import javax.servlet.Servlet; import org.eclipse.jetty.servlet.FilterHolder; @@ -21,6 +24,9 @@ import org.eclipse.jetty.servlets.gzip.TestMinGzipSizeServlet; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * Perform specific tests on the IncludableGzipFilter's ability to manage @@ -28,17 +34,36 @@ import org.junit.Test; * * @see http://bugs.eclipse.org/366106 */ +@RunWith(Parameterized.class) public class IncludableGzipFilterMinSizeTest { + @Parameters + public static Collection data() + { + String[][] data = new String[][] + { + { GzipFilter.GZIP }, + { GzipFilter.DEFLATE } + }; + + return Arrays.asList(data); + } + + public IncludableGzipFilterMinSizeTest(String compressionType) + { + this.compressionType = compressionType; + } + @Rule public TestingDir testdir = new TestingDir(); + private String compressionType; private Class testServlet = TestMinGzipSizeServlet.class; @Test public void testUnderMinSize() throws Exception { - GzipTester tester = new GzipTester(testdir); + GzipTester tester = new GzipTester(testdir, compressionType); // Use IncludableGzipFilter tester.setGzipFilterClass(IncludableGzipFilter.class); @@ -64,7 +89,7 @@ public class IncludableGzipFilterMinSizeTest @Test public void testOverMinSize() throws Exception { - GzipTester tester = new GzipTester(testdir); + GzipTester tester = new GzipTester(testdir, compressionType); // Use IncludableGzipFilter tester.setGzipFilterClass(IncludableGzipFilter.class); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java index 3ad66eab23f..4b26b75fde0 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java @@ -22,7 +22,11 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; import javax.servlet.http.HttpServletResponse; @@ -36,9 +40,28 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class IncludableGzipFilterTest { + @Parameters + public static Collection data() + { + String[][] data = new String[][] + { + { GzipFilter.GZIP }, + { GzipFilter.DEFLATE } + }; + + return Arrays.asList(data); + } + + @Rule + public TestingDir testdir = new TestingDir(); + private static String __content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+ "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+ @@ -53,11 +76,14 @@ public class IncludableGzipFilterTest "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+ "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque."; - @Rule - public TestingDir testdir = new TestingDir(); - private ServletTester tester; - + private String compressionType; + + public IncludableGzipFilterTest(String compressionType) + { + this.compressionType = compressionType; + } + @Before public void setUp() throws Exception { @@ -95,7 +121,7 @@ public class IncludableGzipFilterTest request.setMethod("GET"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); - request.setHeader("accept-encoding","gzip"); + request.setHeader("accept-encoding", compressionType); request.setURI("/context/file.txt"); ByteArrayBuffer reqsBuff = new ByteArrayBuffer(request.generate().getBytes()); @@ -103,10 +129,19 @@ public class IncludableGzipFilterTest response.parse(respBuff.asArray()); assertTrue(response.getMethod()==null); - assertTrue(response.getHeader("Content-Encoding").equalsIgnoreCase("gzip")); + assertTrue(response.getHeader("Content-Encoding").equalsIgnoreCase(compressionType)); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); + InputStream testIn = null; + ByteArrayInputStream compressedResponseStream = new ByteArrayInputStream(response.getContentBytes()); + if (compressionType.equals(GzipFilter.GZIP)) + { + testIn = new GZIPInputStream(compressedResponseStream); + } + else if (compressionType.equals(GzipFilter.DEFLATE)) + { + testIn = new InflaterInputStream(compressedResponseStream, new Inflater(true)); + } ByteArrayOutputStream testOut = new ByteArrayOutputStream(); IO.copy(testIn,testOut); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java index 97d948a97dd..fad0306325e 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java @@ -47,7 +47,19 @@ public class MultipartFilterTest private ServletTester tester; - + public static class TestServlet extends DumpServlet + { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + assertNotNull(req.getParameter("fileup")); + assertNotNull(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX)); + assertEquals(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX), "application/octet-stream"); + super.doPost(req, resp); + } + + } @@ -63,7 +75,7 @@ public class MultipartFilterTest tester=new ServletTester(); tester.setContextPath("/context"); tester.setResourceBase(_dir.getCanonicalPath()); - tester.addServlet(DumpServlet.class, "/"); + tester.addServlet(TestServlet.class, "/"); tester.setAttribute("javax.servlet.context.tempdir", _dir); FilterHolder multipartFilter = tester.addFilter(MultiPartFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST)); multipartFilter.setInitParameter("deleteFiles", "true"); @@ -253,7 +265,6 @@ public class MultipartFilterTest protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { assertEquals("How now brown cow.", req.getParameterMap().get("strupContent-Type:")); - super.doPost(req, resp); } diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java index 3f0e039b3e8..ad58c1bad6e 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java @@ -25,8 +25,9 @@ public class PipelineHelper private Socket socket; private OutputStream outputStream; private InputStream inputStream; + private String encodingHeader; - public PipelineHelper(URI uri) + public PipelineHelper(URI uri, String encodingHeader) { if (LOG instanceof StdErrLog) { @@ -34,6 +35,7 @@ public class PipelineHelper } this.uri = uri; this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort()); + this.encodingHeader = encodingHeader; } /** @@ -76,7 +78,7 @@ public class PipelineHelper req.append("Accept-Language: en-us\r\n"); if (acceptGzipped) { - req.append("Accept-Encoding: gzip, deflate\r\n"); + req.append("Accept-Encoding: " + encodingHeader + "\r\n"); } req.append("Cookie: JSESSIONID=spqx8v8szylt1336t96vc6mw0\r\n"); if ( close ) @@ -134,7 +136,7 @@ public class PipelineHelper while (!(foundCR && foundLF)) { b = inputStream.read(); - Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1)); + Assert.assertThat("Should not have hit EOL (yet) during chunk size read",b,not(-1)); if (b == 0x0D) { foundCR = true; @@ -163,7 +165,7 @@ public class PipelineHelper while (!(foundCR && foundLF)) { b = inputStream.read(); - Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1)); + Assert.assertThat("Should not have hit EOL (yet) during chunk size read",b,not(-1)); if (b == 0x0D) { foundCR = true; diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java index afe3e9eadfd..6a866ec85aa 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java @@ -1,5 +1,13 @@ package org.eclipse.jetty.servlets.gzip; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -13,8 +21,10 @@ import java.util.Enumeration; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; - +import java.util.zip.Inflater; import javax.servlet.DispatcherType; +import java.util.zip.InflaterInputStream; + import javax.servlet.Servlet; import javax.servlet.http.HttpServletResponse; @@ -29,12 +39,6 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Assert; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; - public class GzipTester { private Class gzipFilterClass = GzipFilter.class; @@ -42,10 +46,12 @@ public class GzipTester private String userAgent = null; private ServletTester servletTester; private TestingDir testdir; + private String compressionType; - public GzipTester(TestingDir testingdir) + public GzipTester(TestingDir testingdir, String compressionType) { this.testdir = testingdir; + this.compressionType = compressionType; // Make sure we start with a clean testing directory. // DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty(); } @@ -64,7 +70,7 @@ public class GzipTester request.setMethod("GET"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); - request.setHeader("Accept-Encoding","gzip"); + request.setHeader("Accept-Encoding",compressionType); if (this.userAgent != null) request.setHeader("User-Agent", this.userAgent); request.setURI("/context/" + requestedFilename); @@ -79,7 +85,7 @@ public class GzipTester Assert.assertThat("Response.method",response.getMethod(),nullValue()); 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("gzip")); + Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),containsString(compressionType)); // Assert that the decompressed contents are what we expect. File serverFile = testdir.getFile(serverFilename); @@ -92,12 +98,19 @@ public class GzipTester try { bais = new ByteArrayInputStream(response.getContentBytes()); - in = new GZIPInputStream(bais); + if (compressionType.equals(GzipFilter.GZIP)) + { + in = new GZIPInputStream(bais); + } + else if (compressionType.equals(GzipFilter.DEFLATE)) + { + in = new InflaterInputStream(bais, new Inflater(true)); + } out = new ByteArrayOutputStream(); IO.copy(in,out); actual = out.toString(encoding); - Assert.assertEquals("Uncompressed contents",expected,actual); + assertThat("Uncompressed contents",actual,equalTo(expected)); } finally { @@ -131,7 +144,7 @@ public class GzipTester request.setMethod("GET"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); - request.setHeader("Accept-Encoding","gzip"); + request.setHeader("Accept-Encoding",compressionType); if (this.userAgent != null) request.setHeader("User-Agent", this.userAgent); request.setURI("/context/" + requestedFilename); @@ -218,7 +231,7 @@ public class GzipTester request.setMethod("GET"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); - request.setHeader("Accept-Encoding","gzip"); + request.setHeader("Accept-Encoding",compressionType); if (this.userAgent != null) request.setHeader("User-Agent", this.userAgent); if (filename == null) @@ -241,7 +254,7 @@ public class GzipTester int serverLength = Integer.parseInt(response.getHeader("Content-Length")); Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize)); } - Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),not(containsString("gzip"))); + Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),not(containsString(compressionType))); // Assert that the contents are what we expect. if (filename != null) diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml index 39ce044081a..75cc1d9902a 100644 --- a/jetty-spdy/pom.xml +++ b/jetty-spdy/pom.xml @@ -12,6 +12,10 @@ pom Jetty :: SPDY :: Parent + + 1.0.0.v20120402 + + spdy-core spdy-jetty diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml index d29a7afeae2..548c3eb29b8 100644 --- a/jetty-spdy/spdy-core/pom.xml +++ b/jetty-spdy/spdy-core/pom.xml @@ -12,8 +12,9 @@ - org.slf4j - slf4j-api + org.eclipse.jetty + jetty-util + ${project.version} junit diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java index 10e64130726..c69af218385 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/ISession.java @@ -35,6 +35,4 @@ public interface ISession extends Session public void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler handler, C context); public void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler handler, C context); - - public int getWindowSize(); } diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java index 86e3bd98066..2291cf78b64 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/IStream.java @@ -68,8 +68,9 @@ public interface IStream extends Stream * of true puts the stream into closed state.

    * * @param close whether the close state should be updated + * @param local whether the close is local or remote */ - public void updateCloseState(boolean close); + public void updateCloseState(boolean close, boolean local); /** *

    Processes the given control frame, diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java index ef3eee17d1e..d44da62dee8 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Promise.java @@ -80,7 +80,9 @@ public class Promise implements Handler, Future @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - latch.await(timeout, unit); + boolean elapsed = !latch.await(timeout, unit); + if (elapsed) + throw new TimeoutException(); return result(); } diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java index 314695336ac..8b1a53dda42 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java @@ -18,10 +18,10 @@ package org.eclipse.jetty.spdy; import java.nio.ByteBuffer; import java.nio.channels.InterruptedByTimeoutException; -import java.util.ArrayList; -import java.util.Deque; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -61,12 +61,12 @@ import org.eclipse.jetty.spdy.frames.SynStreamFrame; import org.eclipse.jetty.spdy.frames.WindowUpdateFrame; import org.eclipse.jetty.spdy.generator.Generator; import org.eclipse.jetty.spdy.parser.Parser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class StandardSession implements ISession, Parser.Listener, Handler { - private static final Logger logger = LoggerFactory.getLogger(Session.class); + private static final Logger logger = Log.getLogger(Session.class); private static final ThreadLocal handlerInvocations = new ThreadLocal() { @Override @@ -78,7 +78,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler listeners = new CopyOnWriteArrayList<>(); private final ConcurrentMap streams = new ConcurrentHashMap<>(); - private final Deque queue = new LinkedList<>(); + private final LinkedList queue = new LinkedList<>(); private final ByteBufferPool bufferPool; private final Executor threadPool; private final ScheduledExecutorService scheduler; @@ -253,9 +253,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler getStreams() + public Set getStreams() { - List result = new ArrayList<>(); + Set result = new HashSet<>(); result.addAll(streams.values()); return result; } @@ -398,6 +398,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler frameBytes = new ControlFrameBytes<>(handler, context, frame, buffer); + ControlFrameBytes frameBytes = new ControlFrameBytes<>(stream, handler, context, frame, buffer); if (timeout > 0) frameBytes.task = scheduler.schedule(frameBytes, timeout, unit); - enqueueLast(frameBytes); + + // Special handling for PING frames, they must be sent as soon as possible + if (ControlFrameType.PING == frame.getType()) + prepend(frameBytes); + else + append(frameBytes); } flush(); @@ -762,10 +772,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler handler, C context) { logger.debug("Queuing {} on {}", dataInfo, stream); - DataFrameBytes frameBytes = new DataFrameBytes<>(handler, context, stream, dataInfo); + DataFrameBytes frameBytes = new DataFrameBytes<>(stream, handler, context, dataInfo); if (timeout > 0) frameBytes.task = scheduler.schedule(frameBytes, timeout, unit); - enqueueLast(frameBytes); + append(frameBytes); flush(); } @@ -774,56 +784,76 @@ public class StandardSession implements ISession, Parser.Listener, Handler stalledStreams = null; + for (int i = 0; i < queue.size(); ++i) { - enqueueFirst(frameBytes); - logger.debug("Flush skipped, {} frame(s) in queue", queue.size()); - return; + frameBytes = queue.get(i); + + if (stalledStreams != null && stalledStreams.contains(frameBytes.getStream())) + continue; + + buffer = frameBytes.getByteBuffer(); + if (buffer != null) + { + queue.remove(i); + break; + } + + if (stalledStreams == null) + stalledStreams = new HashSet<>(); + stalledStreams.add(frameBytes.getStream()); + + logger.debug("Flush stalled for {}, {} frame(s) in queue", frameBytes, queue.size()); } + if (buffer == null) + return; + flushing = true; logger.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size()); } - - logger.debug("Writing {} frame bytes of {}", buffer.remaining(), frameBytes); write(buffer, this, frameBytes); } - private void enqueueLast(FrameBytes frameBytes) + private void append(FrameBytes frameBytes) { - // TODO: handle priority; e.g. use queues to prioritize the buffers ? synchronized (queue) { - queue.offerLast(frameBytes); + int index = queue.size(); + while (index > 0) + { + FrameBytes element = queue.get(index - 1); + if (element.compareTo(frameBytes) >= 0) + break; + --index; + } + queue.add(index, frameBytes); } } - private void enqueueFirst(FrameBytes frameBytes) + private void prepend(FrameBytes frameBytes) { synchronized (queue) { - queue.offerFirst(frameBytes); + int index = 0; + while (index < queue.size()) + { + FrameBytes element = queue.get(index); + if (element.compareTo(frameBytes) <= 0) + break; + ++index; + } + queue.add(index, frameBytes); } } @@ -847,42 +877,44 @@ public class StandardSession implements ISession, Parser.Listener, Handler handler, FrameBytes frameBytes) { if (controller != null) + { + logger.debug("Writing {} frame bytes of {}", buffer.remaining(), frameBytes); controller.write(buffer, handler, frameBytes); + } } private void complete(final Handler handler, final C context) { - if (handler != null) + // Applications may send and queue up a lot of frames and + // if we call Handler.completed() only synchronously we risk + // starvation (for the last frames sent) and stack overflow. + // Therefore every some invocation, we dispatch to a new thread + Integer invocations = handlerInvocations.get(); + if (invocations >= 4) { - // Applications may send and queue up a lot of frames and - // if we call Handler.completed() only synchronously we risk - // starvation (for the last frames sent) and stack overflow. - // Therefore every some invocation, we dispatch to a new thread - Integer invocations = handlerInvocations.get(); - if (invocations >= 4) + execute(new Runnable() { - execute(new Runnable() + @Override + public void run() { - @Override - public void run() - { + if (handler != null) notifyHandlerCompleted(handler, context); - flush(); - } - }); - } - else - { - handlerInvocations.set(invocations + 1); - try - { - notifyHandlerCompleted(handler, context); flush(); } - finally - { - handlerInvocations.set(invocations); - } + }); + } + else + { + handlerInvocations.set(invocations + 1); + try + { + if (handler != null) + notifyHandlerCompleted(handler, context); + flush(); + } + finally + { + handlerInvocations.set(invocations); } } } @@ -913,8 +945,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler { + public IStream getStream(); + public abstract ByteBuffer getByteBuffer(); public abstract void complete(); @@ -922,16 +956,31 @@ public class StandardSession implements ISession, Parser.Listener, Handler implements FrameBytes, Runnable { + private final IStream stream; private final Handler handler; private final C context; protected volatile ScheduledFuture task; - protected AbstractFrameBytes(Handler handler, C context) + protected AbstractFrameBytes(IStream stream, Handler handler, C context) { + this.stream = stream; this.handler = handler; this.context = context; } + @Override + public IStream getStream() + { + return stream; + } + + @Override + public int compareTo(FrameBytes that) + { + // If this.stream.priority > that.stream.priority => -1 (this.stream has less priority than that.stream) + return that.getStream().getPriority() - getStream().getPriority(); + } + @Override public void complete() { @@ -959,9 +1008,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler handler, C context, ControlFrame frame, ByteBuffer buffer) + private ControlFrameBytes(IStream stream, Handler handler, C context, ControlFrame frame, ByteBuffer buffer) { - super(handler, context); + super(stream, handler, context); this.frame = frame; this.buffer = buffer; } @@ -996,15 +1045,13 @@ public class StandardSession implements ISession, Parser.Listener, Handler extends AbstractFrameBytes { - private final IStream stream; private final DataInfo dataInfo; - private int length; - private ByteBuffer buffer; + private int size; + private volatile ByteBuffer buffer; - private DataFrameBytes(Handler handler, C context, IStream stream, DataInfo dataInfo) + private DataFrameBytes(IStream stream, Handler handler, C context, DataInfo dataInfo) { - super(handler, context); - this.stream = stream; + super(stream, handler, context); this.dataInfo = dataInfo; } @@ -1013,15 +1060,16 @@ public class StandardSession implements ISession, Parser.Listener, Handler windowSize) - length = windowSize; + size = dataInfo.available(); + if (size > windowSize) + size = windowSize; - buffer = generator.data(stream.getId(), length, dataInfo); + buffer = generator.data(stream.getId(), size, dataInfo); return buffer; } catch (Throwable x) @@ -1034,19 +1082,21 @@ public class StandardSession implements ISession, Parser.Listener, Handler 0) { - // If we could not write a full data frame, then we need first - // to finish it, and then process the others (to avoid data garbling) - enqueueFirst(this); + // We have written a frame out of this DataInfo, but there is more to write. + // We need to keep the correct ordering of frames, to avoid that another + // DataInfo for the same stream is written before this one is finished. + prepend(this); } else { super.complete(); - stream.updateCloseState(dataInfo.isClose()); + stream.updateCloseState(dataInfo.isClose(), true); if (stream.isClosed()) removeStream(stream); } @@ -1055,7 +1105,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler attributes = new ConcurrentHashMap<>(); private final SynStreamFrame frame; private final ISession session; private final AtomicInteger windowSize; private volatile StreamFrameListener listener; - private volatile boolean opened; - private volatile boolean halfClosed; - private volatile boolean closed; + private volatile OpenState openState = OpenState.SYN_SENT; + private volatile CloseState closeState = CloseState.OPENED; public StandardStream(SynStreamFrame frame, ISession session, int windowSize) { this.frame = frame; this.session = session; this.windowSize = new AtomicInteger(windowSize); - this.halfClosed = frame.isClose(); } @Override @@ -95,7 +93,10 @@ public class StandardStream implements IStream public boolean isHalfClosed() { - return halfClosed; + CloseState closeState = this.closeState; + return closeState == CloseState.LOCALLY_CLOSED || + closeState == CloseState.REMOTELY_CLOSED || + closeState == CloseState.CLOSED; } @Override @@ -123,14 +124,38 @@ public class StandardStream implements IStream } @Override - public void updateCloseState(boolean close) + public void updateCloseState(boolean close, boolean local) { if (close) { - if (isHalfClosed()) - closed = true; - else - halfClosed = true; + switch (closeState) + { + case OPENED: + { + closeState = local ? CloseState.LOCALLY_CLOSED : CloseState.REMOTELY_CLOSED; + break; + } + case LOCALLY_CLOSED: + { + if (local) + throw new IllegalStateException(); + else + closeState = CloseState.CLOSED; + break; + } + case REMOTELY_CLOSED: + { + if (local) + closeState = CloseState.CLOSED; + else + throw new IllegalStateException(); + break; + } + default: + { + throw new IllegalStateException(); + } + } } } @@ -141,14 +166,14 @@ public class StandardStream implements IStream { case SYN_STREAM: { - opened = true; + openState = OpenState.SYN_RECV; break; } case SYN_REPLY: { - opened = true; + openState = OpenState.REPLY_RECV; SynReplyFrame synReply = (SynReplyFrame)frame; - updateCloseState(synReply.isClose()); + updateCloseState(synReply.isClose(), false); ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose()); notifyOnReply(replyInfo); break; @@ -156,7 +181,7 @@ public class StandardStream implements IStream case HEADERS: { HeadersFrame headers = (HeadersFrame)frame; - updateCloseState(headers.isClose()); + updateCloseState(headers.isClose(), false); HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression()); notifyOnHeaders(headersInfo); break; @@ -183,13 +208,13 @@ public class StandardStream implements IStream @Override public void process(DataFrame frame, ByteBuffer data) { - if (!opened) + if (!canReceive()) { session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); return; } - updateCloseState(frame.isClose()); + updateCloseState(frame.isClose(), false); ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress()) { @@ -286,7 +311,8 @@ public class StandardStream implements IStream @Override public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler handler) { - updateCloseState(replyInfo.isClose()); + openState = OpenState.REPLY_SENT; + updateCloseState(replyInfo.isClose(), true); SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders()); session.control(this, frame, timeout, unit, handler, null); } @@ -302,6 +328,17 @@ public class StandardStream implements IStream @Override public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler handler) { + if (!canSend()) + { + session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); + throw new IllegalStateException("Protocol violation: cannot send a DATA frame before a SYN_REPLY frame"); + } + if (isLocallyClosed()) + { + session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); + throw new IllegalStateException("Protocol violation: cannot send a DATA frame on a closed stream"); + } + // Cannot update the close state here, because the data that we send may // be flow controlled, so we need the stream to update the window size. session.data(this, dataInfo, timeout, unit, handler, null); @@ -318,7 +355,18 @@ public class StandardStream implements IStream @Override public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler handler) { - updateCloseState(headersInfo.isClose()); + if (!canSend()) + { + session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); + throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame before a SYN_REPLY frame"); + } + if (isLocallyClosed()) + { + session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR)); + throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame on a closed stream"); + } + + updateCloseState(headersInfo.isClose(), true); HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders()); session.control(this, frame, timeout, unit, handler, null); } @@ -326,12 +374,40 @@ public class StandardStream implements IStream @Override public boolean isClosed() { - return closed; + return closeState == CloseState.CLOSED; + } + + private boolean isLocallyClosed() + { + CloseState closeState = this.closeState; + return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.CLOSED; } @Override public String toString() { - return String.format("stream=%d v%d closed=%s", getId(), session.getVersion(), isClosed() ? "true" : isHalfClosed() ? "half" : "false"); + return String.format("stream=%d v%d %s", getId(), session.getVersion(), closeState); + } + + private boolean canSend() + { + OpenState openState = this.openState; + return openState == OpenState.SYN_SENT || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT; + } + + private boolean canReceive() + { + OpenState openState = this.openState; + return openState == OpenState.SYN_RECV || openState == OpenState.REPLY_RECV || openState == OpenState.REPLY_SENT; + } + + private enum OpenState + { + SYN_SENT, SYN_RECV, REPLY_SENT, REPLY_RECV + } + + private enum CloseState + { + OPENED, LOCALLY_CLOSED, REMOTELY_CLOSED, CLOSED } } diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java index b3a05150e28..75c27d5a02f 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Session.java @@ -17,7 +17,7 @@ package org.eclipse.jetty.spdy.api; import java.util.EventListener; -import java.util.List; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -181,7 +181,7 @@ public interface Session /** * @return the streams currently active in this session */ - public List getStreams(); + public Set getStreams(); /** *

    Super interface for listeners with callbacks that are invoked on specific session events.

    diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java index f1d9d2c66a6..467919c29cf 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/SessionFrameListener.java @@ -18,8 +18,8 @@ package org.eclipse.jetty.spdy.api; import java.util.EventListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; /** *

    A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening @@ -122,7 +122,7 @@ public interface SessionFrameListener extends EventListener */ public static class Adapter implements SessionFrameListener { - private static final Logger logger = LoggerFactory.getLogger(Adapter.class); + private static final Logger logger = Log.getLogger(Adapter.class); @Override public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java index 7a8496eb4bd..c61846ebd84 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/SettingsGenerator.java @@ -65,7 +65,7 @@ public class SettingsGenerator extends ControlFrameGenerator case SPDY.V2: { // In v2 the format is 24 bits of ID + 8 bits of flag - int idAndFlags = (id << 8) + flags; + int idAndFlags = (id << 8) + (flags & 0xFF); // A bug in the Chromium implementation forces v2 to have // the 3 ID bytes little endian, so we swap first and third int result = idAndFlags & 0x00_FF_00_FF; diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java index dc948968f3d..753ad804d4d 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/GoAwayBodyParser.java @@ -58,6 +58,10 @@ public class GoAwayBodyParser extends ControlFrameBodyParser state = State.STATUS_CODE; break; } + default: + { + throw new IllegalStateException(); + } } } else @@ -87,6 +91,10 @@ public class GoAwayBodyParser extends ControlFrameBodyParser state = State.STATUS_CODE; break; } + default: + { + throw new IllegalStateException(); + } } } break; diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java index 58cb61d1c34..8cc42e18144 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/Parser.java @@ -27,12 +27,12 @@ import org.eclipse.jetty.spdy.StreamException; import org.eclipse.jetty.spdy.api.SessionStatus; import org.eclipse.jetty.spdy.frames.ControlFrame; import org.eclipse.jetty.spdy.frames.DataFrame; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class Parser { - private static final Logger logger = LoggerFactory.getLogger(Parser.class); + private static final Logger logger = Log.getLogger(Parser.class); private final List listeners = new CopyOnWriteArrayList<>(); private final ControlFrameParser controlFrameParser; private final DataFrameParser dataFrameParser; diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java index 610087acb18..85cea3452d7 100644 --- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java +++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/AsyncTimeoutTest.java @@ -22,7 +22,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.spdy.api.Handler; import org.eclipse.jetty.spdy.api.SPDY; @@ -94,17 +93,15 @@ public class AsyncTimeoutTest Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor()); Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator) { - private final AtomicInteger flushes = new AtomicInteger(); - @Override - public void flush() + protected void write(ByteBuffer buffer, Handler handler, FrameBytes frameBytes) { try { - int flushes = this.flushes.incrementAndGet(); - if (flushes == 3) + // Wait if we're writing the data frame (control frame's first byte is 0x80) + if (buffer.get(0) == 0) unit.sleep(2 * timeout); - super.flush(); + super.write(buffer, handler, frameBytes); } catch (InterruptedException x) { diff --git a/jetty-spdy/spdy-jetty-http-webapp/pom.xml b/jetty-spdy/spdy-jetty-http-webapp/pom.xml index bc40e50409a..aa98401fa2e 100644 --- a/jetty-spdy/spdy-jetty-http-webapp/pom.xml +++ b/jetty-spdy/spdy-jetty-http-webapp/pom.xml @@ -1,46 +1,64 @@ - - - org.eclipse.jetty.spdy - spdy-parent - 8.1.3-SNAPSHOT - - 4.0.0 - spdy-jetty-http-webapp - war - Jetty :: SPDY :: Jetty HTTP Web Application - - - - --> + diff --git a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/jetty-spdy.xml b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml similarity index 83% rename from jetty-spdy/spdy-jetty-http-webapp/src/main/config/jetty-spdy.xml rename to jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml index 8d4c1441928..2c45ca72fdc 100644 --- a/jetty-spdy/spdy-jetty-http-webapp/src/main/config/jetty-spdy.xml +++ b/jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml @@ -9,13 +9,6 @@ src/main/resources/truststore.jks storepwd TLSv1 - - - TLSv1 - TLSv1.1 - TLSv1.2 - - diff --git a/jetty-spdy/spdy-jetty-http/pom.xml b/jetty-spdy/spdy-jetty-http/pom.xml index dcb940559db..657b6f4046f 100644 --- a/jetty-spdy/spdy-jetty-http/pom.xml +++ b/jetty-spdy/spdy-jetty-http/pom.xml @@ -1,60 +1,72 @@ - - - org.eclipse.jetty.spdy - spdy-parent - 8.1.3-SNAPSHOT - - 4.0.0 - spdy-jetty-http - Jetty :: SPDY :: Jetty HTTP Layer - - - - maven-surefire-plugin - - true - -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar - - - - - - - eclipse-release - + + + org.eclipse.jetty.spdy + spdy-parent + 8.1.3-SNAPSHOT + + 4.0.0 + spdy-jetty-http + Jetty :: SPDY :: Jetty HTTP Layer + + - - maven-surefire-plugin - - true - - + + maven-dependency-plugin + + + copy + generate-resources + + copy + + + + + org.mortbay.jetty.npn + npn-boot + ${npn.version} + jar + false + ${project.build.directory}/npn + + + + + + + + maven-surefire-plugin + + -Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar + + - - - - - - org.eclipse.jetty.spdy - spdy-jetty - ${project.version} - - - junit - junit - - - org.eclipse.jetty.npn - npn-api - ${project.version} - test - - - org.slf4j - slf4j-log4j12 - ${slf4j-version} - test - - + + + + + org.eclipse.jetty.spdy + spdy-jetty + ${project.version} + + + junit + junit + + + org.eclipse.jetty.npn + npn-api + ${project.version} + test + + + org.slf4j + slf4j-log4j12 + ${slf4j-version} + test + + + diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java index 8f64049f1aa..b8359cde9c0 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/HTTPSPDYServerConnector.java @@ -16,6 +16,11 @@ package org.eclipse.jetty.spdy.http; +import java.io.IOException; + +import org.eclipse.jetty.http.HttpSchemes; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.Request; import org.eclipse.jetty.spdy.AsyncConnectionFactory; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.spdy.api.SPDY; @@ -52,4 +57,34 @@ public class HTTPSPDYServerConnector extends SPDYServerConnector { return defaultConnectionFactory; } + + @Override + public void customize(EndPoint endPoint, Request request) throws IOException + { + super.customize(endPoint, request); + if (getSslContextFactory() != null) + request.setScheme(HttpSchemes.HTTPS); + } + + @Override + public boolean isConfidential(Request request) + { + if (getSslContextFactory() != null) + { + int confidentialPort = getConfidentialPort(); + return confidentialPort == 0 || confidentialPort == request.getServerPort(); + } + return super.isConfidential(request); + } + + @Override + public boolean isIntegral(Request request) + { + if (getSslContextFactory() != null) + { + int integralPort = getIntegralPort(); + return integralPort == 0 || integralPort == request.getServerPort(); + } + return super.isIntegral(request); + } } diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java index e263654af16..2882bc43461 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java @@ -50,13 +50,14 @@ 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.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implements AsyncConnection { - private static final Logger logger = LoggerFactory.getLogger(ServerHTTPSPDYAsyncConnection.class); + private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnection.class); private static final ByteBuffer ZERO_BYTES = ByteBuffer.allocate(0); + private static final DataInfo END_OF_CONTENT = new ByteBufferDataInfo(ZERO_BYTES, true); private final Queue tasks = new LinkedList<>(); private final BlockingQueue dataInfos = new LinkedBlockingQueue<>(); @@ -65,7 +66,6 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem private Headers headers; // No need for volatile, guarded by state private DataInfo dataInfo; // No need for volatile, guarded by state private NIOBuffer buffer; // No need for volatile, guarded by state - private boolean complete; // No need for volatile, guarded by state private volatile State state = State.INITIAL; private boolean dispatched; // Guarded by synchronization on tasks @@ -160,7 +160,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem logger.debug("HTTP > {} {} {}", new Object[]{m, u, v}); startRequest(new ByteArrayBuffer(m), new ByteArrayBuffer(u), new ByteArrayBuffer(v)); - state = State.HEADERS; + updateState(State.HEADERS); handle(); break; } @@ -261,6 +261,12 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem { } + private void updateState(State newState) + { + logger.debug("State update {} -> {}", state, newState); + state = newState; + } + public void beginRequest(final Headers headers) { this.headers = headers.isEmpty() ? null : headers; @@ -270,7 +276,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem public void run() { if (!headers.isEmpty()) - state = State.REQUEST; + updateState(State.REQUEST); handle(); } }); @@ -284,7 +290,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem @Override public void run() { - state = state == State.INITIAL ? State.REQUEST : State.HEADERS; + updateState(state == State.INITIAL ? State.REQUEST : State.HEADERS); handle(); } }); @@ -292,7 +298,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem public void content(final DataInfo dataInfo, boolean endRequest) { - dataInfos.offer(new ByteBufferDataInfo(dataInfo.asByteBuffer(false), dataInfo.isClose(), dataInfo.isCompress()) + // We need to copy the dataInfo since we do not know when its bytes + // will be consumed. When the copy is consumed, we consume also the + // original, so the implementation can send a window update. + ByteBufferDataInfo copyDataInfo = new ByteBufferDataInfo(dataInfo.asByteBuffer(false), dataInfo.isClose(), dataInfo.isCompress()) { @Override public void consume(int delta) @@ -300,8 +309,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem super.consume(delta); dataInfo.consume(delta); } - }); - complete = endRequest; + }; + logger.debug("Queuing last={} content {}", endRequest, copyDataInfo); + dataInfos.offer(copyDataInfo); + if (endRequest) + dataInfos.offer(END_OF_CONTENT); post(new Runnable() { @Override @@ -310,10 +322,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem logger.debug("HTTP > {} bytes of content", dataInfo.length()); if (state == State.HEADERS) { - state = State.HEADERS_COMPLETE; + updateState(State.HEADERS_COMPLETE); handle(); } - state = State.CONTENT; + updateState(State.CONTENT); handle(); } }); @@ -327,10 +339,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem { if (state == State.HEADERS) { - state = State.HEADERS_COMPLETE; + updateState(State.HEADERS_COMPLETE); handle(); } - state = State.FINAL; + updateState(State.FINAL); handle(); } }); @@ -343,10 +355,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem @Override public void run() { - State currentState = state; - state = State.ASYNC; + State oldState = state; + updateState(State.ASYNC); handle(); - state = currentState; + updateState(oldState); } }); } @@ -370,12 +382,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem else { // The application has consumed the buffer, so consume also the DataInfo - if (dataInfo.consumed() == 0) - dataInfo.consume(dataInfo.length()); + dataInfo.consume(dataInfo.length()); + logger.debug("Consumed {} content bytes, queue size {}", dataInfo.consumed(), dataInfos.size()); dataInfo = null; buffer = null; - if (complete && dataInfos.isEmpty()) - return null; // Loop to get content bytes from DataInfos } } @@ -388,9 +398,13 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem logger.debug("Waited {} ms for content bytes", elapsed); if (dataInfo != null) { - // Only consume if it's the last DataInfo - boolean consume = complete && dataInfos.isEmpty(); - ByteBuffer byteBuffer = dataInfo.asByteBuffer(consume); + if (dataInfo == END_OF_CONTENT) + { + logger.debug("End of content bytes, queue size {}", dataInfos.size()); + return null; + } + + ByteBuffer byteBuffer = dataInfo.asByteBuffer(false); buffer = byteBuffer.isDirect() ? new DirectNIOBuffer(byteBuffer, false) : new IndirectNIOBuffer(byteBuffer, false); // Loop to return the buffer } diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java index 71bb51d5c26..93fff9c8467 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java @@ -33,13 +33,13 @@ import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnectionFactory { private static final String CONNECTION_ATTRIBUTE = "org.eclipse.jetty.spdy.http.connection"; - private static final Logger logger = LoggerFactory.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class); + private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class); private final Connector connector; @@ -75,7 +75,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect logger.debug("Received {} on {}", synInfo, stream); - HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(stream); + HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream); ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector, asyncEndPoint, connector.getServer(), (SPDYAsyncConnection)endPoint.getConnection(), stream); @@ -133,10 +133,12 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect private class HTTPSPDYAsyncEndPoint extends EmptyAsyncEndPoint { + private final AsyncEndPoint endPoint; private final Stream stream; - public HTTPSPDYAsyncEndPoint(Stream stream) + private HTTPSPDYAsyncEndPoint(AsyncEndPoint endPoint, Stream stream) { + this.endPoint = endPoint; this.stream = stream; } @@ -146,5 +148,41 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect ServerHTTPSPDYAsyncConnection connection = (ServerHTTPSPDYAsyncConnection)stream.getAttribute(CONNECTION_ATTRIBUTE); connection.async(); } + + @Override + public String getLocalAddr() + { + return endPoint.getLocalAddr(); + } + + @Override + public String getLocalHost() + { + return endPoint.getLocalHost(); + } + + @Override + public int getLocalPort() + { + return endPoint.getLocalPort(); + } + + @Override + public String getRemoteAddr() + { + return endPoint.getRemoteAddr(); + } + + @Override + public String getRemoteHost() + { + return endPoint.getRemoteHost(); + } + + @Override + public int getRemotePort() + { + return endPoint.getRemotePort(); + } } } diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java index cf468f85c59..56d3f7d3b18 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYTest.java @@ -31,8 +31,9 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.continuation.Continuation; +import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.io.ByteArrayBuffer; -import javax.servlet.AsyncContext; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.spdy.api.BytesDataInfo; @@ -189,6 +190,14 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest Assert.assertEquals("POST", httpRequest.getMethod()); Assert.assertEquals("1", httpRequest.getParameter("a")); Assert.assertEquals("2", httpRequest.getParameter("b")); + Assert.assertNotNull(httpRequest.getRemoteHost()); + Assert.assertNotNull(httpRequest.getRemotePort()); + Assert.assertNotNull(httpRequest.getRemoteAddr()); + Assert.assertNotNull(httpRequest.getLocalPort()); + Assert.assertNotNull(httpRequest.getLocalName()); + Assert.assertNotNull(httpRequest.getLocalAddr()); + Assert.assertNotNull(httpRequest.getServerPort()); + Assert.assertNotNull(httpRequest.getServerName()); handlerLatch.countDown(); } }), null); @@ -518,7 +527,6 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest @Override public void onData(Stream stream, DataInfo dataInfo) { - contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining()); if (dataInfo.isClose()) { @@ -975,7 +983,10 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest throws IOException, ServletException { request.setHandled(true); - final AsyncContext async = request.startAsync(); + + final Continuation continuation = ContinuationSupport.getContinuation(request); + continuation.suspend(); + new Thread() { @Override @@ -988,7 +999,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest int read = 0; while (read < data.length) read += input.read(buffer); - async.complete(); + continuation.complete(); latch.countDown(); } catch (IOException x) @@ -1034,7 +1045,10 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest throws IOException, ServletException { request.setHandled(true); - final AsyncContext async = request.startAsync(); + + final Continuation continuation = ContinuationSupport.getContinuation(request); + continuation.suspend(); + new Thread() { @Override @@ -1047,7 +1061,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest int read = 0; while (read < 2 * data.length) read += input.read(buffer); - async.complete(); + continuation.complete(); latch.countDown(); } catch (IOException x) @@ -1094,14 +1108,17 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest throws IOException, ServletException { request.setHandled(true); - if (request.getAsyncContinuation().isInitial()) + + final Continuation continuation = ContinuationSupport.getContinuation(request); + + if (continuation.isInitial()) { InputStream input = request.getInputStream(); byte[] buffer = new byte[256]; int read = 0; while (read < data.length) read += input.read(buffer); - final AsyncContext async = request.startAsync(); + continuation.suspend(); new Thread() { @Override @@ -1110,7 +1127,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest try { TimeUnit.SECONDS.sleep(1); - async.dispatch(); + continuation.resume(); latch.countDown(); } catch (InterruptedException x) diff --git a/jetty-spdy/spdy-jetty/pom.xml b/jetty-spdy/spdy-jetty/pom.xml index 047b3e37f1e..c27e92a5b51 100644 --- a/jetty-spdy/spdy-jetty/pom.xml +++ b/jetty-spdy/spdy-jetty/pom.xml @@ -1,65 +1,77 @@ - - - org.eclipse.jetty.spdy - spdy-parent - 8.1.3-SNAPSHOT - - 4.0.0 - spdy-jetty - Jetty :: SPDY :: Jetty Binding - - - - maven-surefire-plugin - - true - -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar - - - - - - - eclipse-release - + + + org.eclipse.jetty.spdy + spdy-parent + 8.1.3-SNAPSHOT + + 4.0.0 + spdy-jetty + Jetty :: SPDY :: Jetty Binding + + - - maven-surefire-plugin - - true - - + + maven-dependency-plugin + + + copy + generate-resources + + copy + + + + + org.mortbay.jetty.npn + npn-boot + ${npn.version} + jar + false + ${project.build.directory}/npn + + + + + + + + maven-surefire-plugin + + -Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar + + - - - - - - org.eclipse.jetty.spdy - spdy-core - ${project.version} - - - org.eclipse.jetty - jetty-server - ${project.version} - - - org.eclipse.jetty.npn - npn-api - ${project.version} - provided - - - junit - junit - - - org.slf4j - slf4j-log4j12 - ${slf4j-version} - test - - + + + + + org.eclipse.jetty.spdy + spdy-core + ${project.version} + + + org.eclipse.jetty + jetty-server + ${project.version} + + + org.eclipse.jetty.npn + npn-api + ${npn.version} + provided + + + junit + junit + + + org.slf4j + slf4j-log4j12 + ${slf4j-version} + test + + + diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncEndPoint.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncEndPoint.java index 3d4d7f702de..c1fd5e2e251 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncEndPoint.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/EmptyAsyncEndPoint.java @@ -32,6 +32,11 @@ public class EmptyAsyncEndPoint implements AsyncEndPoint private boolean closed; private int maxIdleTime; + @Override + public void dispatch() + { + } + @Override public void asyncDispatch() { diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYAsyncConnection.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYAsyncConnection.java index 3ae8ccaef46..40dfecde36b 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYAsyncConnection.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYAsyncConnection.java @@ -30,12 +30,12 @@ import org.eclipse.jetty.io.nio.NIOBuffer; import org.eclipse.jetty.spdy.api.Handler; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.parser.Parser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class SPDYAsyncConnection extends AbstractConnection implements AsyncConnection, Controller, IdleListener { - private static final Logger logger = LoggerFactory.getLogger(SPDYAsyncConnection.class); + private static final Logger logger = Log.getLogger(SPDYAsyncConnection.class); private final ByteBufferPool bufferPool; private final Parser parser; private volatile Session session; @@ -181,7 +181,7 @@ public class SPDYAsyncConnection extends AbstractConnection implements AsyncConn } catch (IOException x) { - logger.trace("", x); + logger.ignore(x); } } diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java index 818cd8572cd..438e2e3cf03 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYClient.java @@ -38,6 +38,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; @@ -313,7 +314,7 @@ public class SPDYClient } @Override - public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, final Object attachment) + public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, Object attachment) { SessionPromise sessionPromise = (SessionPromise)attachment; final SPDYClient client = sessionPromise.client; @@ -322,11 +323,31 @@ public class SPDYClient { if (sslContextFactory != null) { + final AtomicReference sslEndPointRef = new AtomicReference<>(); + final AtomicReference attachmentRef = new AtomicReference<>(attachment); SSLEngine engine = client.newSSLEngine(sslContextFactory, channel); - SslConnection sslConnection = new SslConnection(engine, endPoint); + SslConnection sslConnection = new SslConnection(engine, endPoint) + { + @Override + public void onClose() + { + sslEndPointRef.set(null); + attachmentRef.set(null); + super.onClose(); + } + }; endPoint.setConnection(sslConnection); - final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); + AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); + sslEndPointRef.set(sslEndPoint); + // Instances of the ClientProvider inner class strong reference the + // SslEndPoint (via lexical scoping), which strong references the SSLEngine. + // Since NextProtoNego stores in a WeakHashMap the SSLEngine as key + // and this instance as value, we are in the situation where the value + // of a WeakHashMap refers indirectly to the key, which is bad because + // the entry will never be removed from the WeakHashMap. + // We use AtomicReferences to be captured via lexical scoping, + // and we null them out above when the connection is closed. NextProtoNego.put(engine, new NextProtoNego.ClientProvider() { @Override @@ -340,7 +361,8 @@ public class SPDYClient { // Server does not support NPN, but this is a SPDY client, so hardcode SPDY ClientSPDYAsyncConnectionFactory connectionFactory = new ClientSPDYAsyncConnectionFactory(); - AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment); + AsyncEndPoint sslEndPoint = sslEndPointRef.get(); + AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get()); sslEndPoint.setConnection(connection); } @@ -352,9 +374,9 @@ public class SPDYClient return null; AsyncConnectionFactory connectionFactory = client.getAsyncConnectionFactory(protocol); - AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment); + AsyncEndPoint sslEndPoint = sslEndPointRef.get(); + AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get()); sslEndPoint.setConnection(connection); - return protocol; } }); diff --git a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java index be25c0fa4a4..41412b3f0c5 100644 --- a/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java +++ b/jetty-spdy/spdy-jetty/src/main/java/org/eclipse/jetty/spdy/SPDYServerConnector.java @@ -29,6 +29,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; @@ -40,14 +41,14 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.ThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class SPDYServerConnector extends SelectChannelConnector { - private static final Logger logger = LoggerFactory.getLogger(SPDYServerConnector.class); + private static final Logger logger = Log.getLogger(SPDYServerConnector.class); // Order is important on server side, so we use a LinkedHashMap private final Map factories = new LinkedHashMap<>(); @@ -76,7 +77,7 @@ public class SPDYServerConnector extends SelectChannelConnector return bufferPool; } - protected Executor getExecutor() + public Executor getExecutor() { final ThreadPool threadPool = getThreadPool(); if (threadPool instanceof Executor) @@ -91,11 +92,16 @@ public class SPDYServerConnector extends SelectChannelConnector }; } - protected ScheduledExecutorService getScheduler() + public ScheduledExecutorService getScheduler() { return scheduler; } + public SslContextFactory getSslContextFactory() + { + return sslContextFactory; + } + @Override protected void doStart() throws Exception { @@ -171,16 +177,35 @@ public class SPDYServerConnector extends SelectChannelConnector if (sslContextFactory != null) { SSLEngine engine = newSSLEngine(sslContextFactory, channel); - SslConnection sslConnection = new SslConnection(engine, endPoint); + final AtomicReference sslEndPointRef = new AtomicReference<>(); + SslConnection sslConnection = new SslConnection(engine, endPoint) + { + @Override + public void onClose() + { + sslEndPointRef.set(null); + super.onClose(); + } + }; endPoint.setConnection(sslConnection); - final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); + AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); + sslEndPointRef.set(sslEndPoint); + // Instances of the ServerProvider inner class strong reference the + // SslEndPoint (via lexical scoping), which strong references the SSLEngine. + // Since NextProtoNego stores in a WeakHashMap the SSLEngine as key + // and this instance as value, we are in the situation where the value + // of a WeakHashMap refers indirectly to the key, which is bad because + // the entry will never be removed from the WeakHashMap. + // We use AtomicReferences to be captured via lexical scoping, + // and we null them out above when the connection is closed. NextProtoNego.put(engine, new NextProtoNego.ServerProvider() { @Override public void unsupported() { AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory(); + AsyncEndPoint sslEndPoint = sslEndPointRef.get(); AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this); sslEndPoint.setConnection(connection); } @@ -195,6 +220,7 @@ public class SPDYServerConnector extends SelectChannelConnector public void protocolSelected(String protocol) { AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol); + AsyncEndPoint sslEndPoint = sslEndPointRef.get(); AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this); sslEndPoint.setConnection(connection); } diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java index 1ee5f8a96c0..f1908e73bcc 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/FlowControlTest.java @@ -23,6 +23,7 @@ import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.spdy.api.BytesDataInfo; import org.eclipse.jetty.spdy.api.DataInfo; @@ -41,6 +42,70 @@ import org.junit.Test; public class FlowControlTest extends AbstractTest { + @Test + public void testFlowControlWithConcurrentSettings() throws Exception + { + // Initial window is 64 KiB. We allow the client to send 1024 B + // then we change the window to 512 B. At this point, the client + // must stop sending data (although the initial window allows it) + + final int size = 512; + final AtomicReference dataInfoRef = new AtomicReference<>(); + final CountDownLatch dataLatch = new CountDownLatch(2); + final CountDownLatch settingsLatch = new CountDownLatch(1); + Session session = startClient(startServer(new ServerSessionFrameListener.Adapter() + { + @Override + public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) + { + stream.reply(new ReplyInfo(true)); + return new StreamFrameListener.Adapter() + { + private final AtomicInteger dataFrames = new AtomicInteger(); + + @Override + public void onData(Stream stream, DataInfo dataInfo) + { + int dataFrameCount = dataFrames.incrementAndGet(); + if (dataFrameCount == 1) + { + dataInfoRef.set(dataInfo); + Settings settings = new Settings(); + settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, size)); + stream.getSession().settings(new SettingsInfo(settings)); + } + else if (dataFrameCount > 1) + { + dataInfo.consume(dataInfo.length()); + dataLatch.countDown(); + } + } + }; + } + }), new SessionFrameListener.Adapter() + { + @Override + public void onSettings(Session session, SettingsInfo settingsInfo) + { + settingsLatch.countDown(); + } + }); + + Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS); + stream.data(new BytesDataInfo(new byte[size * 2], false)); + settingsLatch.await(5, TimeUnit.SECONDS); + + // Send the second chunk of data, must not arrive since we're flow control stalled now + stream.data(new BytesDataInfo(new byte[size * 2], true)); + Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS)); + + // Consume the data arrived to server, this will resume flow control + DataInfo dataInfo = dataInfoRef.get(); + dataInfo.consume(dataInfo.length()); + + Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS)); + } + @Test public void testServerFlowControlOneBigWrite() throws Exception { @@ -242,7 +307,7 @@ public class FlowControlTest extends AbstractTest Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS)); - Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS); + Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS); final int length = 5 * windowSize; stream.data(new BytesDataInfo(new byte[length], true)); @@ -294,6 +359,98 @@ public class FlowControlTest extends AbstractTest Assert.assertEquals(dataInfo.length(), dataInfo.consumed()); } + @Test + public void testStreamsStalledDoesNotStallOtherStreams() throws Exception + { + final int windowSize = 1024; + final CountDownLatch settingsLatch = new CountDownLatch(1); + Session session = startClient(startServer(new ServerSessionFrameListener.Adapter() + { + @Override + public void onSettings(Session session, SettingsInfo settingsInfo) + { + settingsLatch.countDown(); + } + + @Override + public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) + { + stream.reply(new ReplyInfo(false)); + stream.data(new BytesDataInfo(new byte[windowSize * 2], true)); + return null; + } + }), null); + Settings settings = new Settings(); + settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowSize)); + session.settings(new SettingsInfo(settings)); + + Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS)); + + final CountDownLatch latch = new CountDownLatch(3); + final AtomicReference dataInfoRef1 = new AtomicReference<>(); + final AtomicReference dataInfoRef2 = new AtomicReference<>(); + session.syn(new SynInfo(true), new StreamFrameListener.Adapter() + { + private final AtomicInteger dataFrames = new AtomicInteger(); + + @Override + public void onData(Stream stream, DataInfo dataInfo) + { + int frames = dataFrames.incrementAndGet(); + if (frames == 1) + { + // Do not consume it to stall flow control + dataInfoRef1.set(dataInfo); + } + else + { + dataInfo.consume(dataInfo.length()); + if (dataInfo.isClose()) + latch.countDown(); + } + } + }).get(5, TimeUnit.SECONDS); + session.syn(new SynInfo(true), new StreamFrameListener.Adapter() + { + private final AtomicInteger dataFrames = new AtomicInteger(); + + @Override + public void onData(Stream stream, DataInfo dataInfo) + { + int frames = dataFrames.incrementAndGet(); + if (frames == 1) + { + // Do not consume it to stall flow control + dataInfoRef2.set(dataInfo); + } + else + { + dataInfo.consume(dataInfo.length()); + if (dataInfo.isClose()) + latch.countDown(); + } + } + }).get(5, TimeUnit.SECONDS); + session.syn(new SynInfo(true), new StreamFrameListener.Adapter() + { + @Override + public void onData(Stream stream, DataInfo dataInfo) + { + DataInfo dataInfo1 = dataInfoRef1.getAndSet(null); + if (dataInfo1 != null) + dataInfo1.consume(dataInfo1.length()); + DataInfo dataInfo2 = dataInfoRef2.getAndSet(null); + if (dataInfo2 != null) + dataInfo2.consume(dataInfo2.length()); + dataInfo.consume(dataInfo.length()); + if (dataInfo.isClose()) + latch.countDown(); + } + }).get(5, TimeUnit.SECONDS); + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + private void expectException(Class exception, Callable command) { try diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java new file mode 100644 index 00000000000..92e642f2b13 --- /dev/null +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ProtocolViolationsTest.java @@ -0,0 +1,164 @@ +package org.eclipse.jetty.spdy; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.spdy.api.BytesDataInfo; +import org.eclipse.jetty.spdy.api.DataInfo; +import org.eclipse.jetty.spdy.api.Headers; +import org.eclipse.jetty.spdy.api.HeadersInfo; +import org.eclipse.jetty.spdy.api.RstInfo; +import org.eclipse.jetty.spdy.api.SPDY; +import org.eclipse.jetty.spdy.api.Session; +import org.eclipse.jetty.spdy.api.SessionFrameListener; +import org.eclipse.jetty.spdy.api.SessionStatus; +import org.eclipse.jetty.spdy.api.Stream; +import org.eclipse.jetty.spdy.api.StreamFrameListener; +import org.eclipse.jetty.spdy.api.StreamStatus; +import org.eclipse.jetty.spdy.api.StringDataInfo; +import org.eclipse.jetty.spdy.api.SynInfo; +import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; +import org.eclipse.jetty.spdy.frames.ControlFrameType; +import org.eclipse.jetty.spdy.frames.GoAwayFrame; +import org.eclipse.jetty.spdy.frames.SynReplyFrame; +import org.eclipse.jetty.spdy.generator.Generator; +import org.junit.Assert; +import org.junit.Test; + +public class ProtocolViolationsTest extends AbstractTest +{ + @Test + public void testSendDataBeforeReplyIsIllegal() throws Exception + { + final CountDownLatch resetLatch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(1); + Session session = startClient(startServer(new ServerSessionFrameListener.Adapter() + { + @Override + public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) + { + try + { + stream.data(new StringDataInfo("failure", true)); + return null; + } + catch (IllegalStateException x) + { + latch.countDown(); + return null; + } + } + }), new SessionFrameListener.Adapter() + { + @Override + public void onRst(Session session, RstInfo rstInfo) + { + Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus()); + resetLatch.countDown(); + } + }); + session.syn(new SynInfo(true), null); + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testReceiveDataBeforeReplyIsIllegal() throws Exception + { + ServerSocketChannel server = ServerSocketChannel.open(); + server.bind(new InetSocketAddress("localhost", 0)); + + Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null); + session.syn(new SynInfo(true), null); + + SocketChannel channel = server.accept(); + ByteBuffer readBuffer = ByteBuffer.allocate(1024); + channel.read(readBuffer); + readBuffer.flip(); + int streamId = readBuffer.getInt(8); + + Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor()); + byte[] bytes = new byte[1]; + ByteBuffer writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true)); + channel.write(writeBuffer); + + readBuffer.clear(); + channel.read(readBuffer); + readBuffer.flip(); + Assert.assertEquals(ControlFrameType.RST_STREAM.getCode(), readBuffer.getShort(2)); + Assert.assertEquals(streamId, readBuffer.getInt(8)); + + writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode())); + channel.write(writeBuffer); + channel.shutdownOutput(); + channel.close(); + + server.close(); + } + + @Test(expected = IllegalStateException.class) + public void testSendDataAfterCloseIsIllegal() throws Exception + { + Session session = startClient(startServer(null), null); + Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS); + stream.data(new StringDataInfo("test", true)); + } + + @Test(expected = IllegalStateException.class) + public void testSendHeadersAfterCloseIsIllegal() throws Exception + { + Session session = startClient(startServer(null), null); + Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS); + stream.headers(new HeadersInfo(new Headers(), true)); + } + + @Test + public void testDataSentAfterCloseIsDiscardedByRecipient() throws Exception + { + ServerSocketChannel server = ServerSocketChannel.open(); + server.bind(new InetSocketAddress("localhost", 0)); + + Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null); + final CountDownLatch dataLatch = new CountDownLatch(2); + session.syn(new SynInfo(true), new StreamFrameListener.Adapter() + { + @Override + public void onData(Stream stream, DataInfo dataInfo) + { + dataLatch.countDown(); + } + }); + + SocketChannel channel = server.accept(); + ByteBuffer readBuffer = ByteBuffer.allocate(1024); + channel.read(readBuffer); + readBuffer.flip(); + int streamId = readBuffer.getInt(8); + + Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor()); + + ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Headers())); + channel.write(writeBuffer); + + byte[] bytes = new byte[1]; + writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true)); + channel.write(writeBuffer); + + // Write again to simulate the faulty condition + writeBuffer.flip(); + channel.write(writeBuffer); + + Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS)); + + writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode())); + channel.write(writeBuffer); + channel.shutdownOutput(); + channel.close(); + + server.close(); + } +} diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java index 1a18578611c..c6c0a2b4449 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/ResetStreamTest.java @@ -64,6 +64,8 @@ public class ResetStreamTest extends AbstractTest Assert.assertEquals(0, serverSession.getStreams().size()); Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS)); + // Need to sleep a while to give the chance to the implementation to remove the stream + TimeUnit.SECONDS.sleep(1); Assert.assertEquals(0, clientSession.getStreams().size()); } diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SSLEngineLeakTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SSLEngineLeakTest.java new file mode 100644 index 00000000000..cdb9a7ef6e2 --- /dev/null +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SSLEngineLeakTest.java @@ -0,0 +1,59 @@ +package org.eclipse.jetty.spdy; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.npn.NextProtoNego; +import org.eclipse.jetty.spdy.api.Session; +import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.Assert; +import org.junit.Test; + +public class SSLEngineLeakTest extends AbstractTest +{ + @Override + protected SPDYServerConnector newSPDYServerConnector(ServerSessionFrameListener listener) + { + SslContextFactory sslContextFactory = newSslContextFactory(); + return new SPDYServerConnector(listener, sslContextFactory); + } + + @Override + protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool) + { + SslContextFactory sslContextFactory = newSslContextFactory(); + return new SPDYClient.Factory(threadPool, sslContextFactory); + } + + @Test + public void testSSLEngineLeak() throws Exception + { + System.gc(); + Thread.sleep(1000); + + Field field = NextProtoNego.class.getDeclaredField("objects"); + field.setAccessible(true); + Map objects = (Map)field.get(null); + int initialSize = objects.size(); + + avoidStackLocalVariables(); + // Allow the close to arrive to the server and the selector to process it + Thread.sleep(1000); + + // Perform GC to be sure that the WeakHashMap is cleared + System.gc(); + Thread.sleep(1000); + + // Check that the WeakHashMap is empty + Assert.assertEquals(initialSize, objects.size()); + } + + private void avoidStackLocalVariables() throws Exception + { + Session session = startClient(startServer(null), null); + session.goAway().get(5, TimeUnit.SECONDS); + } +} diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynDataReplyDataLoadTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynDataReplyDataLoadTest.java index ef04ad5449c..129ddd86acd 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynDataReplyDataLoadTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynDataReplyDataLoadTest.java @@ -87,25 +87,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest } }); - List> tasks = new ArrayList<>(); - for (int i = 0; i < count; ++i) - { - tasks.add(new Callable() - { - @Override - public Object call() throws Exception - { - synCompletedData(session, headers, iterations); - return null; - } - }); - } - ExecutorService threadPool = Executors.newFixedThreadPool(count); - List> futures = threadPool.invokeAll(tasks); - for (Future future : futures) - future.get(iterations, TimeUnit.SECONDS); - Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS)); + List> tasks = new ArrayList<>(); tasks.clear(); for (int i = 0; i < count; ++i) @@ -120,11 +103,38 @@ public class SynDataReplyDataLoadTest extends AbstractTest } }); } + { + long begin = System.nanoTime(); + List> futures = threadPool.invokeAll(tasks); + for (Future future : futures) + future.get(iterations, TimeUnit.SECONDS); + Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS)); + long end = System.nanoTime(); + System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); + } - futures = threadPool.invokeAll(tasks); - for (Future future : futures) - future.get(iterations, TimeUnit.SECONDS); - Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS)); + tasks.clear(); + for (int i = 0; i < count; ++i) + { + tasks.add(new Callable() + { + @Override + public Object call() throws Exception + { + synCompletedData(session, headers, iterations); + return null; + } + }); + } + { + long begin = System.nanoTime(); + List> futures = threadPool.invokeAll(tasks); + for (Future future : futures) + future.get(iterations, TimeUnit.SECONDS); + Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS)); + long end = System.nanoTime(); + System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); + } threadPool.shutdown(); } diff --git a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynReplyTest.java b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynReplyTest.java index 7e5d67fdd2f..207e1335b03 100644 --- a/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynReplyTest.java +++ b/jetty-spdy/spdy-jetty/src/test/java/org/eclipse/jetty/spdy/SynReplyTest.java @@ -30,12 +30,10 @@ 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.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.StreamStatus; import org.eclipse.jetty.spdy.api.StringDataInfo; import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; @@ -324,39 +322,6 @@ public class SynReplyTest extends AbstractTest Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS)); } - @Test - public void testSynDataRst() throws Exception - { - final AtomicReference ref = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(1); - ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter() - { - @Override - public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) - { - // Do not send the reply, we expect a RST_STREAM - stream.data(new StringDataInfo("foo", true)); - return null; - } - - @Override - public void onRst(Session session, RstInfo rstInfo) - { - ref.set(rstInfo); - latch.countDown(); - } - }; - Session session = startClient(startServer(serverSessionFrameListener), null); - - Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS); - - Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); - RstInfo rstInfo = ref.get(); - Assert.assertNotNull(rstInfo); - Assert.assertEquals(stream.getId(), rstInfo.getStreamId()); - Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus()); - } - @Test public void testSynReplyDataSynReplyData() throws Exception { diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index d625814ba26..70fbe1063f8 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -381,7 +381,7 @@ public class Main } else if (info.equals("@STARTINI")) { - List ini = loadStartIni(null); + List ini = loadStartIni(new File(_jettyHome,"start.ini")); if (ini != null && ini.size() > 0) { for (String a : ini) diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config index ca2d0ae5eac..23bb426162f 100644 --- a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config +++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config @@ -142,9 +142,6 @@ $(jetty.home)/lib/jetty-policy-$(version).jar $(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser $(jetty.home)/lib/jetty-client-$(version).jar ! available org.eclipse.jetty.client.HttpClient -[All,websocket] -$(jetty.home)/lib/jetty-websocket-$(version).jar ! available org.eclipse.jetty.websocket.WebSocket - [Client] $(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java index d5b5b50fc23..ad43c6aec98 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java @@ -287,7 +287,25 @@ public class MultiMap implements ConcurrentMap, Serializable */ public Map toStringArrayMap() { - HashMap map = new HashMap(_map.size()*3/2); + HashMap map = new HashMap(_map.size()*3/2) + { + public String toString() + { + StringBuilder b=new StringBuilder(); + b.append('{'); + for (K k:keySet()) + { + if(b.length()>1) + b.append(','); + b.append(k); + b.append('='); + b.append(Arrays.asList(get(k))); + } + + b.append('}'); + return b.toString(); + } + }; for(Map.Entry entry: _map.entrySet()) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java index a8b87de377b..40f7ac679ee 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java @@ -64,6 +64,7 @@ public class MultiPartInputStream protected String _filename; protected File _file; protected OutputStream _out; + protected ByteArrayOutputStream2 _bout; protected String _contentType; protected MultiMap _headers; protected long _size = 0; @@ -95,7 +96,7 @@ public class MultiPartInputStream { //Write to a buffer in memory until we discover we've exceed the //MultipartConfig fileSizeThreshold - _out = new ByteArrayOutputStream(); + _out = _bout= new ByteArrayOutputStream2(); } } @@ -142,8 +143,9 @@ public class MultiPartInputStream { //already written some bytes, so need to copy them into the file _out.flush(); - ((ByteArrayOutputStream)_out).writeTo(bos); + _bout.writeTo(bos); _out.close(); + _bout = null; } _out = bos; } @@ -168,7 +170,9 @@ public class MultiPartInputStream */ public String getHeader(String name) { - return (String)_headers.getValue(name, 0); + if (name == null) + return null; + return (String)_headers.getValue(name.toLowerCase(), 0); } /** @@ -199,10 +203,17 @@ public class MultiPartInputStream else { //part content is in a ByteArrayOutputStream - return new ByteArrayInputStream(((ByteArrayOutputStream)_out).toByteArray()); + return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size()); } } + public byte[] getBytes() + { + if (_bout!=null) + return _bout.toByteArray(); + return null; + } + /** * @see javax.servlet.http.Part#getName() */ @@ -232,19 +243,22 @@ public class MultiPartInputStream try { bos = new BufferedOutputStream(new FileOutputStream(_file)); - ((ByteArrayOutputStream)_out).writeTo(bos); + _bout.writeTo(bos); bos.flush(); } finally { if (bos != null) bos.close(); + _bout = null; } } else { //the part data is already written to a temporary file, just rename it - _file.renameTo(new File(_tmpDir, fileName)); + File f = new File(_tmpDir, fileName); + if (_file.renameTo(f)) + _file = f; } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java index 5653e446ed9..5f129e0d8cb 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java @@ -27,20 +27,11 @@ import java.io.OutputStream; public class MultiPartOutputStream extends FilterOutputStream { /* ------------------------------------------------------------ */ - private static byte[] __CRLF; - private static byte[] __DASHDASH; + private static final byte[] __CRLF={'\r','\n'}; + private static final byte[] __DASHDASH={'-','-'}; public static String MULTIPART_MIXED="multipart/mixed"; public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace"; - static - { - try - { - __CRLF="\015\012".getBytes(StringUtil.__ISO_8859_1); - __DASHDASH="--".getBytes(StringUtil.__ISO_8859_1); - } - catch (Exception e) {e.printStackTrace(); System.exit(1);} - } /* ------------------------------------------------------------ */ private String boundary; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java index f937c13ad17..18de6d34e5a 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/RolloverFileOutputStream.java @@ -326,6 +326,7 @@ public class RolloverFileOutputStream extends FilterOutputStream } catch(IOException e) { + // Cannot log this exception to a LOG, as RolloverFOS can be used by logging e.printStackTrace(); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index c304427d74f..e87a16fc7ee 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -44,7 +44,7 @@ public class TypeUtil public static int LF = '\012'; /* ------------------------------------------------------------ */ - private static final HashMap name2Class=new HashMap(); + private static final HashMap> name2Class=new HashMap>(); static { name2Class.put("boolean",java.lang.Boolean.TYPE); @@ -92,7 +92,7 @@ public class TypeUtil } /* ------------------------------------------------------------ */ - private static final HashMap class2Name=new HashMap(); + private static final HashMap, String> class2Name=new HashMap, String>(); static { class2Name.put(java.lang.Boolean.TYPE,"boolean"); @@ -119,12 +119,12 @@ public class TypeUtil } /* ------------------------------------------------------------ */ - private static final HashMap class2Value=new HashMap(); + private static final HashMap, Method> class2Value=new HashMap, Method>(); static { try { - Class[] s ={java.lang.String.class}; + Class[] s ={java.lang.String.class}; class2Value.put(java.lang.Boolean.TYPE, java.lang.Boolean.class.getMethod("valueOf",s)); @@ -158,7 +158,7 @@ public class TypeUtil } catch(Exception e) { - e.printStackTrace(); + throw new Error(e); } } @@ -180,7 +180,7 @@ public class TypeUtil * @param name A class or type name. * @return A class , which may be a primitive TYPE field.. */ - public static Class fromName(String name) + public static Class fromName(String name) { return name2Class.get(name); } @@ -190,7 +190,7 @@ public class TypeUtil * @param type A class , which may be a primitive TYPE field. * @return Canonical name. */ - public static String toName(Class type) + public static String toName(Class type) { return class2Name.get(type); } @@ -201,7 +201,7 @@ public class TypeUtil * @param value The value as a string. * @return The value as an Object. */ - public static Object valueOf(Class type, String value) + public static Object valueOf(Class type, String value) { try { @@ -216,7 +216,7 @@ public class TypeUtil type.equals(java.lang.Character.class)) return new Character(value.charAt(0)); - Constructor c = type.getConstructor(java.lang.String.class); + Constructor c = type.getConstructor(java.lang.String.class); return c.newInstance(value); } catch(NoSuchMethodException e) @@ -431,7 +431,7 @@ public class TypeUtil } - public static void dump(Class c) + public static void dump(Class c) { System.err.println("Dump: "+c); dump(c.getClassLoader()); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index c81bdde87cc..6a4c066a3f5 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -14,6 +14,10 @@ package org.eclipse.jetty.util; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; + +import org.eclipse.jetty.util.log.Log; @@ -67,6 +71,7 @@ public class URIUtil */ public static StringBuilder encodePath(StringBuilder buf, String path) { + byte[] bytes=null; if (buf==null) { loop: @@ -84,8 +89,23 @@ public class URIUtil case '<': case '>': case ' ': - buf=new StringBuilder(path.length()<<1); + buf=new StringBuilder(path.length()*2); break loop; + default: + if (c>127) + { + try + { + bytes=path.getBytes(URIUtil.__CHARSET); + } + catch (UnsupportedEncodingException e) + { + throw new IllegalStateException(e); + } + buf=new StringBuilder(path.length()*2); + break loop; + } + } } if (buf==null) @@ -94,41 +114,91 @@ public class URIUtil synchronized(buf) { - for (int i=0;i': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - default: - buf.append(c); - continue; + byte c=bytes[i]; + switch(c) + { + case '%': + buf.append("%25"); + continue; + case '?': + buf.append("%3F"); + continue; + case ';': + buf.append("%3B"); + continue; + case '#': + buf.append("%23"); + continue; + case '"': + buf.append("%22"); + continue; + case '\'': + buf.append("%27"); + continue; + case '<': + buf.append("%3C"); + continue; + case '>': + buf.append("%3E"); + continue; + case ' ': + buf.append("%20"); + continue; + default: + if (c<0) + { + buf.append('%'); + TypeUtil.toHex(c,buf); + } + else + buf.append((char)c); + continue; + } + } + + } + else + { + for (int i=0;i': + buf.append("%3E"); + continue; + case ' ': + buf.append("%20"); + continue; + default: + buf.append(c); + continue; + } } } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java index 84824567afa..f01013dd17d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java @@ -86,7 +86,7 @@ import org.eclipse.jetty.util.log.Logger; */ public class JSON { - private static final Logger LOG = Log.getLogger(JSON.class); + static final Logger LOG = Log.getLogger(JSON.class); public final static JSON DEFAULT = new JSON(); private Map _convertors = new ConcurrentHashMap(); @@ -950,7 +950,7 @@ public class JSON } catch (ClassNotFoundException e) { - e.printStackTrace(); + LOG.warn(e); } } return map; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java index 470564c1f31..6adfdd75e7f 100755 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java @@ -97,7 +97,6 @@ public class JSONObjectConvertor implements JSON.Convertor } catch (Throwable e) { - // e.printStackTrace(); throw new IllegalArgumentException(e); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java index d6b51e84022..64020e2f86a 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java @@ -66,7 +66,7 @@ public class JSONPojoConvertorFactory implements JSON.Convertor } catch (ClassNotFoundException e) { - e.printStackTrace(); + JSON.LOG.warn(e); } } if (convertor!=null) @@ -92,7 +92,7 @@ public class JSONPojoConvertorFactory implements JSON.Convertor } catch (ClassNotFoundException e) { - e.printStackTrace(); + JSON.LOG.warn(e); } } if (convertor!=null) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java index f19407e0d23..917d9ddfc71 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java @@ -350,10 +350,17 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable /* ------------------------------------------------------------ */ public static void dumpObject(Appendable out,Object o) throws IOException { - if (o instanceof LifeCycle) - out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n"); - else - out.append(String.valueOf(o)).append("\n"); + try + { + if (o instanceof LifeCycle) + out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n"); + else + out.append(String.valueOf(o)).append("\n"); + } + catch(Throwable th) + { + out.append(" => ").append(th.toString()).append('\n'); + } } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java index 2dca95127f2..eacb02a795c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java @@ -29,17 +29,7 @@ public class Slf4jLog extends AbstractLogger public Slf4jLog(String name) { - // This checks to make sure that an slf4j implementation is present. - // It is needed because slf4j-api 1.6.x defaults to using NOPLogger. - try - { - Class.forName("org.slf4j.impl.StaticLoggerBinder"); - } - catch (ClassNotFoundException ex) - { - throw new NoClassDefFoundError("org.slf4j.impl.StaticLoggerBinder"); - } - + //NOTE: if only an slf4j-api jar is on the classpath, slf4j will use a NOPLogger org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( name ); // Fix LocationAwareLogger use to indicate FQCN of this class - diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java index 1848e7ad8c6..7636bd870dc 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java @@ -77,7 +77,7 @@ public class StdErrLog extends AbstractLogger private int _level = LEVEL_INFO; // Level that this Logger was configured as (remembered in special case of .setDebugEnabled()) private int _configuredLevel; - private PrintStream _stderr = System.err; + private PrintStream _stderr = null; private boolean _source = __source; // Print the long form names, otherwise use abbreviated private boolean _printLongNames = __long; @@ -271,7 +271,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":WARN:",msg,args); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -286,7 +286,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":WARN:",msg,thrown); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -296,7 +296,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":INFO:",msg,args); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -311,7 +311,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":INFO:",msg,thrown); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -369,7 +369,7 @@ public class StdErrLog extends AbstractLogger public void setStdErrStream(PrintStream stream) { - this._stderr = stream; + this._stderr = stream==System.err?null:stream; } public void debug(String msg, Object... args) @@ -378,7 +378,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":DBUG:",msg,args); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -393,7 +393,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":DBUG:",msg,thrown); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } @@ -616,7 +616,7 @@ public class StdErrLog extends AbstractLogger { StringBuilder buffer = new StringBuilder(64); format(buffer,":IGNORED:","",ignored); - _stderr.println(buffer); + (_stderr==null?System.err:_stderr).println(buffer); } } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java index e3cccc09dd8..a6ae12096f1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java @@ -465,12 +465,16 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo { final StackTraceElement[] trace=thread.getStackTrace(); boolean inIdleJobPoll=false; - for (StackTraceElement t : trace) + // trace can be null on early java 6 jvms + if (trace != null) { - if ("idleJobPoll".equals(t.getMethodName())) + for (StackTraceElement t : trace) { - inIdleJobPoll=true; - break; + if ("idleJobPoll".equals(t.getMethodName())) + { + inIdleJobPoll = true; + break; + } } } final boolean idle=inIdleJobPoll; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java index c66a14eac11..6c01b7d6310 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java @@ -131,21 +131,30 @@ public class MultiPartInputStreamTest extends TestCase IO.copy(is, os); assertEquals("Joe Blow", new String(os.toByteArray())); assertEquals(8, field1.getSize()); + + assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //in internal buffer field1.write("field1.txt"); + assertNull(((MultiPartInputStream.MultiPart)field1).getBytes()); //no longer in internal buffer File f = new File (_dirname+File.separator+"field1.txt"); assertTrue(f.exists()); - field1.delete(); - assertFalse(f.exists()); + field1.write("another_field1.txt"); + File f2 = new File(_dirname+File.separator+"another_field1.txt"); + assertTrue(f2.exists()); + assertFalse(f.exists()); //should have been renamed + field1.delete(); //file should be deleted + assertFalse(f2.exists()); + Part stuff = mpis.getPart("stuff"); assertEquals("text/plain", stuff.getContentType()); - assertEquals("text/plain", stuff.getHeader("content-type")); + assertEquals("text/plain", stuff.getHeader("Content-Type")); assertEquals(1, stuff.getHeaders("content-type").size()); assertEquals("form-data; name=\"stuff\"; filename=\"stuff.txt\"", stuff.getHeader("content-disposition")); assertEquals(2, stuff.getHeaderNames().size()); assertEquals(51, stuff.getSize()); f = ((MultiPartInputStream.MultiPart)stuff).getFile(); assertNotNull(f); // longer than 100 bytes, should already be a file + assertNull(((MultiPartInputStream.MultiPart)stuff).getBytes()); //not in internal buffer any more assertTrue(f.exists()); assertNotSame("stuff.txt", f.getName()); stuff.write("stuff.txt"); diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 9495f68a923..fc9f3734c02 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -305,7 +305,8 @@ public class WebInfConfiguration extends AbstractConfiguration } catch(IOException e) { - LOG.warn("tmpdir",e); System.exit(1); + tmpDir = null; + throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e); } } } @@ -402,8 +403,7 @@ public class WebInfConfiguration extends AbstractConfiguration } if (LOG.isDebugEnabled()) - LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()); - + LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile())); // Is the WAR usable directly? if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:")) { @@ -485,57 +485,50 @@ public class WebInfConfiguration extends AbstractConfiguration LOG.debug("webapp=" + web_app); } - // Do we need to extract WEB-INF/lib? - if (context.isCopyWebInf()) + if (context.isCopyWebInf() && !context.isCopyWebDir()) { Resource web_inf= web_app.addPath("WEB-INF/"); - if (web_inf instanceof ResourceCollection || - web_inf.exists() && - web_inf.isDirectory() && - (web_inf.getFile()==null || !web_inf.getFile().isDirectory())) - { - File extractedWebInfDir= new File(context.getTempDirectory(), "webinf"); - if (extractedWebInfDir.exists()) - IO.delete(extractedWebInfDir); - extractedWebInfDir.mkdir(); - Resource web_inf_lib = web_inf.addPath("lib/"); - File webInfDir=new File(extractedWebInfDir,"WEB-INF"); - webInfDir.mkdir(); + File extractedWebInfDir= new File(context.getTempDirectory(), "webinf"); + if (extractedWebInfDir.exists()) + IO.delete(extractedWebInfDir); + extractedWebInfDir.mkdir(); + Resource web_inf_lib = web_inf.addPath("lib/"); + File webInfDir=new File(extractedWebInfDir,"WEB-INF"); + webInfDir.mkdir(); - if (web_inf_lib.exists()) - { - File webInfLibDir = new File(webInfDir, "lib"); - if (webInfLibDir.exists()) - IO.delete(webInfLibDir); - webInfLibDir.mkdir(); + if (web_inf_lib.exists()) + { + File webInfLibDir = new File(webInfDir, "lib"); + if (webInfLibDir.exists()) + IO.delete(webInfLibDir); + webInfLibDir.mkdir(); - LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir); - web_inf_lib.copyTo(webInfLibDir); - } + LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir); + web_inf_lib.copyTo(webInfLibDir); + } - Resource web_inf_classes = web_inf.addPath("classes/"); - if (web_inf_classes.exists()) - { - File webInfClassesDir = new File(webInfDir, "classes"); - if (webInfClassesDir.exists()) - IO.delete(webInfClassesDir); - webInfClassesDir.mkdir(); - LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath()); - web_inf_classes.copyTo(webInfClassesDir); - } + Resource web_inf_classes = web_inf.addPath("classes/"); + if (web_inf_classes.exists()) + { + File webInfClassesDir = new File(webInfDir, "classes"); + if (webInfClassesDir.exists()) + IO.delete(webInfClassesDir); + webInfClassesDir.mkdir(); + LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath()); + web_inf_classes.copyTo(webInfClassesDir); + } - web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath()); + web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath()); - ResourceCollection rc = new ResourceCollection(web_inf,web_app); + ResourceCollection rc = new ResourceCollection(web_inf,web_app); - if (LOG.isDebugEnabled()) - LOG.debug("context.resourcebase = "+rc); + if (LOG.isDebugEnabled()) + LOG.debug("context.resourcebase = "+rc); - context.setBaseResource(rc); - } + context.setBaseResource(rc); } } diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java index 52894e37a83..359da247524 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java @@ -87,6 +87,24 @@ public class WebAppContextTest assertTrue(Arrays.equals(configs,wac.getConfigurations())); } + @Test + public void testRealPathDoesNotExist() throws Exception + { + Server server = new Server(0); + WebAppContext context = new WebAppContext(".", "/"); + server.setHandler(context); + server.start(); + + // When + ServletContext ctx = context.getServletContext(); + + // Then + // This passes: + assertNotNull(ctx.getRealPath("/doesnotexist")); + // This fails: + assertNotNull(ctx.getRealPath("/doesnotexist/")); + } + /** * tests that the servlet context white list works * diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 3945d9c5933..af8aec2b666 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -1135,7 +1135,7 @@ public class XmlConfiguration final AtomicReference exception = new AtomicReference(); - AccessController.doPrivileged(new PrivilegedAction() + AccessController.doPrivileged(new PrivilegedAction() { public Object run() { @@ -1216,11 +1216,6 @@ public class XmlConfiguration } } } - catch (AccessControlException ace) - { - ace.printStackTrace(System.err); - exception.set(ace); - } catch (Exception e) { LOG.debug(Log.EXCEPTION,e); diff --git a/pom.xml b/pom.xml index 24f8b0bf93c..61df063888c 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ Jetty :: Project ${jetty.url} pom + UTF-8 http://www.eclipse.org/jetty @@ -358,7 +359,6 @@ jetty-http jetty-websocket jetty-continuation - jetty-npn jetty-server jetty-client jetty-xml