1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package jnlp.sample.servlet;
38
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.Node;
42 import org.w3c.dom.NodeList;
43
44 import javax.servlet.ServletContext;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpUtils;
47 import javax.xml.parsers.DocumentBuilder;
48 import javax.xml.parsers.DocumentBuilderFactory;
49 import javax.xml.transform.Transformer;
50 import javax.xml.transform.TransformerFactory;
51 import javax.xml.transform.dom.DOMSource;
52 import javax.xml.transform.stream.StreamResult;
53 import java.io.BufferedReader;
54 import java.io.ByteArrayInputStream;
55 import java.io.IOException;
56 import java.io.InputStreamReader;
57 import java.io.StringWriter;
58 import java.net.URL;
59 import java.net.URLConnection;
60 import java.util.Calendar;
61 import java.util.Date;
62 import java.util.HashMap;
63 import java.util.TimeZone;
64
65
66
67
68 public class JnlpFileHandler
69 {
70 private static final String JNLP_MIME_TYPE = "application/x-java-jnlp-file";
71
72 private static final String HEADER_LASTMOD = "Last-Modified";
73
74 private ServletContext _servletContext;
75
76 private Logger _log = null;
77
78 private HashMap _jnlpFiles = null;
79
80
81
82
83 public JnlpFileHandler( ServletContext servletContext, Logger log )
84 {
85 _servletContext = servletContext;
86 _log = log;
87 _jnlpFiles = new HashMap();
88 }
89
90 private static class JnlpFileEntry
91 {
92
93 DownloadResponse _response;
94
95
96 private long _lastModified;
97
98
99 JnlpFileEntry( DownloadResponse response, long lastmodfied )
100 {
101 _response = response;
102 _lastModified = lastmodfied;
103 }
104
105 public DownloadResponse getResponse()
106 {
107 return _response;
108 }
109
110 long getLastModified()
111 {
112 return _lastModified;
113 }
114 }
115
116
117 public synchronized DownloadResponse getJnlpFile( JnlpResource jnlpres, DownloadRequest dreq )
118 throws IOException
119 {
120 String path = jnlpres.getPath();
121 URL resource = jnlpres.getResource();
122 long lastModified = jnlpres.getLastModified();
123
124 _log.addDebug( "lastModified: " + lastModified + " " + new Date( lastModified ) );
125 if ( lastModified == 0 )
126 {
127 _log.addWarning( "servlet.log.warning.nolastmodified", path );
128 }
129
130
131
132 String reqUrl = HttpUtils.getRequestURL( dreq.getHttpRequest() ).toString();
133
134
135 JnlpFileEntry jnlpFile = (JnlpFileEntry) _jnlpFiles.get( reqUrl );
136
137 if ( jnlpFile != null && jnlpFile.getLastModified() == lastModified )
138 {
139
140 return jnlpFile.getResponse();
141 }
142
143
144 long timeStamp = lastModified;
145 String mimeType = _servletContext.getMimeType( path );
146 if ( mimeType == null )
147 {
148 mimeType = JNLP_MIME_TYPE;
149 }
150
151 StringBuilder jnlpFileTemplate = new StringBuilder();
152 URLConnection conn = resource.openConnection();
153 BufferedReader br = new BufferedReader( new InputStreamReader( conn.getInputStream(), "UTF-8" ) );
154 String line = br.readLine();
155 if ( line != null && line.startsWith( "TS:" ) )
156 {
157 timeStamp = parseTimeStamp( line.substring( 3 ) );
158 _log.addDebug( "Timestamp: " + timeStamp + " " + new Date( timeStamp ) );
159 if ( timeStamp == 0 )
160 {
161 _log.addWarning( "servlet.log.warning.notimestamp", path );
162 timeStamp = lastModified;
163 }
164 line = br.readLine();
165 }
166 while ( line != null )
167 {
168 jnlpFileTemplate.append( line );
169 line = br.readLine();
170 }
171
172 String jnlpFileContent = specializeJnlpTemplate( dreq.getHttpRequest(), path, jnlpFileTemplate.toString() );
173
174
175 byte[] byteContent = jnlpFileContent.getBytes( "UTF-8" );
176
177
178 DownloadResponse resp =
179 DownloadResponse.getFileDownloadResponse( byteContent, mimeType, timeStamp, jnlpres.getReturnVersionId() );
180 jnlpFile = new JnlpFileEntry( resp, lastModified );
181 _jnlpFiles.put( reqUrl, jnlpFile );
182
183 return resp;
184 }
185
186
187 public synchronized DownloadResponse getJnlpFileEx( JnlpResource jnlpres, DownloadRequest dreq )
188 throws IOException
189 {
190 String path = jnlpres.getPath();
191 URL resource = jnlpres.getResource();
192 long lastModified = jnlpres.getLastModified();
193
194 _log.addDebug( "lastModified: " + lastModified + " " + new Date( lastModified ) );
195 if ( lastModified == 0 )
196 {
197 _log.addWarning( "servlet.log.warning.nolastmodified", path );
198 }
199
200
201
202 String reqUrl = HttpUtils.getRequestURL( dreq.getHttpRequest() ).toString();
203
204 if ( dreq.getQuery() != null )
205 {
206 reqUrl += dreq.getQuery();
207 }
208
209
210 JnlpFileEntry jnlpFile = (JnlpFileEntry) _jnlpFiles.get( reqUrl );
211
212 if ( jnlpFile != null && jnlpFile.getLastModified() == lastModified )
213 {
214
215 return jnlpFile.getResponse();
216 }
217
218
219 long timeStamp = lastModified;
220 String mimeType = _servletContext.getMimeType( path );
221 if ( mimeType == null )
222 {
223 mimeType = JNLP_MIME_TYPE;
224 }
225
226 StringBuilder jnlpFileTemplate = new StringBuilder();
227 URLConnection conn = resource.openConnection();
228 BufferedReader br = new BufferedReader( new InputStreamReader( conn.getInputStream(), "UTF-8" ) );
229 String line = br.readLine();
230 if ( line != null && line.startsWith( "TS:" ) )
231 {
232 timeStamp = parseTimeStamp( line.substring( 3 ) );
233 _log.addDebug( "Timestamp: " + timeStamp + " " + new Date( timeStamp ) );
234 if ( timeStamp == 0 )
235 {
236 _log.addWarning( "servlet.log.warning.notimestamp", path );
237 timeStamp = lastModified;
238 }
239 line = br.readLine();
240 }
241 while ( line != null )
242 {
243 jnlpFileTemplate.append( line );
244 line = br.readLine();
245 }
246
247 String jnlpFileContent = specializeJnlpTemplate( dreq.getHttpRequest(), path, jnlpFileTemplate.toString() );
248
249
250
251
252 String query = dreq.getQuery();
253 String testJRE = dreq.getTestJRE();
254 _log.addDebug( "Double check query string: " + query );
255
256
257
258 if ( query != null )
259 {
260 byte[] cb = jnlpFileContent.getBytes( "UTF-8" );
261 ByteArrayInputStream bis = new ByteArrayInputStream( cb );
262 try
263 {
264 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
265 DocumentBuilder builder = factory.newDocumentBuilder();
266 Document document = builder.parse( bis );
267 if ( document != null && document.getNodeType() == Node.DOCUMENT_NODE )
268 {
269 boolean modified = false;
270 Element root = document.getDocumentElement();
271
272 if ( root.hasAttribute( "href" ) )
273 {
274 String href = root.getAttribute( "href" );
275 root.setAttribute( "href", href + "?" + query );
276 modified = true;
277 }
278
279 if ( testJRE != null )
280 {
281 NodeList j2seNL = root.getElementsByTagName( "j2se" );
282 if ( j2seNL != null )
283 {
284 Element j2se = (Element) j2seNL.item( 0 );
285 String ver = j2se.getAttribute( "version" );
286 if ( ver.length() > 0 )
287 {
288 j2se.setAttribute( "version", testJRE );
289 modified = true;
290 }
291 }
292 }
293 TransformerFactory tFactory = TransformerFactory.newInstance();
294 Transformer transformer = tFactory.newTransformer();
295 DOMSource source = new DOMSource( document );
296 StringWriter sw = new StringWriter();
297 StreamResult result = new StreamResult( sw );
298 transformer.transform( source, result );
299 jnlpFileContent = sw.toString();
300 _log.addDebug( "Converted jnlpFileContent: " + jnlpFileContent );
301
302 if ( modified )
303 {
304 timeStamp = new java.util.Date().getTime();
305 _log.addDebug( "Last modified on the fly: " + timeStamp );
306 }
307 }
308 }
309 catch ( Exception e )
310 {
311 _log.addDebug( e.toString(), e );
312 }
313 }
314
315
316 byte[] byteContent = jnlpFileContent.getBytes( "UTF-8" );
317
318
319 DownloadResponse resp =
320 DownloadResponse.getFileDownloadResponse( byteContent, mimeType, timeStamp, jnlpres.getReturnVersionId() );
321 jnlpFile = new JnlpFileEntry( resp, lastModified );
322 _jnlpFiles.put( reqUrl, jnlpFile );
323
324 return resp;
325 }
326
327
328
329
330
331
332 private String specializeJnlpTemplate( HttpServletRequest request, String respath, String jnlpTemplate )
333 {
334 String urlprefix = getUrlPrefix( request );
335 int idx = respath.lastIndexOf( '/' );
336 String name = respath.substring( idx + 1 );
337 String codebase = respath.substring( 0, idx + 1 );
338 jnlpTemplate = substitute( jnlpTemplate, "$$name", name );
339
340 jnlpTemplate = substitute( jnlpTemplate, "$$hostname", request.getServerName() );
341 jnlpTemplate = substitute( jnlpTemplate, "$$codebase", urlprefix + request.getContextPath() + codebase );
342 jnlpTemplate = substitute( jnlpTemplate, "$$context", urlprefix + request.getContextPath() );
343
344 jnlpTemplate = substitute( jnlpTemplate, "$$site", urlprefix );
345 return jnlpTemplate;
346 }
347
348
349 private String getUrlPrefix( HttpServletRequest req )
350 {
351 StringBuilder url = new StringBuilder();
352 String scheme = req.getScheme();
353 int port = req.getServerPort();
354 url.append( scheme );
355 url.append( "://" );
356 url.append( req.getServerName() );
357 if ( ( scheme.equals( "http" ) && port != 80 ) || ( scheme.equals( "https" ) && port != 443 ) )
358 {
359 url.append( ':' );
360 url.append( req.getServerPort() );
361 }
362 return url.toString();
363 }
364
365 private String substitute( String target, String key, String value )
366 {
367 int start = 0;
368 do
369 {
370 int idx = target.indexOf( key, start );
371 if ( idx == -1 )
372 {
373 return target;
374 }
375 target = target.substring( 0, idx ) + value + target.substring( idx + key.length() );
376 start = idx + value.length();
377 }
378 while ( true );
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 private long parseTimeStamp( String timestamp )
401 {
402 int YYYY = 0;
403 int MM = 0;
404 int DD = 0;
405 int hh = 0;
406 int mm = 0;
407 int ss = 0;
408
409 timestamp = timestamp.trim();
410 try
411 {
412
413 if ( matchPattern( "####-##-## ##:##", timestamp ) )
414 {
415 YYYY = getIntValue( timestamp, 0, 4 );
416 MM = getIntValue( timestamp, 5, 7 );
417 DD = getIntValue( timestamp, 8, 10 );
418 hh = getIntValue( timestamp, 11, 13 );
419 mm = getIntValue( timestamp, 14, 16 );
420 timestamp = timestamp.substring( 16 );
421 if ( matchPattern( ":##", timestamp ) )
422 {
423 ss = getIntValue( timestamp, 1, 3 );
424 timestamp = timestamp.substring( 3 );
425 }
426 }
427 else if ( matchPattern( "############", timestamp ) )
428 {
429 YYYY = getIntValue( timestamp, 0, 4 );
430 MM = getIntValue( timestamp, 4, 6 );
431 DD = getIntValue( timestamp, 6, 8 );
432 hh = getIntValue( timestamp, 8, 10 );
433 mm = getIntValue( timestamp, 10, 12 );
434 timestamp = timestamp.substring( 12 );
435 if ( matchPattern( "##", timestamp ) )
436 {
437 ss = getIntValue( timestamp, 0, 2 );
438 timestamp = timestamp.substring( 2 );
439 }
440 }
441 else
442 {
443
444 return 0;
445 }
446 }
447 catch ( NumberFormatException e )
448 {
449
450 return 0;
451 }
452
453 String timezone = null;
454
455 timestamp = timestamp.trim();
456 if ( timestamp.equalsIgnoreCase( "Z" ) )
457 {
458 timezone = "GMT";
459 }
460 else if ( timestamp.startsWith( "+" ) || timestamp.startsWith( "-" ) )
461 {
462 timezone = "GMT" + timestamp;
463 }
464
465 if ( timezone == null )
466 {
467
468 Calendar cal = Calendar.getInstance();
469 cal.set( YYYY, MM - 1, DD, hh, mm, ss );
470 return cal.getTime().getTime();
471 }
472 else
473 {
474
475 Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( timezone ) );
476 cal.set( YYYY, MM - 1, DD, hh, mm, ss );
477 return cal.getTime().getTime();
478 }
479 }
480
481 private int getIntValue( String key, int start, int end )
482 {
483 return Integer.parseInt( key.substring( start, end ) );
484 }
485
486 private boolean matchPattern( String pattern, String key )
487 {
488
489 if ( key.length() < pattern.length() )
490 {
491 return false;
492 }
493 for ( int i = 0; i < pattern.length(); i++ )
494 {
495 char format = pattern.charAt( i );
496 char ch = key.charAt( i );
497 if ( !( ( format == '#' && Character.isDigit( ch ) ) || ( format == ch ) ) )
498 {
499 return false;
500 }
501 }
502 return true;
503 }
504 }
505
506