javaobj 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/javaobs.rb +200 -113
  2. data/rakefile +4 -3
  3. metadata +1 -1
@@ -1,11 +1,60 @@
1
1
 
2
2
  require 'delegate'
3
3
 
4
+ # Java Objects namespace to read and write Java serialized objects to streams.
5
+ # Any Java serialized object can be read from a stream. To write a Java object,
6
+ # the meta class must be primed with a sample input serialized object. The is
7
+ # required because Java uses a UUID to identify classes and it is generated using
8
+ # a complex hashing scheme of data and method signatures. Since this system does
9
+ # not have access to that information, it needs to get it from a serialized object.
10
+ #
11
+ # Objects that have custom serialization methods can be read and written by
12
+ # creating a class as we have for the Date class:
13
+ #
14
+ # class Date < SimpleDelegator
15
+ # extend JavaObject
16
+ #
17
+ # def initialize
18
+ # super(Time)
19
+ # end
20
+ #
21
+ # def time=(time)
22
+ # case time
23
+ # when Time
24
+ # __setobj__(time)
25
+ #
26
+ # when String
27
+ # t, = time.unpack("Q")
28
+ # __setobj__(Time.at(t / 1000, (t % 1000) * 1000))
29
+ # end
30
+ # end
31
+ #
32
+ # def data
33
+ # t = __getobj__.tv_sec * 1000 + __getobj__.tv_usec / 1000
34
+ # [t].pack("Q")
35
+ # end
36
+ # end
37
+ #
38
+ # The important methods are the data method that is used for writing the
39
+ # the object to a stream.
40
+ #
41
+ # All other classes will be auto-generated when the stream is read and persisted.
42
+ # A Java Meta Class is added to the Ruby Class that contains all the Java field
43
+ # information needed to serialize the objects.
44
+
4
45
  module Java
46
+
47
+ # An error thrown when a serialization error occurs.
48
+ class SerializationError < RuntimeError
49
+ end
50
+
51
+ # An representation of a Java instance variable used to serialize
52
+ # data to and from a stream.
5
53
  class JavaField
6
54
  attr_reader :name, :type
7
55
  attr_accessor :subtype
8
56
 
57
+ # Create a java instance variable with a name and data type.
9
58
  def initialize(name, type)
10
59
  @name = name
11
60
  @type = type
@@ -17,6 +66,9 @@ module Java
17
66
  end
18
67
  end
19
68
 
69
+ # The Java meta class with all the information needed for
70
+ # serialization of the Ruby class to the stream. This class
71
+ # is attached to the Ruby class.
20
72
  class JavaClass
21
73
  attr_accessor :superClass, :rubyClass
22
74
  attr_reader :name, :uid, :fields, :javaName, :flags, :arrayType
@@ -31,6 +83,7 @@ module Java
31
83
  @arrayType = name[1] if name[0] == ?[
32
84
  end
33
85
 
86
+ # Add a field to the class.
34
87
  def addField(field)
35
88
  @fields << field
36
89
  end
@@ -40,19 +93,24 @@ module Java
40
93
  end
41
94
  end
42
95
 
96
+ # A small mixin to extend a Ruby class with the associated
97
+ # JavaClass.
43
98
  module JavaObject
44
99
  def javaClass
45
100
  @javaClass
46
101
  end
47
102
  end
48
103
 
104
+ # The custom serialization of the Java Date class
49
105
  class Date < SimpleDelegator
50
106
  extend JavaObject
51
107
 
52
108
  def initialize
53
109
  super(Time)
54
110
  end
55
-
111
+
112
+ # Set the time with a Time object or a Java binary serialized
113
+ # date.
56
114
  def time=(time)
57
115
  case time
58
116
  when Time
@@ -64,16 +122,19 @@ module Java
64
122
  end
65
123
  end
66
124
 
125
+ # Get the data in the form needed for the Java date serialization.
67
126
  def data
68
127
  t = __getobj__.tv_sec * 1000 + __getobj__.tv_usec / 1000
69
128
  [t].pack("Q")
70
129
  end
71
130
  end
72
131
 
132
+ # The Java Array wrapper using an Ruby Array.
73
133
  class JavaArray < Array
74
134
  extend JavaObject
75
135
  end
76
136
 
137
+ # Container for the Java serialization constants.
77
138
  module ObjectStream
78
139
  STREAM_MAGIC = 0xACED
79
140
  STREAM_VERSION = 5
@@ -111,6 +172,8 @@ module Java
111
172
  PRIM_OBJECT = 76 # 'L'
112
173
  end
113
174
 
175
+ # The Ruby version of the Java ObjectInputStream. Creates a Ruby
176
+ # proxy class for each Java Class.
114
177
  class ObjectInputStream
115
178
  include ObjectStream
116
179
 
@@ -125,6 +188,7 @@ module Java
125
188
  def readUID; @str.read(8); end
126
189
  def readLong; @str.read(8).unpack("Q").first; end
127
190
 
191
+ # Read a Java block of data with a size and then the following data.
128
192
  def readBlockData
129
193
  byte = readByte
130
194
  size = nil
@@ -136,15 +200,16 @@ module Java
136
200
  size = readInt
137
201
 
138
202
  else
139
- raise "Expecting TC_BLOCKDATA" unless byte == TC_BLOCKDATA
203
+ raise SerializationError, "Expecting TC_BLOCKDATA" unless byte == TC_BLOCKDATA
140
204
  end
141
205
 
142
206
  data = @str.read(size)
143
207
  byte = readByte
144
- raise "Unexpected byte #{byte}" unless byte == TC_ENDBLOCKDATA
208
+ raise SerializationError, "Unexpected byte #{byte}" unless byte == TC_ENDBLOCKDATA
145
209
  data
146
210
  end
147
211
 
212
+ # Read all the fields from the stream and add them to the class.
148
213
  def readFields(klass)
149
214
  fieldCount = readShort
150
215
  1.upto(fieldCount) do
@@ -160,11 +225,14 @@ module Java
160
225
  end
161
226
  end
162
227
 
228
+ # Read the class annotation. We do not currently handle annotations.
163
229
  def readClassAnnotation
164
230
  ebd = readByte
165
- raise "We do not handle annotations!" unless ebd == TC_ENDBLOCKDATA
231
+ raise SerializationError, "We do not handle annotations!" unless ebd == TC_ENDBLOCKDATA
166
232
  end
167
233
 
234
+ # Read the Java class description and create a Ruby class to proxy it
235
+ # in this name-space. Added special handling for the Date class.
168
236
  def readClassDesc
169
237
  name = readString
170
238
  uid = readUID
@@ -215,6 +283,7 @@ module Java
215
283
  klass
216
284
  end
217
285
 
286
+ # Read an array of objects.
218
287
  def readArray(klass)
219
288
  size = readInt
220
289
  a = klass.rubyClass.new
@@ -225,6 +294,7 @@ module Java
225
294
  a
226
295
  end
227
296
 
297
+ # Read a primitive data type.
228
298
  def readType(type, arrayType = nil, field = nil)
229
299
  case type
230
300
  when PRIM_BYTE
@@ -255,10 +325,11 @@ module Java
255
325
  readObject
256
326
 
257
327
  else
258
- raise "Unknown type #{type}"
328
+ raise SerializationError, "Unknown type #{type}"
259
329
  end
260
330
  end
261
331
 
332
+ # Read class data and recursively read parent classes.
262
333
  def readClassData(klass, object = nil)
263
334
  # Handle special case for Date
264
335
  if object == nil
@@ -285,6 +356,7 @@ module Java
285
356
  object
286
357
  end
287
358
 
359
+ # Read an object from the stream.
288
360
  def readObject
289
361
  object = nil
290
362
  byte = readByte
@@ -313,12 +385,13 @@ module Java
313
385
  object = nil
314
386
 
315
387
  else
316
- raise "Unexpected byte #{byte} at #{@str.pos}"
388
+ raise SerializationError, "Unexpected byte #{byte} at #{@str.pos}"
317
389
  end
318
390
 
319
391
  object
320
392
  end
321
393
 
394
+ # Initialize from a stream.
322
395
  def initialize(str)
323
396
  @str = str
324
397
  magic = readUShort
@@ -330,6 +403,8 @@ module Java
330
403
 
331
404
  end
332
405
 
406
+ # Read all objects in the stream. Calls readObject until the stream
407
+ # eof is reached.
333
408
  def readObjects
334
409
  objs = []
335
410
  until (@str.eof)
@@ -339,6 +414,8 @@ module Java
339
414
  end
340
415
  end
341
416
 
417
+ # A Ruby version of the Java ObjectOutputStream. The Ruby classes must
418
+ # have attached Java meta classes to attach UUID.
342
419
  class ObjectOutputStream
343
420
  include ObjectStream
344
421
 
@@ -353,99 +430,108 @@ module Java
353
430
  def writeUID(u); @str.write u; end
354
431
  def writeLong(l); @str.write [l].pack("Q"); end
355
432
 
356
- def nextHandle
357
- h = @nextHandle
358
- @nextHandle += 1
359
- h
360
- end
361
-
362
- def writeArray(klass, v)
363
- type = klass.arrayType
364
- writeInt(v.length)
365
- v.each do |e|
366
- writeType(type, e)
367
- end
368
- end
369
-
370
- def writeBlockData(data)
371
- if (data.length <= 255)
372
- writeByte(TC_BLOCKDATA)
373
- writeByte(data.length)
374
- else
375
- writeByte(TC_BLOCKDATALONG)
376
- writeInt(data.length)
377
- end
433
+ # Creates object reference handles.
434
+ def nextHandle
435
+ h = @nextHandle
436
+ @nextHandle += 1
437
+ h
438
+ end
378
439
 
379
- @str.write data
380
- writeByte(TC_ENDBLOCKDATA)
381
- end
440
+ # Write an array of objects.
441
+ def writeArray(klass, v)
442
+ type = klass.arrayType
443
+ writeInt(v.length)
444
+ v.each do |e|
445
+ writeType(type, e)
446
+ end
447
+ end
382
448
 
383
- def writeType(type, v)
384
- case type
385
- when PRIM_BYTE
386
- writeByte(v)
387
-
388
- when PRIM_CHAR
389
- writeByte(v)
390
-
391
- when PRIM_DOUBLE
392
- writeDouble(v)
393
-
394
- when PRIM_FLOAT
395
- writeFloat(v)
396
-
397
- when PRIM_INT
398
- writeInt(v)
399
-
400
- when PRIM_LONG
401
- writeLong(v)
402
-
403
- when PRIM_SHORT
404
- writeShort(v)
405
-
406
- when PRIM_BOOL
407
- writeBool(v)
408
-
409
- when PRIM_OBJECT, PRIM_ARRAY
410
- writeObject(v)
411
-
412
- else
413
- raise "Unknown type #{type}"
414
- end
415
- end
416
-
417
- def writeClassDesc(klass)
418
- @handles[klass] = nextHandle
419
-
420
- writeString klass.javaName
421
- writeUID klass.uid
422
- writeByte klass.flags
423
-
424
- writeShort(klass.fields.length)
425
- klass.fields.each do |f|
426
- writeByte(f.type)
427
- writeString(f.name)
428
- writeObject(f.subtype) if f.subtype
429
- end
449
+ # Writes a block of data to the stream.
450
+ def writeBlockData(data)
451
+ if (data.length <= 255)
452
+ writeByte(TC_BLOCKDATA)
453
+ writeByte(data.length)
454
+ else
455
+ writeByte(TC_BLOCKDATALONG)
456
+ writeInt(data.length)
457
+ end
430
458
 
431
- writeByte(TC_ENDBLOCKDATA) # Annotations
432
- writeObject(klass.superClass)
433
- end
434
-
435
- def writeObjectData(klass, obj)
436
- writeObjectData(klass.superClass, obj) if klass.superClass
437
-
438
- if klass.flags == SC_SERIALIZABLE
439
- klass.fields.each do |f|
440
- v = obj.send(f.name.intern)
441
- writeType(f.type, v)
442
- end
443
- else
444
- writeBlockData(obj.data)
445
- end
446
- end
459
+ @str.write data
460
+ writeByte(TC_ENDBLOCKDATA)
461
+ end
462
+
463
+ # Reads a Java primitive type.
464
+ def writeType(type, v)
465
+ case type
466
+ when PRIM_BYTE
467
+ writeByte(v)
468
+
469
+ when PRIM_CHAR
470
+ writeByte(v)
471
+
472
+ when PRIM_DOUBLE
473
+ writeDouble(v)
474
+
475
+ when PRIM_FLOAT
476
+ writeFloat(v)
477
+
478
+ when PRIM_INT
479
+ writeInt(v)
480
+
481
+ when PRIM_LONG
482
+ writeLong(v)
483
+
484
+ when PRIM_SHORT
485
+ writeShort(v)
486
+
487
+ when PRIM_BOOL
488
+ writeBool(v)
489
+
490
+ when PRIM_OBJECT, PRIM_ARRAY
491
+ writeObject(v)
492
+
493
+ else
494
+ raise SerializationError, "Unknown type #{type}"
495
+ end
496
+ end
497
+
498
+ # Writes the class description to the stream.
499
+ def writeClassDesc(klass)
500
+ @handles[klass] = nextHandle
501
+
502
+ writeString klass.javaName
503
+ writeUID klass.uid
504
+ writeByte klass.flags
505
+
506
+ writeShort(klass.fields.length)
507
+ klass.fields.each do |f|
508
+ writeByte(f.type)
509
+ writeString(f.name)
510
+ writeObject(f.subtype) if f.subtype
511
+ end
512
+
513
+ writeByte(TC_ENDBLOCKDATA) # Annotations
514
+ writeObject(klass.superClass)
515
+ end
516
+
517
+ # Write the object and class to the stream.
518
+ def writeObjectData(klass, obj)
519
+ writeObjectData(klass.superClass, obj) if klass.superClass
520
+
521
+ if klass.flags == SC_SERIALIZABLE
522
+ klass.fields.each do |f|
523
+ v = obj.send(f.name.intern)
524
+ writeType(f.type, v)
525
+ end
526
+ else
527
+ writeBlockData(obj.data)
528
+ end
529
+ end
447
530
 
448
- def writeObject(obj)
531
+ # Writes the object and class data to the stream. Will
532
+ # write a reference if the object has already been written
533
+ # once.
534
+ def writeObject(obj)
449
535
  unless obj
450
536
  writeByte(TC_NULL)
451
537
  else
@@ -465,38 +551,39 @@ module Java
465
551
  writeObject(obj.class.javaClass)
466
552
  writeArray(obj.class.javaClass, obj)
467
553
  @handles[obj] = nextHandle
468
-
554
+
469
555
  when String
470
556
  writeByte(TC_STRING)
471
557
  writeString(obj)
472
558
  @handles[obj] = nextHandle
473
-
559
+
474
560
  else
475
561
  writeByte(TC_OBJECT)
476
562
  klass = obj.class.javaClass
477
563
  writeObject(klass)
478
564
  @handles[obj] = nextHandle
479
565
  writeObjectData(klass, obj)
480
-
481
566
  end
482
567
  end
483
568
  end
484
- end
569
+ end
485
570
 
486
- def writeObjects(objs)
487
- objs.each do |o|
488
- writeObject o
489
- end
490
- end
491
-
492
- def initialize(str)
493
- @str = str
494
- @handles = {}
495
- @nextHandle = 0
496
-
497
- writeUShort(STREAM_MAGIC)
498
- writeShort(STREAM_VERSION)
499
- end
571
+ # Write an array of objects to the stream.
572
+ def writeObjects(objs)
573
+ objs.each do |o|
574
+ writeObject o
575
+ end
576
+ end
577
+
578
+ # Create an o writer on with a stream.
579
+ def initialize(str)
580
+ @str = str
581
+ @handles = {}
582
+ @nextHandle = 0
583
+
584
+ writeUShort(STREAM_MAGIC)
585
+ writeShort(STREAM_VERSION)
586
+ end
500
587
  end
501
588
  end
502
589
 
data/rakefile CHANGED
@@ -6,9 +6,9 @@ require 'rake/packagetask'
6
6
  require 'rake/gempackagetask'
7
7
  require 'rake/contrib/rubyforgepublisher'
8
8
 
9
- PKG_BUILD = '1'
9
+ PKG_BUILD = '2'
10
10
  PKG_NAME = 'javaobj'
11
- PKG_VERSION = '0.1'
11
+ PKG_VERSION = '0.2'
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -27,9 +27,10 @@ end
27
27
  Rake::RDocTask.new do |rdoc|
28
28
  rdoc.rdoc_dir = 'doc'
29
29
  rdoc.title = "Java Objects"
30
+ rdoc.main = 'README.rdoc'
30
31
  rdoc.options << '--line-numbers' << '--inline-source'
31
32
  rdoc.template = "#{ENV['template']}.rb" if ENV['template']
32
- rdoc.rdoc_files.include('lib/**/*.rb')
33
+ rdoc.rdoc_files.include('README.rdoc', 'lib/**/*.rb')
33
34
  end
34
35
 
35
36
  dist_dirs = [ "lib", "test", "examples" ]
metadata CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: javaobj
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.1"
6
+ version: "0.2"
7
7
  date: 2006-04-16 00:00:00 -07:00
8
8
  summary: Decode Java Serialized Objects to Ruby Objects.
9
9
  require_paths: