View Javadoc

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