Merge branch 'master' into release

This commit is contained in:
Jesse McConnell 2012-04-12 09:14:15 -05:00
commit 76788d730f
125 changed files with 4407 additions and 3342 deletions

View File

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

View File

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

View File

@ -276,6 +276,11 @@ class SelectConnector extends AggregateLifeCycle implements HttpClient.Connector
_endp.shutdownOutput();
}
public void dispatch()
{
_endp.asyncDispatch();
}
public void asyncDispatch()
{
_endp.asyncDispatch();

View File

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

View File

@ -103,6 +103,16 @@
<outputDirectory>${assembly-directory}/webapps</outputDirectory>
<destFileName>test.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty-http-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps</outputDirectory>
<destFileName>spdy.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-start</artifactId>
@ -124,12 +134,24 @@
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit</excludeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy</excludeGroupIds>
<excludeArtifactIds>jetty-all,jetty-start,jetty-monitor,jetty-jsp</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-lib-spdy-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.spdy</includeGroupIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/spdy</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-orbit-servlet-api-deps</id>
<phase>generate-resources</phase>
@ -222,7 +244,7 @@
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.spdy</includeGroupIds>
<classifier>config</classifier>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<excludes>META-INF/**</excludes>
@ -398,6 +420,27 @@
<artifactId>jetty-overlay-deployer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-jetty-http-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>

View File

@ -293,7 +293,6 @@ public class HttpFields
/* -------------------------------------------------------------- */
private final ArrayList<Field> _fields = new ArrayList<Field>(20);
private final HashMap<Buffer,Field> _names = new HashMap<Buffer,Field>(32);
private final int _maxCookieVersion;
/* ------------------------------------------------------------ */
/**
@ -301,19 +300,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<String, Buffer> __cache = new ConcurrentHashMap<String, Buffer>();
private static int __cacheSize = Integer.getInteger("org.eclipse.jetty.http.HttpFields.CACHE",2000);
@ -929,7 +917,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)
@ -938,29 +926,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=");
@ -977,23 +954,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");

View File

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

View File

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

View File

@ -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<String> _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<String> 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<Integer.MAX_VALUE)
@ -166,15 +160,16 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String)
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#addHeader(java.lang.String, java.lang.String)
*/
@Override
public void addHeader(String name, String value)
{
if ("content-length".equalsIgnoreCase(name))
{
_contentLength=Long.parseLong(value);
if (_gzStream!=null)
_gzStream.setContentLength(_contentLength);
if (_compressedStream!=null)
_compressedStream.setContentLength(_contentLength);
}
else if ("content-type".equalsIgnoreCase(name))
{
@ -185,7 +180,7 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper
super.addHeader(name,value);
if (!isCommitted())
{
noGzip();
noCompression();
}
}
else
@ -194,8 +189,119 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String)
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#flushBuffer()
*/
@Override
public void flushBuffer() throws IOException
{
if (_writer!=null)
_writer.flush();
if (_compressedStream!=null)
_compressedStream.finish();
else
getResponse().flushBuffer();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#reset()
*/
@Override
public void reset()
{
super.reset();
if (_compressedStream!=null)
_compressedStream.resetBuffer();
_writer=null;
_compressedStream=null;
_noCompression=false;
_contentLength=-1;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#resetBuffer()
*/
@Override
public void resetBuffer()
{
super.resetBuffer();
if (_compressedStream!=null)
_compressedStream.resetBuffer();
_writer=null;
_compressedStream=null;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendError(int, java.lang.String)
*/
@Override
public void sendError(int sc, String msg) throws IOException
{
resetBuffer();
super.sendError(sc,msg);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendError(int)
*/
@Override
public void sendError(int sc) throws IOException
{
resetBuffer();
super.sendError(sc);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendRedirect(java.lang.String)
*/
@Override
public void sendRedirect(String location) throws IOException
{
resetBuffer();
super.sendRedirect(location);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#noCompression()
*/
public void noCompression()
{
_noCompression=true;
if (_compressedStream!=null)
{
try
{
_compressedStream.doNotCompress();
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#finish()
*/
public void finish() throws IOException
{
if (_writer!=null && !_compressedStream.isClosed())
_writer.flush();
if (_compressedStream!=null)
_compressedStream.finish();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setHeader(java.lang.String, java.lang.String)
*/
@Override
public void setHeader(String name, String value)
{
if ("content-length".equalsIgnoreCase(name))
@ -211,197 +317,77 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper
super.setHeader(name,value);
if (!isCommitted())
{
noGzip();
noCompression();
}
}
else
super.setHeader(name,value);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#setIntHeader(java.lang.String, int)
*/
public void setIntHeader(String name, int value)
{
if ("content-length".equalsIgnoreCase(name))
{
_contentLength=value;
if (_gzStream!=null)
_gzStream.setContentLength(_contentLength);
}
else
super.setIntHeader(name,value);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.ServletResponseWrapper#flushBuffer()
*/
public void flushBuffer() throws IOException
{
if (_writer!=null)
_writer.flush();
if (_gzStream!=null)
_gzStream.finish();
else
getResponse().flushBuffer();
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.ServletResponseWrapper#reset()
*/
public void reset()
{
super.reset();
if (_gzStream!=null)
_gzStream.resetBuffer();
_writer=null;
_gzStream=null;
_noGzip=false;
_contentLength=-1;
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.ServletResponseWrapper#resetBuffer()
*/
public void resetBuffer()
{
super.resetBuffer();
if (_gzStream!=null)
_gzStream.resetBuffer();
_writer=null;
_gzStream=null;
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#sendError(int, java.lang.String)
*/
public void sendError(int sc, String msg) throws IOException
{
resetBuffer();
super.sendError(sc,msg);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#sendError(int)
*/
public void sendError(int sc) throws IOException
{
resetBuffer();
super.sendError(sc);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.http.HttpServletResponseWrapper#sendRedirect(java.lang.String)
*/
public void sendRedirect(String location) throws IOException
{
resetBuffer();
super.sendRedirect(location);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.ServletResponseWrapper#getOutputStream()
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#getOutputStream()
*/
@Override
public ServletOutputStream getOutputStream() throws IOException
{
if (_gzStream==null)
if (_compressedStream==null)
{
if (getResponse().isCommitted() || _noGzip)
if (getResponse().isCommitted() || _noCompression)
{
setContentLength(_contentLength);
return getResponse().getOutputStream();
}
_gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minCompressSize);
}
else if (_writer!=null)
throw new IllegalStateException("getWriter() called");
return _gzStream;
return (ServletOutputStream)_compressedStream;
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.ServletResponseWrapper#getWriter()
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#getWriter()
*/
@Override
public PrintWriter getWriter() throws IOException
{
if (_writer==null)
{
if (_gzStream!=null)
if (_compressedStream!=null)
throw new IllegalStateException("getOutputStream() called");
if (getResponse().isCommitted() || _noGzip)
if (getResponse().isCommitted() || _noCompression)
{
setContentLength(_contentLength);
return getResponse().getWriter();
}
_gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
_writer=newWriter(_gzStream,getCharacterEncoding());
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minCompressSize);
_writer=newWriter((OutputStream)_compressedStream,getCharacterEncoding());
}
return _writer;
}
/* ------------------------------------------------------------ */
/**
* No gzip.
*/
public void noGzip()
{
_noGzip=true;
if (_gzStream!=null)
{
try
{
_gzStream.doNotGzip();
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
}
/* ------------------------------------------------------------ */
/**
* Finish.
*
* @throws IOException Signals that an I/O exception has occurred.
* @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setIntHeader(java.lang.String, int)
*/
public void finish() throws IOException
@Override
public void setIntHeader(String name, int value)
{
if (_writer!=null && !_gzStream._closed)
_writer.flush();
if (_gzStream!=null)
_gzStream.finish();
if ("content-length".equalsIgnoreCase(name))
{
_contentLength=value;
if (_compressedStream!=null)
_compressedStream.setContentLength(_contentLength);
}
else
super.setIntHeader(name,value);
}
/* ------------------------------------------------------------ */
/**
* Allows derived implementations to replace GzipStream implementation.
*
* @param request the request
* @param response the response
* @param contentLength the content length
* @param bufferSize the buffer size
* @param minGzipSize the min gzip size
* @return the gzip stream
* @throws IOException Signals that an I/O exception has occurred.
*/
protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
{
return new GzipStream(request,response,contentLength,bufferSize,minGzipSize);
}
/* ------------------------------------------------------------ */
/**
* Allows derived implementations to replace PrintWriter implementation.
@ -415,5 +401,11 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper
{
return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
}
}
/* ------------------------------------------------------------ */
/**
*@return the underlying CompressedStream implementation
*/
protected abstract AbstractCompressedStream newCompressedStream(HttpServletRequest _request, HttpServletResponse response, long _contentLength2, int _bufferSize2, int _minCompressedSize2) throws IOException;
}

View File

@ -1,305 +0,0 @@
// ========================================================================
// Copyright (c) Webtide LLC
// ------------------------------------------------------------------------
// 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.GZIPOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
/* ------------------------------------------------------------ */
/**
*/
public class GzipStream extends ServletOutputStream
{
protected HttpServletRequest _request;
protected HttpServletResponse _response;
protected OutputStream _out;
protected ByteArrayOutputStream2 _bOut;
protected GZIPOutputStream _gzOut;
protected boolean _closed;
protected int _bufferSize;
protected int _minGzipSize;
protected long _contentLength;
protected boolean _doNotGzip;
/**
* Instantiates a new gzip stream.
*
* @param request the request
* @param response the response
* @param contentLength the content length
* @param bufferSize the buffer size
* @param minGzipSize the min gzip size
* @throws IOException Signals that an I/O exception has occurred.
*/
public GzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
{
_request=request;
_response=response;
_contentLength=contentLength;
_bufferSize=bufferSize;
_minGzipSize=minGzipSize;
if (minGzipSize==0)
doGzip();
}
/**
* Reset buffer.
*/
public void resetBuffer()
{
if (_response.isCommitted())
throw new IllegalStateException("Committed");
_closed=false;
_out=null;
_bOut=null;
if (_gzOut!=null)
_response.setHeader("Content-Encoding",null);
_gzOut=null;
_doNotGzip=false;
}
/**
* Sets the content length.
*
* @param length the new content length
*/
public void setContentLength(long length)
{
_contentLength=length;
if (_doNotGzip && length>=0)
{
if(_contentLength<Integer.MAX_VALUE)
_response.setContentLength((int)_contentLength);
else
_response.setHeader("Content-Length",Long.toString(_contentLength));
}
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#flush()
*/
public void flush() throws IOException
{
if (_out==null || _bOut!=null)
{
if (_contentLength>0 && _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));
}
}

View File

@ -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<String> 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<String> enum2set(Enumeration<String> e)

View File

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

View File

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

View File

@ -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,46 +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
{
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;

View File

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

View File

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

View File

@ -69,7 +69,7 @@
-->
<!--
<Call name="createRegistry" class="java.rmi.registry.LocateRegistry">
<Arg type="java.lang.Integer">1099</Arg>
<Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
<Call name="sleep" class="java.lang.Thread">
<Arg type="java.lang.Integer">1000</Arg>
</Call>
@ -91,11 +91,11 @@
<New class="javax.management.remote.JMXServiceURL">
<Arg type="java.lang.String">rmi</Arg>
<Arg type="java.lang.String" />
<Arg type="java.lang.Integer">0</Arg>
<Arg type="java.lang.String">/jndi/rmi://localhost:1099/jettyjmx</Arg>
<Arg type="java.lang.Integer"><SystemProperty name="jetty.jmxrmiport" default="1099"/></Arg>
<Arg type="java.lang.String">/jndi/rmi://<SystemProperty name="jetty.jmxrmihost" default="localhost"/>:<SystemProperty name="jetty.jmxrmiport" default="1099"/>/jmxrmi</Arg>
</New>
</Arg>
<Arg>org.eclipse.jetty:name=rmiconnectorserver</Arg>
<Arg>org.eclipse.jetty.jmx:name=rmiconnectorserver</Arg>
<Call name="start" />
</New>
-->

View File

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

View File

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

View File

@ -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: 7.6.2.qualifier
Bundle-Version: 7.6.3.qualifier
Bundle-Vendor: Mort Bay Consulting
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: com.sun.el;resolution:=optional,
@ -31,7 +31,7 @@ Import-Package: com.sun.el;resolution:=optional,
org.apache.jasper.tagplugins.jstl;version="6.0.0";resolution:=optional,
org.apache.jasper.util;version="6.0.0";resolution:=optional,
org.apache.jasper.xmlparser;version="6.0.0";resolution:=optional,
org.glassfish.jasper.api;version="2.1.3";resolution:=optional,
org.glassfish.jsp.api;version="2.1.3";resolution:=optional,
org.apache.taglibs.standard;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional,

View File

@ -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
* &lt;New class="org.eclipse.jetty.deploy.providers.WebAppProvider"&gt;
* ....
* &lt;Set name="tldBundles"&gt;&ltProperty name="org.eclipse.jetty.osgi.tldsbundles" default="" /&gt;&lt;/Set&gt;
* &lt;New&gt;
* For example:
* -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet
* ,com.opensymphony.module.sitemesh Otherwise use an attribute to the
* WebAppDeployer &lt;New
* class="org.eclipse.jetty.deploy.providers.WebAppProvider"&gt; .... &lt;Set
* name="tldBundles"&gt;&ltProperty name="org.eclipse.jetty.osgi.tldsbundles"
* default="" /&gt;&lt;/Set&gt; &lt;New&gt;
*/
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<String> 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<String> tldbundles = new HashSet<String>();
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<String> 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<String> tldbundles = new HashSet<String>();
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<URL> urls = new ArrayList<URL>();
//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<String> tldbundles = getTldBundles(provider);
for (Bundle bundle : bundles)
{
if (tldbundles.contains(bundle.getSymbolicName()))
{
registerTldBundle(locatorHelper, bundle, urls);
}
}
return urls.toArray(new URL[urls.size()]);
List<URL> urls = new ArrayList<URL>();
// 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<String> 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());
}
}
}

View File

@ -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.
* <p>
* Pluggable tlds at the server level are handled by {@link PluggableWebAppRegistrationCustomizerImpl}.
* Pluggable tlds at the server level are handled by
* {@link PluggableWebAppRegistrationCustomizerImpl}.
* </p>
*/
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,85 +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<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
//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);
HashSet<Class<?>> classesToAddToTheTldBundles = new HashSet<Class<?>>();
// 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<URL> urls = new ArrayList<URL>();
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.
* <p>
* 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 :(
* </p>
*/
void fixupDtdResolution()
void fixupDtdResolution()
{
try
{
@ -199,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++)
@ -242,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;
}
@ -254,5 +254,5 @@ public class WebappRegistrationCustomizerImpl implements WebappRegistrationCusto
return null;
}
}
}

View File

@ -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.
* <p>
* 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.
* </p>
*/
public class FragmentActivator implements BundleActivator
@ -35,7 +34,8 @@ public class FragmentActivator implements BundleActivator
/**
*
*/
public void start(BundleContext context) throws Exception {
public void start(BundleContext context) throws Exception
{
System.setProperty("org.apache.jasper.compiler.disablejsr199", Boolean.TRUE.toString());
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
{
}
}

View File

@ -35,123 +35,120 @@ import org.osgi.util.tracker.ServiceTracker;
* Replacement for {@link TagLibConfiguration} for the OSGi integration.
* </p>
* <p>
* 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'
* </p>
* <p>
* Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
* Discussions about this are logged in
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
* </p>
*/
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.
* <p>
* Set or add to the property TldProcessor.TLDResources the list of located jars
* so that the super class will scan those.
* </p>
*/
private ServiceTracker packageAdminServiceTracker = null;
/**
* Override the preConfigure; locates the bundles that contain tld files
* according to the value of the manifest header Require-TldBundle.
* <p>
* Set or add to the property TldProcessor.TLDResources the list of located
* jars so that the super class will scan those.
* </p>
*/
public void preConfigure(WebAppContext context) throws Exception
{
String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requireTldBundle != null)
{
Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
if (resources != null && !resources.isEmpty())
{
Collection<Resource> previouslySet = (Collection<Resource>)
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<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
if (resources != null && !resources.isEmpty())
{
Collection<Resource> previouslySet = (Collection<Resource>) 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<Resource> getRequireTldBundleAsJettyResources(
WebAppContext context, String requireTldBundle)
private Collection<Resource> getRequireTldBundleAsJettyResources(WebAppContext context, String requireTldBundle)
{
Bundle bundle = (Bundle)
context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
PackageAdmin packAdmin = getBundleAdmin();
String[] symbNames = requireTldBundle.split(", ");
Collection<Resource> tlds = new LinkedHashSet<Resource>();
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<URL> 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<Resource> tlds = new LinkedHashSet<Resource>();
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<URL> 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();
}
}

View File

@ -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: 7.6.2.qualifier
Bundle-Version: 7.6.3.qualifier
Bundle-Vendor: Mort Bay Consulting
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: ch.qos.logback.classic,

View File

@ -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: 7.6.2.qualifier
Bundle-Version: 7.6.3.qualifier
Bundle-Activator: org.eclipse.jetty.osgi.boot.warurl.WarUrlActivator
Bundle-Vendor: Mort Bay Consulting
Bundle-RequiredExecutionEnvironment: J2SE-1.5

View File

@ -24,12 +24,13 @@ import java.util.jar.Manifest;
import org.eclipse.jetty.osgi.boot.warurl.internal.WarBundleManifestGenerator;
import org.eclipse.jetty.osgi.boot.warurl.internal.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)
{
}
}
}
}

View File

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

View File

@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: Jetty OSGi bootstrap
Bundle-SymbolicName: org.eclipse.jetty.osgi.boot;singleton:=true
Bundle-Vendor: Mort Bay Consulting
Bundle-Version: 7.6.2.qualifier
Bundle-Version: 7.6.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,19 +14,19 @@ Import-Package: javax.mail;version="1.4.0";resolution:=optional,
javax.servlet.http;version="2.5.0",
javax.transaction;version="1.1.0";resolution:=optional,
javax.transaction.xa;version="1.1.0";resolution:=optional,
org.eclipse.jetty.deploy;version="7.6.2",
org.eclipse.jetty.deploy.providers;version="7.6.2",
org.eclipse.jetty.http;version="7.6.2",
org.eclipse.jetty.nested;version="7.6.2";resolution:=optional,
org.eclipse.jetty.server;version="7.6.2",
org.eclipse.jetty.server.handler;version="7.6.2",
org.eclipse.jetty.servlet;version="7.6.2",
org.eclipse.jetty.util;version="7.6.2",
org.eclipse.jetty.util.component;version="7.6.2",
org.eclipse.jetty.util.log;version="7.6.2",
org.eclipse.jetty.util.resource;version="7.6.2",
org.eclipse.jetty.webapp;version="7.6.2",
org.eclipse.jetty.xml;version="7.6.2",
org.eclipse.jetty.deploy;version="7.6.3",
org.eclipse.jetty.deploy.providers;version="7.6.3",
org.eclipse.jetty.http;version="7.6.3",
org.eclipse.jetty.nested;version="7.6.3";resolution:=optional,
org.eclipse.jetty.server;version="7.6.3",
org.eclipse.jetty.server.handler;version="7.6.3",
org.eclipse.jetty.servlet;version="7.6.3",
org.eclipse.jetty.util;version="7.6.3",
org.eclipse.jetty.util.component;version="7.6.3",
org.eclipse.jetty.util.log;version="7.6.3",
org.eclipse.jetty.util.resource;version="7.6.3",
org.eclipse.jetty.webapp;version="7.6.3",
org.eclipse.jetty.xml;version="7.6.3",
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
@ -40,7 +40,7 @@ Import-Package: javax.mail;version="1.4.0";resolution:=optional,
org.xml.sax.helpers
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-Classpath: .
Export-Package: org.eclipse.jetty.osgi.boot;version="7.6.2",
org.eclipse.jetty.osgi.nested;version="7.6.2",
org.eclipse.jetty.osgi.boot.utils;version="7.6.2"
Export-Package: org.eclipse.jetty.osgi.boot;version="7.6.3",
org.eclipse.jetty.osgi.nested;version="7.6.3",
org.eclipse.jetty.osgi.boot.utils;version="7.6.3"
DynamicImport-Package: org.eclipse.jetty.*;version="[7.3,8)"

View File

@ -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<String, String> 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<String, String>());
registerContext(contributor, contextFilePath, new Hashtable<String, String>());
}
/**
@ -270,33 +267,30 @@ public class JettyBootstrapActivator implements BundleActivator
* registers it as an OSGi service. The tracker
* {@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<String, String> 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;
}
}

View File

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

View File

@ -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.
* </p>
*/
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<URL> enUrls = BundleFileLocatorHelper.DEFAULT
.findEntries(configurationBundle, etcFile);
//default for org.eclipse.osgi.boot where we look inside jettyhome for the default embedded configuration.
//default inside jettyhome. this way fragments to the bundle can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()) && etcFile.endsWith("etc/jetty.xml"))
{
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<URL> enUrls = BundleFileLocatorHelper.DEFAULT
.findEntries(configurationBundle, etcFile);
//default for org.eclipse.osgi.boot where we look inside jettyhome for the default embedded configuration.
//default inside jettyhome. this way fragments to the bundle can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()) && etcFile.endsWith("etc/jetty.xml"))
{
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;
}
}
}

View File

@ -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<String, ServerInstanceWrapper> _serversIndexedByName = new HashMap<String, ServerInstanceWrapper>();
/** The context-handler to deactivate indexed by ServerInstanceWrapper */
private Map<ServiceReference, ServerInstanceWrapper> _indexByServiceReference = new HashMap<ServiceReference, ServerInstanceWrapper>();
/**
* 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 <code>ServiceEvent</code> object.
* @param ev The <code>ServiceEvent</code> 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);
}
}

View File

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

View File

@ -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<File> 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<File> 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<URL> res = new ArrayList<URL>();
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<URL> jettyConfigurations = jettyConfigurationUrls != null
? extractResources(jettyConfigurationUrls) : null;
if (jettyConfigurations == null || jettyConfigurations.isEmpty())
{
return;
}
Map<String,Object> id_map = new HashMap<String,Object>();
id_map.put("Server",server);
Map<String,String> properties = new HashMap<String,String>();
List<URL> jettyConfigurations = jettyConfigurationUrls != null ? extractResources(jettyConfigurationUrls) : null;
if (jettyConfigurations == null || jettyConfigurations.isEmpty()) { return; }
Map<String, Object> id_map = new HashMap<String, Object>();
id_map.put("Server", server);
Map<String, String> properties = new HashMap<String, String>();
Enumeration<Object> 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<String,String> properties2 = new HashMap<String,String>(properties);
Map<String, String> properties2 = new HashMap<String, String>(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<DeploymentManager> 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<File> 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;
}
}

View File

@ -24,6 +24,8 @@ import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHom
import org.eclipse.jetty.osgi.boot.internal.serverfactory.IManagedJettyServerRegistry;
import org.eclipse.jetty.osgi.boot.internal.serverfactory.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<ServiceReference, ContextHandler> _indexByServiceReference = new HashMap<ServiceReference, ContextHandler>();
/**
* The index is the bundle-symbolic-name/path/to/context/file when there is such thing
* The index is the bundle-symbolic-name/path/to/context/file when there is
* such thing
*/
private Map<String, ServiceReference> _indexByContextFile = new HashMap<String, ServiceReference>();
@ -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 <code>ServiceEvent</code> object.
* @param ev The <code>ServiceEvent</code> 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;
}
}

View File

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

View File

@ -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.
* <p>
* 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.
* </p>
*/
public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
@ -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<Resource> patchResources =
(List<Resource>)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<Resource> patchResources = (List<Resource>) context.getAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch");
if (patchResources != null)
{
LinkedList<Resource> resourcesPath = new LinkedList<Resource>();
//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<Resource> resourcesPath = new LinkedList<Resource>();
// 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,16 +413,14 @@ 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
{
@ -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,53 +492,58 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
* @see {WebAppDeployer#scan} around the comment
* <code>// configure it</code>
*/
protected void configureWebAppContext(ContextHandler wah, Bundle contributor,
String requireTldBundle) throws IOException
protected void configureWebAppContext(ContextHandler wah, Bundle contributor, String requireTldBundle) throws IOException
{
// rfc66
wah.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT,contributor.getBundleContext());
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);
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<String,Resource> patchResourcesPath = new TreeMap<String,Resource>();
TreeMap<String,Resource> appendedResourcesPath = new TreeMap<String,Resource>();
for (Bundle frag : fragments) {
String fragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
String patchFragFolder = (String)frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
// 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<String, Resource> patchResourcesPath = new TreeMap<String, Resource>();
TreeMap<String, Resource> appendedResourcesPath = new TreeMap<String, Resource>();
for (Bundle frag : fragments)
{
String fragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_FRAGMENT_FOLDER_PATH);
String patchFragFolder = (String) frag.getHeaders().get(OSGiWebappConstants.JETTY_WAR_PATCH_FRAGMENT_FOLDER_PATH);
if (fragFolder != null)
{
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));
@ -539,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));
@ -551,92 +563,92 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
}
if (!appendedResourcesPath.isEmpty())
{
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, new ArrayList<Resource>(appendedResourcesPath.values()));
}
if (!patchResourcesPath.isEmpty())
{
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(patchResourcesPath.values()));
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS + ".patch", new ArrayList<Resource>(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<Resource> frags = (List<Resource>)wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
List<Resource> resfrags = (List<Resource>)wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
List<Resource> tldfrags = (List<Resource>)wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
for (Bundle frag : fragments)
{
URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
Enumeration<URL> 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<Resource>();
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<Resource>();
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
}
resfrags.add(Resource.newResource(
DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
}
}
if (tldEnum != null && tldEnum.hasMoreElements())
{
if (tldfrags == null)
{
tldfrags = new ArrayList<Resource>();
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<Resource> frags = (List<Resource>) wah.getAttribute(FragmentConfiguration.FRAGMENT_RESOURCES);
List<Resource> resfrags = (List<Resource>) wah.getAttribute(WebInfConfiguration.RESOURCE_URLS);
List<Resource> tldfrags = (List<Resource>) wah.getAttribute(TagLibConfiguration.TLD_RESOURCES);
for (Bundle frag : fragments)
{
URL webFrag = frag.getEntry("/META-INF/web-fragment.xml");
Enumeration<URL> resEnum = frag.findEntries("/META-INF/resources", "*", true);
Enumeration<URL> 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<Resource>();
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<Resource>();
wah.setAttribute(WebInfConfiguration.RESOURCE_URLS, resfrags);
}
resfrags.add(Resource.newResource(DefaultFileLocatorHelper.getLocalURL(resourcesEntry)));
}
}
if (tldEnum != null && tldEnum.hasMoreElements())
{
if (tldfrags == null)
{
tldfrags = new ArrayList<Resource>();
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);
}
}
}
}
}
}
/**
@ -644,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;
}
@ -667,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:
@ -686,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());
}
}
@ -722,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
{
@ -775,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
{
@ -797,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 &quot;this.bundle.install&quot; to point to the location
* of the bundle. Useful when <SystemProperty name="this.bundle.home"/> is
@ -823,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)
{
@ -834,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());
}
@ -872,12 +895,11 @@ 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();
}
}

View File

@ -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 <code>BundleTracker</code>.
*
* <p>
* This method is called before a bundle which matched the search parameters
* of the <code>BundleTracker</code> is added to the
* <code>BundleTracker</code>. This method should return the object to be
* tracked for the specified <code>Bundle</code>. The returned object is
* stored in the <code>BundleTracker</code> and is available from the
* {@link BundleTracker#getObject(Bundle) getObject} method.
*
* @param bundle The <code>Bundle</code> being added to the
* <code>BundleTracker</code>.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event
* associated with the call to this method.
* @return The object to be tracked for the specified <code>Bundle</code>
* object or <code>null</code> if the specified <code>Bundle</code>
* object should not be tracked.
*/
public Object addingBundle(Bundle bundle, BundleEvent event)
{
if (bundle.getState() == Bundle.ACTIVE)
{
boolean isWebBundle = register(bundle);
return isWebBundle ? bundle : null;
}
else if (bundle.getState() == Bundle.STOPPING)
{
unregister(bundle);
}
else
{
// we should not be called in that state as
// we are registered only for ACTIVE and STOPPING
}
return null;
}
/**
* A bundle is being added to the <code>BundleTracker</code>.
*
* <p>
* This method is called before a bundle which matched the search parameters
* of the <code>BundleTracker</code> is added to the
* <code>BundleTracker</code>. This method should return the object to be
* tracked for the specified <code>Bundle</code>. The returned object is
* stored in the <code>BundleTracker</code> and is available from the
* {@link BundleTracker#getObject(Bundle) getObject} method.
*
* @param bundle The <code>Bundle</code> being added to the
* <code>BundleTracker</code>.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event associated
* with the call to this method.
* @return The object to be tracked for the specified <code>Bundle</code>
* object or <code>null</code> if the specified <code>Bundle</code>
* object should not be tracked.
*/
public Object addingBundle(Bundle bundle, BundleEvent event)
{
if (bundle.getState() == Bundle.ACTIVE)
{
boolean isWebBundle = register(bundle);
return isWebBundle ? bundle : null;
}
else if (bundle.getState() == Bundle.STOPPING)
{
unregister(bundle);
}
else
{
//we should not be called in that state as
//we are registered only for ACTIVE and STOPPING
}
return null;
}
/**
* A bundle tracked by the <code>BundleTracker</code> has been modified.
*
* <p>
* This method is called when a bundle being tracked by the
* <code>BundleTracker</code> has had its state modified.
*
* @param bundle The <code>Bundle</code> whose state has been modified.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event
* associated with the call to this method.
* @param object The tracked object for the specified bundle.
*/
public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
{
// nothing the web-bundle was already track. something changed.
// we only reload the webapps if the bundle is stopped and restarted.
if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
{
unregister(bundle);
}
if (bundle.getState() == Bundle.ACTIVE)
{
register(bundle);
}
}
/**
* A bundle tracked by the <code>BundleTracker</code> has been modified.
*
* <p>
* This method is called when a bundle being tracked by the
* <code>BundleTracker</code> has had its state modified.
*
* @param bundle The <code>Bundle</code> whose state has been modified.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event associated
* with the call to this method.
* @param object The tracked object for the specified bundle.
*/
public void modifiedBundle(Bundle bundle, BundleEvent event,
Object object)
{
//nothing the web-bundle was already track. something changed.
//we only reload the webapps if the bundle is stopped and restarted.
// System.err.println(bundle.getSymbolicName());
if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
{
unregister(bundle);
}
if (bundle.getState() == Bundle.ACTIVE)
{
register(bundle);
}
}
/**
* A bundle tracked by the <code>BundleTracker</code> has been removed.
*
* <p>
* This method is called after a bundle is no longer being tracked by the
* <code>BundleTracker</code>.
*
* @param bundle The <code>Bundle</code> that has been removed.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event
* associated with the call to this method.
* @param object The tracked object for the specified bundle.
*/
public void removedBundle(Bundle bundle, BundleEvent event, Object object)
{
unregister(bundle);
}
/**
* A bundle tracked by the <code>BundleTracker</code> has been removed.
*
* <p>
* This method is called after a bundle is no longer being tracked by the
* <code>BundleTracker</code>.
*
* @param bundle The <code>Bundle</code> that has been removed.
* @param event The bundle event which caused this customizer method to be
* called or <code>null</code> if there is no bundle event associated
* with the call to this method.
* @param object The tracked object for the specified bundle.
*/
public void removedBundle(Bundle bundle, BundleEvent event,
Object object)
{
unregister(bundle);
}
/**
* @param bundle
* @return true if this bundle in indeed a web-bundle.
*/
/**
* @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 = getWebContextPath(bundle, dic, false);//(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,42 +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,rfc66Webxml==null);
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, 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;
}
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("/"))
@ -259,7 +252,7 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
}
return rfc66ContextPath;
}
private void unregister(Bundle bundle)
{
// nothing to do: when the bundle is stopped, each one of its service
@ -268,7 +261,4 @@ public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {
// webapps registered in that bundle.
}
}

View File

@ -25,6 +25,7 @@ import java.util.zip.ZipFile;
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.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,19 +69,23 @@ 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
return new FileResource(url).getFile().getParentFile().getParentFile();
// 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"))
{
// 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<URL> 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<URL> 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<URL> 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<URL> 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
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
* jar: or http: etc.).
* </p>
* @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
* <p>
* Get a URL to the content of the bundle entry that uses the file: protocol.
* The content of the bundle entry may be downloaded or extracted to the local
* file system in order to create a file: URL.
* @return a URL to the content of the bundle entry that uses the file: protocol
* </p>
*/
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
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
* jar: or http: etc.).
* </p>
*
* @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
* <p>
* Get a URL to the content of the bundle entry that uses the file:
* protocol. The content of the bundle entry may be downloaded or extracted
* to the local file system in order to create a file: URL.
*
* @return a URL to the content of the bundle entry that uses the file:
* protocol
* </p>
*/
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;
}
}

View File

@ -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: 7.6.2.qualifier
Bundle-Version: 7.6.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="7.6.2",
org.eclipse.jetty.io;version="7.6.2",
org.eclipse.jetty.util;version="7.6.2",
org.eclipse.jetty.util.log;version="7.6.2",
org.eclipse.jetty.websocket;version="7.6.2",
org.eclipse.jetty.continuation;version="7.6.3",
org.eclipse.jetty.io;version="7.6.3",
org.eclipse.jetty.util;version="7.6.3",
org.eclipse.jetty.util.log;version="7.6.3",
org.eclipse.jetty.websocket;version="7.6.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="7.6.2",
org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="7.6.2"
Export-Package: org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="7.6.3",
org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="7.6.3"
Bundle-RequiredExecutionEnvironment: J2SE-1.5

View File

@ -1,16 +1,16 @@
Bundle-ManifestVersion: 2
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-SymbolicName: org.eclipse.jetty.osgi.httpservice
Bundle-Version: 7.6.2.qualifier
Bundle-Version: 7.6.3.qualifier
Bundle-Vendor: Mort Bay Consulting
Bundle-Name: OSGi HttpService provided by equinox HttpServiceServlet deployed on jetty
Jetty-ContextFilePath: contexts/httpservice.xml
Import-Package: javax.servlet;version="2.5.0",
javax.servlet.http;version="2.5.0",
org.eclipse.equinox.http.servlet,
org.eclipse.jetty.server;version="7.6.2",
org.eclipse.jetty.server.handler;version="7.6.2",
org.eclipse.jetty.servlet;version="7.6.2",
org.eclipse.jetty.util.component;version="7.6.2"
Export-Package: org.eclipse.jetty.osgi.httpservice;version="7.6.2"
org.eclipse.jetty.server;version="7.6.3",
org.eclipse.jetty.server.handler;version="7.6.3",
org.eclipse.jetty.servlet;version="7.6.3",
org.eclipse.jetty.util.component;version="7.6.3"
Export-Package: org.eclipse.jetty.osgi.httpservice;version="7.6.3"

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,10 @@ package org.eclipse.jetty.rewrite.handler;
import java.io.IOException;
import 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<String> params=new MultiMap<String>();
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());
}
}
}

View File

@ -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.
</New>
</Set>
<Set name="checkWelcomeFiles">true</Set>
</Get>
8
</Get>

View File

@ -254,7 +254,7 @@ public class DigestAuthenticator extends LoginAuthenticator
Nonce nonce=_nonceQueue.peek();
while (nonce!=null && nonce._ts<expired)
{
_nonceQueue.remove();
_nonceQueue.remove(nonce);
_nonceCount.remove(nonce._nonce);
nonce=_nonceQueue.peek();
}

View File

@ -146,7 +146,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection
HttpBuffers ab = (HttpBuffers)_connector;
_parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
_requestFields = new HttpFields();
_responseFields = new HttpFields(server.getMaxCookieVersion());
_responseFields = new HttpFields();
_request = new Request(this);
_response = new Response(this);
_generator = newHttpGenerator(ab.getResponseBuffers(), endpoint);
@ -164,7 +164,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection
_connector = connector;
_parser = parser;
_requestFields = new HttpFields();
_responseFields = new HttpFields(server.getMaxCookieVersion());
_responseFields = new HttpFields();
_request = request;
_response = new Response(this);
_generator = generator;

View File

@ -819,6 +819,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
{
synchronized (this)
{
_responseWrapped=!(response instanceof Response);
doSuspend(context,request,response);
if (request instanceof HttpServletRequest)
{

View File

@ -193,7 +193,7 @@ public class CookieCutter
continue;
case ';':
case ',':
// case ',':
if (tokenstart>=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);

View File

@ -136,7 +136,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;
}

View File

@ -72,7 +72,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;
@ -265,7 +264,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++)
{
@ -450,21 +449,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;
}
/* ------------------------------------------------------------ */

View File

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

View File

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

View File

@ -669,7 +669,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");
@ -694,6 +694,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

View File

@ -383,7 +383,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"},
@ -391,7 +391,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;i<tests.length;i++)
@ -515,7 +516,7 @@ public class ResponseTest
String set = response.getHttpFields().getStringField("Set-Cookie");
assertEquals("name=value;Path=/path;Domain=domain;Secure;HttpOnly",set);
assertEquals("name=value;Comment=comment;Path=/path;Domain=domain;Secure;HttpOnly",set);
}
private Response newResponse()

View File

@ -31,6 +31,12 @@ public class SelectChannelServerTest extends HttpServerTestBase
{
super.testCommittedError();
}
@Override
public void testSuspendedPipeline() throws Exception
{
super.testSuspendedPipeline();
}
}

View File

@ -121,8 +121,7 @@ class SuspendHandler extends HandlerWrapper
try
{
Thread.sleep(_resumeAfter);
if(((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId()!=null)
asyncContext.dispatch();
asyncContext.dispatch();
}
catch(Exception e)
{

View File

@ -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<buf.length;i++)
buf[i]=(byte)('0'+(i%10));
out.write(buf);

View File

@ -15,20 +15,29 @@ package org.eclipse.jetty.server.ssl;
import static org.junit.Assert.assertEquals;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.nio.SslConnection;
import org.eclipse.jetty.server.HttpServerTestBase;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.lessThan;
/**
* HttpServer Tester.
@ -46,11 +55,28 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
return __sslContext.getSocketFactory().createSocket(host,port);
}
private static final AtomicInteger _handlecount = new AtomicInteger();
@BeforeClass
public static void init() throws Exception
{
SslSelectChannelConnector connector = new SslSelectChannelConnector();
SslSelectChannelConnector connector = new SslSelectChannelConnector()
{
@Override
protected SslConnection newSslConnection(AsyncEndPoint endPoint, SSLEngine engine)
{
return new SslConnection(engine, endPoint)
{
@Override
public Connection handle() throws IOException
{
_handlecount.incrementAndGet();
return super.handle();
}
};
}
};
String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore";
SslContextFactory cf = connector.getSslContextFactory();
cf.setKeyStorePath(keystorePath);
@ -69,7 +95,6 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
__sslContext = SSLContext.getInstance("TLS");
__sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
try
{
HttpsURLConnection.setDefaultHostnameVerifier(__hostnameverifier);
@ -105,7 +130,6 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
{
OutputStream os=client.getOutputStream();
int last=0;
// Write out the fragments
@ -137,12 +161,17 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
}
}
@Override
@Ignore
public void testAvailable() throws Exception
{
}
@Override
public void testSuspendedPipeline() throws Exception
{
_handlecount.set(0);
super.testSuspendedPipeline();
assertThat(_handlecount.get(),lessThan(50));
}
}

View File

@ -583,7 +583,8 @@ public class ServletHandler extends ScopedHandler
}
finally
{
baseRequest.setHandled(true);
if (servlet_holder!=null)
baseRequest.setHandled(true);
}
}
@ -1232,7 +1233,7 @@ public class ServletHandler extends ScopedHandler
{
if(LOG.isDebugEnabled())
LOG.debug("Not Found "+request.getRequestURI());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
//Override to send an error back, eg with: response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
/* ------------------------------------------------------------ */

View File

@ -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;
@ -12,6 +13,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;
@ -223,17 +225,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();
}
}
}
@ -241,14 +246,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();
}
}
@ -347,4 +356,13 @@ public class AsyncContextTest
}
}
private class Wrapper extends HttpServletResponseWrapper
{
public Wrapper (HttpServletResponse response)
{
super(response);
}
}
}

View File

@ -992,7 +992,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;

View File

@ -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: <ul>
* This filter will gzip or deflate the content of a response if: <ul>
* <li>The filter is mapped to a matching path</li>
* <li>accept-encoding header is set to either gzip, deflate or a combination of those</li>
* <li>The response status code is >=200 and <300
* <li>The content length is unknown or more than the <code>minGzipSize</code> initParameter or the minGzipSize is 0(default)</li>
* <li>The content-type is in the comma separated list of mimeTypes set in the <code>mimeTypes</code> initParameter or
@ -50,8 +51,11 @@ import org.eclipse.jetty.util.log.Logger;
* </ul>
*
* <p>
* If both gzip and deflate are specified in the accept-encoding header, then gzip will be used.
* </p>
* <p>
* 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.
* </p>
@ -59,15 +63,49 @@ import org.eclipse.jetty.util.log.Logger;
* This filter extends {@link UserAgentFilter} and if the the initParameter <code>excludedAgents</code>
* is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
* </p>
* <p>Init Parameters:</p>
* <PRE>
* bufferSize The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
* {@link IllegalArgumentException}.
* @see java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)
* @see java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)
*
* minGzipSize Content will only be compressed if content length is either unknown or greater
* than <code>minGzipSize</code>.
*
* deflateCompressionLevel The compression level used for deflate compression. (0-9).
* @see java.util.zip.Deflater#Deflater(int, boolean)
*
* 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 <code>excludePathPatterns</code>
* instead.
*
* excludePathPatterns Same as excludePath, but accepts regex patterns for more complex matching.
* </PRE>
*/
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<String> _mimeTypes;
protected int _bufferSize=8192;
protected int _minGzipSize=256;
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
protected boolean _deflateNoWrap = true;
protected Set<String> _excludedAgents;
protected Set<Pattern> _excludedAgentPatterns;
protected Set<String> _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));
}
}

View File

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

View File

@ -503,7 +503,7 @@ public class MultiPartFilter implements Filter
}
catch(Exception e)
{
e.printStackTrace();
throw new RuntimeException(e);
}
}
else if (o instanceof String)

View File

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

View File

@ -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<? extends Servlet> testServlet, String compressionType)
{
this.testServlet = testServlet;
this.compressionType = compressionType;
}
@Rule
public TestingDir testingdir = new TestingDir();
private Class<? extends Servlet> testServlet;
public GzipFilterContentLengthTest(Class<? extends Servlet> 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);

View File

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

View File

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

View File

@ -1,17 +1,28 @@
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.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 org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
@ -25,21 +36,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<String[]> 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
@ -86,7 +122,7 @@ public class GzipWithPipeliningTest
testingdir.ensureEmpty();
File outputDir = testingdir.getDir();
PipelineHelper client = new PipelineHelper(serverUri);
PipelineHelper client = new PipelineHelper(serverUri, encodingHeader);
try
{
@ -95,7 +131,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;
@ -106,8 +142,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");
@ -118,7 +155,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
@ -133,14 +170,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");
@ -149,14 +186,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");
@ -164,7 +210,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
@ -183,7 +229,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
@ -192,7 +238,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();
}

View File

@ -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 <a href="Eclipse Bug 366106">http://bugs.eclipse.org/366106</a>
*/
@RunWith(Parameterized.class)
public class IncludableGzipFilterMinSizeTest
{
@Parameters
public static Collection<String[]> 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<? extends Servlet> 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);

View File

@ -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<String[]> 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);

View File

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

View File

@ -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;
@ -12,6 +20,9 @@ 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 java.util.zip.InflaterInputStream;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
@ -26,12 +37,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<? extends GzipFilter> gzipFilterClass = GzipFilter.class;
@ -39,10 +44,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();
}
@ -61,7 +68,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);
@ -76,7 +83,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);
@ -89,12 +96,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
{
@ -128,7 +142,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);
@ -215,7 +229,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)
@ -238,7 +252,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)

View File

@ -12,6 +12,10 @@
<packaging>pom</packaging>
<name>Jetty :: SPDY :: Parent</name>
<properties>
<npn.version>1.0.0.v20120402</npn.version>
</properties>
<modules>
<module>spdy-core</module>
<module>spdy-jetty</module>

View File

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

View File

@ -35,6 +35,4 @@ public interface ISession extends Session
public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context);
public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context);
public int getWindowSize();
}

View File

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

View File

@ -80,7 +80,9 @@ public class Promise<T> implements Handler<T>, Future<T>
@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();
}

View File

@ -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<StandardSession.FrameBytes>
{
private static final Logger logger = LoggerFactory.getLogger(Session.class);
private static final Logger logger = Log.getLogger(Session.class);
private static final ThreadLocal<Integer> handlerInvocations = new ThreadLocal<Integer>()
{
@Override
@ -78,7 +78,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
private final Deque<FrameBytes> queue = new LinkedList<>();
private final LinkedList<FrameBytes> queue = new LinkedList<>();
private final ByteBufferPool bufferPool;
private final Executor threadPool;
private final ScheduledExecutorService scheduler;
@ -253,9 +253,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
@Override
public List<Stream> getStreams()
public Set<Stream> getStreams()
{
List<Stream> result = new ArrayList<>();
Set<Stream> result = new HashSet<>();
result.addAll(streams.values());
return result;
}
@ -398,6 +398,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private void onSyn(SynStreamFrame frame)
{
IStream stream = newStream(frame);
stream.updateCloseState(frame.isClose(), false);
logger.debug("Opening {}", stream);
int streamId = frame.getStreamId();
IStream existing = streams.putIfAbsent(streamId, stream);
@ -429,6 +430,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private IStream createStream(SynStreamFrame synStream, StreamFrameListener listener)
{
IStream stream = newStream(synStream);
stream.updateCloseState(synStream.isClose(), true);
stream.setStreamFrameListener(listener);
if (streams.putIfAbsent(synStream.getStreamId(), stream) != null)
{
@ -540,7 +542,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
Settings.Setting windowSizeSetting = frame.getSettings().get(Settings.ID.INITIAL_WINDOW_SIZE);
if (windowSizeSetting != null)
{
int prevWindowSize = windowSize;
windowSize = windowSizeSetting.value();
for (IStream stream : streams.values())
stream.updateWindowSize(windowSize - prevWindowSize);
logger.debug("Updated window size to {}", windowSize);
}
@ -728,10 +733,15 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
{
ByteBuffer buffer = generator.control(frame);
logger.debug("Queuing {} on {}", frame, stream);
ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(handler, context, frame, buffer);
ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream, handler, context, frame, buffer);
if (timeout > 0)
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<Stand
public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context)
{
logger.debug("Queuing {} on {}", dataInfo, stream);
DataFrameBytes<C> frameBytes = new DataFrameBytes<>(handler, context, stream, dataInfo);
DataFrameBytes<C> frameBytes = new DataFrameBytes<>(stream, handler, context, dataInfo);
if (timeout > 0)
frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
enqueueLast(frameBytes);
append(frameBytes);
flush();
}
@ -774,56 +784,76 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
threadPool.execute(task);
}
@Override
public int getWindowSize()
{
return windowSize;
}
@Override
public void flush()
{
FrameBytes frameBytes;
ByteBuffer buffer;
FrameBytes frameBytes = null;
ByteBuffer buffer = null;
synchronized (queue)
{
if (flushing)
if (flushing || queue.isEmpty())
return;
frameBytes = queue.poll();
if (frameBytes == null)
return;
buffer = frameBytes.getByteBuffer();
if (buffer == null)
Set<IStream> 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<Stand
protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{
if (controller != null)
{
logger.debug("Writing {} frame bytes of {}", buffer.remaining(), frameBytes);
controller.write(buffer, handler, frameBytes);
}
}
private <C> void complete(final Handler<C> handler, final C context)
{
if (handler != null)
// Applications may send and queue up a lot of frames and
// if we call Handler.completed() only synchronously we risk
// starvation (for the last frames sent) and stack overflow.
// Therefore every some invocation, we dispatch to a new thread
Integer invocations = handlerInvocations.get();
if (invocations >= 4)
{
// Applications may send and queue up a lot of frames and
// 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<Stand
}
}
public interface FrameBytes
public interface FrameBytes extends Comparable<FrameBytes>
{
public IStream getStream();
public abstract ByteBuffer getByteBuffer();
public abstract void complete();
@ -922,16 +956,31 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private abstract class AbstractFrameBytes<C> implements FrameBytes, Runnable
{
private final IStream stream;
private final Handler<C> handler;
private final C context;
protected volatile ScheduledFuture<?> task;
protected AbstractFrameBytes(Handler<C> handler, C context)
protected AbstractFrameBytes(IStream stream, Handler<C> handler, C context)
{
this.stream = stream;
this.handler = handler;
this.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<Stand
private final ControlFrame frame;
private final ByteBuffer buffer;
private ControlFrameBytes(Handler<C> handler, C context, ControlFrame frame, ByteBuffer buffer)
private ControlFrameBytes(IStream stream, Handler<C> handler, C context, ControlFrame frame, ByteBuffer buffer)
{
super(handler, context);
super(stream, handler, context);
this.frame = frame;
this.buffer = buffer;
}
@ -996,15 +1045,13 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
private class DataFrameBytes<C> extends AbstractFrameBytes<C>
{
private final IStream stream;
private final DataInfo dataInfo;
private int length;
private ByteBuffer buffer;
private int size;
private volatile ByteBuffer buffer;
private DataFrameBytes(Handler<C> handler, C context, IStream stream, DataInfo dataInfo)
private DataFrameBytes(IStream stream, Handler<C> handler, C context, DataInfo dataInfo)
{
super(handler, context);
this.stream = stream;
super(stream, handler, context);
this.dataInfo = dataInfo;
}
@ -1013,15 +1060,16 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
{
try
{
IStream stream = getStream();
int windowSize = stream.getWindowSize();
if (windowSize <= 0)
return null;
length = dataInfo.length();
if (length > 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<Stand
@Override
public void complete()
{
stream.updateWindowSize(-length);
bufferPool.release(buffer);
IStream stream = getStream();
stream.updateWindowSize(-size);
if (dataInfo.available() > 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<Stand
@Override
public String toString()
{
return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), stream);
return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), getStream());
}
}
}

View File

@ -39,27 +39,25 @@ import org.eclipse.jetty.spdy.frames.HeadersFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class StandardStream implements IStream
{
private static final Logger logger = LoggerFactory.getLogger(Stream.class);
private static final Logger logger = Log.getLogger(Stream.class);
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
private final 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<Void> 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<Void> handler)
{
if (!canSend())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a DATA frame on a closed stream");
}
// Cannot update the close state here, because the data that we send may
// 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<Void> handler)
{
updateCloseState(headersInfo.isClose());
if (!canSend())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame before a SYN_REPLY frame");
}
if (isLocallyClosed())
{
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
throw new IllegalStateException("Protocol violation: cannot send a HEADERS frame on a closed stream");
}
updateCloseState(headersInfo.isClose(), true);
HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders());
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
}
}

View File

@ -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<Stream> getStreams();
public Set<Stream> getStreams();
/**
* <p>Super interface for listeners with callbacks that are invoked on specific session events.</p>

View File

@ -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;
/**
* <p>A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening
@ -122,7 +122,7 @@ public interface SessionFrameListener extends EventListener
*/
public static class Adapter implements SessionFrameListener
{
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)

View File

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

View File

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

View File

@ -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<Listener> listeners = new CopyOnWriteArrayList<>();
private final ControlFrameParser controlFrameParser;
private final DataFrameParser dataFrameParser;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,13 +50,14 @@ import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.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<Runnable> tasks = new LinkedList<>();
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
@ -65,7 +66,6 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
private Headers headers; // No need for volatile, guarded by state
private 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
}

View File

@ -33,13 +33,13 @@ import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.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();
}
}
}

View File

@ -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 org.eclipse.jetty.server.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)

View File

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

View File

@ -32,6 +32,11 @@ public class EmptyAsyncEndPoint implements AsyncEndPoint
private boolean closed;
private int maxIdleTime;
@Override
public void dispatch()
{
}
@Override
public void asyncDispatch()
{

View File

@ -30,12 +30,12 @@ import org.eclipse.jetty.io.nio.NIOBuffer;
import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.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<StandardSession.FrameBytes>, IdleListener
{
private static final Logger logger = LoggerFactory.getLogger(SPDYAsyncConnection.class);
private static final Logger logger = Log.getLogger(SPDYAsyncConnection.class);
private final ByteBufferPool bufferPool;
private final 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);
}
}

Some files were not shown because too many files have changed in this diff Show More