PageRenderTime 39ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/ruby/lib/rmi/encoder/ruby1e1.rb

https://github.com/sakoht/RMI
Ruby | 270 lines | 127 code | 18 blank | 125 comment | 45 complexity | 499e8e6aecb4579706d34e6be8fb4d04 MD5 | raw file
Possible License(s): AGPL-1.0
  1. require 'rmi'
  2. class RMI::Encoder::Ruby1e1 < RMI::Encoder
  3. @@value = 0
  4. @@object_reference = 1
  5. @@return_proxy = 3
  6. @@sym = 4
  7. @@remote_id_for_object = {}
  8. @@node_for_object = {}
  9. @@proxy_subclasses = {}
  10. @@proc_src = {}
  11. def encode(message_data, opts)
  12. encoded = []
  13. message_data.each { |o|
  14. klass = o.class
  15. remote_id = @@remote_id_for_object[o.__id__]
  16. if remote_id != nil
  17. # this is a proxy object on THIS side: the real object will be used on the remote side
  18. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} proxy #{o} references remote #{remote_id}\n")
  19. encoded.push(@@return_proxy, remote_id)
  20. elsif remote_id == nil && ( o.kind_of?(String) || o.kind_of?(Fixnum) || o.kind_of?(NilClass) || o.kind_of?(FalseClass) || o.kind_of?(TrueClass) )
  21. # sending a non-reference value
  22. encoded.push(@@value, o)
  23. else
  24. # sending some sort of reference which originates on this side
  25. if (opts != nil and (opts['copy'] == true or opts['copy_params'] == true))
  26. # a reference on this side which should be copied on the other side instead of proxied
  27. # this never happens by default in the RMI modules, only when specially requested for performance
  28. # or to get around known bugs in the C<->Ruby interaction in some modules (DBI).
  29. o = _create_remote_copy(o)
  30. redo
  31. else
  32. # a reference originating on this side: send info so the remote side can create a proxy
  33. # TODO: use something better than stringification since this can be overridden!!!
  34. local_id = o.class.to_s + "=" + o.__id__.to_s
  35. #if (allowed = self->{allow_modules})
  36. # unless (allowed->{ref(o)})
  37. # die "objects of type " + ref(o) + " cannot be passed from this RMI node!"
  38. # end
  39. #end
  40. encoded.push(@@object_reference, local_id)
  41. @sent_objects[local_id] = o
  42. end
  43. end
  44. }
  45. return encoded
  46. end
  47. # decode from a Ruby5::E1 remote node
  48. def decode(encoded)
  49. message_data = []
  50. while encoded.length > 0
  51. type = encoded.shift()
  52. value = encoded.shift()
  53. if type == @@value
  54. # primitive value
  55. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} - primitive #{value}\n")
  56. message_data.push(value)
  57. elsif type == @@return_proxy
  58. # exists on this side, and was a proxy on the other side: get the real reference by id
  59. o = @sent_objects[value]
  60. if o == nil
  61. msg = "#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} reconstituting local object #{value}, but not found in sent objects!\n"
  62. raise IOError, msg
  63. end
  64. message_data.push(o)
  65. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} - resolved local object for #{value}\n")
  66. elsif type == @@object_reference
  67. # exists on the other side: we need a proxy for it
  68. o = @received_objects[value]
  69. if o == nil
  70. pos = value.index('=')
  71. if pos == nil
  72. raise IOError, "no '=' found in object identifier to separate the class name from the rest of the object id"
  73. end
  74. remote_class = value[0..pos-1]
  75. # Put the object into a custom subclass of RMI::ProxyObject
  76. # this allows class-wide customization of how proxying should
  77. # occur. It also makes Data::Dumper results more readable.
  78. target_class_name = 'RMI::ProxyObject::' + remote_class #gsub(':','')
  79. target_class = @@proxy_subclasses[target_class_name]
  80. if target_class == nil
  81. mod = target_class_name.downcase.split('::').join('/')
  82. begin
  83. require mod
  84. target_class = eval "#{target_class_name}"
  85. rescue Exception
  86. src = ''
  87. words = remote_class.split('::')
  88. prev = ''
  89. words.each do |word|
  90. if (prev.length > 0)
  91. src += "class RMI::ProxyObject#{prev}; end; "
  92. end
  93. prev += '::' + word
  94. end
  95. src += "class #{target_class_name} < RMI::ProxyObject\nend\n#{target_class_name}"
  96. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} - defining proxy sub-class #{src}\n")
  97. target_class = eval src
  98. end
  99. @@proxy_subclasses[target_class_name] = target_class
  100. end
  101. o = target_class.new(@node,value,remote_class)
  102. o_id = o.__id__
  103. @received_objects[value] = WeakRef.new(o)
  104. @@node_for_object[o_id] = @node
  105. @@remote_id_for_object[o_id] = value
  106. if target_class_name == 'RMI::ProxyObject::Proc'
  107. orig = o
  108. arity = orig.arity
  109. src = @@proc_src[arity]
  110. if src == nil
  111. params = ''
  112. if arity > 0
  113. arity.times do |n|
  114. if n != 0
  115. params += ','
  116. end
  117. params += "p#{n}"
  118. end
  119. elsif arity < 0
  120. arity.times do |n|
  121. if n != 0
  122. params += ','
  123. end
  124. if n == arity-1
  125. params += "*p#{n}"
  126. else
  127. params += "p#{n}"
  128. end
  129. end
  130. params = '*p'
  131. end
  132. src = "RMI::ProxyWrapper::Proc.new(o) do |#{params}| orig.call(#{params}) end"
  133. end
  134. #print src,"\n"
  135. o = eval src
  136. end
  137. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} - made proxy for #{value} (remote class #{remote_class}) #{o}\n")
  138. #if remote_class == 'Proc'
  139. # # wrap our proxy in a real Proc which is callable on this side
  140. # orig = o
  141. # o = Proc.new do |*args|
  142. # orig.call(*args)
  143. # end
  144. #end
  145. else
  146. $RMI_DEBUG && print("#{$RMI_DEBUG_MSG_PREFIX} N: #{$$} - using previous remote proxy for #{value}\n")
  147. end
  148. message_data.push(o)
  149. else
  150. raise ArgumentError, "Unknown type #{type}????"
  151. end
  152. end
  153. return message_data
  154. end
  155. =begin
  156. =pod
  157. =head1 NAME
  158. RMI::Encoder::Ruby1e1
  159. =head1 VERSION
  160. This document describes RMI::Encoder::Ruby5e1 for RMI v0.11.
  161. =head1 DESCRIPTION
  162. All encoding protocol modules have an encode() method which takes an array of values and
  163. return an array which has no references. The complimentary decode() method
  164. must be able to take a copy of the encode() results, in another process on the opposite
  165. side of the node pair, and turn it into something which behaves like the original array.
  166. The RMI::Encoder::Ruby5e1 module handles encode/decode for RMI nodes where
  167. the remote node specifies ruby5e1 as its encoding. It uses a simple 4-value system
  168. of categorizing a data value, and the categorized value, when a reference, embeds both
  169. the class/module and the object identity.
  170. This implementation is in Ruby, so it is used for Ruby processes to talk with each other.
  171. For a process in another language to talk with a Ruby process, it could implement a
  172. Ruby5e1 encoding module. Alternatively, that process's language could implement an
  173. encoding module for which there is a Ruby implementation.
  174. Currently, each RMI Node knows its encoding protocol, and it is up to the constructor
  175. of the node to ensure that it is using a protocol which matches the protocol on the other
  176. side. In a future release auto-negotiation of protocols could be implemented, but
  177. because RMI is meant to be a low-level protocol, behind layers which handle things like
  178. security and asynchronicity, this may be left out of this layer.
  179. =head1 METHODS
  180. =head2 encode
  181. Turns an array of real data values which contain references into an array
  182. of values which contains no references.
  183. =head2 decode
  184. Takes an array made by encode on the other side, and turns it into an array
  185. which functions like the one which was originally encoded.
  186. =head2 ENCODING
  187. An array of message_data of length n to is converted to have a length of n*2.
  188. Each value is preceded by an integer which categorizes the value.
  189. 0 a primitive, non-reference value
  190. The value itself follows, it is not a reference, and it is passed by-copy.
  191. 1 an object reference originating on the sender's side
  192. A unique identifier for the object follows instead of the object.
  193. The remote side should construct a transparent proxy which uses that ID.
  194. 2 a non-object (unblessed) reference originating on the sender's side
  195. A unique identifier for the reference follows, instead of the reference.
  196. The remote side should construct a transparent proxy which uses that ID.
  197. 3 passing-back a proxy: a reference which originated on the receiver's side
  198. The following value is the identifier the remote side sent previously.
  199. The remote side should substitue the original object when deserializing
  200. =head1 SEE ALSO
  201. B<RMI>, B<RMI::Node>
  202. =head1 AUTHORS
  203. Scott Smith <https://github.com/sakoht>
  204. =head1 COPYRIGHT
  205. Copyright (c) 2008 - 2010 Scott Smith <https://github.com/sakoht> All rights reserved.
  206. =head1 LICENSE
  207. This program is free software you can redistribute it and/or modify it under
  208. the same terms as Ruby itself.
  209. The full text of the license can be found in the LICENSE file included with this
  210. module.
  211. =cut
  212. 1
  213. =end
  214. end