Merge branch 'master' of ssh://git.eclipse.org:29418/jetty/org.eclipse.jetty.project

This commit is contained in:
Thomas Becker 2012-04-11 13:02:14 +02:00
commit 5ddaa62469
80 changed files with 3620 additions and 3108 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

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

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

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,6 +18,7 @@ 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;
/**
@ -89,14 +90,20 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
/* ------------------------------------------------------------ */
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 (_queryGroup||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

@ -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,21 +27,21 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{
private String[][] _tests=
{
{"/foo/bar",null,".*","/replace","/replace",null},
{"/foo/bar","n=v",".*","/replace","/replace","n=v"},
{"/foo/bar",null,"/xxx.*","/replace",null,null},
{"/foo/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx",null},
{"/foo/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo"},
{"/foo/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo"},
{"/foo/bar",null,"/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","p2=bar&p1=foo"},
{"/foo/bar","n=v","/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","n=v&p2=bar&p1=foo"},
{"/foo/bar",null,"/(foo)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo/xxx",null},
{"/foo/$bar",null,".*","/$replace","/$replace",null},
{"/foo/$bar",null,"/foo/(.*)","/$1/replace","/$bar/replace",null},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&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;
@ -53,20 +57,35 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{
for (String[] test : _tests)
{
reset();
_request.setRequestURI(null);
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();
_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(t,test[4], _request.getRequestURI());
assertEquals(t,test[5], _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));
}
}
}
@ -78,6 +97,7 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
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]);

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

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

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

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

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

@ -13,14 +13,12 @@
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.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@ -34,14 +32,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 +50,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>
@ -64,6 +67,8 @@ import org.eclipse.jetty.util.log.Logger;
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;
@ -144,7 +149,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 +161,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 +176,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 +190,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,7 +206,102 @@ 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());
}
};
}
};
}
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
*
@ -285,42 +368,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,18 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
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 +56,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());
}
};
}
};
}
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

@ -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,19 +10,41 @@ 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
@ -52,10 +76,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);
@ -75,10 +99,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);
@ -98,10 +122,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);
@ -121,7 +145,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);
@ -142,13 +166,13 @@ 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");
tester.setUserAgent("foo");
int filesize = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
try

View File

@ -1,17 +1,22 @@
package org.eclipse.jetty.servlets;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
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.InflaterInputStream;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
@ -25,21 +30,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 +116,7 @@ public class GzipWithPipeliningTest
testingdir.ensureEmpty();
File outputDir = testingdir.getDir();
PipelineHelper client = new PipelineHelper(serverUri);
PipelineHelper client = new PipelineHelper(serverUri, encodingHeader);
try
{
@ -95,7 +125,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 +136,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 +149,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 +164,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 +180,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);
}
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 +204,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 +223,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 +232,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,10 @@ 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.InflaterInputStream;
import javax.servlet.http.HttpServletResponse;
@ -36,9 +39,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 +75,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 +120,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 +128,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);
}
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,8 @@ import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
@ -26,12 +36,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 +43,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 +67,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 +82,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 +95,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);
}
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 +141,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 +228,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 +251,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,13 +12,9 @@
<packaging>pom</packaging>
<name>Jetty :: SPDY :: Parent</name>
<properties>
<!--
npn version only needs to change when there are changes in that binary, not
with each and every release.
-->
<npn.version>7.6.2.v20120308</npn.version>
</properties>
<properties>
<npn.version>1.0.0.v20120402</npn.version>
</properties>
<modules>
<module>spdy-core</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

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

@ -18,7 +18,6 @@ package org.eclipse.jetty.spdy;
import java.nio.ByteBuffer;
import java.nio.channels.InterruptedByTimeoutException;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -62,12 +61,12 @@ import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
import org.eclipse.jetty.spdy.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
@ -79,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;
@ -399,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);
@ -430,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)
{
@ -732,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();
@ -766,10 +772,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
public <C> void data(IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Handler<C> handler, C context)
{
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();
}
@ -781,47 +787,73 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
@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);
}
}
@ -845,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);
}
}
}
@ -911,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();
@ -920,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()
{
@ -957,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;
}
@ -994,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;
}
@ -1011,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)
@ -1032,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);
}
@ -1053,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,8 @@ public class StandardStream implements IStream
public boolean isHalfClosed()
{
return halfClosed;
CloseState closeState = this.closeState;
return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.REMOTELY_CLOSED;
}
@Override
@ -123,14 +122,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 +164,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 +179,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 +206,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 +309,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 +326,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 +353,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 +372,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

@ -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,63 +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>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>config</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<!--
<plugin>
<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>
<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.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>
-->
</plugins>
</build>
</project>
<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>

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,84 +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-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${build.directory}/npn/npn-boot-${npn.version}.jar </argLine>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
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

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

@ -190,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);
@ -519,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())
{

View File

@ -1,88 +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-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.npn</groupId>
<artifactId>npn-boot</artifactId>
<version>${npn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${build.directory}/npn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${build.directory}/npn/npn-boot-${npn.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

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

View File

@ -41,14 +41,14 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SPDYServerConnector extends SelectChannelConnector
{
private static final Logger logger = LoggerFactory.getLogger(SPDYServerConnector.class);
private static final Logger logger = Log.getLogger(SPDYServerConnector.class);
// Order is important on server side, so we use a LinkedHashMap
private final Map<String, AsyncConnectionFactory> factories = new LinkedHashMap<>();

View File

@ -307,7 +307,7 @@ public class FlowControlTest extends AbstractTest
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS);
final int length = 5 * windowSize;
stream.data(new BytesDataInfo(new byte[length], true));
@ -359,6 +359,98 @@ public class FlowControlTest extends AbstractTest
Assert.assertEquals(dataInfo.length(), dataInfo.consumed());
}
@Test
public void testStreamsStalledDoesNotStallOtherStreams() throws Exception
{
final int windowSize = 1024;
final CountDownLatch settingsLatch = new CountDownLatch(1);
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public void onSettings(Session session, SettingsInfo settingsInfo)
{
settingsLatch.countDown();
}
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
stream.reply(new ReplyInfo(false));
stream.data(new BytesDataInfo(new byte[windowSize * 2], true));
return null;
}
}), null);
Settings settings = new Settings();
settings.put(new Settings.Setting(Settings.ID.INITIAL_WINDOW_SIZE, windowSize));
session.settings(new SettingsInfo(settings));
Assert.assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
final CountDownLatch latch = new CountDownLatch(3);
final AtomicReference<DataInfo> dataInfoRef1 = new AtomicReference<>();
final AtomicReference<DataInfo> dataInfoRef2 = new AtomicReference<>();
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
int frames = dataFrames.incrementAndGet();
if (frames == 1)
{
// Do not consume it to stall flow control
dataInfoRef1.set(dataInfo);
}
else
{
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
latch.countDown();
}
}
}).get(5, TimeUnit.SECONDS);
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
int frames = dataFrames.incrementAndGet();
if (frames == 1)
{
// Do not consume it to stall flow control
dataInfoRef2.set(dataInfo);
}
else
{
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
latch.countDown();
}
}
}).get(5, TimeUnit.SECONDS);
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
DataInfo dataInfo1 = dataInfoRef1.getAndSet(null);
if (dataInfo1 != null)
dataInfo1.consume(dataInfo1.length());
DataInfo dataInfo2 = dataInfoRef2.getAndSet(null);
if (dataInfo2 != null)
dataInfo2.consume(dataInfo2.length());
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
latch.countDown();
}
}).get(5, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
private void expectException(Class<? extends Exception> exception, Callable command)
{
try

View File

@ -0,0 +1,164 @@
package org.eclipse.jetty.spdy;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.api.BytesDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.SessionStatus;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.spdy.frames.ControlFrameType;
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
import org.eclipse.jetty.spdy.generator.Generator;
import org.junit.Assert;
import org.junit.Test;
public class ProtocolViolationsTest extends AbstractTest
{
@Test
public void testSendDataBeforeReplyIsIllegal() throws Exception
{
final CountDownLatch resetLatch = new CountDownLatch(1);
final CountDownLatch latch = new CountDownLatch(1);
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
try
{
stream.data(new StringDataInfo("failure", true));
return null;
}
catch (IllegalStateException x)
{
latch.countDown();
return null;
}
}
}), new SessionFrameListener.Adapter()
{
@Override
public void onRst(Session session, RstInfo rstInfo)
{
Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus());
resetLatch.countDown();
}
});
session.syn(new SynInfo(true), null);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testReceiveDataBeforeReplyIsIllegal() throws Exception
{
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("localhost", 0));
Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
session.syn(new SynInfo(true), null);
SocketChannel channel = server.accept();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
channel.read(readBuffer);
readBuffer.flip();
int streamId = readBuffer.getInt(8);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
byte[] bytes = new byte[1];
ByteBuffer writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
channel.write(writeBuffer);
readBuffer.clear();
channel.read(readBuffer);
readBuffer.flip();
Assert.assertEquals(ControlFrameType.RST_STREAM.getCode(), readBuffer.getShort(2));
Assert.assertEquals(streamId, readBuffer.getInt(8));
writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode()));
channel.write(writeBuffer);
channel.shutdownOutput();
channel.close();
server.close();
}
@Test(expected = IllegalStateException.class)
public void testSendDataAfterCloseIsIllegal() throws Exception
{
Session session = startClient(startServer(null), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
stream.data(new StringDataInfo("test", true));
}
@Test(expected = IllegalStateException.class)
public void testSendHeadersAfterCloseIsIllegal() throws Exception
{
Session session = startClient(startServer(null), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
stream.headers(new HeadersInfo(new Headers(), true));
}
@Test
public void testDataSentAfterCloseIsDiscardedByRecipient() throws Exception
{
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("localhost", 0));
Session session = startClient(new InetSocketAddress("localhost", server.socket().getLocalPort()), null);
final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataLatch.countDown();
}
});
SocketChannel channel = server.accept();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
channel.read(readBuffer);
readBuffer.flip();
int streamId = readBuffer.getInt(8);
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
ByteBuffer writeBuffer = generator.control(new SynReplyFrame(SPDY.V2, (byte)0, streamId, new Headers()));
channel.write(writeBuffer);
byte[] bytes = new byte[1];
writeBuffer = generator.data(streamId, bytes.length, new BytesDataInfo(bytes, true));
channel.write(writeBuffer);
// Write again to simulate the faulty condition
writeBuffer.flip();
channel.write(writeBuffer);
Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
writeBuffer = generator.control(new GoAwayFrame(SPDY.V2, 0, SessionStatus.OK.getCode()));
channel.write(writeBuffer);
channel.shutdownOutput();
channel.close();
server.close();
}
}

View File

@ -64,6 +64,8 @@ public class ResetStreamTest extends AbstractTest
Assert.assertEquals(0, serverSession.getStreams().size());
Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
// Need to sleep a while to give the chance to the implementation to remove the stream
TimeUnit.SECONDS.sleep(1);
Assert.assertEquals(0, clientSession.getStreams().size());
}

View File

@ -87,25 +87,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}
});
List<Callable<Object>> tasks = new ArrayList<>();
for (int i = 0; i < count; ++i)
{
tasks.add(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
synCompletedData(session, headers, iterations);
return null;
}
});
}
ExecutorService threadPool = Executors.newFixedThreadPool(count);
List<Future<Object>> futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
List<Callable<Object>> tasks = new ArrayList<>();
tasks.clear();
for (int i = 0; i < count; ++i)
@ -120,11 +103,38 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}
});
}
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
long end = System.nanoTime();
System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
tasks.clear();
for (int i = 0; i < count; ++i)
{
tasks.add(new Callable<Object>()
{
@Override
public Object call() throws Exception
{
synCompletedData(session, headers, iterations);
return null;
}
});
}
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
for (Future<Object> future : futures)
future.get(iterations, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(count * iterations, TimeUnit.SECONDS));
long end = System.nanoTime();
System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
threadPool.shutdown();
}

View File

@ -30,12 +30,10 @@ import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
@ -324,39 +322,6 @@ public class SynReplyTest extends AbstractTest
Assert.assertTrue(clientDataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testSynDataRst() throws Exception
{
final AtomicReference<RstInfo> ref = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
ServerSessionFrameListener serverSessionFrameListener = new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
// Do not send the reply, we expect a RST_STREAM
stream.data(new StringDataInfo("foo", true));
return null;
}
@Override
public void onRst(Session session, RstInfo rstInfo)
{
ref.set(rstInfo);
latch.countDown();
}
};
Session session = startClient(startServer(serverSessionFrameListener), null);
Stream stream = session.syn(new SynInfo(true), null).get(5, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
RstInfo rstInfo = ref.get();
Assert.assertNotNull(rstInfo);
Assert.assertEquals(stream.getId(), rstInfo.getStreamId());
Assert.assertSame(StreamStatus.PROTOCOL_ERROR, rstInfo.getStreamStatus());
}
@Test
public void testSynReplyDataSynReplyData() throws Exception
{

View File

@ -381,7 +381,7 @@ public class Main
}
else if (info.equals("@STARTINI"))
{
List<String> ini = loadStartIni(null);
List<String> ini = loadStartIni(new File(_jettyHome,"start.ini"));
if (ini != null && ini.size() > 0)
{
for (String a : ini)

View File

@ -287,7 +287,25 @@ public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable
*/
public Map<K,String[]> toStringArrayMap()
{
HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2);
HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2)
{
public String toString()
{
StringBuilder b=new StringBuilder();
b.append('{');
for (K k:keySet())
{
if(b.length()>1)
b.append(',');
b.append(k);
b.append('=');
b.append(Arrays.asList(get(k)));
}
b.append('}');
return b.toString();
}
};
for(Map.Entry<K,Object> entry: _map.entrySet())
{

View File

@ -14,6 +14,10 @@
package org.eclipse.jetty.util;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import org.eclipse.jetty.util.log.Log;
@ -67,6 +71,7 @@ public class URIUtil
*/
public static StringBuilder encodePath(StringBuilder buf, String path)
{
byte[] bytes=null;
if (buf==null)
{
loop:
@ -84,8 +89,23 @@ public class URIUtil
case '<':
case '>':
case ' ':
buf=new StringBuilder(path.length()<<1);
buf=new StringBuilder(path.length()*2);
break loop;
default:
if (c>127)
{
try
{
bytes=path.getBytes(URIUtil.__CHARSET);
}
catch (UnsupportedEncodingException e)
{
throw new IllegalStateException(e);
}
buf=new StringBuilder(path.length()*2);
break loop;
}
}
}
if (buf==null)
@ -94,41 +114,91 @@ public class URIUtil
synchronized(buf)
{
for (int i=0;i<path.length();i++)
if (bytes!=null)
{
char c=path.charAt(i);
switch(c)
for (int i=0;i<bytes.length;i++)
{
case '%':
buf.append("%25");
continue;
case '?':
buf.append("%3F");
continue;
case ';':
buf.append("%3B");
continue;
case '#':
buf.append("%23");
continue;
case '"':
buf.append("%22");
continue;
case '\'':
buf.append("%27");
continue;
case '<':
buf.append("%3C");
continue;
case '>':
buf.append("%3E");
continue;
case ' ':
buf.append("%20");
continue;
default:
buf.append(c);
continue;
byte c=bytes[i];
switch(c)
{
case '%':
buf.append("%25");
continue;
case '?':
buf.append("%3F");
continue;
case ';':
buf.append("%3B");
continue;
case '#':
buf.append("%23");
continue;
case '"':
buf.append("%22");
continue;
case '\'':
buf.append("%27");
continue;
case '<':
buf.append("%3C");
continue;
case '>':
buf.append("%3E");
continue;
case ' ':
buf.append("%20");
continue;
default:
if (c<0)
{
buf.append('%');
TypeUtil.toHex(c,buf);
}
else
buf.append((char)c);
continue;
}
}
}
else
{
for (int i=0;i<path.length();i++)
{
char c=path.charAt(i);
switch(c)
{
case '%':
buf.append("%25");
continue;
case '?':
buf.append("%3F");
continue;
case ';':
buf.append("%3B");
continue;
case '#':
buf.append("%23");
continue;
case '"':
buf.append("%22");
continue;
case '\'':
buf.append("%27");
continue;
case '<':
buf.append("%3C");
continue;
case '>':
buf.append("%3E");
continue;
case ' ':
buf.append("%20");
continue;
default:
buf.append(c);
continue;
}
}
}
}

View File

@ -305,7 +305,8 @@ public class WebInfConfiguration extends AbstractConfiguration
}
catch(IOException e)
{
LOG.warn("tmpdir",e); System.exit(1);
tmpDir = null;
throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
}
}
}
@ -402,8 +403,7 @@ public class WebInfConfiguration extends AbstractConfiguration
}
if (LOG.isDebugEnabled())
LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory());
LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile()));
// Is the WAR usable directly?
if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
{
@ -485,57 +485,50 @@ public class WebInfConfiguration extends AbstractConfiguration
LOG.debug("webapp=" + web_app);
}
// Do we need to extract WEB-INF/lib?
if (context.isCopyWebInf())
if (context.isCopyWebInf() && !context.isCopyWebDir())
{
Resource web_inf= web_app.addPath("WEB-INF/");
if (web_inf instanceof ResourceCollection ||
web_inf.exists() &&
web_inf.isDirectory() &&
(web_inf.getFile()==null || !web_inf.getFile().isDirectory()))
{
File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
if (extractedWebInfDir.exists())
IO.delete(extractedWebInfDir);
extractedWebInfDir.mkdir();
Resource web_inf_lib = web_inf.addPath("lib/");
File webInfDir=new File(extractedWebInfDir,"WEB-INF");
webInfDir.mkdir();
File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
if (extractedWebInfDir.exists())
IO.delete(extractedWebInfDir);
extractedWebInfDir.mkdir();
Resource web_inf_lib = web_inf.addPath("lib/");
File webInfDir=new File(extractedWebInfDir,"WEB-INF");
webInfDir.mkdir();
if (web_inf_lib.exists())
{
File webInfLibDir = new File(webInfDir, "lib");
if (webInfLibDir.exists())
IO.delete(webInfLibDir);
webInfLibDir.mkdir();
if (web_inf_lib.exists())
{
File webInfLibDir = new File(webInfDir, "lib");
if (webInfLibDir.exists())
IO.delete(webInfLibDir);
webInfLibDir.mkdir();
LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
web_inf_lib.copyTo(webInfLibDir);
}
LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
web_inf_lib.copyTo(webInfLibDir);
}
Resource web_inf_classes = web_inf.addPath("classes/");
if (web_inf_classes.exists())
{
File webInfClassesDir = new File(webInfDir, "classes");
if (webInfClassesDir.exists())
IO.delete(webInfClassesDir);
webInfClassesDir.mkdir();
LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
web_inf_classes.copyTo(webInfClassesDir);
}
Resource web_inf_classes = web_inf.addPath("classes/");
if (web_inf_classes.exists())
{
File webInfClassesDir = new File(webInfDir, "classes");
if (webInfClassesDir.exists())
IO.delete(webInfClassesDir);
webInfClassesDir.mkdir();
LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
web_inf_classes.copyTo(webInfClassesDir);
}
web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
ResourceCollection rc = new ResourceCollection(web_inf,web_app);
ResourceCollection rc = new ResourceCollection(web_inf,web_app);
if (LOG.isDebugEnabled())
LOG.debug("context.resourcebase = "+rc);
if (LOG.isDebugEnabled())
LOG.debug("context.resourcebase = "+rc);
context.setBaseResource(rc);
}
context.setBaseResource(rc);
}
}

View File

@ -359,7 +359,6 @@
<module>jetty-http</module>
<module>jetty-websocket</module>
<module>jetty-continuation</module>
<module>jetty-npn</module>
<module>jetty-server</module>
<module>jetty-client</module>
<module>jetty-xml</module>