# frozen_string_literal: false
# 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)
# 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
# [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://pragprog.com/book/sidruby/the-druby-book]
# The dRuby Book: Distributed and Parallel Computing with Ruby
# by Masatoshi Seki and Makoto Inoue
# [http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html]
# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
# Translation of presentation on Ruby by Masatoshi Seki.
# 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
# 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
# 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
# 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.
# For more dRuby samples, see the +samples+ directory in the full
# === 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.
# # The URI for the server to connect to
# URI="druby://localhost:8787"
# # The object that handles requests on the server
# FRONT_OBJECT=TimeServer.new
# DRb.start_service(URI, FRONT_OBJECT)
# # Wait for the drb server thread to finish before exiting.
# # 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
# # Note: this must be called at least once per process to take any effect.
# # This is particularly important if your application forks.
# 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.
# URI="druby://localhost:8787"
# # Make dRuby send Logger instances as dRuby references,
# include DRb::DRbUndumped
# def initialize(n, fname)
# File.open(@filename, "a") do |f|
# f.puts("#{Time.now}: #{@name}: #{message}")
# # 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
# if !@loggers.has_key? name
# # make the filename safe, then declare it to be so
# fname = name.gsub(/[.\/\\\:]/, "_")
# @loggers[name] = Logger.new(name, @basedir + "/" + fname)
# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
# DRb.start_service(URI, FRONT_OBJECT)
# SERVER_URI="druby://localhost:8787"
# 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 ===")
# 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:
# ro = DRbObject::new_with_uri("druby://your.server.com:8989")
# undef :instance_eval # force call to be passed to remote object
# ro.instance_eval("`rm -rf *`")
# The dangers posed by instance_eval and friends are such that a
# DRbServer should only be used when clients are trusted.
# 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 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
# 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
# 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.
# Superclass of all errors raised in the DRb module.
class DRbError < RuntimeError; end
# Error raised when an error occurs on the underlying communication
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 drb/timeridconv.rb
# and DRbNameIdConv in sample/name.rb in the full drb distribution.
# 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.
# Convert an object into a reference id.
# This implementation returns the object's __id__ in the local
obj.nil? ? nil : obj.__id__
# 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.
def _dump(dummy) # :nodoc:
raise TypeError, 'can\'t dump'
# 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.
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+
# Get the wrapped DRb::DRbUnknown object.
def self._load(s) # :nodoc:
# An exception wrapping an error object
class DRbRemoteError < DRbError
# Creates a new remote error that wraps the Exception +error+
@reason = error.class.to_s
super("#{error.message} (#{error.class})")
set_backtrace(error.backtrace)
# the class of the error, as a string.
# 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+
# 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.
when /uninitialized constant (\S+)/
when /undefined class\/module (\S+)/
# The name of the unknown thing.
# Class name for unknown objects; variable name for unknown
# Buffer contained the marshalled, unknown object.
def self._load(s) # :nodoc:
rescue NameError, ArgumentError
# 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