jnlp 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/jnlp/jnlp.rb ADDED
@@ -0,0 +1,1075 @@
1
+ # :main: Jnlp::Jnlp
2
+ # :title: Jnlp::Jnlp RDoc
3
+ #
4
+ # to regenerate and display this rdoc:
5
+ # rdoc -U -SN jnlp.rb otrunk.rb ; open doc/index.html
6
+ #
7
+ require 'rubygems'
8
+ require 'open-uri'
9
+ require 'hpricot'
10
+ require 'fileutils'
11
+ require 'net/http'
12
+ require 'date'
13
+
14
+ if RUBY_PLATFORM =~ /java/
15
+ include Java
16
+ import java.util.jar.JarInputStream unless defined? JarInputStream
17
+ import java.io.FileInputStream unless defined? FileInputStream
18
+ import java.net.URL unless defined? URL
19
+ end
20
+
21
+ unless Net::HTTP::Get.new('/')['User-Agent'] # unless a 'User-Agent' is already defined add one
22
+ #
23
+ # Monkey patch Net::HTTP so it always provides a User-Agent
24
+ # if the request doesn't already specify one.
25
+ #
26
+ # This compensates for a bug in Tomcat/webstart servers which
27
+ # throw a 500 error for requests w/o a User-Agent header.
28
+ #
29
+ class Net::HTTPGenericRequest
30
+ include Net::HTTPHeader
31
+ def initialize(m, reqbody, resbody, path, initheader = nil)
32
+ @method = m
33
+ @request_has_body = reqbody
34
+ @response_has_body = resbody
35
+ raise ArgumentError, "HTTP request path is empty" if path.empty?
36
+ @path = path
37
+ initialize_http_header initheader
38
+ self['Accept'] ||= '*/*'
39
+ self['User-Agent'] ||= 'Ruby' # this is the new line
40
+ @body = nil
41
+ @body_stream = nil
42
+ end
43
+ end
44
+ end
45
+
46
+ module Jnlp #:nodoc:
47
+ #
48
+ # Property objects encapsulate <property> elements present in a
49
+ # Java Web Start Jnlp <resources> element.
50
+ #
51
+ class Property
52
+ #
53
+ # Contains the Hpricot element parsed from the orginal Jnlp
54
+ # that was used to create the Property
55
+ #
56
+ attr_reader :property
57
+ #
58
+ # Contains the name of the Property
59
+ #
60
+ #
61
+ # Example:
62
+ #
63
+ # "maven.jnlp.version"
64
+ #
65
+ attr_reader :name
66
+ #
67
+ # Contains the value of the Property
68
+ #
69
+ #
70
+ # Example:
71
+ #
72
+ # "all-otrunk-snapshot-0.1.0-20080310.211515"
73
+ #
74
+ attr_reader :value
75
+ #
76
+ # Contains the value of the os attribute in the
77
+ # parent <resources> element that contains this property
78
+ # if the attribute was set in the parent.
79
+ # Example:
80
+ #
81
+ # "Mac OS X"
82
+ #
83
+ attr_reader :os
84
+ #
85
+ # Creates a new Jnlp::Property object.
86
+ # * _prop_: the Hpricot parsing of the specific jnlp/resources/property element
87
+ # * _os_: optional: include it if the resources parent element that contains the property has this attribute set
88
+ #
89
+ def initialize(prop, os=nil)
90
+ @property = prop
91
+ @name = prop['name']
92
+ @value = prop['value']
93
+ @os = os
94
+ end
95
+ end
96
+ #
97
+ # Icon objects encapsulate the <icon> element optionally present in
98
+ # the Java Web Start jnlp <information> element.
99
+ #
100
+ class Icon
101
+ #
102
+ # Contains the Hpricot element parsed from the orginal Jnlp
103
+ # that was used to create the Icon
104
+ #
105
+ attr_reader :icon
106
+ #
107
+ # Contains the href attribute of the <icon> element.
108
+ #
109
+ # Example:
110
+ #
111
+ # "http://itsi.concord.org/images/itsi-logo-64x64.png"
112
+ #
113
+ attr_reader :href
114
+ #
115
+ # Contains the height attribute of the <icon> element
116
+ #
117
+ # Example:
118
+ #
119
+ # 64
120
+ #
121
+ attr_reader :height
122
+ #
123
+ # Contains the width attribute of the <icon> element
124
+ #
125
+ # Example:
126
+ #
127
+ # 64
128
+ #
129
+ attr_reader :width
130
+ #
131
+ # Creates a new Icon object
132
+ # * _icon_: pass in a Hpricot parse of the <icon> element
133
+ #
134
+ def initialize(icon)
135
+ @icon = icon
136
+ @href = icon.attr('href')
137
+ @height = icon.attr('height').to_i
138
+ @width = icon.attr('width').to_i
139
+ end
140
+ end
141
+ #
142
+ # Jnlp::Resource objects encapsulate both jar and nativelib elements present in a
143
+ # Java Web Start Jnlp <resources> element.
144
+ #
145
+ class Resource
146
+ #
147
+ # Contains the Hpricot element parsed from the orginal Jnlp
148
+ # that was used to create the resource
149
+ #
150
+ attr_reader :resource
151
+ #
152
+ # Contains the path to the resource taken from the href attribute
153
+ #
154
+ # Example:
155
+ #
156
+ # "org/concord/httpclient/" || ""
157
+ #
158
+ attr_reader :href_path
159
+ #
160
+ # Contains the kind of the resource
161
+ #
162
+ # Example:
163
+ #
164
+ # "jar" || "nativelib"
165
+ #
166
+ attr_reader :kind
167
+ #
168
+ # Contains the base name of the resource
169
+ #
170
+ # Example:
171
+ #
172
+ # "httpclient"
173
+ #
174
+ attr_reader :name
175
+ #
176
+ # Contains the Java Web Start specification of the OS for
177
+ # the <resources> parent of this resource if the attribute
178
+ # value exists
179
+ #
180
+ # Example:
181
+ #
182
+ # ""
183
+ #
184
+ attr_reader :os
185
+ #
186
+ # Contains the href attribute of the resource
187
+ #
188
+ # Example:
189
+ #
190
+ # "net/sf/sail/webstart-proxy/jetty-proxy/jetty-proxy.jar"
191
+ #
192
+ attr_reader :href
193
+ #
194
+ # Contains the url reference to the resource
195
+ #
196
+ # Example:
197
+ #
198
+ # "http://jnlp.concord.org/dev/org/concord/httpclient/httpclient.jar?version-id=0.1.0-20071212.220020-17"
199
+ #
200
+ attr_reader :url
201
+ #
202
+ # Contains the url reference to the gzipped pack200 version of the resource
203
+ #
204
+ # Example:
205
+ #
206
+ # "http://jnlp.concord.org/dev/org/concord/httpclient/httpclient__V0.1.0-20071212.220020-17.jar.pack.gz"
207
+ #
208
+ attr_reader :url_pack_gz
209
+ #
210
+ # Contains the filename of the resource
211
+ #
212
+ # Example:
213
+ #
214
+ # "httpclient__V0.1.0-20071212.220020-17.jar"
215
+ #
216
+ attr_reader :filename
217
+ #
218
+ # Contains the filename of the gzipped pack200 version of the resource
219
+ #
220
+ # Example:
221
+ #
222
+ # "httpclient__V0.1.0-20071212.220020-17.jar.pack.gz"
223
+ #
224
+ attr_reader :filename_pack_gz
225
+ #
226
+ # Contains the size of the resource
227
+ #
228
+ # Example:
229
+ #
230
+ # 294248
231
+ #
232
+ attr_reader :size
233
+ #
234
+ # Contains the size of the gzipped pack200 version of the resource
235
+ #
236
+ # Example:
237
+ #
238
+ # 112213
239
+ #
240
+ attr_reader :size_pack_gz
241
+ #
242
+ # Contains boolean value indicating whether the signature of the
243
+ # cached local copy of the resource verified successfully
244
+ #
245
+ # The value is nil if no local cache has been created.
246
+ #
247
+ # Example:
248
+ #
249
+ # true || false || nil
250
+ #
251
+ attr_reader :signature_verified
252
+ #
253
+ # Contains the absolute local path of cache directory
254
+ #
255
+ # Example:
256
+ #
257
+ # "/Users/stephen/dev/jetty-jnlp-proxy/cache"
258
+ #
259
+ attr_reader :local_cache_dir
260
+ #
261
+ # Contains the relative local path of the resource
262
+ #
263
+ # Example:
264
+ #
265
+ # "net/sf/sail/webstart-proxy/jetty-proxy/jetty-proxy__V0.1.0-20080318.093629-72.jar"
266
+ #
267
+ attr_reader :relative_local_path
268
+ #
269
+ # Contains the absolute local path of the resource
270
+ #
271
+ # Example:
272
+ #
273
+ # "/Users/stephen/dev/jetty-jnlp-proxy/cache/net/sf/sail/webstart-proxy/jetty-proxy/jetty-proxy__V0.1.0-20080318.093629-72.jar"
274
+ #
275
+ attr_reader :local_path
276
+ #
277
+ # Contains the relative local path of the resource
278
+ #
279
+ # Example:
280
+ #
281
+ # "net/sf/sail/webstart-proxy/jetty-proxy/jetty-proxy__V0.1.0-20080318.093629-72.jar.pack.gz"
282
+ #
283
+ attr_reader :relative_local_path_pack_gz
284
+ #
285
+ # Contains the absolute local path of the resource
286
+ #
287
+ # Example:
288
+ #
289
+ # "/Users/stephen/dev/jetty-jnlp-proxy/cache/net/sf/sail/webstart-proxy/jetty-proxy/jetty-proxy__V0.1.0-20080318.093629-72.jar.pack.gz"
290
+ #
291
+ attr_reader :local_path_pack_gz
292
+ #
293
+ # Contains the version string of the resource
294
+ #
295
+ # Example:
296
+ #
297
+ # "0.1.0-20080318.093629-72"
298
+ #
299
+ attr_reader :version_str
300
+ #
301
+ # Contains the version of the resource
302
+ #
303
+ # Example:
304
+ #
305
+ # "0.1.0"
306
+ #
307
+ attr_reader :version
308
+ #
309
+ # Contains the date string of the resource
310
+ #
311
+ # Example:
312
+ #
313
+ # "20080318.093629"
314
+ #
315
+ attr_reader :date_str
316
+ #
317
+ # Contains a Ruby DateTime object representation of the resource's date string
318
+ #
319
+ # Example:
320
+ #
321
+ # #<DateTime: 85338394589/86400,0,2299161>
322
+ #
323
+ attr_reader :date_time
324
+ #
325
+ # Contains the revision of the resource
326
+ #
327
+ # Example:
328
+ #
329
+ # 72
330
+ #
331
+ attr_reader :revision
332
+ def initialize(res, codebase, os)
333
+ @resource = res
334
+ @kind = res.name
335
+ @href = res['href']
336
+ @href_path = File.dirname(@href)
337
+ if @href_path == '.'
338
+ @href_path = ''
339
+ else
340
+ @href_path = @href_path + '/'
341
+ end
342
+ @name = File.basename(@href).chomp('.jar')
343
+ @version_str = res['version']
344
+ if @version_str
345
+ @suffix = "__V#{@version_str}.jar"
346
+ else
347
+ @suffix = ".jar"
348
+ end
349
+ @filename = "#{@name}#{@suffix}"
350
+ @filename_pack = @filename + ".pack"
351
+ @filename_pack_gz = @filename_pack + ".gz"
352
+ # example: "0.1.0-20070926.155656-107"
353
+ # but ...
354
+ # some version strings just look like this: "0.1.0"
355
+ # or this: "2.1.7-r2"
356
+ @url = "#{codebase}/#{@href_path}#{@name}.jar"
357
+ @url << "?version-id=#{@version_str}" if @version_str
358
+ # example: data-util__V0.1.0-20070926.155656-107.jar.pack
359
+ # @url_pack = "#{codebase}/#{@href_path}#{@filename}.pack"
360
+ # example: data-util__V0.1.0-20070926.155656-107.jar.pack.gz
361
+ @url_pack_gz = "#{codebase}/#{@href_path}#{@filename}.pack.gz"
362
+ if @version_str && @version_str[/-[\d]{8}.[\d]{6}-/] && version_data = /(.*)-(.*)-(.*)/.match(@version_str)
363
+ @version = version_data[1]
364
+ @revision = version_data[3] && version_data[3].to_i
365
+ @date_str = version_data[2] # example: "20070926.155656"
366
+ d, t = @date_str.scan(/\d+/) # => ["20070926", "155656"]
367
+ d1 = "#{d[0..3]}-#{d[4..5]}-#{d[6..7]}" # => "2007-09-26"
368
+ t1 = "#{t[0..1]}:#{t[2..3]}:#{t[4..5]}" # => "15:56:56"
369
+ dt = "-#{d1}T#{t1}Z" # => "-2007-09-26T15:56:56Z"
370
+ @date_time = DateTime.parse(dt) # => #<DateTime: 10673317777/10800,0,2299161>
371
+ else
372
+ @version = @version_str # some version strings just look like this: "0.1.0"
373
+ end
374
+ @os = os
375
+ end
376
+ #
377
+ # Set's up the local cache directory references
378
+ #
379
+ def local_cache_dir=(dir_path)
380
+ @local_cache_dir = File.expand_path(dir_path)
381
+ @relative_local_path = "#{@href_path}#{@filename}"
382
+ @local_path = "#{@local_cache_dir}/#{@relative_local_path}"
383
+ end
384
+ #
385
+ # Copies the file referenced in _source_ to _destination_
386
+ # _source_ can be a url or local file path
387
+ # _destination_ must be a local path
388
+ #
389
+ # Will copy file if the file does not exists
390
+ # OR if the the file exists but the signature has
391
+ # not been successfully verified.
392
+ #
393
+ # Returns file size if cached succesfully, false otherwise.
394
+ #
395
+ def update_cache(source=@url, destination=@local_path)
396
+ unless destination
397
+ raise ArgumentError, "Must specify destination directory when updatng resource", caller
398
+ end
399
+ file_exists = File.exists?(destination)
400
+ if file_exists && @signature_verified == nil
401
+ verify_signature
402
+ end
403
+ unless file_exists && @signature_verified
404
+ FileUtils.mkdir_p(File.dirname(destination))
405
+ jarfile = open(source)
406
+ if jarfile.class == Tempfile
407
+ FileUtils.cp(jarfile.path, destination)
408
+ else
409
+ File.open(destination, 'w') {|f| f.write jarfile.read }
410
+ end
411
+ verify_signature ? jarfile.size : false
412
+ else
413
+ File.size(destination)
414
+ end
415
+ end
416
+ #
417
+ # Verifys signature of locallly cached resource
418
+ #
419
+ # Returns boolean value indicating whether the signature of the
420
+ # cached local copy of the resource verified successfully
421
+ #
422
+ # The value return is nil if no local cache has been created.
423
+ #
424
+ # Example:
425
+ #
426
+ # true || false || nil
427
+ #
428
+ def verify_signature
429
+ if @local_path
430
+ if RUBY_PLATFORM =~ /java/
431
+ begin
432
+ jarfile = java.util.jar.JarInputStream.new(FileInputStream.new(@local_path), true)
433
+ @signature_verified = true
434
+ rescue NativeException
435
+ @signature_verified = false
436
+ end
437
+ else
438
+ # Use IO.popen instead of system() to absorb
439
+ # jarsigners messages to $stdout
440
+ response = IO.popen("jarsigner -verify #{@local_path}"){|io| io.gets}
441
+ @signature_verified = ($?.exitstatus == 0)
442
+ end
443
+ else
444
+ nil
445
+ end
446
+ end
447
+ #
448
+ # Copies the resource referenced in Resource#url
449
+ # to the local cache.
450
+ #
451
+ # If the resource is successully cached locally and the
452
+ # signature is verified the size of the resource is retured.
453
+ #
454
+ # If the signature is not verified then false is returned.
455
+ #
456
+ def cache_resource(dest_dir=@local_cache_dir, include_pack_gz = false)
457
+ unless dest_dir
458
+ raise ArgumentError, "Must specify destination directory when creating resource", caller
459
+ end
460
+ self.local_cache_dir=dest_dir
461
+ @size = update_cache(@url, @local_path)
462
+ if include_pack_gz
463
+ @relative_local_path_gz = "#{@relative_local_path_gz}.pack.gz"
464
+ @local_path_pack_gz = "#{dest_dir}/#{@relative_local_path_pack_gz}"
465
+ @size_pack_gz = update_cache(@url_pack_gz, @local_path_pack_gz)
466
+ end
467
+ @signature_verified ? @size : @signature_verified
468
+ end
469
+ end
470
+ #
471
+ #
472
+ # == Jnlp
473
+ #
474
+ # A library for encapslating the content and resources referenced by Java Web Start jnlps
475
+ #
476
+ # For more information about the structure of Java Web Start see:
477
+ # * http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/contents.html
478
+ #
479
+ # To create a new Jnlp call Jnlp#new with a string that contains either a local path or a url.
480
+ #
481
+ # Examples:
482
+ #
483
+ # * Creating a new Jnlp object from a local Java Web Start jnlp file.
484
+ # j = Jnlp::Jnlp.new("authoring.jnlp")
485
+ # * Creating a new Jnlp object from a Java Web Start jnlp referenced with a url.
486
+ # j = Jnlp::Jnlp.new("http://jnlp.concord.org/dev/org/concord/maven-jnlp/otrunk-sensor/otrunk-sensor.jnlp")
487
+ #
488
+ # Once the Jnlp object is created you can call Jnlp#cache_resources to create a local
489
+ # cache of all the jar and nativelib resources. The structure of the cache directory
490
+ # and the naming using for the jar and nativelib files is the same as that used
491
+ # by the Java Web Start Download Servlet, see:
492
+ # * http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/downloadservletguide.html
493
+ #
494
+ # === Example: Creating an jnlp object and caching all the resources
495
+ #
496
+ # j = Jnlp::Jnlp.new("http://jnlp.concord.org/dev/org/concord/maven-jnlp/otrunk-sensor/otrunk-sensor.jnlp", 'web_start_cache')
497
+ #
498
+ # Will populate the local directory: web_start_cache with the following files:
499
+ #
500
+ # avalon-framework/avalon-framework/avalon-framework__V4.1.3.jar
501
+ # commons-beanutils/commons-beanutils/commons-beanutils__V1.7.0.jar
502
+ # commons-collections/commons-collections/commons-collections__V3.1.jar
503
+ # commons-digester/commons-digester/commons-digester__V1.8.jar
504
+ # commons-lang/commons-lang/commons-lang__V2.1.jar
505
+ # commons-logging/commons-logging/commons-logging__V1.1.jar
506
+ # commons-validator/commons-validator/commons-validator__V1.3.1.jar
507
+ # jdom/jdom/jdom__V1.0.jar
508
+ # jug/jug/jug__V1.1.2.jar
509
+ # log4j/log4j/log4j__V1.2.12.jar
510
+ # logkit/logkit/logkit__V1.0.1.jar
511
+ # org/apache/velocity/velocity/velocity__V1.5.jar
512
+ # org/apache/velocity/velocity-tools/velocity-tools__V1.3.jar
513
+ # org/concord/apple-support/apple-support__V0.1.0-20071207.221705-16.jar
514
+ # org/concord/data/data__V0.1.0-20080307.190013-90.jar
515
+ # org/concord/datagraph/datagraph__V0.1.0-20080306.220034-101.jar
516
+ # org/concord/external/ekit/ekit__V1.0.jar
517
+ # org/concord/external/jep/jep__V1.0.jar
518
+ # org/concord/external/rxtx/rxtx-comm/rxtx-comm__V2.1.7-r2.jar
519
+ # org/concord/external/rxtx/rxtx-serial/rxtx-serial-linux-nar__V2.1.7-r2.jar
520
+ # org/concord/external/rxtx/rxtx-serial/rxtx-serial-macosx-nar__V2.1.7-r2.jar
521
+ # org/concord/external/rxtx/rxtx-serial/rxtx-serial-win32-nar__V2.1.7-r2.jar
522
+ # org/concord/external/sound/jlayer/jlayer__V1.0.jar
523
+ # org/concord/external/sound/mp3spi/mp3spi__V1.9.4.jar
524
+ # org/concord/external/sound/tritonus/tritonus__V0.1.jar
525
+ # org/concord/external/vecmath/vecmath__V2.0.jar
526
+ # org/concord/framework/framework__V0.1.0-20080306.210010-141.jar
527
+ # org/concord/frameworkview/frameworkview__V0.1.0-20080305.205955-32.jar
528
+ # org/concord/ftdi-serial-wrapper/ftdi-serial-wrapper__V0.1.0-20071208.222126-20.jar
529
+ # org/concord/ftdi-serial-wrapper-native/ftdi-serial-wrapper-native-win32-nar__V0.1.0-20070303.181906-4.jar
530
+ # org/concord/graph/graph__V0.1.0-20071208.221811-45.jar
531
+ # org/concord/graphutil/graphutil__V0.1.0-20080206.170117-89.jar
532
+ # org/concord/httpclient/httpclient__V0.1.0-20071212.220020-17.jar
533
+ # org/concord/loader/loader__V0.1.0-20071207.234707-24.jar
534
+ # org/concord/math/math__V0.1.0-20071207.221359-24.jar
535
+ # org/concord/otrunk/data-util/data-util__V0.1.0-20080215.170056-146.jar
536
+ # org/concord/otrunk/otrunk-velocity/otrunk-velocity__V0.1.0-20071207.201957-21.jar
537
+ # org/concord/otrunk/otrunk__V0.1.0-20080305.205802-425.jar
538
+ # org/concord/otrunk-ui/otrunk-ui__V0.1.0-20080310.210028-132.jar
539
+ # org/concord/portfolio/portfolio__V0.1.0-20080303.230151-55.jar
540
+ # org/concord/sensor/labpro-usb/labpro-usb__V0.1.0-20071207.225156-8.jar
541
+ # org/concord/sensor/labpro-usb-native/labpro-usb-native-win32-nar__V0.1.0-20070606.192155-3.jar
542
+ # org/concord/sensor/sensor-dataharvest/sensor-dataharvest__V0.1.0-20070607.155431-8.jar
543
+ # org/concord/sensor/sensor-pasco/sensor-pasco__V0.1.0-20070405.151628-5.jar
544
+ # org/concord/sensor/sensor-vernier/sensor-vernier__V0.1.0-20080101.035208-31.jar
545
+ # org/concord/sensor/sensor__V0.1.0-20080229.053155-68.jar
546
+ # org/concord/sensor/ti/ti-cbl/ti-cbl-win32-nar__V0.1.1.jar
547
+ # org/concord/sensor/vernier/vernier-goio/vernier-goio-macosx-nar__V1.0.0.jar
548
+ # org/concord/sensor/vernier/vernier-goio/vernier-goio-win32-nar__V1.0.0.jar
549
+ # org/concord/sensor-native/sensor-native__V0.1.0-20080220.200100-39.jar
550
+ # org/concord/swing/swing__V0.1.0-20080303.163057-72.jar
551
+ # oro/oro/oro__V2.0.8.jar
552
+ # sslext/sslext/sslext__V1.2-0.jar
553
+ # velocity/velocity/velocity__V1.4.jar
554
+ # velocity/velocity-dep/velocity-dep__V1.4.jar"]
555
+ #
556
+ # Adding the option _:include_pack_gz => true_ will result in also copying
557
+ # all the *.pack.gz versions of the resources to the local cache directory.
558
+ #
559
+ # j = Jnlp::Jnlp.new("http://jnlp.concord.org/dev/org/concord/maven-jnlp/otrunk-sensor/otrunk-sensor.jnlp", 'web_start_cache', :include_pack_gz => true)
560
+ #
561
+ # Adding the option _:verbose => true_ will display a log of actions
562
+ #
563
+ # Note:
564
+ #
565
+ # The current Hpricot v6.0 Gem does not work with JRuby 1.1 release candidates
566
+ #
567
+ # You can get a version that does work here:
568
+ #
569
+ # http://www.telscenter.org/confluence/download/attachments/20236/hpricot-0.6.159-java.gem
570
+ #
571
+ # After you download it -- install it like this:
572
+ #
573
+ # jruby -S gem install pkg/hpricot-0.6.159-jruby.gem
574
+ #
575
+ # FYI: If you need to build it yourself (Nick Sieger's patch still applies
576
+ # cleanly to the current trunk revision: 161 of Hpricot):
577
+ #
578
+ # svn co https://code.whytheluckystiff.net/svn/hpricot/trunk hpricot
579
+ # cd hpricot
580
+ # curl http://caldersphere.net/hpricot-0.6.157-jruby-trunk.patch > hpricot-0.6.157-jruby-trunk.patch
581
+ # patch -p0 -i hpricot-0.6.157-jruby-trunk.patch
582
+ # jruby -S rake package_jruby
583
+ #
584
+ # You'll find the gem here:
585
+ #
586
+ # pkg/hpricot-0.6.159-jruby.gem
587
+ #
588
+ # If you want to run the tests (there is only one failing test in JRuby Hpricot, C Hpricot has two different failures):
589
+ #
590
+ # jruby -S rake hpricot_java
591
+ # jruby -S rake test
592
+ #
593
+ # I reported all this on why's tracker for hpricot:
594
+ #
595
+ # https://code.whytheluckystiff.net/hpricot/ticket/131#comment:1
596
+ #
597
+ class Jnlp
598
+ #
599
+ # Sets the Java Classpath delimiter used when generating
600
+ # a Classpath string
601
+ CLASSPATH_DELIMETER = ':' unless defined? CLASSPATH_DELIMETER
602
+ #
603
+ # Contains a Hpricot XML doc of the original jnlp
604
+ #
605
+ attr_reader :jnlp
606
+ #
607
+ # Contains a Hpricot XML doc of the jnlp converted to reference the local cache
608
+ #
609
+ attr_reader :local_jnlp
610
+ #
611
+ # Contains the full path of the original jnlp
612
+ #
613
+ # Example:
614
+ #
615
+ # "http://jnlp.concord.org/dev/org/concord/maven-jnlp/otrunk-sensor/otrunk-sensor.jnlp"
616
+ #
617
+ attr_reader :path
618
+ #
619
+ # Contains the name of the jnlp
620
+ #
621
+ # Example:
622
+ #
623
+ # "otrunk-sensor.jnlp"
624
+ #
625
+ attr_reader :name
626
+ #
627
+ # Contains the local file-based href attribute of the
628
+ # local jnlp if it has been created.
629
+ #
630
+ # Example:
631
+ #
632
+ # file:/Users/stephen/dev/otrunk-sensor/otrunk-sensor.jnlp"
633
+ #
634
+ attr_reader :local_jnlp_href
635
+ #
636
+ # Contains the local file-based name attribute of the
637
+ # of the local jnlp if it has been created.
638
+ #
639
+ # Example:
640
+ #
641
+ # otrunk-sensor.jnlp"
642
+ #
643
+ attr_reader :local_jnlp_name
644
+ #
645
+ # Contains the relative path to local cache directory if it has been set
646
+ #
647
+ # Example:
648
+ #
649
+ # "cache"
650
+ #
651
+ attr_reader :relative_local_cache_dir
652
+ #
653
+ # Contains the absolute path to the local cache directory if it has been set
654
+ #
655
+ # Example:
656
+ #
657
+ # "/Users/stephen/dev/jetty-jnlp-proxy/cache"
658
+ #
659
+ attr_reader :local_cache_dir
660
+ #
661
+ # Contains a boolean value indicating if the signatures of all
662
+ # resources copied to local cache have been verified.
663
+ #
664
+ # The value is nil if no local cache has been created.
665
+ #
666
+ # Example:
667
+ #
668
+ # true || false || nil
669
+ #
670
+ attr_accessor :local_resource_signatures_verified
671
+ #
672
+ # contans the boolean attribute determining whether
673
+ # gzipped pack200 resources should be copied to the
674
+ # local cache when the cache is updated
675
+ #
676
+ attr_accessor :include_pack_gzip
677
+ #
678
+ # Contains the spec attribute in the jnlp element
679
+ # Example:
680
+ #
681
+ # "1.0+"
682
+ #
683
+ attr_reader :spec
684
+ #
685
+ # Contains the codebase attribute in the jnlp element
686
+ # Example:
687
+ #
688
+ # "http://jnlp.concord.org/dev"
689
+ #
690
+ attr_reader :codebase
691
+ #
692
+ # Contains the href attribute in the jnlp element
693
+ # Example:
694
+ #
695
+ # "http://jnlp.concord.org/dev/org/concord/maven-jnlp/all-otrunk-snapshot/all-otrunk-snapshot.jnlp"
696
+ #
697
+ attr_reader :href
698
+ #
699
+ # Contains the Hpricot parsing of the <j2se> element in the Jnlp <resources> element.
700
+ #
701
+ attr_reader :j2se
702
+ #
703
+ # Contains the version attribute in the jnlp/resources/j2se element
704
+ # Example:
705
+ #
706
+ # "1.5+"
707
+ #
708
+ attr_reader :j2se_version
709
+ #
710
+ # Contains the max-heap-size attribute in the jnlp/resources/j2se element
711
+ # Example:
712
+ #
713
+ # "128m"
714
+ #
715
+ attr_reader :max_heap_size
716
+ #
717
+ # Contains the initial-heap-size attribute in the jnlp/resources/j2se element
718
+ # Example:
719
+ #
720
+ # "32m"
721
+ #
722
+ attr_reader :initial_heap_size
723
+ #
724
+ # Contains the value of the jnlp/information/title element
725
+ # Example:
726
+ #
727
+ # "All OTrunk snapshot"
728
+ #
729
+ attr_reader :title
730
+ #
731
+ # Contains the value of the jnlp/information/vendor element
732
+ # Example:
733
+ #
734
+ # "Concord Consortium"
735
+ #
736
+ attr_reader :vendor
737
+ #
738
+ # Contains the value of the href attribute in the jnlp/information/homepage element
739
+ # Example:
740
+ #
741
+ # "http://www.concord.org"
742
+ #
743
+ attr_reader :homepage
744
+ #
745
+ # Contains the value of the jnlp/information/description element
746
+ # Example:
747
+ #
748
+ # "http://www.concord.org"
749
+ #
750
+ attr_reader :description
751
+ #
752
+ # Contains a Jnlp::Icon object encapsulating the
753
+ # jnlp/information/icon element if it exists in the
754
+ # original jnlp
755
+ #
756
+ attr_reader :icon
757
+ #
758
+ # Contains a boolean value indictating the presence of the
759
+ # jnlp/information/offline-allowed element
760
+ #
761
+ attr_reader :offline_allowed
762
+ #
763
+ # Contains an array of Jnlp::Property objects encapsulating
764
+ # jnlp/resources/property elements
765
+ #
766
+ attr_reader :properties
767
+ #
768
+ # Contains an array of Jnlp::Resource objects encapsulating
769
+ # jnlp/resources/jar elements
770
+ #
771
+ attr_reader :jars
772
+ #
773
+ # Contains an array of Jnlp::Resource objects encapsulating
774
+ # jnlp/resources/nativelib elements
775
+ #
776
+ attr_reader :nativelibs
777
+ #
778
+ # Contains the value of the main-class attribute in the jnlp/application-desc element
779
+ # Example: "http://www.concord.org"
780
+ #
781
+ attr_reader :main_class
782
+ #
783
+ # Contains the value of the jnlp/application-desc/argument element
784
+ # Example: "http://www.concord.org"
785
+ #
786
+ attr_reader :argument
787
+ #
788
+ # Create a new Jnlp by loading and parsing the Java Web Start
789
+ # Jnlp located at _path_ -- _path_ can be a local path or a url.
790
+ # * if you include _cache_dir_ then the jnlp resources will be cached locally when the object is created
791
+ # * If you also include a boolean true the pack_gzip versions of the resources will be cached also.
792
+ #
793
+ def initialize(path=nil, cache_dir=nil, options={})
794
+ if cache_dir
795
+ self.local_cache_dir=(cache_dir)
796
+ end
797
+ @include_pack_gzip = options[:include_pack_gzip]
798
+ @verbose = options[:verbose]
799
+ import_jnlp(path) unless path.empty?
800
+ end
801
+ #
802
+ # Saves a YAML version of the jnlp object.
803
+ #
804
+ # You can later recreate the object like this:
805
+ #
806
+ # j = YAML.load(File.read("saved_jnlp_object"))
807
+ #
808
+ def save(filename="saved_jnlp_object")
809
+ File.open(filename, 'w') {|f| f.write YAML.dump(self)}
810
+ end
811
+ #
812
+ # set
813
+ #
814
+ def local_cache_dir=(dir)
815
+ @local_cache_dir = File.expand_path(dir)
816
+ @relative_local_cache_dir = @local_cache_dir.split(File::SEPARATOR).last
817
+ end
818
+ #
819
+ # Rebuild the Jnlp by loading and parsing the Java Web Start
820
+ # Jnlp located at _path_ -- _path_ can be a local path or a url.
821
+ #
822
+ # If @local_cache_dir is set then the cache directory wll be updated also.
823
+ #
824
+ # If @include_pack_gzip is set then the gzipped pack200 versions of the resources
825
+ # will be cached also.
826
+ #
827
+ def import_jnlp(path)
828
+ @path = path
829
+ @name = File.basename(@path)
830
+ #
831
+ # @local_jnlp_href and @local_jnlp_name are only placeholders
832
+ # values so far-- they will become valid when a local jnlp file
833
+ # is written to the filesystem with this method:
834
+ #
835
+ # Jnlp#write_local_jnlp
836
+ #
837
+ @local_jnlp_name = "local-#{@name}"
838
+ @local_jnlp_href = File.expand_path("#{Dir.pwd}/#{@local_jnlp_name}")
839
+ @jnlp = Hpricot.XML(open(path))
840
+ @spec = (@jnlp/"jnlp").attr('spec')
841
+ @codebase = (@jnlp/"jnlp").attr('codebase')
842
+ @href = (@jnlp/"jnlp").attr('href')
843
+ @j2se_version, @max_heap_size, @initial_heap_size = nil, nil, nil
844
+ @title, @vendor, @homepage, @description, @icon = nil, nil, nil, nil, nil
845
+ unless (info = (@jnlp/"information")).empty?
846
+ @title = (info/"title").inner_html
847
+ @vendor = (info/"vendor").inner_html
848
+ @homepage = (info/"homepage").empty? ? '' : (info/"homepage").attr('href')
849
+ icon = (info/"icon")
850
+ @icon = Icon.new(icon) unless icon.empty?
851
+ @offline_allowed = (info/"offline-allowed") ? true : false
852
+ end
853
+ @main_class = (@jnlp/"application-desc").attr('main-class')
854
+ @argument = (@jnlp/"argument").inner_html
855
+ @properties, @jars, @nativelibs = [], [], []
856
+ (@jnlp/"resources").each do |resources|
857
+ if os = resources[:os]
858
+ os = os.strip.downcase.gsub(/\W+/, '_').gsub(/^_+|_+$/, '')
859
+ end
860
+ unless (j = (resources/"j2se")).empty?
861
+ @j2se = j
862
+ @j2se_version = j.attr('version')
863
+ @max_heap_size = j.attr('max-heap-size')
864
+ @initial_heap_size = j.attr('initial-heap-size')
865
+ end
866
+ (resources/"property").each { |prop| @properties << Property.new(prop, os) }
867
+ (resources/"jar").each { |res| @jars << Resource.new(res, @codebase, os) }
868
+ (resources/"nativelib").each { |res| @nativelibs << Resource.new(res, @codebase, os) }
869
+ end
870
+ if @local_cache_dir
871
+ cache_resources(@local_cache_dir, @include_pack_gzip)
872
+ generate_local_jnlp
873
+ end
874
+ end
875
+ #
876
+ # Copy all the jars and nativelib resources referenced in the Jnlp to
877
+ # a local cache dir passed in _dest_dir_. The structure of the cache directory
878
+ # and the naming using for the jar and nativelib files is the same as that used
879
+ # by the Java Web Start Download Servlet, see:
880
+ # * http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/downloadservletguide.html
881
+ #
882
+ def cache_resources(dest_dir=@local_cache_dir, include_pack_gz = @include_pack_gzip)
883
+ unless dest_dir
884
+ raise ArgumentError, "Must specify destination directory when caching resources", caller
885
+ end
886
+ self.local_cache_dir=dest_dir
887
+ @local_resource_signatures_verified = true
888
+ @jars.each do |j|
889
+ @local_resource_signatures_verified &&= j.cache_resource(dest_dir, include_pack_gz)
890
+ end
891
+ @nativelibs.each do |n|
892
+ @local_resource_signatures_verified &&= n.cache_resource(dest_dir, include_pack_gz)
893
+ end
894
+ if @local_resource_signatures_verified
895
+ generate_local_jnlp
896
+ end
897
+ @local_resource_signatures_verified = @local_resource_signatures_verified ? true : false
898
+ end
899
+ #
900
+ # Copies the original Hpricot jnlp into @local_jnlp
901
+ # and modified it to use the local cache.
902
+ #
903
+ def generate_local_jnlp
904
+ #
905
+ # get a copy of the existing jnlp
906
+ # (it should be easier than this)
907
+ #
908
+ @local_jnlp = Hpricot.XML(@jnlp.to_s)
909
+ #
910
+ # we'll be working with the Hpricot root element
911
+ #
912
+ jnlp_elem = (@local_jnlp/"jnlp").first
913
+ #
914
+ # set the new codebase
915
+ #
916
+ jnlp_elem[:codebase] = "file:#{@local_cache_dir}"
917
+ #
918
+ # set the new href
919
+ #
920
+ jnlp_elem[:href] = @local_jnlp_href
921
+ #
922
+ # for each jar and nativelib remove the version
923
+ # attribute and point the href to he local ache
924
+ #
925
+ @jars.each do |jar|
926
+ j = @local_jnlp.at("//jar[@href=#{jar.href}]")
927
+ j.remove_attribute(:version)
928
+ j[:href] = jar.relative_local_path
929
+ end
930
+ @nativelibs.each do |nativelib|
931
+ nl = @local_jnlp.at("//nativelib[@href=#{nativelib.href}]")
932
+ nl.remove_attribute(:version)
933
+ nl[:href] = nativelib.relative_local_path
934
+ end
935
+ end
936
+ #
937
+ # Returns an array containing all the local paths for this jnlp's resources.
938
+ #
939
+ # Pass in the optional hash: (:remove_jruby => true) and
940
+ # the first resource that contains the string /jruby/ will
941
+ # be removed from the returned array.
942
+ #
943
+ # Example:
944
+ #
945
+ # resource_paths(:remove_jruby => true)
946
+ #
947
+ # This is useful when the jruby-complete jar has been included
948
+ # in the jnlp and you don't want to require that jruby into this
949
+ # specific instance which is already running jruby.
950
+ #
951
+ # Here's an example of a jruby resource reference:
952
+ #
953
+ # "org/jruby/jruby/jruby__V1.0RC2.jar"
954
+ #
955
+ def resource_paths(options={})
956
+ cp_jars = @jars.collect {|j| j.local_path}
957
+ cp_nativelibs = @nativelibs.collect {|n| n.local_path}
958
+ resources = cp_jars + cp_nativelibs
959
+ if options[:remove_jruby]
960
+ resources = resources.reject {|r| r =~ /\/jruby\//}
961
+ end
962
+ resources
963
+ end
964
+ #
965
+ # Returns a string in Java classpath format for all the jars for this jnlp
966
+ # The delimiter used is the ':' which is valid for MacOS X and Linux.
967
+ # Windows uses the ';' as a delimeter.
968
+ #
969
+ # Pass in the optional hash: (:remove_jruby => true) and
970
+ # the first resource that contains the string /jruby/ will
971
+ # be removed from the returned classapath string.
972
+ #
973
+ # Example:
974
+ #
975
+ # local_classpath(:remove_jruby => true)
976
+ #
977
+ def local_classpath(options={})
978
+ resource_paths(options).join(CLASSPATH_DELIMETER)
979
+ end
980
+ #
981
+ # Writes a shell script to the local filesystem
982
+ # that will export a modified CLASSPATH environmental
983
+ # variable with the paths to the resources used by
984
+ # this jnlp.
985
+ #
986
+ # Writes a jnlp to current working directory
987
+ # using name of original jnlp.
988
+ #
989
+ # Pass in the optional hash: (:remove_jruby => true) and
990
+ # the first resource that contains the string /jruby/ will
991
+ # be removed from the classapath shell script.
992
+ #
993
+ # Example:
994
+ #
995
+ # write_local_classpath_shell_scrip(:remove_jruby => true)
996
+ #
997
+ def write_local_classpath_shell_script(filename="#{@name[/[A-Za-z0-9_-]*/]}_classpath.sh", options={})
998
+ script = "export CLASSPATH=$CLASSPATH#{local_classpath(options)}"
999
+ File.open(filename, 'w'){|f| f.write script}
1000
+ end
1001
+ #
1002
+ # returns the jnlp as a string
1003
+ #
1004
+ def to_jnlp
1005
+ @jnlp.to_s
1006
+ end
1007
+ #
1008
+ # returns the local file-based jnlp as a string
1009
+ #
1010
+ def to_local_jnlp
1011
+ @local_jnlp.to_s
1012
+ end
1013
+ #
1014
+ # Writes a local copy of the original jnlp .
1015
+ #
1016
+ # Writes jnlp to current working directory
1017
+ # using name of original jnlp.
1018
+ #
1019
+ def write_jnlp(filename=@name)
1020
+ File.open(filename, 'w') {|f| f.write to_jnlp }
1021
+ end
1022
+ #
1023
+ # Writes a local file-based jnlp.
1024
+ #
1025
+ # Will write jnlp to current working directory
1026
+ # using name of original jnlp with "local-" prefix.
1027
+ #
1028
+ def write_local_jnlp(filename=@local_jnlp_name)
1029
+ destination = File.expand_path(filename)
1030
+ unless @local_jnlp_href == destination
1031
+ @local_jnlp_href = destination
1032
+ @local_jnlp_name = File.basename(destination)
1033
+ self.generate_local_jnlp
1034
+ end
1035
+ File.open(filename, 'w') {|f| f.write to_local_jnlp }
1036
+ end
1037
+ #
1038
+ # This will add all the jars for this jnlp to the effective
1039
+ # classpath for this Java process.
1040
+ #
1041
+ def require_resources
1042
+ if RUBY_PLATFORM =~ /java/
1043
+ resource_paths(:remove_jruby => true).each {|res| require res}
1044
+ end
1045
+ end
1046
+ #
1047
+ # This will start the jnlp as a local process.
1048
+ #
1049
+ # This only works in MRI at this point.
1050
+ #
1051
+ def run_local(argument=@argument)
1052
+ if RUBY_PLATFORM =~ /java/
1053
+ puts "not implemented in JRuby yet"
1054
+ else
1055
+ command = "java -classpath #{local_classpath} #{@main_class} '#{@argument}'"
1056
+ $pid = fork { exec command }
1057
+ end
1058
+ end
1059
+ #
1060
+ # This will stop the running local process,
1061
+ # started by Jnlp#run_local.
1062
+ #
1063
+ # This only works in MRI at this point.
1064
+ #
1065
+ def stop_local
1066
+ if RUBY_PLATFORM =~ /java/
1067
+ puts "not implemented in JRuby yet"
1068
+ else
1069
+ Process.kill 15, $pid
1070
+ Process.wait($pid)
1071
+ end
1072
+ end
1073
+ end
1074
+ end
1075
+