[前][次][番号順一覧][スレッド一覧][生データ]

yarv-diff:280

From: ko1 atdot.net
Date: 20 Feb 2006 01:56:40 -0000
Subject: [yarv-diff:280] r444 - in trunk: . lib lib/drb lib/soap lib/soap/encodingstyle lib/soap/header lib/soap/mapping lib/soap/rpc lib/wsdl lib/wsdl/soap lib/wsdl/xmlSchema lib/xsd lib/xsd/codegen lib/xsd/xmlparser

Author: aamine
Date: 2006-02-20 10:56:39 +0900 (Mon, 20 Feb 2006)
New Revision: 444

Added:
   trunk/lib/drb.rb
   trunk/lib/drb/
   trunk/lib/drb/acl.rb
   trunk/lib/drb/drb.rb
   trunk/lib/drb/eq.rb
   trunk/lib/drb/extserv.rb
   trunk/lib/drb/extservm.rb
   trunk/lib/drb/gw.rb
   trunk/lib/drb/invokemethod.rb
   trunk/lib/drb/observer.rb
   trunk/lib/drb/ssl.rb
   trunk/lib/drb/timeridconv.rb
   trunk/lib/drb/unix.rb
   trunk/lib/monitor.rb
   trunk/lib/mutex_m.rb
   trunk/lib/observer.rb
   trunk/lib/soap/
   trunk/lib/soap/attachment.rb
   trunk/lib/soap/baseData.rb
   trunk/lib/soap/element.rb
   trunk/lib/soap/encodingstyle/
   trunk/lib/soap/encodingstyle/aspDotNetHandler.rb
   trunk/lib/soap/encodingstyle/handler.rb
   trunk/lib/soap/encodingstyle/literalHandler.rb
   trunk/lib/soap/encodingstyle/soapHandler.rb
   trunk/lib/soap/generator.rb
   trunk/lib/soap/header/
   trunk/lib/soap/header/handler.rb
   trunk/lib/soap/header/handlerset.rb
   trunk/lib/soap/header/simplehandler.rb
   trunk/lib/soap/httpconfigloader.rb
   trunk/lib/soap/mapping.rb
   trunk/lib/soap/mapping/
   trunk/lib/soap/mapping/factory.rb
   trunk/lib/soap/mapping/mapping.rb
   trunk/lib/soap/mapping/registry.rb
   trunk/lib/soap/mapping/rubytypeFactory.rb
   trunk/lib/soap/mapping/typeMap.rb
   trunk/lib/soap/mapping/wsdlencodedregistry.rb
   trunk/lib/soap/mapping/wsdlliteralregistry.rb
   trunk/lib/soap/marshal.rb
   trunk/lib/soap/mimemessage.rb
   trunk/lib/soap/netHttpClient.rb
   trunk/lib/soap/parser.rb
   trunk/lib/soap/processor.rb
   trunk/lib/soap/property.rb
   trunk/lib/soap/rpc/
   trunk/lib/soap/rpc/cgistub.rb
   trunk/lib/soap/rpc/driver.rb
   trunk/lib/soap/rpc/element.rb
   trunk/lib/soap/rpc/httpserver.rb
   trunk/lib/soap/rpc/proxy.rb
   trunk/lib/soap/rpc/router.rb
   trunk/lib/soap/rpc/rpc.rb
   trunk/lib/soap/rpc/soaplet.rb
   trunk/lib/soap/rpc/standaloneServer.rb
   trunk/lib/soap/soap.rb
   trunk/lib/soap/streamHandler.rb
   trunk/lib/soap/wsdlDriver.rb
   trunk/lib/wsdl/
   trunk/lib/wsdl/binding.rb
   trunk/lib/wsdl/data.rb
   trunk/lib/wsdl/definitions.rb
   trunk/lib/wsdl/documentation.rb
   trunk/lib/wsdl/import.rb
   trunk/lib/wsdl/importer.rb
   trunk/lib/wsdl/info.rb
   trunk/lib/wsdl/message.rb
   trunk/lib/wsdl/operation.rb
   trunk/lib/wsdl/operationBinding.rb
   trunk/lib/wsdl/param.rb
   trunk/lib/wsdl/parser.rb
   trunk/lib/wsdl/part.rb
   trunk/lib/wsdl/port.rb
   trunk/lib/wsdl/portType.rb
   trunk/lib/wsdl/service.rb
   trunk/lib/wsdl/soap/
   trunk/lib/wsdl/soap/address.rb
   trunk/lib/wsdl/soap/binding.rb
   trunk/lib/wsdl/soap/body.rb
   trunk/lib/wsdl/soap/cgiStubCreator.rb
   trunk/lib/wsdl/soap/classDefCreator.rb
   trunk/lib/wsdl/soap/classDefCreatorSupport.rb
   trunk/lib/wsdl/soap/clientSkeltonCreator.rb
   trunk/lib/wsdl/soap/complexType.rb
   trunk/lib/wsdl/soap/data.rb
   trunk/lib/wsdl/soap/definitions.rb
   trunk/lib/wsdl/soap/driverCreator.rb
   trunk/lib/wsdl/soap/element.rb
   trunk/lib/wsdl/soap/fault.rb
   trunk/lib/wsdl/soap/header.rb
   trunk/lib/wsdl/soap/headerfault.rb
   trunk/lib/wsdl/soap/mappingRegistryCreator.rb
   trunk/lib/wsdl/soap/methodDefCreator.rb
   trunk/lib/wsdl/soap/operation.rb
   trunk/lib/wsdl/soap/servantSkeltonCreator.rb
   trunk/lib/wsdl/soap/standaloneServerStubCreator.rb
   trunk/lib/wsdl/soap/wsdl2ruby.rb
   trunk/lib/wsdl/types.rb
   trunk/lib/wsdl/wsdl.rb
   trunk/lib/wsdl/xmlSchema/
   trunk/lib/wsdl/xmlSchema/all.rb
   trunk/lib/wsdl/xmlSchema/annotation.rb
   trunk/lib/wsdl/xmlSchema/any.rb
   trunk/lib/wsdl/xmlSchema/attribute.rb
   trunk/lib/wsdl/xmlSchema/choice.rb
   trunk/lib/wsdl/xmlSchema/complexContent.rb
   trunk/lib/wsdl/xmlSchema/complexType.rb
   trunk/lib/wsdl/xmlSchema/content.rb
   trunk/lib/wsdl/xmlSchema/data.rb
   trunk/lib/wsdl/xmlSchema/element.rb
   trunk/lib/wsdl/xmlSchema/enumeration.rb
   trunk/lib/wsdl/xmlSchema/import.rb
   trunk/lib/wsdl/xmlSchema/importer.rb
   trunk/lib/wsdl/xmlSchema/include.rb
   trunk/lib/wsdl/xmlSchema/length.rb
   trunk/lib/wsdl/xmlSchema/parser.rb
   trunk/lib/wsdl/xmlSchema/pattern.rb
   trunk/lib/wsdl/xmlSchema/schema.rb
   trunk/lib/wsdl/xmlSchema/sequence.rb
   trunk/lib/wsdl/xmlSchema/simpleContent.rb
   trunk/lib/wsdl/xmlSchema/simpleExtension.rb
   trunk/lib/wsdl/xmlSchema/simpleRestriction.rb
   trunk/lib/wsdl/xmlSchema/simpleType.rb
   trunk/lib/wsdl/xmlSchema/unique.rb
   trunk/lib/wsdl/xmlSchema/xsd2ruby.rb
   trunk/lib/xsd/
   trunk/lib/xsd/charset.rb
   trunk/lib/xsd/codegen.rb
   trunk/lib/xsd/codegen/
   trunk/lib/xsd/codegen/classdef.rb
   trunk/lib/xsd/codegen/commentdef.rb
   trunk/lib/xsd/codegen/gensupport.rb
   trunk/lib/xsd/codegen/methoddef.rb
   trunk/lib/xsd/codegen/moduledef.rb
   trunk/lib/xsd/datatypes.rb
   trunk/lib/xsd/datatypes1999.rb
   trunk/lib/xsd/iconvcharset.rb
   trunk/lib/xsd/mapping.rb
   trunk/lib/xsd/namedelements.rb
   trunk/lib/xsd/ns.rb
   trunk/lib/xsd/qname.rb
   trunk/lib/xsd/xmlparser.rb
   trunk/lib/xsd/xmlparser/
   trunk/lib/xsd/xmlparser/parser.rb
   trunk/lib/xsd/xmlparser/rexmlparser.rb
   trunk/lib/xsd/xmlparser/xmlparser.rb
   trunk/lib/xsd/xmlparser/xmlscanner.rb
Modified:
   trunk/ChangeLog
Log:
* lib/mutex_m.rb: imported from Ruby CVS trunk HEAD.
* lib/observer.rb: ditto.
* lib/wsdl: ditto.
* lib/monitor.rb: ditto (removing Thread.critical=).
* lib/xsd: ditto.
* lib/soap: ditto.
* lib/drb.rb: ditto.
* lib/drb: ditto.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/ChangeLog	2006-02-20 01:56:39 UTC (rev 444)
@@ -4,6 +4,25 @@
 #  from Mon, 03 May 2004 01:24:19 +0900
 #
 
+2006-02-20(Mon) 10:55:59 +0900  Minero Aoki  <aamine loveruby.net>
+
+	* lib/mutex_m.rb: imported from Ruby CVS trunk HEAD.
+
+	* lib/observer.rb: ditto.
+
+	* lib/wsdl: ditto.
+
+	* lib/monitor.rb: ditto (removing Thread.critical=).
+
+	* lib/xsd: ditto.
+
+	* lib/soap: ditto.
+
+	* lib/drb.rb: ditto.
+
+	* lib/drb: ditto.
+
+
 2006-02-20(Mon) 10:49:31 +0900  Minero Aoki  <aamine loveruby.net>
 
 	* yarvcore.c (Init_yarvcore): fix typo (duo -> dup).

Added: trunk/lib/drb/acl.rb
===================================================================
--- trunk/lib/drb/acl.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/acl.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,144 @@
+# acl-2.0 - simple Access Control List
+#
+# Copyright (c) 2000,2002,2003 Masatoshi SEKI
+#
+# acl.rb is copyrighted free software by Masatoshi SEKI.
+# You can redistribute it and/or modify it under the same terms as Ruby.
+
+require 'ipaddr'
+
+class ACL
+  VERSION=["2.0.0"]
+  class ACLEntry
+    def initialize(str)
+      if str == '*' or str == 'all'
+	@pat = [:all]
+      else
+	begin
+	  @pat = [:ip, IPAddr.new(str)]
+	rescue ArgumentError
+	  @pat = [:name, dot_pat(str)]
+	end
+      end
+    end
+
+    private
+    def dot_pat_str(str)
+      list = str.split('.').collect { |s|
+	(s == '*') ? '.+' : s
+      }
+      list.join("\\.")
+    end
+
+    private
+    def dot_pat(str)
+      exp = "^" + dot_pat_str(str) + "$"
+      Regexp.new(exp)
+    end
+
+    public
+    def match(addr)
+      case @pat[0]
+      when :all
+	true
+      when :ip
+	begin
+	  ipaddr = IPAddr.new(addr[3])
+	  ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
+	rescue ArgumentError
+	  return false
+	end
+	( pat[1].include?(ipaddr)) ? true : false
+      when :name
+	(@pat[1] =~ addr[2]) ? true : false
+      else
+	false
+      end
+    end
+  end
+
+  class ACLList
+    def initialize
+      @list = []
+    end
+
+    public
+    def match(addr)
+      @list.each do |e|
+	return true if e.match(addr)
+      end
+      false
+    end
+
+    public
+    def add(str)
+      @list.push(ACLEntry.new(str))
+    end
+  end
+
+  DENY_ALLOW = 0
+  ALLOW_DENY = 1
+
+  def initialize(list=nil, order = DENY_ALLOW)
+    @order = order
+    @deny = ACLList.new
+    @allow = ACLList.new
+    install_list(list) if list
+  end
+
+  public
+  def allow_socket?(soc)
+    allow_addr?(soc.peeraddr)
+  end
+
+  public
+  def allow_addr?(addr)
+    case @order
+    when DENY_ALLOW
+      return true if @allow.match(addr)
+      return false if @deny.match(addr)
+      return true
+    when ALLOW_DENY
+      return false if @deny.match(addr)
+      return true if @allow.match(addr)
+      return false
+    else
+      false
+    end
+  end
+
+  public
+  def install_list(list)
+    i = 0
+    while i < list.size
+      permission, domain = list.slice(i,2)
+      case permission.downcase
+      when 'allow'
+	@allow.add(domain)
+      when 'deny'
+	@deny.add(domain)
+      else
+	raise "Invalid ACL entry #{list.to_s}"
+      end
+      i += 2
+    end
+  end
+end
+
+if __FILE__ == $0
+  # example
+  list = %w(deny all
+	    allow 192.168.1.1
+            allow ::ffff:192.168.1.2
+            allow 192.168.1.3
+            )
+
+  addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
+
+  acl = ACL.new
+  p acl.allow_addr?(addr)
+
+  acl = ACL.new(list, ACL::DENY_ALLOW)
+  p acl.allow_addr?(addr)
+end
+

Added: trunk/lib/drb/drb.rb
===================================================================
--- trunk/lib/drb/drb.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/drb.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,1760 @@
+#
+# = drb/drb.rb
+#
+# Distributed Ruby: _dRuby_ version 2.0.4
+#
+# Copyright (c) 1999-2003 Masatoshi SEKI.  You can redistribute it and/or
+# modify it under the same terms as Ruby.
+#
+# Author: Masatoshi SEKI
+#
+# Documentation: William Webber (william williamwebber.com)
+#
+# == Overview
+#
+# dRuby is a distributed object system for Ruby.  It allows an object in one
+# Ruby process to invoke methods on an object in another Ruby process on the
+# same or a different machine.
+#
+# The Ruby standard library contains the core classes of the dRuby package.
+# However, the full package also includes access control lists and the
+# Rinda tuple-space distributed task management system, as well as a 
+# large number of samples.  The full dRuby package can be downloaded from
+# the dRuby home page (see *References*).
+#
+# For an introduction and examples of usage see the documentation to the
+# DRb module.
+#
+# == References
+#
+# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
+#    The dRuby home page, in Japanese.  Contains the full dRuby package
+#    and links to other Japanese-language sources.
+#
+# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
+#    The English version of the dRuby home page.
+#
+# [http://www.chadfowler.com/ruby/drb.html]
+#    A quick tutorial introduction to using dRuby by Chad Fowler.
+#
+# [http://www.linux-mag.com/2002-09/ruby_05.html]
+#   A tutorial introduction to dRuby in Linux Magazine by Dave Thomas.
+#   Includes a discussion of Rinda.
+#
+# [http://www.eng.cse.dmu.ac.uk/~hgs/ruby/dRuby/]
+#   Links to English-language Ruby material collected by Hugh Sasse.
+#
+# [http://www.rubycentral.com/book/ospace.html]
+#   The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
+#   which discusses dRuby.
+#
+# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
+#   Translation of presentation on Ruby by Masatoshi Seki.
+
+require 'socket'
+require 'thread'
+require 'fcntl'
+require 'drb/eq'
+
+#
+# == Overview
+#
+# dRuby is a distributed object system for Ruby.  It is written in
+# pure Ruby and uses its own protocol.  No add-in services are needed
+# beyond those provided by the Ruby runtime, such as TCP sockets.  It
+# does not rely on or interoperate with other distributed object
+# systems such as CORBA, RMI, or .NET.
+#
+# dRuby allows methods to be called in one Ruby process upon a Ruby
+# object located in another Ruby process, even on another machine.
+# References to objects can be passed between processes.  Method
+# arguments and return values are dumped and loaded in marshalled
+# format.  All of this is done transparently to both the caller of the
+# remote method and the object that it is called upon.
+#
+# An object in a remote process is locally represented by a
+# DRb::DRbObject instance.  This acts as a sort of proxy for the
+# remote object.  Methods called upon this DRbObject instance are
+# forwarded to its remote object.  This is arranged dynamically at run
+# time.  There are no statically declared interfaces for remote
+# objects, such as CORBA's IDL.
+#
+# dRuby calls made into a process are handled by a DRb::DRbServer
+# instance within that process.  This reconstitutes the method call,
+# invokes it upon the specified local object, and returns the value to
+# the remote caller.  Any object can receive calls over dRuby.  There
+# is no need to implement a special interface, or mixin special
+# functionality.  Nor, in the general case, does an object need to
+# explicitly register itself with a DRbServer in order to receive
+# dRuby calls.
+#
+# One process wishing to make dRuby calls upon another process must
+# somehow obtain an initial reference to an object in the remote
+# process by some means other than as the return value of a remote
+# method call, as there is initially no remote object reference it can
+# invoke a method upon.  This is done by attaching to the server by
+# URI.  Each DRbServer binds itself to a URI such as
+# 'druby://example.com:8787'.  A DRbServer can have an object attached
+# to it that acts as the server's *front* *object*.  A DRbObject can
+# be explicitly created from the server's URI.  This DRbObject's
+# remote object will be the server's front object.  This front object
+# can then return references to other Ruby objects in the DRbServer's
+# process.
+#
+# Method calls made over dRuby behave largely the same as normal Ruby
+# method calls made within a process.  Method calls with blocks are
+# supported, as are raising exceptions.  In addition to a method's
+# standard errors, a dRuby call may also raise one of the
+# dRuby-specific errors, all of which are subclasses of DRb::DRbError.
+#
+# Any type of object can be passed as an argument to a dRuby call or
+# returned as its return value.  By default, such objects are dumped
+# or marshalled at the local end, then loaded or unmarshalled at the
+# remote end.  The remote end therefore receives a copy of the local
+# object, not a distributed reference to it; methods invoked upon this
+# copy are executed entirely in the remote process, not passed on to
+# the local original.  This has semantics similar to pass-by-value.
+#
+# However, if an object cannot be marshalled, a dRuby reference to it
+# is passed or returned instead.  This will turn up at the remote end
+# as a DRbObject instance.  All methods invoked upon this remote proxy
+# are forwarded to the local object, as described in the discussion of
+# DRbObjects.  This has semantics similar to the normal Ruby
+# pass-by-reference.
+# 
+# The easiest way to signal that we want an otherwise marshallable
+# object to be passed or returned as a DRbObject reference, rather
+# than marshalled and sent as a copy, is to include the
+# DRb::DRbUndumped mixin module.
+#
+# dRuby supports calling remote methods with blocks.  As blocks (or
+# rather the Proc objects that represent them) are not marshallable,
+# the block executes in the local, not the remote, context.  Each
+# value yielded to the block is passed from the remote object to the
+# local block, then the value returned by each block invocation is
+# passed back to the remote execution context to be collected, before
+# the collected values are finally returned to the local context as
+# the return value of the method invocation.
+# 
+# == Examples of usage
+#
+# For more dRuby samples, see the +samples+ directory in the full
+# dRuby distribution.
+#
+# === dRuby in client/server mode
+#
+# This illustrates setting up a simple client-server drb
+# system.  Run the server and client code in different terminals,
+# starting the server code first.
+#
+# ==== Server code
+#    
+#   require 'drb/drb'
+#     
+#   # The URI for the server to connect to
+#   URI="druby://localhost:8787" 
+#     
+#   class TimeServer
+#     
+#     def get_current_time
+#       return Time.now
+#     end
+#     
+#   end
+#     
+#   # The object that handles requests on the server
+#   FRONT_OBJECT=TimeServer.new
+#
+#   $SAFE = 1   # disable eval() and friends
+#   
+#   DRb.start_service(URI, FRONT_OBJECT)
+#   # Wait for the drb server thread to finish before exiting.
+#   DRb.thread.join
+#
+# ==== Client code
+#     
+#   require 'drb/drb'
+#   
+#   # The URI to connect to
+#   SERVER_URI="druby://localhost:8787"
+#
+#   # Start a local DRbServer to handle callbacks.
+#   #
+#   # Not necessary for this small example, but will be required
+#   # as soon as we pass a non-marshallable object as an argument
+#   # to a dRuby call.
+#   DRb.start_service
+#   
+#   timeserver = DRbObject.new_with_uri(SERVER_URI)
+#   puts timeserver.get_current_time 
+#
+# === Remote objects under dRuby
+#
+# This example illustrates returning a reference to an object
+# from a dRuby call.  The Logger instances live in the server
+# process.  References to them are returned to the client process,
+# where methods can be invoked upon them.  These methods are 
+# executed in the server process.
+#
+# ==== Server code
+#   
+#   require 'drb/drb'
+#   
+#   URI="druby://localhost:8787"
+#   
+#   class Logger
+#
+#       # Make dRuby send Logger instances as dRuby references,
+#       # not copies.
+#       include DRb::DRbUndumped
+#   
+#       def initialize(n, fname)
+#           @name = n
+#           @filename = fname
+#       end
+#   
+#       def log(message)
+#           File.open(@filename, "a") do |f|
+#               f.puts("#{Time.now}: #{@name}: #{message}")
+#           end
+#       end
+#   
+#   end
+#   
+#   # We have a central object for creating and retrieving loggers.
+#   # This retains a local reference to all loggers created.  This
+#   # is so an existing logger can be looked up by name, but also
+#   # to prevent loggers from being garbage collected.  A dRuby
+#   # reference to an object is not sufficient to prevent it being
+#   # garbage collected!
+#   class LoggerFactory
+#   
+#       def initialize(bdir)
+#           @basedir = bdir
+#           @loggers = {}
+#       end
+#   
+#       def get_logger(name)
+#           if ! loggers.has_key? name
+#               # make the filename safe, then declare it to be so
+#               fname = name.gsub(/[.\/]/, "_").untaint
+#               @loggers[name] = Logger.new(name, @basedir + "/" + fname)
+#           end
+#           return @loggers[name]
+#       end
+#   
+#   end
+#   
+#   FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
+#
+#   $SAFE = 1   # disable eval() and friends
+#   
+#   DRb.start_service(URI, FRONT_OBJECT)
+#   DRb.thread.join
+#
+# ==== Client code
+#
+#   require 'drb/drb'
+#   
+#   SERVER_URI="druby://localhost:8787"
+#
+#   DRb.start_service
+#   
+#   log_service=DRbObject.new_with_uri(SERVER_URI)
+#   
+#   ["loga", "logb", "logc"].each do |logname|
+#   
+#       logger=log_service.get_logger(logname)
+#   
+#       logger.log("Hello, world!")
+#       logger.log("Goodbye, world!")
+#       logger.log("=== EOT ===")
+#   
+#   end
+#
+# == Security
+#
+# As with all network services, security needs to be considered when
+# using dRuby.  By allowing external access to a Ruby object, you are
+# not only allowing outside clients to call the methods you have
+# defined for that object, but by default to execute arbitrary Ruby
+# code on your server.  Consider the following:
+#
+#    # !!! UNSAFE CODE !!!
+#    ro = DRbObject::new_with_uri("druby://your.server.com:8989")
+#    class << ro
+#      undef :instance_eval  # force call to be passed to remote object
+#    end
+#    ro.instance_eval("`rm -rf *`")
+#
+# The dangers posed by instance_eval and friends are such that a
+# DRbServer should generally be run with $SAFE set to at least 
+# level 1.  This will disable eval() and related calls on strings 
+# passed across the wire.  The sample usage code given above follows 
+# this practice.
+#
+# A DRbServer can be configured with an access control list to
+# selectively allow or deny access from specified IP addresses.  The
+# main druby distribution provides the ACL class for this purpose.  In
+# general, this mechanism should only be used alongside, rather than
+# as a replacement for, a good firewall.
+#
+# == dRuby internals
+#
+# dRuby is implemented using three main components: a remote method
+# call marshaller/unmarshaller; a transport protocol; and an
+# ID-to-object mapper.  The latter two can be directly, and the first
+# indirectly, replaced, in order to provide different behaviour and
+# capabilities.
+#
+# Marshalling and unmarshalling of remote method calls is performed by
+# a DRb::DRbMessage instance.  This uses the Marshal module to dump
+# the method call before sending it over the transport layer, then
+# reconstitute it at the other end.  There is normally no need to
+# replace this component, and no direct way is provided to do so.
+# However, it is possible to implement an alternative marshalling
+# scheme as part of an implementation of the transport layer.
+#
+# The transport layer is responsible for opening client and server
+# network connections and forwarding dRuby request across them.
+# Normally, it uses DRb::DRbMessage internally to manage marshalling
+# and unmarshalling.  The transport layer is managed by
+# DRb::DRbProtocol.  Multiple protocols can be installed in
+# DRbProtocol at the one time; selection between them is determined by
+# the scheme of a dRuby URI.  The default transport protocol is
+# selected by the scheme 'druby:', and implemented by
+# DRb::DRbTCPSocket.  This uses plain TCP/IP sockets for
+# communication.  An alternative protocol, using UNIX domain sockets,
+# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
+# selected by the scheme 'drbunix:'.  A sample implementation over
+# HTTP can be found in the samples accompanying the main dRuby
+# distribution.
+#
+# The ID-to-object mapping component maps dRuby object ids to the
+# objects they refer to, and vice versa.  The implementation to use
+# can be specified as part of a DRb::DRbServer's configuration.  The
+# default implementation is provided by DRb::DRbIdConv.  It uses an
+# object's ObjectSpace id as its dRuby id.  This means that the dRuby
+# reference to that object only remains meaningful for the lifetime of
+# the object's process and the lifetime of the object within that
+# process.  A modified implementation is provided by DRb::TimerIdConv
+# in the file drb/timeridconv.rb.  This implementation retains a local
+# reference to all objects exported over dRuby for a configurable
+# period of time (defaulting to ten minutes), to prevent them being
+# garbage-collected within this time.  Another sample implementation
+# is provided in sample/name.rb in the main dRuby distribution.  This
+# allows objects to specify their own id or "name".  A dRuby reference
+# can be made persistent across processes by having each process
+# register an object using the same dRuby name.
+#
+module DRb
+
+  # Superclass of all errors raised in the DRb module.
+  class DRbError < RuntimeError; end
+
+  # Error raised when an error occurs on the underlying communication
+  # protocol.
+  class DRbConnError < DRbError; end
+
+  # Class responsible for converting between an object and its id.
+  #
+  # This, the default implementation, uses an object's local ObjectSpace
+  # __id__ as its id.  This means that an object's identification over
+  # drb remains valid only while that object instance remains alive 
+  # within the server runtime.
+  #
+  # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
+  # and DRbNameIdConv in sample/name.rb in the full drb distribution.
+  class DRbIdConv
+
+    # Convert an object reference id to an object.
+    #
+    # This implementation looks up the reference id in the local object
+    # space and returns the object it refers to.
+    def to_obj(ref)
+      ObjectSpace._id2ref(ref)
+    end
+    
+    # Convert an object into a reference id.
+    #
+    # This implementation returns the object's __id__ in the local
+    # object space.
+    def to_id(obj)
+      obj.nil? ? nil : obj.__id__
+    end
+  end
+
+  # Mixin module making an object undumpable or unmarshallable.
+  #
+  # If an object which includes this module is returned by method
+  # called over drb, then the object remains in the server space
+  # and a reference to the object is returned, rather than the
+  # object being marshalled and moved into the client space.
+  module DRbUndumped 
+    def _dump(dummy)  # :nodoc:
+      raise TypeError, 'can\'t dump'
+    end
+  end
+
+  # Error raised by the DRb module when an attempt is made to refer to
+  # the context's current drb server but the context does not have one.
+  # See #current_server.
+  class DRbServerNotFound < DRbError; end
+
+  # Error raised by the DRbProtocol module when it cannot find any
+  # protocol implementation support the scheme specified in a URI.
+  class DRbBadURI < DRbError; end
+
+  # Error raised by a dRuby protocol when it doesn't support the
+  # scheme specified in a URI.  See DRb::DRbProtocol.
+  class DRbBadScheme < DRbError; end
+
+  # An exception wrapping a DRb::DRbUnknown object
+  class DRbUnknownError < DRbError
+
+    # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
+    def initialize(unknown)
+      @unknown = unknown
+      super(unknown.name)
+    end
+
+    # Get the wrapped DRb::DRbUnknown object.
+    attr_reader :unknown
+
+    def self._load(s)  # :nodoc:
+      Marshal::load(s)
+    end
+    
+    def _dump(lv) # :nodoc:
+      Marshal::dump(@unknown)
+    end
+  end
+
+  class DRbRemoteError < DRbError
+    def initialize(error)
+      @reason = error.class.to_s
+      super("#{error.message} (#{error.class})")
+      set_backtrace(error.backtrace)
+    end
+    attr_reader :reason
+  end
+
+  # Class wrapping a marshalled object whose type is unknown locally.
+  #
+  # If an object is returned by a method invoked over drb, but the
+  # class of the object is unknown in the client namespace, or
+  # the object is a constant unknown in the client namespace, then
+  # the still-marshalled object is returned wrapped in a DRbUnknown instance.
+  #
+  # If this object is passed as an argument to a method invoked over
+  # drb, then the wrapped object is passed instead.
+  #
+  # The class or constant name of the object can be read from the
+  # +name+ attribute.  The marshalled object is held in the +buf+
+  # attribute.
+  class DRbUnknown
+    
+    # Create a new DRbUnknown object.
+    #
+    # +buf+ is a string containing a marshalled object that could not
+    # be unmarshalled.  +err+ is the error message that was raised 
+    # when the unmarshalling failed.  It is used to determine the
+    # name of the unmarshalled object.
+    def initialize(err, buf)
+      case err.to_s
+      when /uninitialized constant (\S+)/
+	@name = $1
+      when /undefined class\/module (\S+)/
+	@name = $1
+      else
+	@name = nil
+      end
+      @buf = buf
+    end
+
+    # The name of the unknown thing.
+    #
+    # Class name for unknown objects; variable name for unknown
+    # constants.
+    attr_reader :name
+
+    # Buffer contained the marshalled, unknown object.
+    attr_reader :buf
+
+    def self._load(s) # :nodoc:
+      begin
+	Marshal::load(s)
+      rescue NameError, ArgumentError
+	DRbUnknown.new($!, s)
+      end
+    end
+
+    def _dump(lv) # :nodoc:
+      @buf
+    end
+
+    # Attempt to load the wrapped marshalled object again.
+    #
+    # If the class of the object is now known locally, the object
+    # will be unmarshalled and returned.  Otherwise, a new 
+    # but identical DRbUnknown object will be returned.
+    def reload
+      self.class._load(@buf)
+    end
+
+    # Create a DRbUnknownError exception containing this object.
+    def exception
+      DRbUnknownError.new(self)
+    end
+  end
+
+  class DRbArray
+    def initialize(ary)
+      @ary = ary.collect { |obj| 
+	if obj.kind_of? DRbUndumped
+	  DRbObject.new(obj)
+	else
+	  begin
+	    Marshal.dump(obj)
+	    obj
+	  rescue
+	    DRbObject.new(obj)
+	  end
+	end
+      }
+    end
+
+    def self._load(s)
+      Marshal::load(s)
+    end
+
+    def _dump(lv)
+      Marshal.dump(@ary)
+    end
+  end
+
+  # Handler for sending and receiving drb messages.
+  #
+  # This takes care of the low-level marshalling and unmarshalling
+  # of drb requests and responses sent over the wire between server
+  # and client.  This relieves the implementor of a new drb
+  # protocol layer with having to deal with these details.
+  #
+  # The user does not have to directly deal with this object in
+  # normal use.
+  class DRbMessage
+    def initialize(config) # :nodoc:
+      @load_limit = config[:load_limit]
+      @argc_limit = config[:argc_limit]
+    end
+
+    def dump(obj, error=false)  # :nodoc:
+      obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
+      begin
+	str = Marshal::dump(obj)
+      rescue
+	str = Marshal::dump(make_proxy(obj, error))
+      end
+      [str.size].pack('N') + str
+    end
+
+    def load(soc)  # :nodoc:
+      begin
+        sz = soc.read(4)	# sizeof (N)
+      rescue
+        raise(DRbConnError, $!.message, $!.backtrace)
+      end
+      raise(DRbConnError, 'connection closed') if sz.nil?
+      raise(DRbConnError, 'premature header') if sz.size < 4
+      sz = sz.unpack('N')[0]
+      raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
+      begin
+        str = soc.read(sz)
+      rescue
+        raise(DRbConnError, $!.message, $!.backtrace)
+      end
+      raise(DRbConnError, 'connection closed') if str.nil?
+      raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
+      Thread.exclusive do
+        begin
+          save = Thread.current[:drb_untaint]
+          Thread.current[:drb_untaint] = []
+          Marshal::load(str)
+        rescue NameError, ArgumentError
+          DRbUnknown.new($!, str)
+        ensure
+          Thread.current[:drb_untaint].each do |x|
+            x.untaint
+          end
+          Thread.current[:drb_untaint] = save
+        end
+      end
+    end
+
+    def send_request(stream, ref, msg_id, arg, b) # :nodoc:
+      ary = []
+      ary.push(dump(ref.__drbref))
+      ary.push(dump(msg_id.id2name))
+      ary.push(dump(arg.length))
+      arg.each do |e|
+	ary.push(dump(e))
+      end
+      ary.push(dump(b))
+      stream.write(ary.join(''))
+    rescue
+      raise(DRbConnError, $!.message, $!.backtrace)
+    end
+    
+    def recv_request(stream) # :nodoc:
+      ref = load(stream)
+      ro = DRb.to_obj(ref)
+      msg = load(stream)
+      argc = load(stream)
+      raise ArgumentError, 'too many arguments' if @argc_limit < argc
+      argv = Values.new(argc, nil)
+      argc.times do |n|
+	argv[n] = load(stream)
+      end
+      block = load(stream)
+      return ro, msg, argv, block
+    end
+
+    def send_reply(stream, succ, result)  # :nodoc:
+      stream.write(dump(succ) + dump(result, !succ))
+    rescue
+      raise(DRbConnError, $!.message, $!.backtrace)
+    end
+
+    def recv_reply(stream)  # :nodoc:
+      succ = load(stream)
+      result = load(stream)
+      [succ, result]
+    end
+
+    private
+    def make_proxy(obj, error=false)
+      if error
+        DRbRemoteError.new(obj)
+      else
+        DRbObject.new(obj)
+      end
+    end
+  end
+
+  # Module managing the underlying network protocol(s) used by drb.
+  #
+  # By default, drb uses the DRbTCPSocket protocol.  Other protocols
+  # can be defined.  A protocol must define the following class methods:
+  #
+  #   [open(uri, config)] Open a client connection to the server at +uri+,
+  #                       using configuration +config+.  Return a protocol
+  #                       instance for this connection.
+  #   [open_server(uri, config)] Open a server listening at +uri+,
+  #                              using configuration +config+.  Return a
+  #                              protocol instance for this listener.
+  #   [uri_option(uri, config)] Take a URI, possibly containing an option
+  #                             component (e.g. a trailing '?param=val'), 
+  #                             and return a [uri, option] tuple.
+  #
+  # All of these methods should raise a DRbBadScheme error if the URI 
+  # does not identify the protocol they support (e.g. "druby:" for
+  # the standard Ruby protocol).  This is how the DRbProtocol module,
+  # given a URI, determines which protocol implementation serves that
+  # protocol.
+  #
+  # The protocol instance returned by #open_server must have the
+  # following methods:
+  #
+  # [accept] Accept a new connection to the server.  Returns a protocol
+  #          instance capable of communicating with the client.
+  # [close] Close the server connection.
+  # [uri] Get the URI for this server.
+  #
+  # The protocol instance returned by #open must have the following methods:
+  #
+  # [send_request (ref, msg_id, arg, b)] 
+  #      Send a request to +ref+ with the given message id and arguments.
+  #      This is most easily implemented by calling DRbMessage.send_request,
+  #      providing a stream that sits on top of the current protocol.
+  # [recv_reply]
+  #      Receive a reply from the server and return it as a [success-boolean,
+  #      reply-value] pair.  This is most easily implemented by calling
+  #      DRb.recv_reply, providing a stream that sits on top of the 
+  #      current protocol.
+  # [alive?]
+  #      Is this connection still alive?
+  # [close]
+  #      Close this connection.
+  #
+  # The protocol instance returned by #open_server().accept() must have
+  # the following methods:
+  #
+  # [recv_request]
+  #     Receive a request from the client and return a [object, message,
+  #     args, block] tuple.  This is most easily implemented by calling
+  #     DRbMessage.recv_request, providing a stream that sits on top of
+  #     the current protocol.
+  # [send_reply(succ, result)]
+  #     Send a reply to the client.  This is most easily implemented
+  #     by calling DRbMessage.send_reply, providing a stream that sits
+  #     on top of the current protocol.
+  # [close]
+  #     Close this connection.
+  #
+  # A new protocol is registered with the DRbProtocol module using
+  # the add_protocol method.
+  #
+  # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
+  # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
+  # drb distribution.
+  module DRbProtocol
+
+    # Add a new protocol to the DRbProtocol module.
+    def add_protocol(prot)
+      @protocol.push(prot)
+    end
+    module_function :add_protocol
+
+    # Open a client connection to +uri+ with the configuration +config+.
+    #
+    # The DRbProtocol module asks each registered protocol in turn to
+    # try to open the URI.  Each protocol signals that it does not handle that
+    # URI by raising a DRbBadScheme error.  If no protocol recognises the
+    # URI, then a DRbBadURI error is raised.  If a protocol accepts the
+    # URI, but an error occurs in opening it, a DRbConnError is raised.
+    def open(uri, config, first=true) 
+      @protocol.each do |prot|
+	begin
+	  return prot.open(uri, config)
+	rescue DRbBadScheme
+	rescue DRbConnError
+	  raise($!)
+	rescue
+	  raise(DRbConnError, "#{uri} - #{$!.inspect}")
+	end
+      end
+      if first && (config[:auto_load] != false)
+	auto_load(uri, config)
+	return open(uri, config, false)
+      end
+      raise DRbBadURI, 'can\'t parse uri:' + uri
+    end
+    module_function :open
+
+    # Open a server listening for connections at +uri+ with 
+    # configuration +config+.
+    #
+    # The DRbProtocol module asks each registered protocol in turn to
+    # try to open a server at the URI.  Each protocol signals that it does 
+    # not handle that URI by raising a DRbBadScheme error.  If no protocol 
+    # recognises the URI, then a DRbBadURI error is raised.  If a protocol 
+    # accepts the URI, but an error occurs in opening it, the underlying 
+    # error is passed on to the caller.
+    def open_server(uri, config, first=true)
+      @protocol.each do |prot|
+	begin
+	  return prot.open_server(uri, config)
+	rescue DRbBadScheme
+	end
+      end
+      if first && (config[:auto_load] != false)
+	auto_load(uri, config)
+	return open_server(uri, config, false)
+      end
+      raise DRbBadURI, 'can\'t parse uri:' + uri
+    end
+    module_function :open_server
+
+    # Parse +uri+ into a [uri, option] pair.
+    #
+    # The DRbProtocol module asks each registered protocol in turn to
+    # try to parse the URI.  Each protocol signals that it does not handle that
+    # URI by raising a DRbBadScheme error.  If no protocol recognises the
+    # URI, then a DRbBadURI error is raised.  
+    def uri_option(uri, config, first=true)
+      @protocol.each do |prot|
+	begin
+	  uri, opt = prot.uri_option(uri, config)
+	  # opt = nil if opt == ''
+	  return uri, opt
+	rescue DRbBadScheme
+	end
+      end
+      if first && (config[:auto_load] != false)
+	auto_load(uri, config)
+        return uri_option(uri, config, false)
+      end
+      raise DRbBadURI, 'can\'t parse uri:' + uri
+    end
+    module_function :uri_option
+
+    def auto_load(uri, config)  # :nodoc:
+      if uri =~ /^drb([a-z0-9]+):/
+	require("drb/#{$1}") rescue nil
+      end
+    end
+    module_function :auto_load
+  end
+
+  # The default drb protocol.
+  #
+  # Communicates over a TCP socket.
+  class DRbTCPSocket
+    private
+    def self.parse_uri(uri)
+      if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
+	host = $1
+	port = $2.to_i
+	option = $4
+	[host, port, option]
+      else
+	raise(DRbBadScheme, uri) unless uri =~ /^druby:/
+	raise(DRbBadURI, 'can\'t parse uri:' + uri)
+      end
+    end
+
+    public
+
+    # Open a client connection to +uri+ using configuration +config+.
+    def self.open(uri, config)
+      host, port, option = parse_uri(uri)
+      host.untaint
+      port.untaint
+      soc = TCPSocket.open(host, port)
+      self.new(uri, soc, config)
+    end
+
+    def self.getservername
+      host = Socket::gethostname
+      begin
+        Socket::gethostbyname(host)[0]
+      rescue
+        host
+      end
+    end
+
+    def self.open_server_inaddr_any(host, port)
+      infos = Socket::getaddrinfo(host, nil, 
+                                  Socket::AF_UNSPEC,
+                                  Socket::SOCK_STREAM, 
+                                  0,
+                                  Socket::AI_PASSIVE)
+      family = infos.collect { |af, *_| af }.uniq
+      case family
+      when ['AF_INET']
+        return TCPServer.open('0.0.0.0', port)
+      when ['AF_INET6']
+        return TCPServer.open('::', port)
+      else
+        return TCPServer.open(port)
+      end
+    end
+
+    # Open a server listening for connections at +uri+ using 
+    # configuration +config+.
+    def self.open_server(uri, config)
+      uri = 'druby://:0' unless uri
+      host, port, opt = parse_uri(uri)
+      if host.size == 0
+        host = getservername
+        soc = open_server_inaddr_any(host, port)
+      else
+	soc = TCPServer.open(host, port)
+      end
+      port = soc.addr[1] if port == 0
+      uri = "druby://#{host}:#{port}"
+      self.new(uri, soc, config)
+    end
+
+    # Parse +uri+ into a [uri, option] pair.
+    def self.uri_option(uri, config)
+      host, port, option = parse_uri(uri)
+      return "druby://#{host}:#{port}", option
+    end
+
+    # Create a new DRbTCPSocket instance.
+    #
+    # +uri+ is the URI we are connected to.
+    # +soc+ is the tcp socket we are bound to.  +config+ is our
+    # configuration.
+    def initialize(uri, soc, config={})
+      @uri = uri
+      @socket = soc
+      @config = config
+      @acl = config[:tcp_acl]
+      @msg = DRbMessage.new(config)
+      set_sockopt(@socket)
+    end
+
+    # Get the URI that we are connected to.
+    attr_reader :uri
+
+    # Get the address of our TCP peer (the other end of the socket
+    # we are bound to.
+    def peeraddr
+      @socket.peeraddr
+    end
+    
+    # Get the socket.
+    def stream; @socket; end
+
+    # On the client side, send a request to the server.
+    def send_request(ref, msg_id, arg, b)
+      @msg.send_request(stream, ref, msg_id, arg, b)
+    end
+    
+    # On the server side, receive a request from the client.
+    def recv_request
+      @msg.recv_request(stream)
+    end
+
+    # On the server side, send a reply to the client.
+    def send_reply(succ, result)
+      @msg.send_reply(stream, succ, result)
+    end
+
+    # On the client side, receive a reply from the server.
+    def recv_reply
+      @msg.recv_reply(stream)
+    end
+
+    public
+
+    # Close the connection.
+    #
+    # If this is an instance returned by #open_server, then this stops
+    # listening for new connections altogether.  If this is an instance
+    # returned by #open or by #accept, then it closes this particular
+    # client-server session.
+    def close
+      if @socket
+	@socket.close
+	@socket = nil
+      end
+    end
+    
+    # On the server side, for an instance returned by #open_server, 
+    # accept a client connection and return a new instance to handle
+    # the server's side of this client-server session.
+    def accept
+      while true
+	s = @socket.accept
+	break if (@acl ? @acl.allow_socket?(s) : true) 
+	s.close
+      end
+      self.class.new(nil, s, @config)
+    end
+
+    # Check to see if this connection is alive.
+    def alive?
+      return false unless @socket
+      if IO.select([@socket], nil, nil, 0)
+	close
+	return false
+      end
+      true
+    end
+
+    def set_sockopt(soc) # :nodoc:
+      soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+      soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+    end
+  end
+
+  module DRbProtocol
+    @protocol = [DRbTCPSocket] # default
+  end
+
+  class DRbURIOption  # :nodoc:  I don't understand the purpose of this class...
+    def initialize(option)
+      @option = option.to_s
+    end
+    attr :option
+    def to_s; @option; end
+    
+    def ==(other)
+      return false unless DRbURIOption === other
+      @option == other.option
+    end
+    
+    def hash
+      @option.hash
+    end
+    
+    alias eql? ==
+  end
+
+  # Object wrapping a reference to a remote drb object.
+  #
+  # Method calls on this object are relayed to the remote
+  # object that this object is a stub for.
+  class DRbObject
+
+    # Unmarshall a marshalled DRbObject.
+    #
+    # If the referenced object is located within the local server, then
+    # the object itself is returned.  Otherwise, a new DRbObject is
+    # created to act as a stub for the remote referenced object.
+    def self._load(s)
+      uri, ref = Marshal.load(s)
+      
+      if DRb.here?(uri)
+	obj = DRb.to_obj(ref)
+        if ((! obj.tainted?) && Thread.current[:drb_untaint])
+          Thread.current[:drb_untaint].push(obj)
+        end
+        return obj
+      end
+
+      self.new_with(uri, ref)
+    end
+
+    def self.new_with(uri, ref)
+      it = self.allocate
+      it.instance_variable_set('@uri', uri)
+      it.instance_variable_set('@ref', ref)
+      it
+    end
+
+    # Create a new DRbObject from a URI alone.
+    def self.new_with_uri(uri)
+      self.new(nil, uri)
+    end
+
+    # Marshall this object.
+    #
+    # The URI and ref of the object are marshalled.
+    def _dump(lv)
+      Marshal.dump([@uri, @ref])
+    end
+
+    # Create a new remote object stub.
+    #
+    # +obj+ is the (local) object we want to create a stub for.  Normally
+    # this is +nil+.  +uri+ is the URI of the remote object that this
+    # will be a stub for.
+    def initialize(obj, uri=nil)
+      @uri = nil
+      @ref = nil
+      if obj.nil?
+	return if uri.nil?
+	@uri, option = DRbProtocol.uri_option(uri, DRb.config)
+	@ref = DRbURIOption.new(option) unless option.nil?
+      else
+	@uri = uri ? uri : (DRb.uri rescue nil)
+	@ref = obj ? DRb.to_id(obj) : nil
+      end
+    end
+
+    # Get the URI of the remote object.
+    def __drburi 
+      @uri
+    end
+
+    # Get the reference of the object, if local.
+    def __drbref
+      @ref
+    end
+
+    undef :to_s
+    undef :to_a if respond_to?(:to_a)
+
+    def respond_to?(msg_id, priv=false)
+      case msg_id
+      when :_dump
+        true
+      when :marshal_dump
+        false
+      else
+        method_missing(:respond_to?, msg_id, priv)
+      end
+    end
+
+    # Routes method calls to the referenced object.
+    def method_missing(msg_id, *a, &b)
+      if DRb.here?(@uri)
+	obj = DRb.to_obj(@ref)
+	DRb.current_server.check_insecure_method(obj, msg_id)
+	return obj.__send__(msg_id, *a, &b) 
+      end
+
+      succ, result = self.class.with_friend(@uri) do
+        DRbConn.open(@uri) do |conn|
+          conn.send_message(self, msg_id, a, b)
+        end
+      end
+
+      if succ
+        return result
+      elsif DRbUnknown === result
+        raise result
+      else
+        bt = self.class.prepare_backtrace(@uri, result)
+	result.set_backtrace(bt + caller)
+        raise result
+      end
+    end
+
+    def self.with_friend(uri)
+      friend = DRb.fetch_server(uri)
+      return yield() unless friend
+      
+      save = Thread.current['DRb']
+      Thread.current['DRb'] = { 'server' => friend }
+      return yield
+    ensure
+      Thread.current['DRb'] = save if friend
+    end
+
+    def self.prepare_backtrace(uri, result)
+      prefix = "(#{uri}) "
+      bt = []
+      result.backtrace.each do |x|
+        break if /`__send__'$/ =~ x 
+        if /^\(druby:\/\// =~ x
+          bt.push(x)
+        else
+          bt.push(prefix + x)
+        end
+      end
+      bt
+    end
+
+    def pretty_print(q)   # :nodoc:
+      q.pp_object(self)
+    end
+
+    def pretty_print_cycle(q)   # :nodoc:
+      q.object_address_group(self) {
+        q.breakable
+        q.text '...'
+      }
+    end
+  end
+
+  # Class handling the connection between a DRbObject and the
+  # server the real object lives on.
+  #
+  # This class maintains a pool of connections, to reduce the
+  # overhead of starting and closing down connections for each
+  # method call.
+  #
+  # This class is used internally by DRbObject.  The user does
+  # not normally need to deal with it directly.
+  class DRbConn
+    POOL_SIZE = 16  # :nodoc:
+    @mutex = Mutex.new
+    @pool = []
+
+    def self.open(remote_uri)  # :nodoc:
+      begin
+	conn = nil
+
+	@mutex.synchronize do
+	  #FIXME
+	  new_pool = []
+	  @pool.each do |c|
+	    if conn.nil? and c.uri == remote_uri
+	      conn = c if c.alive?
+	    else
+	      new_pool.push c
+	    end
+	  end
+	  @pool = new_pool
+	end
+
+	conn = self.new(remote_uri) unless conn
+	succ, result = yield(conn)
+	return succ, result
+
+      ensure
+	if conn
+	  if succ
+	    @mutex.synchronize do
+	      @pool.unshift(conn)
+	      @pool.pop.close while @pool.size > POOL_SIZE
+	    end
+	  else
+	    conn.close
+	  end
+	end
+      end
+    end
+
+    def initialize(remote_uri)  # :nodoc:
+      @uri = remote_uri
+      @protocol = DRbProtocol.open(remote_uri, DRb.config)
+    end
+    attr_reader :uri  # :nodoc:
+
+    def send_message(ref, msg_id, arg, block)  # :nodoc:
+      @protocol.send_request(ref, msg_id, arg, block)
+      @protocol.recv_reply
+    end
+
+    def close  # :nodoc:
+      @protocol.close
+      @protocol = nil
+    end
+
+    def alive?  # :nodoc:
+      @protocol.alive?
+    end
+  end
+
+  # Class representing a drb server instance.
+  #
+  # A DRbServer must be running in the local process before any incoming
+  # dRuby calls can be accepted, or any local objects can be passed as
+  # dRuby references to remote processes, even if those local objects are
+  # never actually called remotely. You do not need to start a DRbServer
+  # in the local process if you are only making outgoing dRuby calls
+  # passing marshalled parameters.
+  #
+  # Unless multiple servers are being used, the local DRbServer is normally
+  # started by calling DRb.start_service.
+  class DRbServer
+    @@acl = nil
+    @@idconv = DRbIdConv.new
+    @@secondary_server = nil
+    @@argc_limit = 256
+    @@load_limit = 256 * 102400
+    @@verbose = false
+    @@safe_level = 0
+
+    # Set the default value for the :argc_limit option.
+    #
+    # See #new().  The initial default value is 256.
+    def self.default_argc_limit(argc)
+      @@argc_limit = argc
+    end
+
+    # Set the default value for the :load_limit option.
+    #
+    # See #new().  The initial default value is 25 MB.
+    def self.default_load_limit(sz)
+      @@load_limit = sz
+    end
+
+    # Set the default value for the :acl option.
+    #
+    # See #new().  The initial default value is nil.
+    def self.default_acl(acl)
+      @@acl = acl
+    end
+
+    # Set the default value for the :id_conv option.
+    #
+    # See #new().  The initial default value is a DRbIdConv instance.
+    def self.default_id_conv(idconv)
+      @@idconv = idconv
+    end
+
+    def self.default_safe_level(level)
+      @@safe_level = level
+    end
+
+    # Set the default value of the :verbose option.
+    #
+    # See #new().  The initial default value is false.
+    def self.verbose=(on)
+      @@verbose = on
+    end
+    
+    # Get the default value of the :verbose option.
+    def self.verbose
+      @@verbose
+    end
+
+    def self.make_config(hash={})  # :nodoc:
+      default_config = { 
+	:idconv => @@idconv,
+	:verbose => @@verbose,
+	:tcp_acl => @@acl,
+	:load_limit => @@load_limit,
+	:argc_limit => @@argc_limit,
+        :safe_level => @@safe_level
+      }
+      default_config.update(hash)
+    end
+
+    # Create a new DRbServer instance.
+    #
+    # +uri+ is the URI to bind to.  This is normally of the form
+    # 'druby://<hostname>:<port>' where <hostname> is a hostname of
+    # the local machine.  If nil, then the system's default hostname
+    # will be bound to, on a port selected by the system; these value
+    # can be retrieved from the +uri+ attribute.  'druby:' specifies
+    # the default dRuby transport protocol: another protocol, such
+    # as 'drbunix:', can be specified instead.
+    #
+    # +front+ is the front object for the server, that is, the object
+    # to which remote method calls on the server will be passed.  If
+    # nil, then the server will not accept remote method calls.
+    #
+    # If +config_or_acl+ is a hash, it is the configuration to
+    # use for this server.  The following options are recognised:
+    #
+    # :idconv :: an id-to-object conversion object.  This defaults
+    #            to an instance of the class DRb::DRbIdConv.
+    # :verbose :: if true, all unsuccessful remote calls on objects
+    #             in the server will be logged to $stdout. false
+    #             by default.
+    # :tcp_acl :: the access control list for this server.  See
+    #             the ACL class from the main dRuby distribution.
+    # :load_limit :: the maximum message size in bytes accepted by
+    #                the server.  Defaults to 25 MB (26214400).
+    # :argc_limit :: the maximum number of arguments to a remote
+    #                method accepted by the server.  Defaults to
+    #                256.
+    #
+    # The default values of these options can be modified on
+    # a class-wide basis by the class methods #default_argc_limit,
+    # #default_load_limit, #default_acl, #default_id_conv,
+    # and #verbose=
+    #
+    # If +config_or_acl+ is not a hash, but is not nil, it is
+    # assumed to be the access control list for this server.
+    # See the :tcp_acl option for more details.
+    #
+    # If no other server is currently set as the primary server,
+    # this will become the primary server.
+    #
+    # The server will immediately start running in its own thread.
+    def initialize(uri=nil, front=nil, config_or_acl=nil)
+      if Hash === config_or_acl
+	config = config_or_acl.dup
+      else
+	acl = config_or_acl || @@acl
+	config = {
+	  :tcp_acl => acl
+	}
+      end
+
+      @config = self.class.make_config(config)
+
+      @protocol = DRbProtocol.open_server(uri, @config)
+      @uri = @protocol.uri
+
+      @front = front
+      @idconv = @config[:idconv]
+      @safe_level = @config[:safe_level]
+
+      @grp = ThreadGroup.new
+      @thread = run
+
+      DRb.regist_server(self)
+    end
+
+    # The URI of this DRbServer.
+    attr_reader :uri
+
+    # The main thread of this DRbServer.
+    #
+    # This is the thread that listens for and accepts connections
+    # from clients, not that handles each client's request-response
+    # session.
+    attr_reader :thread
+
+    # The front object of the DRbServer.
+    # 
+    # This object receives remote method calls made on the server's
+    # URI alone, with an object id.
+    attr_reader :front
+
+    # The configuration of this DRbServer
+    attr_reader :config
+
+    attr_reader :safe_level
+
+    # Set whether to operate in verbose mode.
+    #
+    # In verbose mode, failed calls are logged to stdout.
+    def verbose=(v); @config[:verbose]=v; end
+
+    # Get whether the server is in verbose mode.
+    #
+    # In verbose mode, failed calls are logged to stdout.
+    def verbose; @config[:verbose]; end
+
+    # Is this server alive?
+    def alive?
+      @thread.alive?
+    end
+
+    # Stop this server.
+    def stop_service
+      DRb.remove_server(self)
+      if  Thread.current['DRb'] && Thread.current['DRb']['server'] == self
+        Thread.current['DRb']['stop_service'] = true
+      else
+        @thread.kill
+      end
+    end
+
+    # Convert a dRuby reference to the local object it refers to.
+    def to_obj(ref)
+      return front if ref.nil?
+      return front[ref.to_s] if DRbURIOption === ref
+      @idconv.to_obj(ref)
+    end
+
+    # Convert a local object to a dRuby reference.
+    def to_id(obj)
+      return nil if obj.__id__ == front.__id__
+      @idconv.to_id(obj)
+    end
+
+    private
+    def kill_sub_thread
+      Thread.new do
+	grp = ThreadGroup.new
+	grp.add(Thread.current)
+	list = @grp.list
+	while list.size > 0
+	  list.each do |th|
+	    th.kill if th.alive?
+	  end
+	  list = @grp.list
+	end
+      end
+    end
+
+    def run
+      Thread.start do
+	begin
+	  while true
+	    main_loop
+	  end
+	ensure
+	  @protocol.close if @protocol
+	  kill_sub_thread
+	end
+      end
+    end
+
+    # List of insecure methods.
+    #
+    # These methods are not callable via dRuby.
+    INSECURE_METHOD = [
+      :__send__
+    ]
+
+    # Has a method been included in the list of insecure methods?
+    def insecure_method?(msg_id)
+      INSECURE_METHOD.include?(msg_id)
+    end
+
+    # Coerce an object to a string, providing our own representation if
+    # to_s is not defined for the object.
+    def any_to_s(obj)
+      obj.to_s + ":#{obj.class}"
+    rescue
+      sprintf("#<%s:0x%lx>", obj.class, obj.__id__)      
+    end
+
+    # Check that a method is callable via dRuby.
+    #
+    # +obj+ is the object we want to invoke the method on. +msg_id+ is the
+    # method name, as a Symbol.
+    #
+    # If the method is an insecure method (see #insecure_method?) a 
+    # SecurityError is thrown.  If the method is private or undefined,
+    # a NameError is thrown.
+    def check_insecure_method(obj, msg_id)
+      return true if Proc === obj && msg_id == :__drb_yield
+      raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
+      raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
+      
+      if obj.private_methods.include?(msg_id.to_s)
+	desc = any_to_s(obj)
+        raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
+      elsif obj.protected_methods.include?(msg_id.to_s)
+	desc = any_to_s(obj)
+        raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
+      else
+        true
+      end
+    end
+    public :check_insecure_method
+    
+    class InvokeMethod  # :nodoc:
+      def initialize(drb_server, client)
+	@drb_server = drb_server
+        @safe_level = drb_server.safe_level
+	@client = client
+      end
+
+      def perform
+	@result = nil
+	@succ = false
+	setup_message
+
+        if $SAFE < @safe_level
+          info = Thread.current['DRb']
+          if @block
+            @result = Thread.new { 
+              Thread.current['DRb'] = info
+              $SAFE = @safe_level
+              perform_with_block
+            }.value
+          else
+            @result = Thread.new { 
+              Thread.current['DRb'] = info
+              $SAFE = @safe_level
+              perform_without_block
+            }.value
+          end
+        else
+          if @block
+            @result = perform_with_block
+          else
+            @result = perform_without_block
+          end
+        end
+	@succ = true
+	if @msg_id == :to_ary
+	  @result = DRbArray.new(@result) 
+	end
+	return @succ, @result
+      rescue StandardError, ScriptError, Interrupt
+	@result = $!
+	return @succ, @result
+      end
+
+      private
+      def init_with_client
+	obj, msg, argv, block = @client.recv_request
+        @obj = obj
+        @msg_id = msg.intern
+        @argv = argv
+        @block = block
+      end
+      
+      def check_insecure_method
+        @drb_server.check_insecure_method(@obj, @msg_id)
+      end
+
+      def setup_message
+	init_with_client
+	check_insecure_method
+      end
+      
+      def perform_without_block
+	if Proc === @obj && @msg_id == :__drb_yield
+          if @argv.size == 1
+	    ary = @argv
+	  else
+	    ary = [@argv]
+	  end
+	  ary.collect(&@obj)[0]
+	else
+	  @obj.funcall(@msg_id, *@argv)
+	end
+      end
+
+    end
+
+    if RUBY_VERSION >= '1.8'
+      require 'drb/invokemethod'
+      class InvokeMethod
+        include InvokeMethod18Mixin
+      end
+    else
+      require 'drb/invokemethod16'
+      class InvokeMethod
+        include InvokeMethod16Mixin
+      end
+    end
+
+    # The main loop performed by a DRbServer's internal thread.
+    #
+    # Accepts a connection from a client, and starts up its own
+    # thread to handle it.  This thread loops, receiving requests
+    # from the client, invoking them on a local object, and
+    # returning responses, until the client closes the connection
+    # or a local method call fails.
+    def main_loop
+      Thread.start( protocol.accept) do |client|
+	@grp.add Thread.current
+	Thread.current['DRb'] = { 'client' => client ,
+	                          'server' => self }
+	loop do
+	  begin
+	    succ = false
+	    invoke_method = InvokeMethod.new(self, client)
+	    succ, result = invoke_method.perform
+	    if !succ && verbose
+	      p result
+	      result.backtrace.each do |x|
+		puts x
+	      end
+	    end
+	    client.send_reply(succ, result) rescue nil
+	  ensure
+            client.close unless succ
+            if Thread.current['DRb']['stop_service']
+              Thread.new { stop_service }
+            end
+            break unless succ
+	  end
+	end
+      end
+    end
+  end
+
+  @primary_server = nil
+
+  # Start a dRuby server locally.
+  #
+  # The new dRuby server will become the primary server, even
+  # if another server is currently the primary server.
+  #
+  # +uri+ is the URI for the server to bind to.  If nil,
+  # the server will bind to random port on the default local host
+  # name and use the default dRuby protocol.
+  #
+  # +front+ is the server's front object.  This may be nil.
+  #
+  # +config+ is the configuration for the new server.  This may
+  # be nil.
+  #
+  # See DRbServer::new.
+  def start_service(uri=nil, front=nil, config=nil)
+    @primary_server = DRbServer.new(uri, front, config)
+  end
+  module_function :start_service
+
+  # The primary local dRuby server.
+  #
+  # This is the server created by the #start_service call.  
+  attr_accessor :primary_server
+  module_function :primary_server=, :primary_server
+
+  # Get the 'current' server.
+  #
+  # In the context of execution taking place within the main
+  # thread of a dRuby server (typically, as a result of a remote
+  # call on the server or one of its objects), the current
+  # server is that server.  Otherwise, the current server is
+  # the primary server.
+  #
+  # If the above rule fails to find a server, a DRbServerNotFound
+  # error is raised.
+  def current_server
+    drb = Thread.current['DRb'] 
+    server = (drb && drb['server']) ? drb['server'] : @primary_server 
+    raise DRbServerNotFound unless server
+    return server
+  end
+  module_function :current_server
+
+  # Stop the local dRuby server.
+  #
+  # This operates on the primary server.  If there is no primary
+  # server currently running, it is a noop.
+  def stop_service
+    @primary_server.stop_service if @primary_server
+    @primary_server = nil
+  end
+  module_function :stop_service
+
+  # Get the URI defining the local dRuby space.
+  #
+  # This is the URI of the current server.  See #current_server.
+  def uri
+    current_server.uri
+  end
+  module_function :uri
+
+  # Is +uri+ the URI for the current local server?
+  def here?(uri)
+    (current_server.uri rescue nil) == uri
+  end
+  module_function :here?
+
+  # Get the configuration of the current server.
+  #
+  # If there is no current server, this returns the default configuration.
+  # See #current_server and DRbServer::make_config.
+  def config
+    current_server.config
+  rescue
+    DRbServer.make_config
+  end
+  module_function :config
+  
+  # Get the front object of the current server.
+  #
+  # This raises a DRbServerNotFound error if there is no current server.
+  # See #current_server.
+  def front
+    current_server.front
+  end
+  module_function :front
+
+  # Convert a reference into an object using the current server.
+  #
+  # This raises a DRbServerNotFound error if there is no current server.
+  # See #current_server.
+  def to_obj(ref)
+    current_server.to_obj(ref)
+  end
+
+  # Get a reference id for an object using the current server.
+  #
+  # This raises a DRbServerNotFound error if there is no current server.
+  # See #current_server.
+  def to_id(obj)
+    current_server.to_id(obj)
+  end
+  module_function :to_id
+  module_function :to_obj
+
+  # Get the thread of the primary server.
+  #
+  # This returns nil if there is no primary server.  See #primary_server.
+  def thread
+    @primary_server ? @primary_server.thread : nil
+  end
+  module_function :thread
+
+  # Set the default id conv object.
+  #
+  # See DRbServer#default_id_conv.
+  def install_id_conv(idconv)
+    DRbServer.default_id_conv(idconv)
+  end
+  module_function :install_id_conv
+
+  # Set the default acl.
+  #
+  # See DRb::DRbServer.default_acl.
+  def install_acl(acl)
+    DRbServer.default_acl(acl)
+  end
+  module_function :install_acl
+
+  @server = {}
+  def regist_server(server)
+    @server[server.uri] = server
+    Thread.exclusive do
+      @primary_server = server unless @primary_server
+    end
+  end
+  module_function :regist_server
+
+  def remove_server(server)
+    @server.delete(server.uri)
+  end
+  module_function :remove_server
+  
+  def fetch_server(uri)
+    @server[uri]
+  end
+  module_function :fetch_server
+end
+
+DRbObject = DRb::DRbObject
+DRbUndumped = DRb::DRbUndumped
+DRbIdConv = DRb::DRbIdConv

Added: trunk/lib/drb/eq.rb
===================================================================
--- trunk/lib/drb/eq.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/eq.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,16 @@
+require 'drb/drb'
+
+module DRb
+  class DRbObject
+    def ==(other)
+      return false unless DRbObject === other
+     (@ref == other.__drbref) && (@uri == other.__drburi)
+    end
+
+    def hash
+      [@uri, @ref].hash
+    end
+
+    alias eql? ==
+  end
+end

Added: trunk/lib/drb/extserv.rb
===================================================================
--- trunk/lib/drb/extserv.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/extserv.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,64 @@
+=begin
+ external service
+ 	Copyright (c) 2000,2002 Masatoshi SEKI 
+=end
+
+require 'drb/drb'
+
+module DRb
+  class ExtServ
+    include DRbUndumped
+
+    def initialize(there, name, server=nil)
+      @server = server || DRb::primary_server
+      @name = name
+      ro = DRbObject.new(nil, there)
+      @invoker = ro.regist(name, DRbObject.new(self, @server.uri))
+    end
+    attr_reader :server
+
+    def front
+      DRbObject.new(nil, @server.uri)
+    end
+
+    def stop_service
+      @invoker.unregist(@name)
+      server = @server
+      @server = nil
+      server.stop_service
+      true
+    end
+
+    def alive?
+      @server ? @server.alive? : false
+    end
+  end
+end
+
+if __FILE__ == $0
+  class Foo
+    include DRbUndumped
+
+    def initialize(str)
+      @str = str
+    end
+
+    def hello(it)
+      "#{it}: #{self}"
+    end
+
+    def to_s
+      @str
+    end
+  end
+
+  cmd = ARGV.shift
+  case cmd
+  when 'itest1', 'itest2'
+    front = Foo.new(cmd)
+    manager = DRb::DRbServer.new(nil, front)
+    es = DRb::ExtServ.new(ARGV.shift, ARGV.shift, manager)
+    es.server.thread.join
+  end
+end
+

Added: trunk/lib/drb/extservm.rb
===================================================================
--- trunk/lib/drb/extservm.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/extservm.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,93 @@
+=begin
+ external service manager
+ 	Copyright (c) 2000 Masatoshi SEKI 
+=end
+
+require 'drb/drb'
+require 'thread'
+
+module DRb
+  class ExtServManager
+    include DRbUndumped
+
+    @@command = {}
+
+    def self.command
+      @@command
+    end
+
+    def self.command=(cmd)
+      @@command = cmd
+    end
+      
+    def initialize
+      @servers = {}
+      @waiting = []
+      @queue = Queue.new
+      @thread = invoke_thread
+      @uri = nil
+    end
+    attr_accessor :uri
+
+    def service(name)
+      while true
+	server = nil
+	Thread.exclusive do
+	  server = @servers[name] if @servers[name]
+	end
+	return server if server && server.alive?
+	invoke_service(name)
+      end
+    end
+
+    def regist(name, ro)
+      ary = nil
+      Thread.exclusive do
+	@servers[name] = ro
+	ary = @waiting
+	@waiting = []
+      end
+      ary.each do |th|
+	begin
+	  th.run
+	rescue ThreadError
+	end
+      end
+      self
+    end
+    
+    def unregist(name)
+      Thread.exclusive do
+	@servers.delete(name)
+      end
+    end
+
+    private
+    def invoke_thread
+      Thread.new do
+	while true
+	  name = @queue.pop
+	  invoke_service_command(name, @@command[name])
+	end
+      end
+    end
+
+    def invoke_service(name)
+      Thread.critical = true
+      @waiting.push Thread.current
+      @queue.push name
+      Thread.stop
+    end
+
+    def invoke_service_command(name, command)
+      raise "invalid command. name: #{name}" unless command
+      Thread.exclusive do
+	return if @servers.include?(name)
+	@servers[name] = false
+      end
+      uri = @uri || DRb.uri
+      Process.detach(Process.spawn("#{command} #{uri} #{name}"))
+      true
+    end
+  end
+end

Added: trunk/lib/drb/gw.rb
===================================================================
--- trunk/lib/drb/gw.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/gw.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,122 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+  class GWIdConv < DRbIdConv
+    def to_obj(ref)
+      if Array === ref && ref[0] == :DRbObject
+        return DRbObject.new_with(ref[1], ref[2])
+      end
+      super(ref)
+    end
+  end
+
+  class GW
+    include MonitorMixin
+    def initialize
+      super()
+      @hash = {}
+    end
+
+    def [](key)
+      synchronize do
+        @hash[key]
+      end
+    end
+
+    def []=(key, v)
+      synchronize do
+        @hash[key] = v
+      end
+    end
+  end
+
+  class DRbObject
+    def self._load(s)
+      uri, ref = Marshal.load(s)
+      if DRb.uri == uri
+        return ref ? DRb.to_obj(ref) : DRb.front
+      end
+
+      self.new_with(DRb.uri, [:DRbObject, uri, ref])
+    end
+
+    def _dump(lv)
+      if DRb.uri == @uri
+        if Array === @ref && @ref[0] == :DRbObject
+          Marshal.dump([@ref[1], @ref[2]])
+        else
+          Marshal.dump([@uri, @ref]) # ??
+        end
+      else
+        Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]])
+      end
+    end
+  end
+end
+
+=begin
+DRb.install_id_conv(DRb::GWIdConv.new)
+
+front = DRb::GW.new
+
+s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front)
+s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front)
+
+s1.thread.join
+s2.thread.join
+=end
+
+=begin
+# foo.rb
+
+require 'drb/drb'
+
+class Foo
+  include DRbUndumped
+  def initialize(name, peer=nil)
+    @name = name
+    @peer = peer
+  end
+
+  def ping(obj)
+    puts "#{@name}: ping: #{obj.inspect}"
+    @peer.ping(self) if @peer
+  end
+end
+=end
+
+=begin
+# gw_a.rb
+require 'drb/unix'
+require 'foo'
+
+obj = Foo.new('a')
+DRb.start_service("drbunix:/tmp/gw_a", obj)
+
+robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a')
+robj[:a] = obj
+
+DRb.thread.join
+=end
+
+=begin
+# gw_c.rb
+require 'drb/unix'
+require 'foo'
+
+foo = Foo.new('c', nil)
+
+DRb.start_service("drbunix:/tmp/gw_c", nil)
+
+robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c")
+
+puts "c->b"
+a = robj[:a]
+sleep 2
+
+a.ping(foo)
+
+DRb.thread.join
+=end
+

Added: trunk/lib/drb/invokemethod.rb
===================================================================
--- trunk/lib/drb/invokemethod.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/invokemethod.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,36 @@
+# for ruby-1.8.0
+
+module DRb
+  class DRbServer
+    module InvokeMethod18Mixin
+      def block_yield(x)
+	if x.size == 1 && x[0].class == Values
+	  x[0] = DRbArray.new(x[0])
+	end
+        block_value = @block.call(*x)
+      end
+
+      def perform_with_block
+        @obj.__send__(@msg_id, *@argv) do |*x|
+          jump_error = nil
+          begin
+            block_value = block_yield(x)
+          rescue LocalJumpError
+            jump_error = $!
+          end
+          if jump_error
+            case jump_error.reason
+            when :retry
+              retry
+            when :break
+              break(jump_error.exit_value)
+            else
+              raise jump_error
+            end
+          end
+          block_value
+        end
+      end
+    end
+  end
+end

Added: trunk/lib/drb/observer.rb
===================================================================
--- trunk/lib/drb/observer.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/observer.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,23 @@
+require 'observer'
+
+module DRb
+  module DRbObservable
+    include Observable
+
+    def notify_observers(*arg)
+      if defined? @observer_state and @observer_state
+        if defined? @observer_peers
+          @observer_peers.delete_if do |k, v|
+            begin
+              k.send(v, *arg)
+              false
+            rescue
+              true
+            end
+          end
+        end
+        @observer_state = false
+      end
+    end
+  end
+end

Added: trunk/lib/drb/ssl.rb
===================================================================
--- trunk/lib/drb/ssl.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/ssl.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,190 @@
+require 'socket'
+require 'openssl'
+require 'drb/drb'
+require 'singleton'
+
+module DRb
+
+  class DRbSSLSocket < DRbTCPSocket
+
+    class SSLConfig
+
+      DEFAULT = {
+	:SSLCertificate       => nil,
+	:SSLPrivateKey        => nil,
+	:SSLClientCA          => nil,
+	:SSLCACertificatePath => nil,
+	:SSLCACertificateFile => nil,
+	:SSLVerifyMode        => ::OpenSSL::SSL::VERIFY_NONE, 
+	:SSLVerifyDepth       => nil,
+	:SSLVerifyCallback    => nil,   # custom verification
+        :SSLCertificateStore  => nil,
+	# Must specify if you use auto generated certificate.
+	:SSLCertName          => nil,   # e.g. [["CN","fqdn.example.com"]]
+	:SSLCertComment       => "Generated by Ruby/OpenSSL"
+      }
+
+      def initialize(config)
+	@config  = config
+        @cert    = config[:SSLCertificate]
+        @pkey    = config[:SSLPrivateKey]
+        @ssl_ctx = nil
+      end
+
+      def [](key); 
+	@config[key] || DEFAULT[key]
+      end
+
+      def connect(tcp)
+	ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
+	ssl.sync = true
+	ssl.connect
+	ssl
+      end
+      
+      def accept(tcp)
+	ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
+	ssl.sync = true
+	ssl.accept
+	ssl
+      end
+      
+      def setup_certificate
+        if @cert && @pkey
+          return
+        end
+
+	rsa = OpenSSL::PKey::RSA.new(512){|p, n|
+	  next unless self[:verbose]
+	  case p
+	  when 0; $stderr.putc "."  # BN_generate_prime
+	  when 1; $stderr.putc "+"  # BN_generate_prime
+	  when 2; $stderr.putc "*"  # searching good prime,
+	                            # n = #of try,
+                          	    # but also data from BN_generate_prime
+	  when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
+                         	    # but also data from BN_generate_prime
+	  else;   $stderr.putc "*"  # BN_generate_prime
+	  end
+	}
+
+	cert = OpenSSL::X509::Certificate.new
+	cert.version = 3
+	cert.serial = 0
+	name = OpenSSL::X509::Name.new(self[:SSLCertName])
+	cert.subject = name
+	cert.issuer = name
+	cert.not_before = Time.now
+	cert.not_after = Time.now + (365*24*60*60)
+	cert.public_key = rsa.public_key
+	
+	ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+	cert.extensions = [
+	  ef.create_extension("basicConstraints","CA:FALSE"),
+	  ef.create_extension("subjectKeyIdentifier", "hash") ]
+	ef.issuer_certificate = cert
+	cert.add_extension(ef.create_extension("authorityKeyIdentifier",
+					       "keyid:always,issuer:always"))
+	if comment = self[:SSLCertComment]
+	  cert.add_extension(ef.create_extension("nsComment", comment))
+	end
+	cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+	
+	@cert = cert
+        @pkey = rsa
+      end
+
+      def setup_ssl_context
+        ctx = ::OpenSSL::SSL::SSLContext.new
+        ctx.cert            = @cert
+        ctx.key             = @pkey
+	ctx.client_ca       = self[:SSLClientCA]
+	ctx.ca_path         = self[:SSLCACertificatePath]
+	ctx.ca_file         = self[:SSLCACertificateFile]
+	ctx.verify_mode     = self[:SSLVerifyMode]
+	ctx.verify_depth    = self[:SSLVerifyDepth]
+	ctx.verify_callback = self[:SSLVerifyCallback]
+        ctx.cert_store      = self[:SSLCertificateStore]
+        @ssl_ctx = ctx
+      end
+    end
+
+    def self.parse_uri(uri)
+      if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
+	host = $1
+	port = $2.to_i
+	option = $4
+	[host, port, option]
+      else
+	raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
+	raise(DRbBadURI, 'can\'t parse uri:' + uri)
+      end
+    end
+
+    def self.open(uri, config)
+      host, port, option = parse_uri(uri)
+      host.untaint
+      port.untaint
+      soc = TCPSocket.open(host, port)
+      ssl_conf = SSLConfig::new(config)
+      ssl_conf.setup_ssl_context
+      ssl = ssl_conf.connect(soc)
+      self.new(uri, ssl, ssl_conf, true)
+    end
+
+    def self.open_server(uri, config)
+      uri = 'drbssl://:0' unless uri
+      host, port, opt = parse_uri(uri)
+      if host.size == 0
+        host = getservername
+        soc = open_server_inaddr_any(host, port)
+      else
+	soc = TCPServer.open(host, port)
+      end
+      port = soc.addr[1] if port == 0
+      @uri = "drbssl://#{host}:#{port}"
+      
+      ssl_conf = SSLConfig.new(config)
+      ssl_conf.setup_certificate
+      ssl_conf.setup_ssl_context
+      self.new(@uri, soc, ssl_conf, false)
+    end
+
+    def self.uri_option(uri, config)
+      host, port, option = parse_uri(uri)
+      return "drbssl://#{host}:#{port}", option
+    end
+
+    def initialize(uri, soc, config, is_established)
+      @ssl = is_established ? soc : nil
+      super(uri, soc.to_io, config)
+    end
+    
+    def stream; @ssl; end
+
+    def close
+      if @ssl
+	@ssl.close
+	@ssl = nil
+      end
+      super
+    end
+      
+    def accept
+      begin
+      while true
+	soc = @socket.accept
+	break if (@acl ? @acl.allow_socket?(soc) : true) 
+	soc.close
+      end
+      ssl = @config.accept(soc)
+      self.class.new(uri, ssl, @config, true)
+      rescue OpenSSL::SSL::SSLError
+	warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose]
+	retry
+      end
+    end
+  end
+  
+  DRbProtocol.add_protocol(DRbSSLSocket)
+end

Added: trunk/lib/drb/timeridconv.rb
===================================================================
--- trunk/lib/drb/timeridconv.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/timeridconv.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,91 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+  class TimerIdConv < DRbIdConv
+    class TimerHolder2
+      include MonitorMixin
+
+      class InvalidIndexError < RuntimeError; end
+
+      def initialize(timeout=600)
+	super()
+	@sentinel = Object.new
+	@gc = {}
+	@curr = {}
+	@renew = {}
+	@timeout = timeout
+	@keeper = keeper
+      end
+
+      def add(obj)
+	synchronize do 
+	  key = obj.__id__
+	  @curr[key] = obj
+	  return key
+	end
+      end
+
+      def fetch(key, dv=@sentinel)
+	synchronize do 
+	  obj = peek(key)
+	  if obj == @sentinel
+	    return dv unless dv == @sentinel
+	    raise InvalidIndexError
+	  end
+	  @renew[key] = obj # KeepIt
+	  return obj
+	end
+      end
+
+      def include?(key)
+	synchronize do 
+	  obj = peek(key)
+	  return false if obj == @sentinel
+	  true
+	end
+      end
+
+      def peek(key)
+	synchronize do 
+	  return @curr.fetch(key, @renew.fetch(key, @gc.fetch(key, @sentinel)))
+	end
+      end
+
+      private
+      def alternate
+	synchronize do
+	  @gc = @curr       # GCed
+	  @curr = @renew
+	  @renew = {}
+	end
+      end
+
+      def keeper
+	Thread.new do
+	  loop do
+	    size = alternate
+	    sleep(@timeout)
+	  end
+	end
+      end
+    end
+
+    def initialize(timeout=600)
+      @holder = TimerHolder2.new(timeout)
+    end
+
+    def to_obj(ref)
+      return super if ref.nil?
+      @holder.fetch(ref)
+    rescue TimerHolder2::InvalidIndexError
+      raise "invalid reference"
+    end
+
+    def to_id(obj)
+      return @holder.add(obj)
+    end
+  end
+end
+
+# DRb.install_id_conv(TimerIdConv.new)

Added: trunk/lib/drb/unix.rb
===================================================================
--- trunk/lib/drb/unix.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb/unix.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,108 @@
+require 'socket'
+require 'drb/drb'
+require 'tmpdir'
+
+raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer)
+
+module DRb
+
+  class DRbUNIXSocket < DRbTCPSocket
+    def self.parse_uri(uri)
+      if /^drbunix:(.*?)(\?(.*))?$/ =~ uri 
+	filename = $1
+	option = $3
+	[filename, option]
+      else
+	raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/
+	raise(DRbBadURI, 'can\'t parse uri:' + uri)
+      end
+    end
+
+    def self.open(uri, config)
+      filename, option = parse_uri(uri)
+      filename.untaint
+      soc = UNIXSocket.open(filename)
+      self.new(uri, soc, config)
+    end
+
+    def self.open_server(uri, config)
+      filename, option = parse_uri(uri)
+      if filename.size == 0
+	soc = temp_server
+        filename = soc.path
+	uri = 'drbunix:' + soc.path
+      else
+	soc = UNIXServer.open(filename)
+      end
+      owner = config[:UNIXFileOwner]
+      group = config[:UNIXFileGroup]
+      if owner || group
+        require 'etc'
+        owner = Etc.getpwnam( owner ).uid  if owner
+        group = Etc.getgrnam( group ).gid  if group
+        File.chown owner, group, filename
+      end
+      mode = config[:UNIXFileMode]
+      File.chmod(mode, filename) if mode
+
+      self.new(uri, soc, config, true)
+    end
+
+    def self.uri_option(uri, config)
+      filename, option = parse_uri(uri)
+      return "drbunix:#{filename}", option
+    end
+
+    def initialize(uri, soc, config={}, server_mode = false)
+      super(uri, soc, config)
+      set_sockopt(@socket)
+      @server_mode = server_mode
+      @acl = nil
+    end
+    
+    # import from tempfile.rb
+    Max_try = 10
+    private
+    def self.temp_server
+      tmpdir = Dir::tmpdir
+      n = 0
+      while true
+	begin
+	  tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n)
+	  lock = tmpname + '.lock'
+	  unless File.exist?(tmpname) or File.exist?(lock)
+	    Dir.mkdir(lock)
+	    break
+	  end
+	rescue
+	  raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try
+	  #sleep(1)
+	end
+	n += 1
+      end
+      soc = UNIXServer.new(tmpname)
+      Dir.rmdir(lock)
+      soc
+    end
+
+    public
+    def close
+      return unless @socket
+      path = @socket.path
+      @socket.close
+      File.unlink(path) if @server_mode
+      @socket = nil
+    end
+
+    def accept
+      s = @socket.accept
+      self.class.new(nil, s, @config)
+    end
+
+    def set_sockopt(soc)
+      soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+    end
+  end
+
+  DRbProtocol.add_protocol(DRbUNIXSocket)
+end

Added: trunk/lib/drb.rb
===================================================================
--- trunk/lib/drb.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/drb.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,2 @@
+require 'drb/drb'
+

Added: trunk/lib/monitor.rb
===================================================================
--- trunk/lib/monitor.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/monitor.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,323 @@
+=begin
+
+= monitor.rb
+
+Copyright (C) 2001  Shugo Maeda <shugo ruby-lang.org>
+
+This library is distributed under the terms of the Ruby license.
+You can freely distribute/modify this library.
+
+== example
+
+This is a simple example.
+
+  require 'monitor.rb'
+  
+  buf = []
+  buf.extend(MonitorMixin)
+  empty_cond = buf.new_cond
+  
+  # consumer
+  Thread.start do
+    loop do
+      buf.synchronize do
+        empty_cond.wait_while { buf.empty? }
+        print buf.shift
+      end
+    end
+  end
+  
+  # producer
+  while line = ARGF.gets
+    buf.synchronize do
+      buf.push(line)
+      empty_cond.signal
+    end
+  end
+
+The consumer thread waits for the producer thread to push a line
+to buf while buf.empty?, and the producer thread (main thread)
+reads a line from ARGF and push it to buf, then call
+empty_cond.signal.
+
+=end
+  
+
+#
+# Adds monitor functionality to an arbitrary object by mixing the module with
+# +include+.  For example:
+#
+#    require 'monitor'
+#    
+#    buf = []
+#    buf.extend(MonitorMixin)
+#    empty_cond = buf.new_cond
+#    
+#    # consumer
+#    Thread.start do
+#      loop do
+#        buf.synchronize do
+#          empty_cond.wait_while { buf.empty? }
+#          print buf.shift
+#        end
+#      end
+#    end
+#    
+#    # producer
+#    while line = ARGF.gets
+#      buf.synchronize do
+#        buf.push(line)
+#        empty_cond.signal
+#      end
+#    end
+# 
+# The consumer thread waits for the producer thread to push a line
+# to buf while buf.empty?, and the producer thread (main thread)
+# reads a line from ARGF and push it to buf, then call
+# empty_cond.signal.
+#
+module MonitorMixin
+  #
+  # FIXME: This isn't documented in Nutshell.
+  #
+  # Since MonitorMixin.new_cond returns a ConditionVariable, and the example
+  # above calls while_wait and signal, this class should be documented.
+  #
+  class ConditionVariable
+    class Timeout < Exception; end
+    
+    def wait(timeout = nil)
+      @monitor.funcall(:mon_check_owner)
+      timer = create_timer(timeout)
+      
+      @mutex.lock
+      count = @monitor.funcall(:mon_exit_for_cond)
+      @waiters.push(Thread.current)
+
+      begin
+        @mutex.unlock_and_stop
+        return true
+      rescue Timeout
+        return false
+      ensure
+	@mutex.synchronize {
+          if timer && timer.alive?
+            Thread.kill(timer)
+          end
+          if @waiters.include?(Thread.current)  # interrupted?
+            @waiters.delete(Thread.current)
+          end
+          @monitor.funcall(:mon_enter_for_cond, count)
+        }
+      end
+    end
+    
+    def wait_while
+      while yield
+	wait
+      end
+    end
+    
+    def wait_until
+      until yield
+	wait
+      end
+    end
+    
+    def signal
+      @monitor.funcall(:mon_check_owner)
+      @mutex.synchronize {
+        t = @waiters.shift
+        t.wakeup if t
+      }
+      Thread.pass
+    end
+    
+    def broadcast
+      @monitor.funcall(:mon_check_owner)
+      @mutex.synchronize {
+        for t in @waiters
+          t.wakeup
+        end
+        @waiters.clear
+      }
+      Thread.pass
+    end
+    
+    def count_waiters
+      return @waiters.length
+    end
+    
+    private
+
+    def initialize(monitor)
+      @monitor = monitor
+      @waiters = []
+      @mutex = Mutex.new
+    end
+
+    def create_timer(timeout)
+      if timeout
+	waiter = Thread.current
+	return Thread.start {
+	  Thread.pass
+	  sleep(timeout)
+	  #Thread.critical = true
+	  waiter.raise(Timeout.new)
+	}
+      else
+        return nil
+      end
+    end
+  end
+  
+  def self.extend_object(obj)
+    super(obj)
+    obj.funcall(:mon_initialize)
+  end
+  
+  #
+  # Attempts to enter exclusive section.  Returns +false+ if lock fails.
+  #
+  def mon_try_enter
+    result = false
+    @mon_mutex.synchronize {
+      if @mon_owner.nil?
+        @mon_owner = Thread.current
+      end
+      if @mon_owner == Thread.current
+        @mon_count += 1
+        result = true
+      end
+    }
+    return result
+  end
+  # For backward compatibility
+  alias try_mon_enter mon_try_enter
+
+  #
+  # Enters exclusive section.
+  #
+  def mon_enter
+    @mon_mutex.synchronize {
+      mon_acquire(@mon_entering_queue)
+      @mon_count += 1
+    }
+  end
+  
+  #
+  # Leaves exclusive section.
+  #
+  def mon_exit
+    mon_check_owner
+    @mon_mutex.synchronize {
+      @mon_count -= 1
+      if @mon_count == 0
+        mon_release
+      end
+    }
+    Thread.pass
+  end
+
+  #
+  # Enters exclusive section and executes the block.  Leaves the exclusive
+  # section automatically when the block exits.  See example under
+  # +MonitorMixin+.
+  #
+  def mon_synchronize
+    mon_enter
+    begin
+      yield
+    ensure
+      mon_exit
+    end
+  end
+  alias synchronize mon_synchronize
+  
+  #
+  # FIXME: This isn't documented in Nutshell.
+  #
+  def new_cond
+    return ConditionVariable.new(self)
+  end
+
+  private
+
+  def initialize(*args)
+    super
+    mon_initialize
+  end
+
+  def mon_initialize
+    @mon_owner = nil
+    @mon_count = 0
+    @mon_entering_queue = []
+    @mon_waiting_queue = []
+    @mon_mutex = Mutex.new
+  end
+
+  def mon_check_owner
+    if @mon_owner != Thread.current
+      raise ThreadError, "current thread not owner"
+    end
+  end
+
+  def mon_acquire(queue)
+    while @mon_owner && @mon_owner != Thread.current
+      queue.push(Thread.current)
+      @mutex.unlock_and_stop
+      @mutex.lock
+    end
+    @mon_owner = Thread.current
+  end
+
+  def mon_release
+    @mon_owner = nil
+    t = @mon_waiting_queue.shift
+    t = @mon_entering_queue.shift unless t
+    t.wakeup if t
+  end
+
+  def mon_enter_for_cond(count)
+    mon_acquire(@mon_waiting_queue)
+    @mon_count = count
+  end
+
+  def mon_exit_for_cond
+    count = @mon_count
+    @mon_count = 0
+    mon_release
+    return count
+  end
+end
+
+class Monitor
+  include MonitorMixin
+  alias try_enter try_mon_enter
+  alias enter mon_enter
+  alias exit mon_exit
+end
+
+
+# Documentation comments:
+#  - All documentation comes from Nutshell.
+#  - MonitorMixin.new_cond appears in the example, but is not documented in
+#    Nutshell.
+#  - All the internals (internal modules Accessible and Initializable, class
+#    ConditionVariable) appear in RDoc.  It might be good to hide them, by
+#    making them private, or marking them :nodoc:, etc.
+#  - The entire example from the RD section at the top is replicated in the RDoc
+#    comment for MonitorMixin.  Does the RD section need to remain?
+#  - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
+#    not synchronize.
+#  - mon_owner is in Nutshell, but appears as an accessor in a separate module
+#    here, so is hard/impossible to RDoc.  Some other useful accessors
+#    (mon_count and some queue stuff) are also in this module, and don't appear
+#    directly in the RDoc output.
+#  - in short, it may be worth changing the code layout in this file to make the
+#    documentation easier
+
+# Local variables:
+# mode: Ruby
+# tab-width: 8
+# End:

Added: trunk/lib/mutex_m.rb
===================================================================
--- trunk/lib/mutex_m.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/mutex_m.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,90 @@
+#
+#   mutex_m.rb - 
+#   	$Release Version: 3.0$
+#   	$Revision: 1.7 $
+#   	$Date: 1998/02/27 04:28:57 $
+#       Original from mutex.rb
+#   	by Keiju ISHITSUKA(keiju ishitsuka.com)
+#       modified by matz
+#       patched by akira yamada
+#
+# --
+#   Usage:
+#	require "mutex_m.rb"
+#	obj = Object.new
+#	obj.extend Mutex_m
+#	...
+#	extended object can be handled like Mutex
+#       or
+#	class Foo
+#	  include Mutex_m
+#	  ...
+#	end
+#	obj = Foo.new
+#	this obj can be handled like Mutex
+#
+
+module Mutex_m
+  def Mutex_m.define_aliases(cl)
+    cl.module_eval %q{
+      alias locked? mu_locked?
+      alias lock mu_lock
+      alias unlock mu_unlock
+      alias try_lock mu_try_lock
+      alias synchronize mu_synchronize
+    }
+  end  
+
+  def Mutex_m.append_features(cl)
+    super
+    define_aliases(cl) unless cl.instance_of?(Module)
+  end
+  
+  def Mutex_m.extend_object(obj)
+    super
+    obj.mu_extended
+  end
+
+  def mu_extended
+    unless (defined? locked? and
+	    defined? lock and
+	    defined? unlock and
+	    defined? try_lock and
+	    defined? synchronize)
+      Mutex_m.define_aliases(class<<self;self;end)
+    end
+    mu_initialize
+  end
+  
+  # locking 
+  def mu_synchronize(&block)
+    @_mutex.synchronize(&block)
+  end
+  
+  def mu_locked?
+    @_mutex.locked?
+  end
+  
+  def mu_try_lock
+    @_mutex.try_lock
+  end
+  
+  def mu_lock
+    @_mutex.lock
+  end
+  
+  def mu_unlock
+    @_mutex.unlock
+  end
+  
+  private
+  
+  def mu_initialize
+    @_mutex = Mutex.new
+  end
+
+  def initialize(*args)
+    mu_initialize
+    super
+  end
+end

Added: trunk/lib/observer.rb
===================================================================
--- trunk/lib/observer.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/observer.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,193 @@
+#
+# observer.rb implements the _Observer_ object-oriented design pattern.  The
+# following documentation is copied, with modifications, from "Programming
+# Ruby", by Hunt and Thomas; http://www.rubycentral.com/book/lib_patterns.html.
+#
+# == About
+#
+# The Observer pattern, also known as Publish/Subscribe, provides a simple
+# mechanism for one object to inform a set of interested third-party objects
+# when its state changes. 
+#
+# == Mechanism
+#
+# In the Ruby implementation, the notifying class mixes in the +Observable+
+# module, which provides the methods for managing the associated observer
+# objects.
+#
+# The observers must implement the +update+ method to receive notifications.
+#
+# The observable object must:
+# * assert that it has +changed+
+# * call +notify_observers+
+#
+# == Example
+#
+# The following example demonstrates this nicely.  A +Ticker+, when run,
+# continually receives the stock +Price+ for its +@symbol+.  A +Warner+ is a
+# general observer of the price, and two warners are demonstrated, a +WarnLow+
+# and a +WarnHigh+, which print a warning if the price is below or above their
+# set limits, respectively.
+#
+# The +update+ callback allows the warners to run without being explicitly
+# called.  The system is set up with the +Ticker+ and several observers, and the
+# observers do their duty without the top-level code having to interfere.
+#
+# Note that the contract between publisher and subscriber (observable and
+# observer) is not declared or enforced.  The +Ticker+ publishes a time and a
+# price, and the warners receive that.  But if you don't ensure that your
+# contracts are correct, nothing else can warn you.
+#
+#   require "observer"
+#   
+#   class Ticker          ### Periodically fetch a stock price.
+#     include Observable
+#   
+#     def initialize(symbol)
+#       @symbol = symbol
+#     end
+#   
+#     def run
+#       lastPrice = nil
+#       loop do
+#         price = Price.fetch(@symbol)
+#         print "Current price: #{price}\n"
+#         if price != lastPrice
+#           changed                 # notify observers
+#           lastPrice = price
+#           notify_observers(Time.now, price)
+#         end
+#         sleep 1
+#       end
+#     end
+#   end
+#
+#   class Price           ### A mock class to fetch a stock price (60 - 140).
+#     def Price.fetch(symbol)
+#       60 + rand(80)
+#     end
+#   end
+#   
+#   class Warner          ### An abstract observer of Ticker objects.
+#     def initialize(ticker, limit)
+#       @limit = limit
+#       ticker.add_observer(self)
+#     end
+#   end
+#   
+#   class WarnLow < Warner
+#     def update(time, price)       # callback for observer
+#       if price < @limit
+#         print "--- #{time.to_s}: Price below #@limit: #{price}\n"
+#       end
+#     end
+#   end
+#   
+#   class WarnHigh < Warner
+#     def update(time, price)       # callback for observer
+#       if price > @limit
+#         print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
+#       end
+#     end
+#   end
+#
+#   ticker = Ticker.new("MSFT")
+#   WarnLow.new(ticker, 80)
+#   WarnHigh.new(ticker, 120)
+#   ticker.run
+#
+# Produces:
+#
+#   Current price: 83
+#   Current price: 75
+#   --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
+#   Current price: 90
+#   Current price: 134
+#   +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
+#   Current price: 134
+#   Current price: 112
+#   Current price: 79
+#   --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
+
+
+#
+# Implements the Observable design pattern as a mixin so that other objects can
+# be notified of changes in state.  See observer.rb for details and an example.
+#
+module Observable
+
+  #
+  # Add +observer+ as an observer on this object. +observer+ will now receive
+  # notifications.  The second optional argument specifies a method to notify
+  # updates, of which default value is +update+.
+  #
+  def add_observer(observer, func=:update)
+    @observer_peers = {} unless defined? @observer_peers
+    unless observer.respond_to? func
+      raise NoMethodError, "observer does not respond to `#{func.to_s}'"
+    end
+    @observer_peers[observer] = func
+  end
+
+  #
+  # Delete +observer+ as an observer on this object. It will no longer receive
+  # notifications.
+  #
+  def delete_observer(observer)
+    @observer_peers.delete observer if defined? @observer_peers
+  end
+
+  #
+  # Delete all observers associated with this object.
+  #
+  def delete_observers
+    @observer_peers.clear if defined? @observer_peers
+  end
+
+  #
+  # Return the number of observers associated with this object.
+  #
+  def count_observers
+    if defined? @observer_peers
+      @observer_peers.size
+    else
+      0
+    end
+  end
+
+  #
+  # Set the changed state of this object.  Notifications will be sent only if
+  # the changed +state+ is +true+.
+  #
+  def changed(state=true)
+    @observer_state = state
+  end
+
+  #
+  # Query the changed state of this object.
+  #
+  def changed?
+    if defined? @observer_state and @observer_state
+      true
+    else
+      false
+    end
+  end
+
+  #
+  # If this object's changed state is +true+, invoke the update method in each
+  # currently associated observer in turn, passing it the given arguments. The
+  # changed state is then set to +false+.
+  #
+  def notify_observers(*arg)
+    if defined? @observer_state and @observer_state
+      if defined? @observer_peers
+       @observer_peers.each { |k, v|
+      k.send v, *arg
+    }
+      end
+      @observer_state = false
+    end
+  end
+
+end

Added: trunk/lib/soap/attachment.rb
===================================================================
--- trunk/lib/soap/attachment.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/soap/attachment.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,107 @@
+# soap/attachment.rb: SOAP4R - SwA implementation.
+# Copyright (C) 2002, 2003  Jamie Herre and NAKAMURA, Hiroshi <nahi ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/baseData'
+require 'soap/mapping'
+
+
+module SOAP
+
+
+class SOAPAttachment < SOAPExternalReference
+  attr_reader :data
+
+  def initialize(value)
+    super()
+    @data = value
+  end
+
+private
+
+  def external_contentid
+    @data.contentid
+  end
+end
+
+
+class Attachment
+  attr_reader :io
+  attr_accessor :contenttype
+
+  def initialize(string_or_readable = nil)
+    @string_or_readable = string_or_readable
+    @contenttype = "application/octet-stream"
+    @contentid = nil
+  end
+
+  def contentid
+    @contentid ||= Attachment.contentid(self)
+  end
+
+  def contentid=(contentid)
+    @contentid = contentid
+  end
+
+  def mime_contentid
+    '<' + contentid + '>'
+  end
+
+  def content
+    if @content == nil and @string_or_readable != nil
+      @content = @string_or_readable.respond_to?(:read) ?
+	@string_or_readable.read : @string_or_readable
+    end
+    @content
+  end
+
+  def to_s
+    content
+  end
+
+  def write(out)
+    out.write(content)
+  end
+
+  def save(filename)
+    File.open(filename, "wb") do |f|
+      write(f)
+    end
+  end
+
+  def self.contentid(obj)
+    # this needs to be fixed
+    [obj.__id__.to_s, Process.pid.to_s].join('.')
+  end
+
+  def self.mime_contentid(obj)
+    '<' + contentid(obj) + '>'
+  end
+end
+
+
+module Mapping
+  class AttachmentFactory < SOAP::Mapping::Factory
+    def obj2soap(soap_class, obj, info, map)
+      soap_obj = soap_class.new(obj)
+      mark_marshalled_obj(obj, soap_obj)
+      soap_obj
+    end
+
+    def soap2obj(obj_class, node, info, map)
+      obj = node.data
+      mark_unmarshalled_obj(node, obj)
+      return true, obj
+    end
+  end
+
+  DefaultRegistry.add(::SOAP::Attachment, ::SOAP::SOAPAttachment,
+    AttachmentFactory.new, nil)
+end
+
+
+end

Added: trunk/lib/soap/baseData.rb
===================================================================
--- trunk/lib/soap/baseData.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/soap/baseData.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,942 @@
+# soap/baseData.rb: SOAP4R - Base type library
+# Copyright (C) 2000, 2001, 2003-2005  NAKAMURA, Hiroshi <nahi ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+require 'soap/soap'
+
+
+module SOAP
+
+
+###
+## Mix-in module for SOAP base type classes.
+#
+module SOAPModuleUtils
+  include SOAP
+
+public
+
+  def decode(elename)
+    d = self.new
+    d.elename = elename
+    d
+  end
+end
+
+
+###
+## for SOAP type(base and compound)
+#
+module SOAPType
+  attr_accessor :encodingstyle
+  attr_accessor :elename
+  attr_accessor :id
+  attr_reader :precedents
+  attr_accessor :root
+  attr_accessor :parent
+  attr_accessor :position
+  attr_reader :extraattr
+  attr_accessor :definedtype
+
+  def initialize(*arg)
+    super
+    @encodingstyle = nil
+    @elename = XSD::QName::EMPTY
+    @id = nil
+    @precedents = []
+    @root = false
+    @parent = nil
+    @position = nil
+    @definedtype = nil
+    @extraattr = {}
+  end
+
+  def inspect
+    if self.is_a?(XSD::NSDBase)
+      sprintf("#<%s:0x%x %s %s>", self.class.name, __id__, self.elename, self.type)
+    else
+      sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
+    end
+  end
+
+  def rootnode
+    node = self
+    while node = node.parent
+      break if SOAPEnvelope === node
+    end
+    node
+  end
+end
+
+
+###
+## for SOAP base type
+#
+module SOAPBasetype
+  include SOAPType
+  include SOAP
+
+  def initialize(*arg)
+    super
+  end
+end
+
+
+###
+## for SOAP compound type
+#
+module SOAPCompoundtype
+  include SOAPType
+  include SOAP
+
+  def initialize(*arg)
+    super
+  end
+end
+
+
+###
+## Convenience datatypes.
+#
+class SOAPReference < XSD::NSDBase
+  include SOAPBasetype
+  extend SOAPModuleUtils
+
+public
+
+  attr_accessor :refid
+
+  # Override the definition in SOAPBasetype.
+  def initialize(obj = nil)
+    super()
+    @type = XSD::QName::EMPTY
+    @refid = nil
+    @obj = nil
+    __setobj__(obj) if obj
+  end
+
+  def __getobj__
+    @obj
+  end
+
+  def __setobj__(obj)
+    @obj = obj
+    @refid = @obj.id || SOAPReference.create_refid(@obj)
+    @obj.id = @refid unless @obj.id
+    @obj.precedents << self
+    # Copies NSDBase information
+    @obj.type = @type unless @obj.type
+  end
+
+  # Why don't I use delegate.rb?
+  # -> delegate requires target object type at initialize time.
+  # Why don't I use forwardable.rb?
+  # -> forwardable requires a list of forwarding methods.
+  #
+  # ToDo: Maybe I should use forwardable.rb and give it a methods list like
+  # delegate.rb...
+  #
+  def method_missing(msg_id, *params)
+    if @obj
+      @obj.send(msg_id, *params)
+    else
+      nil
+    end
+  end
+
+  def refidstr
+    '#' + @refid
+  end
+
+  def self.create_refid(obj)
+    'id' + obj.__id__.to_s
+  end
+
+  def self.decode(elename, refidstr)
+    if /\A#(.*)\z/ =~ refidstr
+      refid = $1
+    elsif /\Acid:(.*)\z/ =~ refidstr
+      refid = $1
+    else
+      raise ArgumentError.new("illegal refid #{refidstr}")
+    end
+    d = super(elename)
+    d.refid = refid
+    d
+  end
+end
+
+
+class SOAPExternalReference < XSD::NSDBase
+  include SOAPBasetype
+  extend SOAPModuleUtils
+
+  def initialize
+    super()
+    @type = XSD::QName::EMPTY
+  end
+
+  def referred
+    rootnode.external_content[external_contentid] = self
+  end
+
+  def refidstr
+    'cid:' + external_contentid
+  end
+
+private
+
+  def external_contentid
+    raise NotImplementedError.new
+  end
+end
+
+
+class SOAPNil < XSD::XSDNil
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+# SOAPRawString is for sending raw string.  In contrast to SOAPString,
+# SOAP4R does not do XML encoding and does not convert its CES.  The string it
+# holds is embedded to XML instance directly as a 'xsd:string'.
+class SOAPRawString < XSD::XSDString
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+
+###
+## Basic datatypes.
+#
+class SOAPAnySimpleType < XSD::XSDAnySimpleType
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPString < XSD::XSDString
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPBoolean < XSD::XSDBoolean
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPDecimal < XSD::XSDDecimal
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPFloat < XSD::XSDFloat
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPDouble < XSD::XSDDouble
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPDuration < XSD::XSDDuration
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPDateTime < XSD::XSDDateTime
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPTime < XSD::XSDTime
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPDate < XSD::XSDDate
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPGYearMonth < XSD::XSDGYearMonth
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPGYear < XSD::XSDGYear
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPGMonthDay < XSD::XSDGMonthDay
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPGDay < XSD::XSDGDay
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPGMonth < XSD::XSDGMonth
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPHexBinary < XSD::XSDHexBinary
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPBase64 < XSD::XSDBase64Binary
+  include SOAPBasetype
+  extend SOAPModuleUtils
+  Type = QName.new(EncodingNamespace, Base64Literal)
+
+public
+  # Override the definition in SOAPBasetype.
+  def initialize(value = nil)
+    super(value)
+    @type = Type
+  end
+
+  def as_xsd
+    @type = XSD::XSDBase64Binary::Type
+  end
+end
+
+class SOAPAnyURI < XSD::XSDAnyURI
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPQName < XSD::XSDQName
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+
+class SOAPInteger < XSD::XSDInteger
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPNonPositiveInteger < XSD::XSDNonPositiveInteger
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPNegativeInteger < XSD::XSDNegativeInteger
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPLong < XSD::XSDLong
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPInt < XSD::XSDInt
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPShort < XSD::XSDShort
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPByte < XSD::XSDByte
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPNonNegativeInteger < XSD::XSDNonNegativeInteger
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPUnsignedLong < XSD::XSDUnsignedLong
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPUnsignedInt < XSD::XSDUnsignedInt
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPUnsignedShort < XSD::XSDUnsignedShort
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPUnsignedByte < XSD::XSDUnsignedByte
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+class SOAPPositiveInteger < XSD::XSDPositiveInteger
+  include SOAPBasetype
+  extend SOAPModuleUtils
+end
+
+
+###
+## Compound datatypes.
+#
+class SOAPStruct < XSD::NSDBase
+  include SOAPCompoundtype
+  include Enumerable
+
+public
+
+  def initialize(type = nil)
+    super()
+    @type = type || XSD::QName::EMPTY
+    @array = []
+    @data = []
+  end
+
+  def to_s()
+    str = ''
+    self.each do |key, data|
+      str << "#{key}: #{data}\n"
+    end
+    str
+  end
+
+  def add(name, value)
+    add_member(name, value)
+  end
+
+  def [](idx)
+    if idx.is_a?(Range)
+      @data[idx]
+    elsif idx.is_a?(Integer)
+      if (idx > @array.size)
+        raise ArrayIndexOutOfBoundsError.new('In ' << @type.name)
+      end
+      @data[idx]
+    else
+      if @array.include?(idx)
+	@data[ array.index(idx)]
+      else
+	nil
+      end
+    end
+  end
+
+  def []=(idx, data)
+    if @array.include?(idx)
+      data.parent = self if data.respond_to?(:parent=)
+      @data[ array.index(idx)] = data
+    else
+      add(idx, data)
+    end
+  end
+
+  def key?(name)
+    @array.include?(name)
+  end
+
+  def members
+    @array
+  end
+
+  def to_obj
+    hash = {}
+    proptype = {}
+    each do |k, v|
+      value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
+      case proptype[k]
+      when :single
+        hash[k] = [hash[k], value]
+        proptype[k] = :multi
+      when :multi
+        hash[k] << value
+      else
+        hash[k] = value
+        proptype[k] = :single
+      end
+    end
+    hash
+  end
+
+  def each
+    idx = 0
+    while idx < @array.length
+      yield(@array[idx], @data[idx])
+      idx += 1
+    end
+  end
+
+  def replace
+    members.each do |member|
+      self[member] = yield(self[member])
+    end
+  end
+
+  def self.decode(elename, type)
+    s = SOAPStruct.new(type)
+    s.elename = elename
+    s
+  end
+
+private
+
+  def add_member(name, value = nil)
+    value = SOAPNil.new() if value.nil?
+    @array.push(name)
+    value.elename = value.elename.dup_name(name)
+    @data.push(value)
+    value.parent = self if value.respond_to?(:parent=)
+    value
+  end
+end
+
+
+# SOAPElement is not typed so it is not derived from NSDBase.
+class SOAPElement
+  include Enumerable
+
+  attr_accessor :encodingstyle
+
+  attr_accessor :elename
+  attr_accessor :id
+  attr_reader :precedents
+  attr_accessor :root
+  attr_accessor :parent
+  attr_accessor :position
+  attr_accessor :extraattr
+
+  attr_accessor :qualified
+
+  def initialize(elename, text = nil)
+    if !elename.is_a?(XSD::QName)
+      elename = XSD::QName.new(nil, elename)
+    end
+    @encodingstyle = LiteralNamespace
+    @elename = elename
+    @id = nil
+    @precedents = []
+    @root = false
+    @parent = nil
+    @position = nil
+    @extraattr = {}
+
+    @qualified = nil
+
+    @array = []
+    @data = []
+    @text = text
+  end
+
+  def inspect
+    sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
+  end
+
+  # Text interface.
+  attr_accessor :text
+  alias data text
+
+  # Element interfaces.
+  def add(value)
+    add_member(value.elename.name, value)
+  end
+
+  def [](idx)
+    if @array.include?(idx)
+      @data[ array.index(idx)]
+    else
+      nil
+    end
+  end
+
+  def []=(idx, data)
+    if @array.include?(idx)
+      data.parent = self if data.respond_to?(:parent=)
+      @data[ array.index(idx)] = data
+    else
+      add(data)
+    end
+  end
+
+  def key?(name)
+    @array.include?(name)
+  end
+
+  def members
+    @array
+  end
+
+  def to_obj
+    if members.empty?
+      @text
+    else
+      hash = {}
+      proptype = {}
+      each do |k, v|
+        value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
+        case proptype[k]
+        when :single
+          hash[k] = [hash[k], value]
+          proptype[k] = :multi
+        when :multi
+          hash[k] << value
+        else
+          hash[k] = value
+          proptype[k] = :single
+        end
+      end
+      hash
+    end
+  end
+
+  def each
+    idx = 0
+    while idx < @array.length
+      yield(@array[idx], @data[idx])
+      idx += 1
+    end
+  end
+
+  def self.decode(elename)
+    o = SOAPElement.new(elename)
+    o
+  end
+
+  def self.from_obj(obj, namespace = nil)
+    o = SOAPElement.new(nil)
+    case obj
+    when nil
+      o.text = nil
+    when Hash
+      obj.each do |elename, value|
+        if value.is_a?(Array)
+          value.each do |subvalue|
+            child = from_obj(subvalue, namespace)
+            child.elename = to_elename(elename, namespace)
+            o.add(child)
+          end
+        else
+          child = from_obj(value, namespace)
+          child.elename = to_elename(elename, namespace)
+          o.add(child)
+        end
+      end
+    else
+      o.text = obj.to_s
+    end
+    o
+  end
+
+  def self.to_elename(obj, namespace = nil)
+    if obj.is_a?(XSD::QName)
+      obj
+    elsif /\A(.+):([^:]+)\z/ =~ obj.to_s
+      XSD::QName.new($1, $2)
+    else
+      XSD::QName.new(namespace, obj.to_s)
+    end
+  end
+
+private
+
+  def add_member(name, value)
+    add_accessor(name)
+    @array.push(name)
+    @data.push(value)
+    value.parent = self if value.respond_to?(:parent=)
+    value
+  end
+
+  if RUBY_VERSION > "1.7.0"
+    def add_accessor(name)
+      methodname = name
+      if self.respond_to?(methodname)
+        methodname = safe_accessor_name(methodname)
+      end
+      Mapping.define_singleton_method(self, methodname) do
+        @data[ array.index(name)]
+      end
+      Mapping.define_singleton_method(self, methodname + '=') do |value|
+        @data[ array.index(name)] = value
+      end
+    end
+  else
+    def add_accessor(name)
+      methodname = safe_accessor_name(name)
+      instance_eval <<-EOS
+        def #{methodname}
+          @data[ array.index(#{name.dump})]
+        end
+
+        def #{methodname}=(value)
+          @data[ array.index(#{name.dump})] = value
+        end
+      EOS
+    end
+  end
+
+  def safe_accessor_name(name)
+    "var_" << name.gsub(/[^a-zA-Z0-9_]/, '')
+  end
+end
+
+
+class SOAPArray < XSD::NSDBase
+  include SOAPCompoundtype
+  include Enumerable
+
+public
+
+  attr_accessor :sparse
+
+  attr_reader :offset, :rank
+  attr_accessor :size, :size_fixed
+  attr_reader :arytype
+
+  def initialize(type = nil, rank = 1, arytype = nil)
+    super()
+    @type = type || ValueArrayName
+    @rank = rank
+    @data = Array.new
+    @sparse = false
+    @offset = Array.new(rank, 0)
+    @size = Array.new(rank, 0)
+    @size_fixed = false
+    @position = nil
+    @arytype = arytype
+  end
+
+  def offset=(var)
+    @offset = var
+    @sparse = true
+  end
+
+  def add(value)
+    self[*(@offset)] = value
+  end
+
+  def [](*idxary)
+    if idxary.size != @rank
+      raise ArgumentError.new("given #{idxary.size} params does not match rank: #{@rank}")
+    end
+
+    retrieve(idxary)
+  end
+
+  def []=(*idxary)
+    value = idxary.slice!(-1)
+
+    if idxary.size != @rank
+      raise ArgumentError.new("given #{idxary.size} params(#{idxary})" +
+        " does not match rank: #{@rank}")
+    end
+
+    idx = 0
+    while idx < idxary.size
+      if idxary[idx] + 1 > @size[idx]
+	@size[idx] = idxary[idx] + 1
+      end
+      idx += 1
+    end
+
+    data = retrieve(idxary[0, idxary.size - 1])
+    data[idxary.last] = value
+
+    if value.is_a?(SOAPType)
+      value.elename = ITEM_NAME
+      # Sync type
+      unless @type.name
+	@type = XSD::QName.new(value.type.namespace,
+	  SOAPArray.create_arytype(value.type.name, @rank))
+      end
+      value.type ||= @type
+    end
+
+    @offset = idxary
+    value.parent = self if value.respond_to?(:parent=)
+    offsetnext
+  end
+
+  def each
+    @data.each do |data|
+      yield(data)
+    end
+  end
+
+  def to_a
+    @data.dup
+  end
+
+  def replace
+    @data = deep_map(@data) do |ele|
+      yield(ele)
+    end
+  end
+
+  def deep_map(ary, &block)
+    ary.collect do |ele|
+      if ele.is_a?(Array)
+	deep_map(ele, &block)
+      else
+	new_obj = block.call(ele)
+	new_obj.elename = ITEM_NAME
+	new_obj
+      end
+    end
+  end
+
+  def include?(var)
+    traverse_data(@data) do |v, *rank|
+      if v.is_a?(SOAPBasetype) && v.data == var
+	return true
+      end
+    end
+    false
+  end
+
+  def traverse
+    traverse_data(@data) do |v, *rank|
+      unless @sparse
+       yield(v)
+      else
+       yield(v, *rank) if v && !v.is_a?(SOAPNil)
+      end
+    end
+  end
+
+  def soap2array(ary)
+    traverse_data(@data) do |v, *position|
+      iteary = ary
+      rank = 1
+      while rank < position.size
+	idx = position[rank - 1]
+	if iteary[idx].nil?
+	  iteary = iteary[idx] = Array.new
+	else
+	  iteary = iteary[idx]
+	end
+        rank += 1
+      end
+      if block_given?
+	iteary[position.last] = yield(v)
+      else
+	iteary[position.last] = v
+      end
+    end
+  end
+
+  def position
+    @position
+  end
+
+private
+
+  ITEM_NAME = XSD::QName.new(nil, 'item')
+
+  def retrieve(idxary)
+    data = @data
+    rank = 1
+    while rank <= idxary.size
+      idx = idxary[rank - 1]
+      if data[idx].nil?
+	data = data[idx] = Array.new
+      else
+	data = data[idx]
+      end
+      rank += 1
+    end
+    data
+  end
+
+  def traverse_data(data, rank = 1)
+    idx = 0
+    while idx < ranksize(rank)
+      if rank < @rank
+	traverse_data(data[idx], rank + 1) do |*v|
+	  v[1, 0] = idx
+       	  yield(*v)
+	end
+      else
+	yield(data[idx], idx)
+      end
+      idx += 1
+    end
+  end
+
+  def ranksize(rank)
+    @size[rank - 1]
+  end
+
+  def offsetnext
+    move = false
+    idx = @offset.size - 1
+    while !move && idx >= 0
+      @offset[idx] += 1
+      if @size_fixed
+	if @offset[idx] < @size[idx]
+	  move = true
+	else
+	  @offset[idx] = 0
+	  idx -= 1
+	end
+      else
+	move = true
+      end
+    end
+  end
+
+  # Module function
+
+public
+
+  def self.decode(elename, type, arytype)
+    typestr, nofary = parse_type(arytype.name)
+    rank = nofary.count(',') + 1
+    plain_arytype = XSD::QName.new(arytype.namespace, typestr)
+    o = SOAPArray.new(type, rank, plain_arytype)
+    size = []
+    nofary.split(',').each do |s|
+      if s.empty?
+	size.clear
+	break
+      else
+	size << s.to_i
+      end
+    end
+    unless size.empty?
+      o.size = size
+      o.size_fixed = true
+    end
+    o.elename = elename
+    o
+  end
+
+private
+
+  def self.create_arytype(typename, rank)
+    "#{typename}[" << ',' * (rank - 1) << ']'
+  end
+
+  TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$')
+
+  def self.parse_type(string)
+    TypeParseRegexp =~ string
+    return $1, $2
+  end
+end
+
+
+require 'soap/mapping/typeMap'
+
+
+end

Added: trunk/lib/soap/element.rb
===================================================================
--- trunk/lib/soap/element.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/soap/element.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,258 @@
+# SOAP4R - SOAP elements library
+# Copyright (C) 2000, 2001, 2003-2005  NAKAMURA, Hiroshi <nahi ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'soap/baseData'
+
+
+module SOAP
+
+
+###
+## SOAP elements
+#
+module SOAPEnvelopeElement; end
+
+class SOAPFault < SOAPStruct
+  include SOAPEnvelopeElement
+  include SOAPCompoundtype
+
+public
+
+  def faultcode
+    self['faultcode']
+  end
+
+  def faultstring
+    self['faultstring']
+  end
+
+  def faultactor
+    self['faultactor']
+  end
+
+  def detail
+    self['detail']
+  end
+
+  def faultcode=(rhs)
+    self['faultcode'] = rhs
+  end
+
+  def faultstring=(rhs)
+    self['faultstring'] = rhs
+  end
+
+  def faultactor=(rhs)
+    self['faultactor'] = rhs
+  end
+
+  def detail=(rhs)
+    self['detail'] = rhs
+  end
+
+  def initialize(faultcode = nil, faultstring = nil, faultactor = nil, detail = nil)
+    super(EleFaultName)
+    @elename = EleFaultName
+    @encodingstyle = EncodingNamespace
+
+    if faultcode
+      self.faultcode = faultcode
+      self.faultstring = faultstring
+      self.faultactor = faultactor
+      self.detail = detail
+      self.faultcode.elename = EleFaultCodeName if self.faultcode
+      self.faultstring.elename = EleFaultStringName if self.faultstring
+      self.faultactor.elename = EleFaultActorName if self.faultactor
+      self.detail.elename = EleFaultDetailName if self.detail
+    end
+    faultcode.parent = self if faultcode
+    faultstring.parent = self if faultstring
+    faultactor.parent = self if faultactor
+    detail.parent = self if detail
+  end
+
+  def encode(generator, ns, attrs = {})
+    SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace)
+    SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace)
+    attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
+    name = ns.name(@elename)
+    generator.encode_tag(name, attrs)
+    yield(self.faultcode)
+    yield(self.faultstring)
+    yield(self.faultactor)
+    yield(self.detail) if self.detail
+    generator.encode_tag_end(name, true)
+  end
+end
+
+
+class SOAPBody < SOAPStruct
+  include SOAPEnvelopeElement
+
+  def initialize(data = nil, is_fault = false)
+    super(nil)
+    @elename = EleBodyName
+    @encodingstyle = nil
+    if data
+      if data.respond_to?(:elename)
+        add(data.elename.name, data)
+      else
+        data.to_a.each do |datum|
+          add(datum.elename.name, datum)
+        end
+      end
+    end
+    @is_fault = is_fault
+  end
+
+  def encode(generator, ns, attrs = {})
+    name = ns.name(@elename)
+    generator.encode_tag(name, attrs)
+    if @is_fault
+      yield(@data)
+    else
+      @data.each do |data|
+	yield(data)
+      end
+    end
+    generator.encode_tag_end(name, true)
+  end
+
+  def root_node
+    @data.each do |node|
+      if node.root == 1
+	return node
+      end
+    end
+    # No specified root...
+    @data.each do |node|
+      if node.root != 0
+	return node
+      end
+    end
+
+    raise Parser::FormatDecodeError.new('no root element')
+  end
+end
+
+
+class SOAPHeaderItem < XSD::NSDBase
+  include SOAPEnvelopeElement
+  include SOAPCompoundtype
+
+public
+
+  attr_accessor :element
+  attr_accessor :mustunderstand
+  attr_accessor :encodingstyle
+
+  def initialize(element, mustunderstand = true, encodingstyle = nil)
+    super()
+    @type = nil
+    @element = element
+    @mustunderstand = mustunderstand
+    @encodingstyle = encodingstyle
+    element.parent = self if element
+  end
+
+  def encode(generator, ns, attrs = {})
+    attrs.each do |key, value|
+      @element.extraattr[key] = value
+    end
+    @element.extraattr[ns.name(AttrMustUnderstandName)] =
+      (@mustunderstand ? '1' : '0')
+    if @encodingstyle
+      @element.extraattr[ns.name(AttrEncodingStyleName)] = @encodingstyle
+    end
+    @element.encodingstyle = @encodingstyle if ! element.encodingstyle
+    yield(@element)
+  end
+end
+
+
+class SOAPHeader < SOAPStruct
+  include SOAPEnvelopeElement
+
+  def initialize
+    super(nil)
+    @elename = EleHeaderName
+    @encodingstyle = nil
+  end
+
+  def encode(generator, ns, attrs = {})
+    name = ns.name(@elename)
+    generator.encode_tag(name, attrs)
+    @data.each do |data|
+      yield(data)
+    end
+    generator.encode_tag_end(name, true)
+  end
+
+  def add(name, value)
+    mu = (value.extraattr[AttrMustUnderstandName] == '1')
+    encstyle = value.extraattr[AttrEncodingStyleName]
+    item = SOAPHeaderItem.new(value, mu, encstyle)
+    super(name, item)
+  end
+
+  def length
+    @data.length
+  end
+  alias size length
+end
+
+
+class SOAPEnvelope < XSD::NSDBase
+  include SOAPEnvelopeElement
+  include SOAPCompoundtype
+
+  attr_reader :header
+  attr_reader :body
+  attr_reader :external_content
+
+  def initialize(header = nil, body = nil)
+    super()
+    @type = nil
+    @elename = EleEnvelopeName
+    @encodingstyle = nil
+    @header = header
+    @body = body
+    @external_content = {}
+    header.parent = self if header
+    body.parent = self if body
+  end
+
+  def header=(header)
+    header.parent = self
+    @header = header
+  end
+
+  def body=(body)
+    body.parent = self
+    @body = body
+  end
+
+  def encode(generator, ns, attrs = {})
+    SOAPGenerator.assign_ns(attrs, ns, elename.namespace, SOAPNamespaceTag)
+    name = ns.name(@elename)
+    generator.encode_tag(name, attrs)
+
+    yield(@header) if @header and @header.length > 0
+    yield(@body)
+
+    generator.encode_tag_end(name, true)
+  end
+
+  def to_ary
+    [header, body]
+  end
+end
+
+
+end

Added: trunk/lib/soap/encodingstyle/aspDotNetHandler.rb
===================================================================
--- trunk/lib/soap/encodingstyle/aspDotNetHandler.rb	2006-02-20 01:49:55 UTC (rev 443)
+++ trunk/lib/soap/encodingstyle/aspDotNetHandler.rb	2006-02-20 01:56:39 UTC (rev 444)
@@ -0,0 +1,213 @@
+# SOAP4R - ASP.NET EncodingStyle handler library
+# Copyright (C) 2001, 2003, 2005  NAKAMURA, Hiroshi <nahi ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class ASPDotNetHandler < Handler
+  Namespace = 'http://tempuri.org/ASP.NET'
+  add_handler
+
+  def initialize(charset = nil)
+    super(charset)
+    @textbuf = ''
+    @decode_typemap = nil
+  end
+
+
+  ###
+  ## encode interface.
+  #
+  def encode_data(generator, ns, data, parent)
+    attrs = {}
+    # ASPDotNetHandler is intended to be used for accessing an ASP.NET doc/lit
+    # service as an rpc/encoded service.  in the situation, local elements
+    # should be qualified.  propagate parent's namespace to children.
+    if data.elename.namespace.nil?
+      data.elename.namespace = parent.elename.namespace
+    end
+    name = generator.encode_name(ns, data, attrs)
+    case data
+    when SOAPRawString
+      generator.encode_tag(name, attrs)
+      generator.encode_rawstring(data.to_s)
+    when XSD::XSDString
+      generator.encode_tag(name, attrs)
+      generator.encode_string(@charset ?
+        XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
+    when XSD::XSDAnySimpleType
+      generator.encode_tag(name, attrs)
+      generator.encode_string(data.to_s)
+    when SOAPStruct
+      generator.encode_tag(name, attrs)
+      data.each do |key, value|
+        generator.encode_child(ns, value, data)
+      end
+    when SOAPArray
+      generator.encode_tag(name, attrs)
+      data.traverse do |child, *rank|
+	data.position = nil
+        generator.encode_child(ns, child, data)
+      end
+    else
+      raise EncodingStyleError.new(
+        "unknown object:#{data} in this encodingStyle")
+    end
+  end
+
+  def encode_data_end(generator, ns, data, parent)
+    name = generator.encode_name_end(ns, data)
+    cr = data.is_a?(SOAPCompoundtype)
+    generator.encode_tag_end(name, cr)
+  end
+
+
+  ###
+  ## decode interface.
+  #
+  class SOAPTemporalObject
+    attr_accessor :parent
+
+    def initialize
+      @parent = nil
+    end
+  end
+
+  class SOAPUnknown < SOAPTemporalObject
+    def initialize(handler, elename)
+      super()
+      @handler = handler
+      @elename = elename
+    end
+
+    def as_struct
+      o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
+      o.parent = @parent
+      o.type.name = @name
+      @handler.decode_parent(@parent, o)
+      o
+    end
+
+    def as_string
+      o = SOAPString.decode(@elename)
+      o.parent = @parent
+      @handler.decode_parent(@parent, o)
+      o
+    end
+
+    def as_nil
+      o = SOAPNil.decode(@elename)
+      o.parent = @parent
+      @handler.decode_parent(@parent, o)
+      o
+    end
+  end
+
+  def decode_tag(ns, elename, attrs, parent)
+    @textbuf = ''
+    o = SOAPUnknown.new(self, elename)
+    o.parent = parent
+    o
+  end
+
+  def decode_tag_end(ns, node)
+    o = node.node
+    if o.is_a?(SOAPUnknown)
+      newnode = o.as_string
+#	if /\A\s*\z/ =~ @textbuf
+#	  o.as_struct
+#	else
+#	  o.as_string
+#	end
+      node.replace_node(newnode)
+      o = node.node
+    end
+
+    decode_textbuf(o)
+    @textbuf = ''
+  end
+
+  def decode_text