View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.server.handler.ResourceHandler;
43  import org.eclipse.jetty.servlet.DefaultServlet;
44  import org.eclipse.jetty.util.Callback;
45  import org.eclipse.jetty.util.URIUtil;
46  import org.eclipse.jetty.util.resource.Resource;
47  
48  /* ------------------------------------------------------------ */
49  /** Fast FileServer.
50   * 
51   * <p>This example shows how to use the Jetty APIs for sending static
52   * as fast as possible  using various strategies for small, medium and 
53   * large content.</p>
54   * <p>The Jetty {@link DefaultServlet} does all this and more, and to 
55   * a lesser extent so does the {@link ResourceHandler}, so unless you
56   * have exceptional circumstances it is best to use those classes for
57   * static content</p>
58   */
59  public class FastFileServer
60  {
61      public static void main(String[] args) throws Exception
62      {
63          Server server = new Server(8080);
64                  
65          HandlerList handlers = new HandlerList();
66          handlers.setHandlers(new Handler[] { new FastFileHandler(new File(".")), new DefaultHandler() });
67          server.setHandler(handlers);
68  
69          server.start();
70          server.join();
71      }
72  
73      static class FastFileHandler extends AbstractHandler
74      {
75          private final MimeTypes _mimeTypes = new MimeTypes();
76          private final File _dir;
77          
78          FastFileHandler(File dir)
79          {
80              _dir=dir;
81          }
82          
83          @Override
84          public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
85          {
86              // define small medium and large.  
87              // This should be turned for your content, JVM and OS, but we will huge HTTP response buffer size as a measure
88              final int SMALL=response.getBufferSize();
89              final int MEDIUM=8*SMALL;
90              
91              
92              // What file to serve?
93              final File file = new File(_dir,request.getPathInfo());
94              
95              // Only handle existing files
96              if (!file.exists())
97                  return;
98  
99              // we will handle this request
100             baseRequest.setHandled(true);
101             
102             // Handle directories
103             if (file.isDirectory())
104             {
105                 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
106                 {
107                     response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
108                     return;
109                 }
110                 String listing = Resource.newResource(file).getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
111                 response.setContentType("text/html; charset=UTF-8");
112                 response.getWriter().println(listing);
113                 return;
114             }
115             
116             // Set some content headers
117             // Jetty DefaultServlet will cache formatted date strings, but we will reformat for each request here
118             response.setDateHeader("Last-Modified",file.lastModified());
119             response.setDateHeader("Content-Length",file.length());
120             response.setContentType(_mimeTypes.getMimeByExtension(file.getName()));
121             
122             
123             
124             // send "small" files blocking directly from an input stream
125             if (file.length()<SMALL)
126             {
127                 // need to caste to Jetty output stream for best API
128                 ((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ));
129                 return;
130             }
131             
132             
133             // send not "small" files asynchronously so we don't hold threads if the client is slow
134             final AsyncContext async = request.startAsync();
135             Callback completionCB = new Callback()
136             {
137                 @Override
138                 public void succeeded()
139                 {
140                     // Async content write succeeded, so complete async response
141                     async.complete();
142                 }
143 
144                 @Override
145                 public void failed(Throwable x)
146                 {
147                     // log error and complete async response;
148                     x.printStackTrace();
149                     async.complete();
150                 }
151             };
152             
153             
154             
155             // send "medium" files from an input stream
156             if (file.length()<MEDIUM)
157             {
158                 // the file channel is closed by the async send
159                 ((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ),completionCB);
160                 return;
161             }
162             
163             
164             // for "large" files get the file mapped buffer to send
165             // Typically the resulting buffer should be cached as allocating kernel memory
166             // can be hard to GC on some JVMs.   But for this example we will create a new buffer per file
167             ByteBuffer buffer;
168             try (RandomAccessFile raf = new RandomAccessFile(file,"r");)
169             {
170                 buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
171             }
172             
173             // Assuming the file buffer might be shared cached version, so lets take our own view of it
174             buffer=buffer.asReadOnlyBuffer();
175             
176 
177             // send the content as a buffer with a callback to complete the async request
178             // need to caste to Jetty output stream for best API
179             ((HttpOutput)response.getOutputStream()).sendContent(buffer,completionCB);
180         }   
181     }
182 }