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 jnlp.sample.util.VersionID;
40 import jnlp.sample.util.VersionString;
41 import org.w3c.dom.Document;
42 import org.xml.sax.SAXParseException;
43
44 import javax.servlet.ServletContext;
45 import javax.xml.parsers.DocumentBuilder;
46 import javax.xml.parsers.DocumentBuilderFactory;
47 import java.io.BufferedInputStream;
48 import java.io.File;
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.List;
52
53 public class ResourceCatalog
54 {
55 public static final String VERSION_XML_FILENAME = "version.xml";
56
57 private Logger _log = null;
58
59 private ServletContext _servletContext = null;
60
61 private HashMap _entries;
62
63
64
65
66
67 static private class PathEntries
68 {
69
70 private List _versionXmlList;
71
72 private List _directoryList;
73
74 private List _platformList;
75
76
77 private long _lastModified;
78
79 public PathEntries( List versionXmlList, List directoryList, List platformList, long lastModified )
80 {
81 _versionXmlList = versionXmlList;
82 _directoryList = directoryList;
83 _platformList = platformList;
84 _lastModified = lastModified;
85 }
86
87
88 public void setDirectoryList( List dirList )
89 {
90 _directoryList = dirList;
91 }
92
93 public List getVersionXmlList()
94 {
95 return _versionXmlList;
96 }
97
98 public List getDirectoryList()
99 {
100 return _directoryList;
101 }
102
103 public List getPlatformList()
104 {
105 return _platformList;
106 }
107
108 public long getLastModified()
109 {
110 return _lastModified;
111 }
112 }
113
114 public ResourceCatalog( ServletContext servletContext, Logger log )
115 {
116 _entries = new HashMap();
117 _servletContext = servletContext;
118 _log = log;
119 }
120
121
122 public JnlpResource lookupResource( DownloadRequest dreq )
123 throws ErrorResponseException
124 {
125
126 String path = dreq.getPath();
127 String name = null;
128 String dir = null;
129 int idx = path.lastIndexOf( '/' );
130 if ( idx == -1 )
131 {
132 name = path;
133 }
134 else
135 {
136 name = path.substring( idx + 1 );
137 dir = path.substring( 0, idx + 1 );
138 }
139
140
141 PathEntries pentries = (PathEntries) _entries.get( dir );
142 JnlpResource xmlVersionResPath = new JnlpResource( _servletContext, dir + VERSION_XML_FILENAME );
143 if ( pentries == null ||
144 ( xmlVersionResPath.exists() && xmlVersionResPath.getLastModified() > pentries.getLastModified() ) )
145 {
146 _log.addInformational( "servlet.log.scandir", dir );
147 List dirList = scanDirectory( dir, dreq );
148
149 List versionList = new ArrayList();
150 List platformList = new ArrayList();
151 parseVersionXML( versionList, platformList, dir, xmlVersionResPath );
152 pentries = new PathEntries( versionList, dirList, platformList, xmlVersionResPath.getLastModified() );
153 _entries.put( dir, pentries );
154 }
155
156
157 JnlpResource[] result = new JnlpResource[1];
158
159 if ( dreq.isPlatformRequest() )
160 {
161 int sts = findMatch( pentries.getPlatformList(), name, dreq, result );
162 if ( sts != DownloadResponse.STS_00_OK )
163 {
164 throw new ErrorResponseException( DownloadResponse.getJnlpErrorResponse( sts ) );
165 }
166 }
167 else
168 {
169
170 int sts1 = findMatch( pentries.getVersionXmlList(), name, dreq, result );
171 if ( sts1 != DownloadResponse.STS_00_OK )
172 {
173
174 int sts2 = findMatch( pentries.getDirectoryList(), name, dreq, result );
175 if ( sts2 != DownloadResponse.STS_00_OK )
176 {
177
178
179
180 pentries.setDirectoryList( scanDirectory( dir, dreq ) );
181 sts2 = findMatch( pentries.getDirectoryList(), name, dreq, result );
182
183 if ( sts2 != DownloadResponse.STS_00_OK )
184 {
185
186 throw new ErrorResponseException(
187 DownloadResponse.getJnlpErrorResponse( Math.max( sts1, sts2 ) ) );
188 }
189 }
190 }
191 }
192 return result[0];
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207 public int findMatch( List list, String name, DownloadRequest dreq, JnlpResource[] result )
208 {
209 if ( list == null )
210 {
211 return DownloadResponse.ERR_10_NO_RESOURCE;
212 }
213
214 VersionID bestVersionId = null;
215 int error = DownloadResponse.ERR_10_NO_RESOURCE;
216 VersionString vs = new VersionString( dreq.getVersion() );
217
218 for ( Object aList : list )
219 {
220 JnlpResource respath = (JnlpResource) aList;
221 VersionID vid = new VersionID( respath.getVersionId() );
222 int sts = matchEntry( name, vs, dreq, respath, vid );
223 if ( sts == DownloadResponse.STS_00_OK )
224 {
225 if ( result[0] == null || vid.isGreaterThan( bestVersionId ) )
226 {
227 result[0] = respath;
228 bestVersionId = vid;
229 }
230 }
231 else
232 {
233 error = Math.max( error, sts );
234 }
235 }
236 return ( result[0] != null ) ? DownloadResponse.STS_00_OK : error;
237 }
238
239 public int matchEntry( String name, VersionString vs, DownloadRequest dreq, JnlpResource jnlpres, VersionID vid )
240 {
241 if ( !name.equals( jnlpres.getName() ) )
242 {
243 return DownloadResponse.ERR_10_NO_RESOURCE;
244 }
245 if ( !vs.contains( vid ) )
246 {
247 return DownloadResponse.ERR_11_NO_VERSION;
248 }
249 if ( !prefixMatchLists( jnlpres.getOSList(), dreq.getOS() ) )
250 {
251 return DownloadResponse.ERR_20_UNSUP_OS;
252 }
253 if ( !prefixMatchLists( jnlpres.getArchList(), dreq.getArch() ) )
254 {
255 return DownloadResponse.ERR_21_UNSUP_ARCH;
256 }
257 if ( !prefixMatchLists( jnlpres.getLocaleList(), dreq.getLocale() ) )
258 {
259 return DownloadResponse.ERR_22_UNSUP_LOCALE;
260 }
261 return DownloadResponse.STS_00_OK;
262 }
263
264
265 private static boolean prefixMatchStringList( String[] prefixList, String target )
266 {
267
268 if ( prefixList == null )
269 {
270 return true;
271 }
272
273 if ( target == null )
274 {
275 return false;
276 }
277 for ( String aPrefixList : prefixList )
278 {
279 if ( target.startsWith( aPrefixList ) )
280 {
281 return true;
282 }
283 }
284 return false;
285 }
286
287
288
289
290 public boolean prefixMatchLists( String[] prefixes, String[] keys )
291 {
292
293
294 if ( prefixes == null )
295 {
296 return true;
297 }
298
299
300 if ( keys == null )
301 {
302 return false;
303 }
304
305 for ( String key : keys )
306 {
307 if ( prefixMatchStringList( prefixes, key ) )
308 {
309 return true;
310 }
311 }
312 return false;
313 }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 private String jnlpGetPath( DownloadRequest dreq )
333 {
334
335
336
337 String path = dreq.getPath();
338 String filename = path.substring( path.lastIndexOf( "/" ) + 1 );
339 path = path.substring( 0, path.lastIndexOf( "/" ) + 1 );
340 String name = filename;
341 String ext = null;
342
343 if ( filename.lastIndexOf( "." ) != -1 )
344 {
345 ext = filename.substring( filename.lastIndexOf( "." ) + 1 );
346
347 filename = filename.substring( 0, filename.lastIndexOf( "." ) );
348
349 }
350 if ( dreq.getVersion() != null )
351 {
352 filename += "__V" + dreq.getVersion();
353 }
354
355 String[] temp = dreq.getOS();
356
357 if ( temp != null )
358 {
359 for ( String aTemp : temp )
360 {
361 filename += "__O" + aTemp;
362 }
363 }
364
365 temp = dreq.getArch();
366
367 if ( temp != null )
368 {
369 for ( String aTemp : temp )
370 {
371 filename += "__A" + aTemp;
372 }
373 }
374 temp = dreq.getLocale();
375
376 if ( temp != null )
377 {
378 for ( String aTemp : temp )
379 {
380 filename += "__L" + aTemp;
381 }
382 }
383
384 if ( ext != null )
385 {
386 filename += "." + ext;
387 }
388
389 path += filename;
390
391 return path;
392 }
393
394 public List scanDirectory( String dirPath, DownloadRequest dreq )
395 {
396 ArrayList list = new ArrayList();
397
398
399 if ( _servletContext.getRealPath( dirPath ) == null )
400 {
401 String path = jnlpGetPath( dreq );
402
403 String name = dreq.getPath().substring( path.lastIndexOf( "/" ) + 1 );
404
405 JnlpResource jnlpres =
406 new JnlpResource( _servletContext, name, dreq.getVersion(), dreq.getOS(), dreq.getArch(),
407 dreq.getLocale(), path, dreq.getVersion() );
408
409
410 if ( jnlpres.getResource() == null )
411 {
412 return null;
413 }
414
415 list.add( jnlpres );
416 return list;
417 }
418 File dir = new File( _servletContext.getRealPath( dirPath ) );
419 _log.addDebug( "File directory: " + dir );
420 if ( dir.exists() && dir.isDirectory() )
421 {
422 File[] entries = dir.listFiles();
423 for ( File entry : entries )
424 {
425 JnlpResource jnlpres = parseFileEntry( dirPath, entry.getName() );
426 if ( jnlpres != null )
427 {
428 if ( _log.isDebugLevel() )
429 {
430 _log.addDebug( "Read file resource: " + jnlpres );
431 }
432 list.add( jnlpres );
433 }
434 }
435 }
436 return list;
437 }
438
439 private JnlpResource parseFileEntry( String dir, String filename )
440 {
441 int idx = filename.indexOf( "__" );
442 if ( idx == -1 )
443 {
444 return null;
445 }
446
447
448 String name = filename.substring( 0, idx );
449 String rest = filename.substring( idx );
450
451
452 idx = rest.lastIndexOf( '.' );
453 String extension = "";
454 if ( idx != -1 )
455 {
456 extension = rest.substring( idx );
457 rest = rest.substring( 0, idx );
458 }
459
460
461 String versionId = null;
462 ArrayList osList = new ArrayList();
463 ArrayList archList = new ArrayList();
464 ArrayList localeList = new ArrayList();
465 while ( rest.length() > 0 )
466 {
467
468 if ( !rest.startsWith( "__" ) )
469 {
470 return null;
471 }
472 rest = rest.substring( 2 );
473
474 char option = rest.charAt( 0 );
475 idx = rest.indexOf( "__" );
476 String arg = null;
477 if ( idx == -1 )
478 {
479 arg = rest.substring( 1 );
480 rest = "";
481 }
482 else
483 {
484 arg = rest.substring( 1, idx );
485 rest = rest.substring( idx );
486 }
487 switch ( option )
488 {
489 case 'V':
490 versionId = arg;
491 break;
492 case 'O':
493 osList.add( arg );
494 break;
495 case 'A':
496 archList.add( arg );
497 break;
498 case 'L':
499 localeList.add( arg );
500 break;
501 default:
502 return null;
503 }
504 }
505
506 return new JnlpResource( _servletContext, name + extension,
507 versionId, listToStrings( osList ), listToStrings( archList ),
508 listToStrings( localeList ), dir + filename,
509 versionId );
510 }
511
512 private String[] listToStrings( List list )
513 {
514 if ( list.size() == 0 )
515 {
516 return null;
517 }
518 return (String[]) list.toArray( new String[list.size()] );
519 }
520
521
522 private void parseVersionXML( final List versionList, final List platformList, final String dir,
523 final JnlpResource versionRes )
524 {
525 if ( !versionRes.exists() )
526 {
527 return;
528 }
529
530
531 XMLNode root = null;
532 try
533 {
534 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
535 DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
536 Document doc = docBuilder.parse( new BufferedInputStream( versionRes.getResource().openStream() ) );
537 doc.getDocumentElement().normalize();
538
539
540
541
542
543 root = XMLParsing.convert( doc.getDocumentElement() );
544 }
545 catch ( SAXParseException err )
546 {
547 _log.addWarning( "servlet.log.warning.xml.parsing", versionRes.getPath(),
548 Integer.toString( err.getLineNumber() ), err.getMessage() );
549 return;
550 }
551 catch ( Throwable t )
552 {
553 _log.addWarning( "servlet.log.warning.xml.reading", versionRes.getPath(), t );
554 return;
555 }
556
557
558 if ( !root.getName().equals( "jnlp-versions" ) )
559 {
560 _log.addWarning( "servlet.log.warning.xml.missing-jnlp", versionRes.getPath() );
561 return;
562 }
563
564
565 XMLParsing.visitElements( root, "<resource>", new XMLParsing.ElementVisitor()
566 {
567 public void visitElement( XMLNode node )
568 {
569 XMLNode pattern = XMLParsing.findElementPath( node, "<pattern>" );
570 if ( pattern == null )
571 {
572 _log.addWarning( "servlet.log.warning.xml.missing-pattern", versionRes.getPath() );
573 }
574 else
575 {
576
577 String name = XMLParsing.getElementContent( pattern, "<name>", "" );
578 String versionId = XMLParsing.getElementContent( pattern, "<version-id>" );
579 String[] os = XMLParsing.getMultiElementContent( pattern, "<os>" );
580 String[] arch = XMLParsing.getMultiElementContent( pattern, "<arch>" );
581 String[] locale = XMLParsing.getMultiElementContent( pattern, "<locale>" );
582
583 String file = XMLParsing.getElementContent( node, "<file>" );
584 if ( versionId == null || file == null )
585 {
586 _log.addWarning( "servlet.log.warning.xml.missing-elems", versionRes.getPath() );
587 }
588 else
589 {
590 JnlpResource res =
591 new JnlpResource( _servletContext, name, versionId, os, arch, locale, dir + file,
592 versionId );
593 if ( res.exists() )
594 {
595 versionList.add( res );
596 if ( _log.isDebugLevel() )
597 {
598 _log.addDebug( "Read resource: " + res );
599 }
600 }
601 else
602 {
603 _log.addWarning( "servlet.log.warning.missing-file", file, versionRes.getPath() );
604 }
605 }
606 }
607 }
608 } );
609
610
611 XMLParsing.visitElements( root, "<platform>", new XMLParsing.ElementVisitor()
612 {
613 public void visitElement( XMLNode node )
614 {
615 XMLNode pattern = XMLParsing.findElementPath( node, "<pattern>" );
616 if ( pattern == null )
617 {
618 _log.addWarning( "servlet.log.warning.xml.missing-pattern", versionRes.getPath() );
619 }
620 else
621 {
622
623 String name = XMLParsing.getElementContent( pattern, "<name>", "" );
624 String versionId = XMLParsing.getElementContent( pattern, "<version-id>" );
625 String[] os = XMLParsing.getMultiElementContent( pattern, "<os>" );
626 String[] arch = XMLParsing.getMultiElementContent( pattern, "<arch>" );
627 String[] locale = XMLParsing.getMultiElementContent( pattern, "<locale>" );
628
629 String file = XMLParsing.getElementContent( node, "<file>" );
630 String productId = XMLParsing.getElementContent( node, "<product-version-id>" );
631
632 if ( versionId == null || file == null || productId == null )
633 {
634 _log.addWarning( "servlet.log.warning.xml.missing-elems2", versionRes.getPath() );
635 }
636 else
637 {
638 JnlpResource res =
639 new JnlpResource( _servletContext, name, versionId, os, arch, locale, dir + file,
640 productId );
641 if ( res.exists() )
642 {
643 platformList.add( res );
644 if ( _log.isDebugLevel() )
645 {
646 _log.addDebug( "Read platform resource: " + res );
647 }
648 }
649 else
650 {
651 _log.addWarning( "servlet.log.warning.missing-file", file, versionRes.getPath() );
652 }
653 }
654 }
655 }
656 } );
657 }
658 }
659
660