A {@link Connector} for TCP/IP network connectors
*/
-public interface NetworkConnector extends Connector, AutoCloseable
+public interface NetworkConnector extends Connector, Closeable
{
/**
* Performs the activities needed to open the network communication
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index 44e2518d9aa..40293b210b2 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -355,9 +355,10 @@ public class Server extends HandlerWrapper implements Attributes
futures.add(connector.shutdown());
// Then tell the contexts that we are shutting down
- Handler[] contexts = getChildHandlersByClass(Graceful.class);
- for (Handler context : contexts)
- futures.add(((Graceful)context).shutdown());
+
+ Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
+ for (Handler graceful : gracefuls)
+ futures.add(((Graceful)graceful).shutdown());
// Shall we gracefully wait for zero connections?
long stopTimeout = getStopTimeout();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
index 26a0d8dcf22..3fbbddd7107 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/StatisticsHandler.java
@@ -19,8 +19,11 @@
package org.eclipse.jetty.server.handler;
import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
@@ -32,14 +35,16 @@ import org.eclipse.jetty.server.AsyncContextEvent;
import org.eclipse.jetty.server.HttpChannelState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
+import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic;
@ManagedObject("Request Statistics Gathering")
-public class StatisticsHandler extends HandlerWrapper
+public class StatisticsHandler extends HandlerWrapper implements Graceful
{
private final AtomicLong _statsStartedAt = new AtomicLong();
@@ -59,6 +64,8 @@ public class StatisticsHandler extends HandlerWrapper
private final AtomicInteger _responses5xx = new AtomicInteger();
private final AtomicLong _responsesTotalBytes = new AtomicLong();
+ private final AtomicReference _shutdown=new AtomicReference<>();
+
private final AsyncListener _onCompletion = new AsyncListener()
{
@Override
@@ -86,14 +93,21 @@ public class StatisticsHandler extends HandlerWrapper
Request request = state.getBaseRequest();
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
- _requestStats.decrement();
+ long d=_requestStats.decrement();
_requestTimeStats.set(elapsed);
updateResponse(request);
_asyncWaitStats.decrement();
+
+ // If we have no more dispatches, should we signal shutdown?
+ if (d==0)
+ {
+ FutureCallback shutdown = _shutdown.get();
+ if (shutdown!=null)
+ shutdown.succeeded();
+ }
}
-
};
/**
@@ -162,9 +176,18 @@ public class StatisticsHandler extends HandlerWrapper
}
else if (state.isInitial())
{
- _requestStats.decrement();
+ long d=_requestStats.decrement();
_requestTimeStats.set(dispatched);
updateResponse(request);
+
+ // If we have no more dispatches, should we signal shutdown?
+ FutureCallback shutdown = _shutdown.get();
+ if (shutdown!=null)
+ {
+ httpResponse.flushBuffer();
+ if (d==0)
+ shutdown.succeeded();
+ }
}
// else onCompletion will handle it.
}
@@ -205,9 +228,20 @@ public class StatisticsHandler extends HandlerWrapper
@Override
protected void doStart() throws Exception
{
+ _shutdown.set(null);
super.doStart();
statsReset();
}
+
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ super.doStop();
+ FutureCallback shutdown = _shutdown.get();
+ if (shutdown!=null && !shutdown.isDone())
+ shutdown.failed(new TimeoutException());
+ }
/**
* @return the number of requests handled by this handler
@@ -523,4 +557,15 @@ public class StatisticsHandler extends HandlerWrapper
return sb.toString();
}
+
+ @Override
+ public Future shutdown()
+ {
+ FutureCallback shutdown=new FutureCallback(false);
+ _shutdown.compareAndSet(null,shutdown);
+ shutdown=_shutdown.get();
+ if (_dispatchedStats.getCurrent()==0)
+ shutdown.succeeded();
+ return shutdown;
+ }
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java
new file mode 100644
index 00000000000..0c11502ff68
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java
@@ -0,0 +1,105 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.server;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GracefulStopTest
+{
+ private Server server;
+
+ @Before
+ public void setup() throws Exception
+ {
+ server = new Server(0);
+ StatisticsHandler stats = new StatisticsHandler();
+ TestHandler test=new TestHandler();
+ server.setHandler(stats);
+ stats.setHandler(test);
+ server.setStopTimeout(10 * 1000);
+
+ server.start();
+ }
+
+ @Test
+ public void testGraceful() throws Exception
+ {
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ TimeUnit.SECONDS.sleep(1);
+ server.stop();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+
+ try(Socket socket = new Socket("localhost",server.getBean(NetworkConnector.class).getLocalPort());)
+ {
+ socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StringUtil.__ISO_8859_1_CHARSET));
+ String out = IO.toString(socket.getInputStream());
+ Assert.assertThat(out,Matchers.containsString("200 OK"));
+ }
+ }
+
+ private static class TestHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(final String s, final Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
+ throws IOException, ServletException
+ {
+ try
+ {
+ TimeUnit.SECONDS.sleep(2);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ httpServletResponse.getWriter().write("OK");
+ httpServletResponse.setStatus(200);
+ request.setHandled(true);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
index ce1441f0ade..8fcc49bb384 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.FilterInputStream;
@@ -26,6 +27,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
+import java.util.Arrays;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
@@ -81,6 +83,26 @@ public class HttpOutputTest
assertThat(response,containsString("HTTP/1.1 200 OK"));
}
+ @Test
+ public void testSendArray() throws Exception
+ {
+ byte[] buffer=new byte[16*1024];
+ Arrays.fill(buffer,0,4*1024,(byte)0x99);
+ Arrays.fill(buffer,4*1024,12*1024,(byte)0x58);
+ Arrays.fill(buffer,12*1024,16*1024,(byte)0x66);
+ _handler._content=ByteBuffer.wrap(buffer);
+ _handler._content.limit(12*1024);
+ _handler._content.position(4*1024);
+ String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
+ assertThat(response,containsString("HTTP/1.1 200 OK"));
+ assertThat(response,containsString("\r\nXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
+
+ for (int i=0;i<4*1024;i++)
+ assertEquals("i="+i,(byte)0x99,buffer[i]);
+ for (int i=12*1024;i<16*1024;i++)
+ assertEquals("i="+i,(byte)0x66,buffer[i]);
+ }
+
@Test
public void testSendInputStreamSimple() throws Exception
{
@@ -386,9 +408,9 @@ public class HttpOutputTest
boolean _async;
ByteBuffer _buffer;
byte[] _bytes;
- ByteBuffer _content;
InputStream _contentInputStream;
ReadableByteChannel _contentChannel;
+ ByteBuffer _content;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@@ -398,6 +420,7 @@ public class HttpOutputTest
final HttpOutput out = (HttpOutput) response.getOutputStream();
+
if (_contentInputStream!=null)
{
out.sendContent(_contentInputStream);
@@ -510,10 +533,13 @@ public class HttpOutputTest
return;
}
-
if (_content!=null)
{
- out.sendContent(_content);
+ response.setContentLength(_content.remaining());
+ if (_content.hasArray())
+ out.write(_content.array(),_content.arrayOffset()+_content.position(),_content.remaining());
+ else
+ out.sendContent(_content);
_content=null;
return;
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
index 162871826d1..bba67bd8cc8 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
@@ -82,9 +82,7 @@ public class SlowClientWithPipelinedRequestTest
}
}
- // TODO merged from jetty-8 - not working???
@Test
- @Ignore
public void testSlowClientWithPipelinedRequest() throws Exception
{
final int contentLength = 512 * 1024;
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index 8a4f8208e90..08a05e6eccf 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -569,10 +569,10 @@ public class ServletHandler extends ScopedHandler
LOG.debug(request.toString());
}
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
if (!response.isCommitted())
{
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
if (th instanceof UnavailableException)
{
UnavailableException ue = (UnavailableException)th;
@@ -586,6 +586,10 @@ public class ServletHandler extends ScopedHandler
}
else
LOG.debug("Response already committed",th);
+
+ // Complete async requests
+ if (request.isAsyncStarted())
+ request.getAsyncContext().complete();
}
catch(Error e)
{
@@ -596,15 +600,16 @@ public class ServletHandler extends ScopedHandler
LOG.warn("Error for "+request.getRequestURI(),e);
if(LOG.isDebugEnabled())LOG.debug(request.toString());
- // TODO httpResponse.getHttpConnection().forceClose();
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
if (!response.isCommitted())
- {
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
else
LOG.debug("Response already committed for handling ",e);
+
+ // Complete async requests
+ if (request.isAsyncStarted())
+ request.getAsyncContext().complete();
}
finally
{
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
index 121874ce762..e707884f9f5 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
@@ -41,6 +41,7 @@ import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.QuietServletException;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
@@ -68,7 +69,7 @@ public class AsyncContextTest
_server = new Server();
_contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
_connector = new LocalConnector(_server);
- _connector.setIdleTimeout(30000);
+ _connector.setIdleTimeout(5000);
_server.setConnectors(new Connector[]
{ _connector });
@@ -76,6 +77,7 @@ public class AsyncContextTest
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/path with spaces/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet2()),"/servletPath2");
+ _contextHandler.addServlet(new ServletHolder(new TestStartThrowServlet()),"/startthrow/*");
_contextHandler.addServlet(new ServletHolder(new ForwardingServlet()),"/forward");
_contextHandler.addServlet(new ServletHolder(new AsyncDispatchingServlet()),"/dispatchingServlet");
_contextHandler.addServlet(new ServletHolder(new ExpireServlet()),"/expire/*");
@@ -84,7 +86,8 @@ public class AsyncContextTest
ErrorPageErrorHandler error_handler = new ErrorPageErrorHandler();
_contextHandler.setErrorHandler(error_handler);
- error_handler.addErrorPage(500,"/error");
+ error_handler.addErrorPage(500,"/error/500");
+ error_handler.addErrorPage(IOException.class.getName(),"/error/IOE");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]
@@ -116,6 +119,25 @@ public class AsyncContextTest
}
+ @Test
+ public void testStartThrow() throws Exception
+ {
+ String request = "GET /ctx/startthrow HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ + "Connection: close\r\n" + "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+ Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
+ }
+
@Test
public void testDispatchAsyncContext() throws Exception
{
@@ -327,6 +349,7 @@ public class AsyncContextTest
br.readLine();// empty
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /500",br.readLine());
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: TEST",br.readLine());
}
@@ -365,6 +388,7 @@ public class AsyncContextTest
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.getOutputStream().print("ERROR: " + request.getServletPath() + "\n");
+ response.getOutputStream().print("PathInfo= " + request.getPathInfo() + "\n");
if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)!=null)
response.getOutputStream().print("EXCEPTION: " + request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) + "\n");
}
@@ -462,6 +486,21 @@ public class AsyncContextTest
asyncContext.start(new AsyncRunnable(asyncContext));
}
}
+
+ private class TestStartThrowServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (request.getDispatcherType()==DispatcherType.REQUEST)
+ {
+ request.startAsync(request, response);
+ throw new QuietServletException(new IOException("Test"));
+ }
+ }
+ }
private class AsyncRunnable implements Runnable
{
diff --git a/jetty-util/src/main/config/modules/logging.mod b/jetty-util/src/main/config/modules/logging.mod
index f72863d1f20..ec7d2ca4ea3 100644
--- a/jetty-util/src/main/config/modules/logging.mod
+++ b/jetty-util/src/main/config/modules/logging.mod
@@ -6,6 +6,16 @@
etc/jetty-logging.xml
[ini-template]
-## STDERR / STDOUT Logging
+## Logging Configuration
+# Configure jetty logging for default internal behavior STDERR output
+# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+
+# Configure jetty logging for slf4j
+# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
+
+# Configure jetty logging for java.util.logging
+# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
+
+# STDERR / STDOUT Logging
# Number of days to retain logs
# jetty.log.retain=90
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
index 024bb6378ab..85cc700254b 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartOutputStream.java
@@ -35,12 +35,12 @@ public class MultiPartOutputStream extends FilterOutputStream
private static final byte[] __CRLF={'\r','\n'};
private static final byte[] __DASHDASH={'-','-'};
- public static String MULTIPART_MIXED="multipart/mixed";
- public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
+ public static final String MULTIPART_MIXED="multipart/mixed";
+ public static final String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
/* ------------------------------------------------------------ */
- private String boundary;
- private byte[] boundaryBytes;
+ private final String boundary;
+ private final byte[] boundaryBytes;
/* ------------------------------------------------------------ */
private boolean inPart=false;
@@ -54,8 +54,15 @@ public class MultiPartOutputStream extends FilterOutputStream
boundary = "jetty"+System.identityHashCode(this)+
Long.toString(System.currentTimeMillis(),36);
boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
+ }
- inPart=false;
+ public MultiPartOutputStream(OutputStream out, String boundary)
+ throws IOException
+ {
+ super(out);
+
+ this.boundary = boundary;
+ boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
}
/* ------------------------------------------------------------ */
@@ -66,14 +73,20 @@ public class MultiPartOutputStream extends FilterOutputStream
public void close()
throws IOException
{
- if (inPart)
+ try
+ {
+ if (inPart)
+ out.write(__CRLF);
+ out.write(__DASHDASH);
+ out.write(boundaryBytes);
+ out.write(__DASHDASH);
out.write(__CRLF);
- out.write(__DASHDASH);
- out.write(boundaryBytes);
- out.write(__DASHDASH);
- out.write(__CRLF);
- inPart=false;
- super.close();
+ inPart=false;
+ }
+ finally
+ {
+ super.close();
+ }
}
/* ------------------------------------------------------------ */
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
index 67699911c9b..51a82ea3bb4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/statistic/CounterStatistic.java
@@ -55,37 +55,31 @@ public class CounterStatistic
/**
* @param delta the amount to add to the count
*/
- public void add(final long delta)
+ public long add(final long delta)
{
long value=_curr.addAndGet(delta);
if (delta > 0)
+ {
_total.addAndGet(delta);
- Atomics.updateMax(_max,value);
- }
-
- /* ------------------------------------------------------------ */
- /**
- * @param delta the amount to subtract the count by.
- */
- public void subtract(final long delta)
- {
- add(-delta);
+ Atomics.updateMax(_max,value);
+ }
+ return value;
}
/* ------------------------------------------------------------ */
/**
*/
- public void increment()
+ public long increment()
{
- add(1);
+ return add(1);
}
/* ------------------------------------------------------------ */
/**
*/
- public void decrement()
+ public long decrement()
{
- add(-1);
+ return add(-1);
}
/* ------------------------------------------------------------ */
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index 2fe990edb68..81534cb1a3c 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -154,6 +154,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private String[] _contextWhiteList = null;
private File _tmpDir;
+ private boolean _persistTmpDir = false;
+
private String _war;
private String _extraClasspath;
private Throwable _unavailableException;
@@ -1222,7 +1224,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
LOG.warn(e);
}
_tmpDir=dir;
- setAttribute(TEMPDIR,_tmpDir);
+ setAttribute(TEMPDIR,_tmpDir);
}
/* ------------------------------------------------------------ */
@@ -1232,6 +1234,27 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _tmpDir;
}
+ /**
+ * If true the temp directory for this
+ * webapp will be kept when the webapp stops. Otherwise,
+ * it will be deleted.
+ *
+ * @param delete
+ */
+ public void setPersistTempDirectory(boolean persist)
+ {
+ _persistTmpDir = persist;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isPersistTempDirectory()
+ {
+ return _persistTmpDir;
+ }
+
+
/* ------------------------------------------------------------ */
/**
* @param war The war to set as a file name or URL
@@ -1480,5 +1503,4 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
{
return _metadata;
}
-
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
index 88db011c2ea..cc3f875da79 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
@@ -27,6 +27,7 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
@@ -57,17 +58,15 @@ public class WebInfConfiguration extends AbstractConfiguration
* resource base as a resource collection.
*/
public static final String RESOURCE_DIRS = "org.eclipse.jetty.resources";
+
protected Resource _preUnpackBaseResource;
+
+
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
- // Look for a work directory
- File work = findWorkDirectory(context);
- if (work != null)
- makeTempDirectory(work, context, false);
-
//Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
@@ -192,20 +191,16 @@ public class WebInfConfiguration extends AbstractConfiguration
@Override
public void deconfigure(WebAppContext context) throws Exception
{
- // delete temp directory if we had to create it or if it isn't called work
- Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
-
- if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
+ //if we're not persisting the temp dir contents delete it
+ if (!context.isPersistTempDirectory())
{
IO.delete(context.getTempDirectory());
- context.setTempDirectory(null);
-
- //clear out the context attributes for the tmp dir only if we had to
- //create the tmp dir
- context.setAttribute(TEMPDIR_CONFIGURED, null);
- context.setAttribute(WebAppContext.TEMPDIR, null);
}
-
+
+ //if it wasn't explicitly configured by the user, then unset it
+ Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
+ if (tmpdirConfigured != null && !tmpdirConfigured)
+ context.setTempDirectory(null);
//reset the base resource back to what it was before we did any unpacking of resources
context.setBaseResource(_preUnpackBaseResource);
@@ -238,51 +233,44 @@ public class WebInfConfiguration extends AbstractConfiguration
* A. Try to use an explicit directory specifically for this webapp:
*
* -
- * Iff an explicit directory is set for this webapp, use it. Do NOT set
- * delete on exit.
+ * Iff an explicit directory is set for this webapp, use it. Set delete on
+ * exit depends on value of persistTempDirectory.
*
* -
* Iff javax.servlet.context.tempdir context attribute is set for
- * this webapp && exists && writeable, then use it. Do NOT set delete on exit.
+ * this webapp && exists && writeable, then use it. Set delete on exit depends on
+ * value of persistTempDirectory.
*
*
*
* B. Create a directory based on global settings. The new directory
- * will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost
- * Work out where to create this directory:
- *
- * -
- * Iff $(jetty.home)/work exists create the directory there. Do NOT
- * set delete on exit. Do NOT delete contents if dir already exists.
- *
- * -
- * Iff WEB-INF/work exists create the directory there. Do NOT set
- * delete on exit. Do NOT delete contents if dir already exists.
- *
- * -
- * Else create dir in $(java.io.tmpdir). Set delete on exit. Delete
- * contents if dir already exists.
- *
- *
+ * will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"
+ *
+ *
+ * If the user has specified the context attribute org.eclipse.jetty.webapp.basetempdir, the
+ * directory specified by this attribute will be the parent of the temp dir created. Otherwise,
+ * the parent dir is $(java.io.tmpdir). Set delete on exit depends on value of persistTempDirectory.
+ *
*/
public void resolveTempDirectory (WebAppContext context)
+ throws Exception
{
- //If a tmp directory is already set, we're done
+ //If a tmp directory is already set we should use it
File tmpDir = context.getTempDirectory();
- if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
+ if (tmpDir != null)
{
- context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
- return; // Already have a suitable tmp dir configured
+ configureTempDirectory(tmpDir, context);
+ context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE); //the tmp dir was set explicitly
+ return;
}
-
- // No temp directory configured, try to establish one.
- // First we check the context specific, javax.servlet specified, temp directory attribute
+ // No temp directory configured, try to establish one via the javax.servlet.context.tempdir.
File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
- if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
+ if (servletTmpDir != null)
{
// Use as tmpDir
tmpDir = servletTmpDir;
+ configureTempDirectory(tmpDir, context);
// Ensure Attribute has File object
context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
// Set as TempDir in context.
@@ -290,60 +278,25 @@ public class WebInfConfiguration extends AbstractConfiguration
return;
}
- try
+ //We need to make a temp dir. Check if the user has set a directory to use instead
+ //of java.io.tmpdir as the parent of the dir
+ File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
+ if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
{
- // Put the tmp dir in the work directory if we had one
- File work = new File(System.getProperty("jetty.base"),"work");
- if (work.exists() && work.canWrite() && work.isDirectory())
- {
- makeTempDirectory(work, context, false); //make a tmp dir inside work, don't delete if it exists
- }
- else
- {
- File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
- if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
- {
- // Use baseTemp directory (allow the funky Jetty_0_0_0_0.. subdirectory logic to kick in
- makeTempDirectory(baseTemp,context,false);
- }
- else
- {
- makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true); //make a tmpdir, delete if it already exists
- }
- }
+ //Make a temp directory as a child of the given base dir
+ makeTempDirectory(baseTemp,context);
}
- catch(Exception e)
+ else
{
- tmpDir=null;
- LOG.ignore(e);
- }
-
- //Third ... Something went wrong trying to make the tmp directory, just make
- //a jvm managed tmp directory
- if (context.getTempDirectory() == null)
- {
- try
- {
- // Last resort
- tmpDir=File.createTempFile("JettyContext","");
- if (tmpDir.exists())
- IO.delete(tmpDir);
- tmpDir.mkdir();
- tmpDir.deleteOnExit();
- context.setTempDirectory(tmpDir);
- }
- catch(IOException e)
- {
- tmpDir = null;
- throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
- }
+ //Make a temp directory in java.io.tmpdir
+ makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context);
}
}
/**
* Given an Object, return File reference for object.
* Typically used to convert anonymous Object from getAttribute() calls to a File object.
- * @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null)
+ * @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null
* @return the File object, null if null, or null if not a File or String
*/
private File asFile(Object fileattr)
@@ -365,45 +318,47 @@ public class WebInfConfiguration extends AbstractConfiguration
- public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
- throws IOException
+ public void makeTempDirectory (File parent, WebAppContext context)
+ throws Exception
{
- if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
+ if (parent == null || !parent.exists() || !parent.canWrite() || !parent.isDirectory())
+ throw new IllegalStateException("Parent for temp dir not configured correctly: "+(parent==null?"null":"writeable="+parent.canWrite()));
+
+ String temp = getCanonicalNameForWebAppTmpDir(context);
+ File tmpDir = File.createTempFile(temp, ".dir", parent);
+ //delete the file that was created
+ tmpDir.delete();
+ //and make a directory of the same name
+ tmpDir.mkdirs();
+ configureTempDirectory(tmpDir, context);
+
+ if(LOG.isDebugEnabled())
+ LOG.debug("Set temp dir "+tmpDir);
+ context.setTempDirectory(tmpDir);
+ }
+
+ private void configureTempDirectory (File dir, WebAppContext context)
+ {
+ if (dir == null)
+ throw new IllegalArgumentException("Null temp dir");
+
+ //if dir exists and we don't want it persisted, delete it
+ if (dir.exists() && !context.isPersistTempDirectory())
{
- String temp = getCanonicalNameForWebAppTmpDir(context);
- File tmpDir = new File(parent,temp);
-
- if (deleteExisting && tmpDir.exists())
- {
- if (!IO.delete(tmpDir))
- {
- if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
- }
-
- //If we can't delete the existing tmp dir, create a new one
- if (tmpDir.exists())
- {
- String old=tmpDir.toString();
- tmpDir=File.createTempFile(temp+"_","");
- if (tmpDir.exists())
- IO.delete(tmpDir);
- LOG.warn("Can't reuse "+old+", using "+tmpDir);
- }
- }
-
- if (!tmpDir.exists())
- tmpDir.mkdir();
-
- //If the parent is not a work directory
- if (!isTempWorkDirectory(tmpDir))
- {
- tmpDir.deleteOnExit();
- }
-
- if(LOG.isDebugEnabled())
- LOG.debug("Set temp dir "+tmpDir);
- context.setTempDirectory(tmpDir);
+ if (!IO.delete(dir))
+ throw new IllegalStateException("Failed to delete temp dir "+dir);
}
+
+ //if it doesn't exist make it
+ if (!dir.exists())
+ dir.mkdirs();
+
+ if (!context.isPersistTempDirectory())
+ dir.deleteOnExit();
+
+ //is it useable
+ if (!dir.canWrite() || !dir.isDirectory())
+ throw new IllegalStateException("Temp dir "+dir+" not useable: writeable="+dir.canWrite()+", dir="+dir.isDirectory());
}
@@ -566,45 +521,17 @@ public class WebInfConfiguration extends AbstractConfiguration
}
- public File findWorkDirectory (WebAppContext context) throws IOException
- {
- if (context.getBaseResource() != null)
- {
- Resource web_inf = context.getWebInf();
- if (web_inf !=null && web_inf.exists())
- {
- return new File(web_inf.getFile(),"work");
- }
- }
- return null;
- }
-
-
- /**
- * Check if the tmpDir itself is called "work", or if the tmpDir
- * is in a directory called "work".
- * @return true if File is a temporary or work directory
- */
- public boolean isTempWorkDirectory (File tmpDir)
- {
- if (tmpDir == null)
- return false;
- if (tmpDir.getName().equalsIgnoreCase("work"))
- return true;
- File t = tmpDir.getParentFile();
- if (t == null)
- return false;
- return (t.getName().equalsIgnoreCase("work"));
- }
/**
* Create a canonical name for a webapp temp directory.
* The form of the name is:
- * "Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36_hashcode_of_whole_string
+ * "jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"
*
* host and port uniquely identify the server
* context and virtual host uniquely identify the webapp
+ * randomdigits ensure every tmp directory is unique
+ *
* @return the canonical name for the webapp temp directory
*/
public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
@@ -697,6 +624,7 @@ public class WebInfConfiguration extends AbstractConfiguration
}
canonicalName.append("-");
+
return canonicalName.toString();
}
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyAnnotatedEventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyAnnotatedEventDriver.java
index a2bec047ae1..e9b818d908f 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyAnnotatedEventDriver.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/JettyAnnotatedEventDriver.java
@@ -54,7 +54,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
}
if (anno.maxBinaryMessageSize() > 0)
{
- this.policy.setMaxTextMessageSize(anno.maxBinaryMessageSize());
+ this.policy.setMaxBinaryMessageSize(anno.maxBinaryMessageSize());
}
if (anno.inputBufferSize() > 0)
{
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
index 85b0cf32e40..790a4f11859 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketCreator.java
@@ -18,8 +18,6 @@
package org.eclipse.jetty.websocket.servlet;
-import org.eclipse.jetty.websocket.api.extensions.Extension;
-
/**
* Abstract WebSocket creator interface.
*