View Javadoc
1   /*
2    * @(#)JnlpDownloadServlet.java	1.10 07/03/15
3    * 
4    * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are met:
8    *
9    * -Redistribution of source code must retain the above copyright notice, this
10   *  list of conditions and the following disclaimer.
11   *
12   * -Redistribution in binary form must reproduce the above copyright notice,
13   *  this list of conditions and the following disclaimer in the documentation
14   *  and/or other materials provided with the distribution.
15   *
16   * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17   * be used to endorse or promote products derived from this software without
18   * specific prior written permission.
19   *
20   * This software is provided "AS IS," without a warranty of any kind. ALL
21   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
22   * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23   * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
24   * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
25   * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
26   * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
27   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
28   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
29   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31   *
32   * You acknowledge that this software is not designed, licensed or intended
33   * for use in the design, construction, operation or maintenance of any
34   * nuclear facility.
35   */
36  
37  package jnlp.sample.servlet;
38  
39  import javax.servlet.ServletConfig;
40  import javax.servlet.ServletException;
41  import javax.servlet.http.HttpServlet;
42  import javax.servlet.http.HttpServletRequest;
43  import javax.servlet.http.HttpServletResponse;
44  import java.io.IOException;
45  import java.util.ResourceBundle;
46  
47  /**
48   * This Servlet class is an implementation of JNLP Specification's
49   * Download Protocols.
50   * <p/>
51   * All requests to this servlet is in the form of HTTP GET commands.
52   * The parameters that are needed are:
53   * <ul>
54   * <li><code>arch</code>,
55   * <li><code>os</code>,
56   * <li><code>locale</code>,
57   * <li><code>version-id</code> or <code>platform-version-id</code>,
58   * <li><code>current-version-id</code>,
59   * <li>code>known-platforms</code>
60   * </ul>
61   * <p/>
62   *
63   * @version 1.8 01/23/03
64   */
65  public class JnlpDownloadServlet
66      extends HttpServlet
67  {
68  
69      // Localization
70      private static ResourceBundle _resourceBundle = null;
71  
72      // Servlet configuration
73      private static final String PARAM_JNLP_EXTENSION = "jnlp-extension";
74  
75      private static final String PARAM_JAR_EXTENSION = "jar-extension";
76  
77      // Servlet configuration
78      private Logger _log = null;
79  
80      private JnlpFileHandler _jnlpFileHandler = null;
81  
82      private JarDiffHandler _jarDiffHandler = null;
83  
84      private ResourceCatalog _resourceCatalog = null;
85  
86      /**
87       * Initialize servlet
88       */
89      public void init( ServletConfig config )
90          throws ServletException
91      {
92          super.init( config );
93  
94          // Setup logging
95          _log = new Logger( config, getResourceBundle() );
96          _log.addDebug( "Initializing" );
97  
98          // Get extension from Servlet configuration, or use default
99          JnlpResource.setDefaultExtensions( config.getInitParameter( PARAM_JNLP_EXTENSION ),
100                                            config.getInitParameter( PARAM_JAR_EXTENSION ) );
101 
102         _jnlpFileHandler = new JnlpFileHandler( config.getServletContext(), _log );
103         _jarDiffHandler = new JarDiffHandler( config.getServletContext(), _log );
104         _resourceCatalog = new ResourceCatalog( config.getServletContext(), _log );
105     }
106 
107     public static synchronized ResourceBundle getResourceBundle()
108     {
109         if ( _resourceBundle == null )
110         {
111             _resourceBundle = ResourceBundle.getBundle( "jnlp/sample/servlet/resources/strings" );
112         }
113         return _resourceBundle;
114     }
115 
116 
117     public void doHead( HttpServletRequest request, HttpServletResponse response )
118         throws ServletException, IOException
119     {
120         handleRequest( request, response, true );
121     }
122 
123     /**
124      * We handle get requests too - eventhough the spec. only requeres POST requests
125      */
126     public void doGet( HttpServletRequest request, HttpServletResponse response )
127         throws ServletException, IOException
128     {
129         handleRequest( request, response, false );
130     }
131 
132     private void handleRequest( HttpServletRequest request, HttpServletResponse response, boolean isHead )
133         throws IOException
134     {
135         String requestStr = request.getRequestURI();
136         if ( request.getQueryString() != null )
137         {
138             requestStr += "?" + request.getQueryString().trim();
139         }
140 
141         // Parse HTTP request
142         DownloadRequest dreq = new DownloadRequest( getServletContext(), request );
143         if ( _log.isInformationalLevel() )
144         {
145             _log.addInformational( "servlet.log.info.request", requestStr );
146             _log.addInformational( "servlet.log.info.useragent", request.getHeader( "User-Agent" ) );
147         }
148         if ( _log.isDebugLevel() )
149         {
150             _log.addDebug( dreq.toString() );
151         }
152 
153         long ifModifiedSince = request.getDateHeader( "If-Modified-Since" );
154 
155         // Check if it is a valid request
156         try
157         {
158             // Check if the request is valid
159             validateRequest( dreq );
160 
161             // Decide what resource to return
162             JnlpResource jnlpres = locateResource( dreq );
163             _log.addDebug( "JnlpResource: " + jnlpres );
164 
165             if ( _log.isInformationalLevel() )
166             {
167                 _log.addInformational( "servlet.log.info.goodrequest", jnlpres.getPath() );
168             }
169 
170             DownloadResponse dres;
171 
172             if ( isHead )
173             {
174 
175                 int cl = jnlpres.getResource().openConnection().getContentLength();
176 
177                 // head request response
178                 dres = DownloadResponse.getHeadRequestResponse( jnlpres.getMimeType(), jnlpres.getVersionId(),
179                                                                 jnlpres.getLastModified(), cl );
180 
181             }
182             else if ( ifModifiedSince != -1 && ( ifModifiedSince / 1000 ) >= ( jnlpres.getLastModified() / 1000 ) )
183             {
184                 // We divide the value returned by getLastModified here by 1000
185                 // because if protocol is HTTP, last 3 digits will always be 
186                 // zero.  However, if protocol is JNDI, that's not the case.
187                 // so we divide the value by 1000 to remove the last 3 digits
188                 // before comparison
189 
190                 // return 304 not modified if possible
191                 _log.addDebug( "return 304 Not modified" );
192                 dres = DownloadResponse.getNotModifiedResponse();
193 
194             }
195             else
196             {
197 
198                 // Return selected resource
199                 dres = constructResponse( jnlpres, dreq );
200             }
201 
202             dres.sendRespond( response );
203 
204         }
205         catch ( ErrorResponseException ere )
206         {
207             if ( _log.isInformationalLevel() )
208             {
209                 _log.addInformational( "servlet.log.info.badrequest", requestStr );
210             }
211             if ( _log.isDebugLevel() )
212             {
213                 _log.addDebug( "Response: " + ere.toString() );
214             }
215             // Return response from exception
216             ere.getDownloadResponse().sendRespond( response );
217         }
218         catch ( Throwable e )
219         {
220             _log.addFatal( "servlet.log.fatal.internalerror", e );
221             response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
222         }
223     }
224 
225     /**
226      * Make sure that it is a valid request. This is also the place to implement the
227      * reverse IP lookup
228      */
229     private void validateRequest( DownloadRequest dreq )
230         throws ErrorResponseException
231     {
232         String path = dreq.getPath();
233         if ( path.endsWith( ResourceCatalog.VERSION_XML_FILENAME ) || path.indexOf( "__" ) != -1 )
234         {
235             throw new ErrorResponseException( DownloadResponse.getNoContentResponse() );
236         }
237     }
238 
239     /**
240      * Interprets the download request and convert it into a resource that is
241      * part of the Web Archive.
242      */
243     private JnlpResource locateResource( DownloadRequest dreq )
244         throws IOException, ErrorResponseException
245     {
246         if ( dreq.getVersion() == null )
247         {
248             return handleBasicDownload( dreq );
249         }
250         else
251         {
252             return handleVersionRequest( dreq );
253         }
254     }
255 
256     private JnlpResource handleBasicDownload( DownloadRequest dreq )
257         throws ErrorResponseException, IOException
258     {
259         _log.addDebug( "Basic Protocol lookup" );
260         // Do not return directory names for basic protocol
261         if ( dreq.getPath() == null || dreq.getPath().endsWith( "/" ) )
262         {
263             throw new ErrorResponseException( DownloadResponse.getNoContentResponse() );
264         }
265         // Lookup resource
266         JnlpResource jnlpres = new JnlpResource( getServletContext(), dreq.getPath() );
267         if ( !jnlpres.exists() )
268         {
269             throw new ErrorResponseException( DownloadResponse.getNoContentResponse() );
270         }
271         return jnlpres;
272     }
273 
274     private JnlpResource handleVersionRequest( DownloadRequest dreq )
275         throws IOException, ErrorResponseException
276     {
277         _log.addDebug( "Version-based/Extension based lookup" );
278         return _resourceCatalog.lookupResource( dreq );
279     }
280 
281     /**
282      * Given a DownloadPath and a DownloadRequest, it constructs the data stream to return
283      * to the requester
284      */
285     private DownloadResponse constructResponse( JnlpResource jnlpres, DownloadRequest dreq )
286         throws IOException
287     {
288         String path = jnlpres.getPath();
289         if ( jnlpres.isJnlpFile() )
290         {
291             // It is a JNLP file. It need to be macro-expanded, so it is handled differently
292             boolean supportQuery = JarDiffHandler.isJavawsVersion( dreq, "1.5+" );
293             _log.addDebug( "SupportQuery in Href: " + supportQuery );
294 
295             // only support query string in href for 1.5 and above
296             if ( supportQuery )
297             {
298                 return _jnlpFileHandler.getJnlpFileEx( jnlpres, dreq );
299             }
300             else
301             {
302                 return _jnlpFileHandler.getJnlpFile( jnlpres, dreq );
303             }
304         }
305 
306         // Check if a JARDiff can be returned
307         if ( dreq.getCurrentVersionId() != null && jnlpres.isJarFile() )
308         {
309             DownloadResponse response = _jarDiffHandler.getJarDiffEntry( _resourceCatalog, dreq, jnlpres );
310             if ( response != null )
311             {
312                 _log.addInformational( "servlet.log.info.jardiff.response" );
313                 return response;
314             }
315         }
316 
317         // check and see if we can use pack resource
318         JnlpResource jr =
319             new JnlpResource( getServletContext(), jnlpres.getName(), jnlpres.getVersionId(), jnlpres.getOSList(),
320                               jnlpres.getArchList(), jnlpres.getLocaleList(), jnlpres.getPath(),
321                               jnlpres.getReturnVersionId(), dreq.getEncoding() );
322 
323         _log.addDebug( "Real resource returned: " + jr );
324 
325         // Return WAR file resource
326         return DownloadResponse.getFileDownloadResponse( jr.getResource(), jr.getMimeType(), jr.getLastModified(),
327                                                          jr.getReturnVersionId() );
328     }
329 }
330 
331