diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java new file mode 100644 index 00000000000..dd819acdf7b --- /dev/null +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java @@ -0,0 +1,181 @@ +// +// ======================================================================== +// 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.embedded; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.StandardOpenOption; + +import javax.servlet.AsyncContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpOutput; +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.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; + +/* ------------------------------------------------------------ */ +/** Fast FileServer. + * + *
This example shows how to use the Jetty APIs for sending static + * as fast as possible using various strategies for small, medium and + * large content.
+ *The Jetty {@link DefaultServlet} does all this and more, and to + * a lesser extent so does the {@link ResourceHandler}, so unless you + * have exceptional circumstances it is best to use those classes for + * static content
+ */ +public class FastFileServer +{ + public static void main(String[] args) throws Exception + { + Server server = new Server(8080); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { new FastFileHandler(new File(".")), new DefaultHandler() }); + server.setHandler(handlers); + + server.start(); + server.join(); + } + + static class FastFileHandler extends AbstractHandler + { + private final MimeTypes _mimeTypes = new MimeTypes(); + private final File _dir; + + FastFileHandler(File dir) + { + _dir=dir; + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + // define small medium and large. + // This should be turned for your content, JVM and OS, but we will huge HTTP response buffer size as a measure + final int SMALL=response.getBufferSize(); + final int MEDIUM=8*SMALL; + + + // What file to serve? + final File file = new File(_dir,request.getPathInfo()); + + // Only handle existing files + if (!file.exists()) + return; + + // we will handle this request + baseRequest.setHandled(true); + + // Handle directories + if (file.isDirectory()) + { + if (!request.getPathInfo().endsWith(URIUtil.SLASH)) + { + response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH))); + return; + } + String listing = Resource.newResource(file).getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0); + response.setContentType("text/html; charset=UTF-8"); + response.getWriter().println(listing); + return; + } + + // Set some content headers + // Jetty DefaultServlet will cache formatted date strings, but we will reformat for each request here + response.setDateHeader("Last-Modified",file.lastModified()); + response.setDateHeader("Content-Length",file.length()); + response.setContentType(_mimeTypes.getMimeByExtension(file.getName())); + + + + // send "small" files blocking directly from an input stream + if (file.length() implements HttpParser.RequestHandlerRequests to write (in a blocking way) the given response content buffer, - * committing the response if needed.
+ *Blocking write, committing the response if needed.
* * @param content the content buffer to write * @param complete whether the content is complete for the response @@ -704,7 +654,21 @@ public class HttpChannelNon-Blocking write, committing the response if needed.
+ * + * @param content the content buffer to write + * @param complete whether the content is complete for the response + * @param callback Callback when complete or failed + * @throws IOException if the write fails + */ + protected void write(ByteBuffer content, boolean complete, Callback callback) + { + sendResponse(null,content,complete,callback); } protected void execute(Runnable task) @@ -725,4 +689,68 @@ public class HttpChannelTo avoid this issue, this callback uses an Atomicboolean to note + * if the success callback has been called during the processing of a + * sub task, and if so then the processing iterates rather than recurses. + *
+ *This callback is passed to the asynchronous handling of each sub + * task and a call the {@link #succeeded()} on this call back represents + * completion of the subtask. Only once all the subtasks are completed is + * the {@link Callback#succeeded()} method called on the {@link Callback} instance + * passed the the {@link #IteratingCallback(Callback)} constructor.
+ * + */ +public abstract class IteratingCallback implements Callback +{ + final AtomicBoolean _iterating = new AtomicBoolean(); + final Callback _callback; + + + public IteratingCallback(Callback callback) + { + _callback=callback; + } + + /* ------------------------------------------------------------ */ + /** + * Process a subtask. + *Called by {@link #iterate()} to process a sub task of the overall task + *
+ * @return True if the total task is complete. If false is returned + * then this Callback must be scheduled to receive either a call to + * {@link #succeeded()} or {@link #failed(Throwable)}. + * @throws Exception + */ + abstract protected boolean process() throws Exception; + + /* ------------------------------------------------------------ */ + /** This method is called initially to start processing and + * is then called by subsequent sub task success to continue + * processing. + */ + public void iterate() + { + try + { + // Keep iterating as long as succeeded() is called during process() + while(_iterating.compareAndSet(false,true)) + { + // process and test if we are complete + if (process()) + { + _callback.succeeded(); + return; + } + } + } + catch(Exception e) + { + _iterating.set(false); + _callback.failed(e); + } + finally + { + _iterating.set(false); + } + } + + + @Override + public void succeeded() + { + if (!_iterating.compareAndSet(true,false)) + iterate(); + } + + @Override + public void failed(Throwable x) + { + _callback.failed(x); + } + +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index cda43cb6c9a..02f57e5f322 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -107,6 +107,17 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { l.start(); } + + /** + * Stops the given lifecycle. + * + * @param l + * @throws Exception + */ + protected void stop(LifeCycle l) throws Exception + { + l.stop(); + } /** * Stops the managed lifecycle beans in the reverse order they were added. @@ -124,7 +135,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { LifeCycle l = (LifeCycle)b._bean; if (l.isRunning()) - l.stop(); + stop(l); } } } @@ -261,7 +272,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { LifeCycle l = (LifeCycle)o; if (!l.isRunning()) - l.start(); + start(l); } break; @@ -276,7 +287,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, else { manage(new_bean); - l.start(); + start(l); } } else @@ -452,6 +463,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { if (_beans.remove(bean)) { + unmanage(bean); for (Container.Listener l:_listeners) @@ -472,7 +484,23 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, } } } - + + // stop managed beans + if (bean._managed==Managed.MANAGED && bean._bean instanceof LifeCycle) + { + try + { + stop((LifeCycle)bean._bean); + } + catch(RuntimeException | Error e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } return true; } return false; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java index f48355bebd6..47fb5515dac 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java @@ -22,6 +22,7 @@ package org.eclipse.jetty.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -143,6 +144,29 @@ public class BufferUtilTest assertEquals(2,from.remaining()); assertEquals("1234567890",BufferUtil.toString(to)); } + + + + @Test + public void testAppend() throws Exception + { + ByteBuffer to = BufferUtil.allocate(8); + ByteBuffer from=BufferUtil.toBuffer("12345"); + + BufferUtil.append(to,from.array(),0,3); + assertEquals("123",BufferUtil.toString(to)); + BufferUtil.append(to,from.array(),3,2); + assertEquals("12345",BufferUtil.toString(to)); + + try + { + BufferUtil.append(to,from.array(),0,5); + Assert.fail(); + } + catch(BufferOverflowException e) + {} + } + @Test public void testPutDirect() throws Exception diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java index c77d735d748..1c5889fd15b 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java @@ -492,7 +492,7 @@ public class ResourceTest // This test is intended to run only on Windows platform assumeTrue(OS.IS_WINDOWS); - String path = __userURL.toURI().getPath().replace('/','\\')+"ResourceTest.java"; + String path = __userURL.toURI().getPath().replace('/','\\')+"resource.txt"; System.err.println(path); Resource resource = Resource.newResource(path, false); diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/HttpTransportOverMux.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/HttpTransportOverMux.java index 0d2fef5d0b4..cc442b1c600 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/HttpTransportOverMux.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/HttpTransportOverMux.java @@ -57,18 +57,7 @@ public class HttpTransportOverMux implements HttpTransport public void send(ResponseInfo info, ByteBuffer responseBodyContent, boolean lastContent) throws IOException { send(info,responseBodyContent,lastContent,streamBlocker); - try - { - streamBlocker.block(); - } - catch (IOException e) - { - throw e; - } - catch (Exception e) - { - throw new EofException(e); - } + streamBlocker.block(); } @Override @@ -87,4 +76,10 @@ public class HttpTransportOverMux implements HttpTransport // prepare the AddChannelResponse // TODO: look at HttpSender in jetty-client for generator loop logic } + + @Override + public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback) + { + send(null,responseBodyContent, lastContent, callback); + } }