View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.embedded;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.RandomAccessFile;
24  import java.nio.ByteBuffer;
25  import java.nio.channels.FileChannel;
26  import java.nio.channels.FileChannel.MapMode;
27  import java.nio.file.StandardOpenOption;
28  
29  import javax.servlet.AsyncContext;
30  import javax.servlet.ServletException;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.eclipse.jetty.http.MimeTypes;
35  import org.eclipse.jetty.server.Handler;
36  import org.eclipse.jetty.server.HttpOutput;
37  import org.eclipse.jetty.server.Request;
38  import org.eclipse.jetty.server.Server;
39  import org.eclipse.jetty.server.handler.AbstractHandler;
40  import org.eclipse.jetty.server.handler.DefaultHandler;
41  import org.eclipse.jetty.server.handler.HandlerList;
42  import org.eclipse.jetty.util.Callback;
43  import org.eclipse.jetty.util.URIUtil;
44  import org.eclipse.jetty.util.resource.Resource;
45  
46  /**
47   * Fast FileServer.
48   * <p>
49   * This example shows how to use the Jetty APIs for sending static as fast as
50   * possible using various strategies for small, medium and large content.
51   * </p>
52   * <p>
53   * The Jetty {@link DefaultServlet} does all this and more, and to a lesser
54   * extent so does the {@link ResourceHandler}, so unless you have exceptional
55   * circumstances it is best to use those classes for static content
56   * </p>
57   */
58  public class FastFileServer
59  {
60      public static void main( String[] args ) throws Exception
61      {
62          Server server = new Server(8080);
63  
64          HandlerList handlers = new HandlerList();
65          handlers.setHandlers(new Handler[] {
66                  new FastFileHandler(new File(System.getProperty("user.dir"))),
67                  new DefaultHandler() });
68          server.setHandler(handlers);
69  
70          server.start();
71          server.join();
72      }
73  
74      static class FastFileHandler extends AbstractHandler
75      {
76          private final MimeTypes mimeTypes = new MimeTypes();
77          private final File dir;
78  
79          private FastFileHandler( File dir )
80          {
81              this.dir = dir;
82          }
83  
84          @Override
85          public void handle( String target,
86                              Request baseRequest,
87                              HttpServletRequest request,
88                              HttpServletResponse response ) throws IOException,
89                                                            ServletException
90          {
91              // define small medium and large.
92              // This should be turned for your content, JVM and OS, but we will
93              // huge HTTP response buffer size as a measure
94              final int SMALL = response.getBufferSize();
95              final int MEDIUM = 8 * SMALL;
96  
97              // What file to serve?
98              final File file = new File(this.dir, request.getPathInfo());
99  
100             // Only handle existing files
101             if (!file.exists())
102                 return;
103 
104             // we will handle this request
105             baseRequest.setHandled(true);
106 
107             // Handle directories
108             if (file.isDirectory())
109             {
110                 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
111                 {
112                     response.sendRedirect(response.encodeRedirectURL(URIUtil
113                             .addPaths(request.getRequestURI(), URIUtil.SLASH)));
114                     return;
115                 }
116                 String listing = Resource.newResource(file).getListHTML(
117                         request.getRequestURI(),
118                         request.getPathInfo().lastIndexOf("/") > 0);
119                 response.setContentType("text/html; charset=utf-8");
120                 response.getWriter().println(listing);
121                 return;
122             }
123 
124             // Set some content headers.
125             
126             // Jetty DefaultServlet will cache formatted date strings, but we
127             // will reformat for each request here
128             response.setDateHeader("Last-Modified", file.lastModified());
129             response.setDateHeader("Content-Length", file.length());
130             response.setContentType(mimeTypes.getMimeByExtension(file.getName()));
131 
132             // send "small" files blocking directly from an input stream
133             if (file.length() < SMALL)
134             {
135                 // need to caste to Jetty output stream for best API
136                 ((HttpOutput) response.getOutputStream())
137                         .sendContent(FileChannel.open(file.toPath(),
138                                 StandardOpenOption.READ));
139                 return;
140             }
141 
142             // send not "small" files asynchronously so we don't hold threads if
143             // the client is slow
144             final AsyncContext async = request.startAsync();
145             Callback completionCB = new Callback()
146             {
147                 @Override
148                 public void succeeded()
149                 {
150                     // Async content write succeeded, so complete async response
151                     async.complete();
152                 }
153 
154                 @Override
155                 public void failed( Throwable x )
156                 {
157                     // log error and complete async response;
158                     x.printStackTrace();
159                     async.complete();
160                 }
161             };
162 
163             // send "medium" files from an input stream
164             if (file.length() < MEDIUM)
165             {
166                 // the file channel is closed by the async send
167                 ((HttpOutput) response.getOutputStream())
168                         .sendContent(FileChannel.open(file.toPath(),
169                                 StandardOpenOption.READ), completionCB);
170                 return;
171             }
172 
173             // for "large" files get the file mapped buffer to send Typically
174             // the resulting buffer should be cached as allocating kernel memory
175             // can be hard to GC on some JVMs. But for this example we will
176             // create a new buffer per file
177             ByteBuffer buffer;
178             try ( RandomAccessFile raf = new RandomAccessFile(file, "r"); )
179             {
180                 buffer = raf.getChannel().map(MapMode.READ_ONLY, 0,
181                         raf.length());
182             }
183 
184             // Assuming the file buffer might be shared cached version, so lets
185             // take our own view of it
186             buffer = buffer.asReadOnlyBuffer();
187 
188             // send the content as a buffer with a callback to complete the
189             // async request need to caste to Jetty output stream for best API
190             ((HttpOutput) response.getOutputStream()).sendContent(buffer,
191                     completionCB);
192         }
193     }
194 }