source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/drb/drb.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 51.7 KB
Line 
1#
2# = drb/drb.rb
3#
4# Distributed Ruby: _dRuby_ version 2.0.4
5#
6# Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
7# modify it under the same terms as Ruby.
8#
9# Author:: Masatoshi SEKI
10#
11# Documentation:: William Webber ([email protected])
12#
13# == Overview
14#
15# dRuby is a distributed object system for Ruby. It allows an object in one
16# Ruby process to invoke methods on an object in another Ruby process on the
17# same or a different machine.
18#
19# The Ruby standard library contains the core classes of the dRuby package.
20# However, the full package also includes access control lists and the
21# Rinda tuple-space distributed task management system, as well as a
22# large number of samples. The full dRuby package can be downloaded from
23# the dRuby home page (see *References*).
24#
25# For an introduction and examples of usage see the documentation to the
26# DRb module.
27#
28# == References
29#
30# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
31# The dRuby home page, in Japanese. Contains the full dRuby package
32# and links to other Japanese-language sources.
33#
34# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
35# The English version of the dRuby home page.
36#
37# [http://www.chadfowler.com/ruby/drb.html]
38# A quick tutorial introduction to using dRuby by Chad Fowler.
39#
40# [http://www.linux-mag.com/2002-09/ruby_05.html]
41# A tutorial introduction to dRuby in Linux Magazine by Dave Thomas.
42# Includes a discussion of Rinda.
43#
44# [http://www.eng.cse.dmu.ac.uk/~hgs/ruby/dRuby/]
45# Links to English-language Ruby material collected by Hugh Sasse.
46#
47# [http://www.rubycentral.com/book/ospace.html]
48# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
49# which discusses dRuby.
50#
51# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
52# Translation of presentation on Ruby by Masatoshi Seki.
53
54require 'socket'
55require 'thread'
56require 'fcntl'
57require 'drb/eq'
58
59#
60# == Overview
61#
62# dRuby is a distributed object system for Ruby. It is written in
63# pure Ruby and uses its own protocol. No add-in services are needed
64# beyond those provided by the Ruby runtime, such as TCP sockets. It
65# does not rely on or interoperate with other distributed object
66# systems such as CORBA, RMI, or .NET.
67#
68# dRuby allows methods to be called in one Ruby process upon a Ruby
69# object located in another Ruby process, even on another machine.
70# References to objects can be passed between processes. Method
71# arguments and return values are dumped and loaded in marshalled
72# format. All of this is done transparently to both the caller of the
73# remote method and the object that it is called upon.
74#
75# An object in a remote process is locally represented by a
76# DRb::DRbObject instance. This acts as a sort of proxy for the
77# remote object. Methods called upon this DRbObject instance are
78# forwarded to its remote object. This is arranged dynamically at run
79# time. There are no statically declared interfaces for remote
80# objects, such as CORBA's IDL.
81#
82# dRuby calls made into a process are handled by a DRb::DRbServer
83# instance within that process. This reconstitutes the method call,
84# invokes it upon the specified local object, and returns the value to
85# the remote caller. Any object can receive calls over dRuby. There
86# is no need to implement a special interface, or mixin special
87# functionality. Nor, in the general case, does an object need to
88# explicitly register itself with a DRbServer in order to receive
89# dRuby calls.
90#
91# One process wishing to make dRuby calls upon another process must
92# somehow obtain an initial reference to an object in the remote
93# process by some means other than as the return value of a remote
94# method call, as there is initially no remote object reference it can
95# invoke a method upon. This is done by attaching to the server by
96# URI. Each DRbServer binds itself to a URI such as
97# 'druby://example.com:8787'. A DRbServer can have an object attached
98# to it that acts as the server's *front* *object*. A DRbObject can
99# be explicitly created from the server's URI. This DRbObject's
100# remote object will be the server's front object. This front object
101# can then return references to other Ruby objects in the DRbServer's
102# process.
103#
104# Method calls made over dRuby behave largely the same as normal Ruby
105# method calls made within a process. Method calls with blocks are
106# supported, as are raising exceptions. In addition to a method's
107# standard errors, a dRuby call may also raise one of the
108# dRuby-specific errors, all of which are subclasses of DRb::DRbError.
109#
110# Any type of object can be passed as an argument to a dRuby call or
111# returned as its return value. By default, such objects are dumped
112# or marshalled at the local end, then loaded or unmarshalled at the
113# remote end. The remote end therefore receives a copy of the local
114# object, not a distributed reference to it; methods invoked upon this
115# copy are executed entirely in the remote process, not passed on to
116# the local original. This has semantics similar to pass-by-value.
117#
118# However, if an object cannot be marshalled, a dRuby reference to it
119# is passed or returned instead. This will turn up at the remote end
120# as a DRbObject instance. All methods invoked upon this remote proxy
121# are forwarded to the local object, as described in the discussion of
122# DRbObjects. This has semantics similar to the normal Ruby
123# pass-by-reference.
124#
125# The easiest way to signal that we want an otherwise marshallable
126# object to be passed or returned as a DRbObject reference, rather
127# than marshalled and sent as a copy, is to include the
128# DRb::DRbUndumped mixin module.
129#
130# dRuby supports calling remote methods with blocks. As blocks (or
131# rather the Proc objects that represent them) are not marshallable,
132# the block executes in the local, not the remote, context. Each
133# value yielded to the block is passed from the remote object to the
134# local block, then the value returned by each block invocation is
135# passed back to the remote execution context to be collected, before
136# the collected values are finally returned to the local context as
137# the return value of the method invocation.
138#
139# == Examples of usage
140#
141# For more dRuby samples, see the +samples+ directory in the full
142# dRuby distribution.
143#
144# === dRuby in client/server mode
145#
146# This illustrates setting up a simple client-server drb
147# system. Run the server and client code in different terminals,
148# starting the server code first.
149#
150# ==== Server code
151#
152# require 'drb/drb'
153#
154# # The URI for the server to connect to
155# URI="druby://localhost:8787"
156#
157# class TimeServer
158#
159# def get_current_time
160# return Time.now
161# end
162#
163# end
164#
165# # The object that handles requests on the server
166# FRONT_OBJECT=TimeServer.new
167#
168# $SAFE = 1 # disable eval() and friends
169#
170# DRb.start_service(URI, FRONT_OBJECT)
171# # Wait for the drb server thread to finish before exiting.
172# DRb.thread.join
173#
174# ==== Client code
175#
176# require 'drb/drb'
177#
178# # The URI to connect to
179# SERVER_URI="druby://localhost:8787"
180#
181# # Start a local DRbServer to handle callbacks.
182# #
183# # Not necessary for this small example, but will be required
184# # as soon as we pass a non-marshallable object as an argument
185# # to a dRuby call.
186# DRb.start_service
187#
188# timeserver = DRbObject.new_with_uri(SERVER_URI)
189# puts timeserver.get_current_time
190#
191# === Remote objects under dRuby
192#
193# This example illustrates returning a reference to an object
194# from a dRuby call. The Logger instances live in the server
195# process. References to them are returned to the client process,
196# where methods can be invoked upon them. These methods are
197# executed in the server process.
198#
199# ==== Server code
200#
201# require 'drb/drb'
202#
203# URI="druby://localhost:8787"
204#
205# class Logger
206#
207# # Make dRuby send Logger instances as dRuby references,
208# # not copies.
209# include DRb::DRbUndumped
210#
211# def initialize(n, fname)
212# @name = n
213# @filename = fname
214# end
215#
216# def log(message)
217# File.open(@filename, "a") do |f|
218# f.puts("#{Time.now}: #{@name}: #{message}")
219# end
220# end
221#
222# end
223#
224# # We have a central object for creating and retrieving loggers.
225# # This retains a local reference to all loggers created. This
226# # is so an existing logger can be looked up by name, but also
227# # to prevent loggers from being garbage collected. A dRuby
228# # reference to an object is not sufficient to prevent it being
229# # garbage collected!
230# class LoggerFactory
231#
232# def initialize(bdir)
233# @basedir = bdir
234# @loggers = {}
235# end
236#
237# def get_logger(name)
238# if [email protected]_key? name
239# # make the filename safe, then declare it to be so
240# fname = name.gsub(/[.\/]/, "_").untaint
241# @loggers[name] = Logger.new(name, @basedir + "/" + fname)
242# end
243# return @loggers[name]
244# end
245#
246# end
247#
248# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
249#
250# $SAFE = 1 # disable eval() and friends
251#
252# DRb.start_service(URI, FRONT_OBJECT)
253# DRb.thread.join
254#
255# ==== Client code
256#
257# require 'drb/drb'
258#
259# SERVER_URI="druby://localhost:8787"
260#
261# DRb.start_service
262#
263# log_service=DRbObject.new_with_uri(SERVER_URI)
264#
265# ["loga", "logb", "logc"].each do |logname|
266#
267# logger=log_service.get_logger(logname)
268#
269# logger.log("Hello, world!")
270# logger.log("Goodbye, world!")
271# logger.log("=== EOT ===")
272#
273# end
274#
275# == Security
276#
277# As with all network services, security needs to be considered when
278# using dRuby. By allowing external access to a Ruby object, you are
279# not only allowing outside clients to call the methods you have
280# defined for that object, but by default to execute arbitrary Ruby
281# code on your server. Consider the following:
282#
283# # !!! UNSAFE CODE !!!
284# ro = DRbObject::new_with_uri("druby://your.server.com:8989")
285# class << ro
286# undef :instance_eval # force call to be passed to remote object
287# end
288# ro.instance_eval("`rm -rf *`")
289#
290# The dangers posed by instance_eval and friends are such that a
291# DRbServer should generally be run with $SAFE set to at least
292# level 1. This will disable eval() and related calls on strings
293# passed across the wire. The sample usage code given above follows
294# this practice.
295#
296# A DRbServer can be configured with an access control list to
297# selectively allow or deny access from specified IP addresses. The
298# main druby distribution provides the ACL class for this purpose. In
299# general, this mechanism should only be used alongside, rather than
300# as a replacement for, a good firewall.
301#
302# == dRuby internals
303#
304# dRuby is implemented using three main components: a remote method
305# call marshaller/unmarshaller; a transport protocol; and an
306# ID-to-object mapper. The latter two can be directly, and the first
307# indirectly, replaced, in order to provide different behaviour and
308# capabilities.
309#
310# Marshalling and unmarshalling of remote method calls is performed by
311# a DRb::DRbMessage instance. This uses the Marshal module to dump
312# the method call before sending it over the transport layer, then
313# reconstitute it at the other end. There is normally no need to
314# replace this component, and no direct way is provided to do so.
315# However, it is possible to implement an alternative marshalling
316# scheme as part of an implementation of the transport layer.
317#
318# The transport layer is responsible for opening client and server
319# network connections and forwarding dRuby request across them.
320# Normally, it uses DRb::DRbMessage internally to manage marshalling
321# and unmarshalling. The transport layer is managed by
322# DRb::DRbProtocol. Multiple protocols can be installed in
323# DRbProtocol at the one time; selection between them is determined by
324# the scheme of a dRuby URI. The default transport protocol is
325# selected by the scheme 'druby:', and implemented by
326# DRb::DRbTCPSocket. This uses plain TCP/IP sockets for
327# communication. An alternative protocol, using UNIX domain sockets,
328# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
329# selected by the scheme 'drbunix:'. A sample implementation over
330# HTTP can be found in the samples accompanying the main dRuby
331# distribution.
332#
333# The ID-to-object mapping component maps dRuby object ids to the
334# objects they refer to, and vice versa. The implementation to use
335# can be specified as part of a DRb::DRbServer's configuration. The
336# default implementation is provided by DRb::DRbIdConv. It uses an
337# object's ObjectSpace id as its dRuby id. This means that the dRuby
338# reference to that object only remains meaningful for the lifetime of
339# the object's process and the lifetime of the object within that
340# process. A modified implementation is provided by DRb::TimerIdConv
341# in the file drb/timeridconv.rb. This implementation retains a local
342# reference to all objects exported over dRuby for a configurable
343# period of time (defaulting to ten minutes), to prevent them being
344# garbage-collected within this time. Another sample implementation
345# is provided in sample/name.rb in the main dRuby distribution. This
346# allows objects to specify their own id or "name". A dRuby reference
347# can be made persistent across processes by having each process
348# register an object using the same dRuby name.
349#
350module DRb
351
352 # Superclass of all errors raised in the DRb module.
353 class DRbError < RuntimeError; end
354
355 # Error raised when an error occurs on the underlying communication
356 # protocol.
357 class DRbConnError < DRbError; end
358
359 # Class responsible for converting between an object and its id.
360 #
361 # This, the default implementation, uses an object's local ObjectSpace
362 # __id__ as its id. This means that an object's identification over
363 # drb remains valid only while that object instance remains alive
364 # within the server runtime.
365 #
366 # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
367 # and DRbNameIdConv in sample/name.rb in the full drb distribution.
368 class DRbIdConv
369
370 # Convert an object reference id to an object.
371 #
372 # This implementation looks up the reference id in the local object
373 # space and returns the object it refers to.
374 def to_obj(ref)
375 ObjectSpace._id2ref(ref)
376 end
377
378 # Convert an object into a reference id.
379 #
380 # This implementation returns the object's __id__ in the local
381 # object space.
382 def to_id(obj)
383 obj.nil? ? nil : obj.__id__
384 end
385 end
386
387 # Mixin module making an object undumpable or unmarshallable.
388 #
389 # If an object which includes this module is returned by method
390 # called over drb, then the object remains in the server space
391 # and a reference to the object is returned, rather than the
392 # object being marshalled and moved into the client space.
393 module DRbUndumped
394 def _dump(dummy) # :nodoc:
395 raise TypeError, 'can\'t dump'
396 end
397 end
398
399 # Error raised by the DRb module when an attempt is made to refer to
400 # the context's current drb server but the context does not have one.
401 # See #current_server.
402 class DRbServerNotFound < DRbError; end
403
404 # Error raised by the DRbProtocol module when it cannot find any
405 # protocol implementation support the scheme specified in a URI.
406 class DRbBadURI < DRbError; end
407
408 # Error raised by a dRuby protocol when it doesn't support the
409 # scheme specified in a URI. See DRb::DRbProtocol.
410 class DRbBadScheme < DRbError; end
411
412 # An exception wrapping a DRb::DRbUnknown object
413 class DRbUnknownError < DRbError
414
415 # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
416 def initialize(unknown)
417 @unknown = unknown
418 super(unknown.name)
419 end
420
421 # Get the wrapped DRb::DRbUnknown object.
422 attr_reader :unknown
423
424 def self._load(s) # :nodoc:
425 Marshal::load(s)
426 end
427
428 def _dump(lv) # :nodoc:
429 Marshal::dump(@unknown)
430 end
431 end
432
433 # An exception wrapping an error object
434 class DRbRemoteError < DRbError
435 def initialize(error)
436 @reason = error.class.to_s
437 super("#{error.message} (#{error.class})")
438 set_backtrace(error.backtrace)
439 end
440
441 # the class of the error, as a string.
442 attr_reader :reason
443 end
444
445 # Class wrapping a marshalled object whose type is unknown locally.
446 #
447 # If an object is returned by a method invoked over drb, but the
448 # class of the object is unknown in the client namespace, or
449 # the object is a constant unknown in the client namespace, then
450 # the still-marshalled object is returned wrapped in a DRbUnknown instance.
451 #
452 # If this object is passed as an argument to a method invoked over
453 # drb, then the wrapped object is passed instead.
454 #
455 # The class or constant name of the object can be read from the
456 # +name+ attribute. The marshalled object is held in the +buf+
457 # attribute.
458 class DRbUnknown
459
460 # Create a new DRbUnknown object.
461 #
462 # +buf+ is a string containing a marshalled object that could not
463 # be unmarshalled. +err+ is the error message that was raised
464 # when the unmarshalling failed. It is used to determine the
465 # name of the unmarshalled object.
466 def initialize(err, buf)
467 case err.to_s
468 when /uninitialized constant (\S+)/
469 @name = $1
470 when /undefined class\/module (\S+)/
471 @name = $1
472 else
473 @name = nil
474 end
475 @buf = buf
476 end
477
478 # The name of the unknown thing.
479 #
480 # Class name for unknown objects; variable name for unknown
481 # constants.
482 attr_reader :name
483
484 # Buffer contained the marshalled, unknown object.
485 attr_reader :buf
486
487 def self._load(s) # :nodoc:
488 begin
489 Marshal::load(s)
490 rescue NameError, ArgumentError
491 DRbUnknown.new($!, s)
492 end
493 end
494
495 def _dump(lv) # :nodoc:
496 @buf
497 end
498
499 # Attempt to load the wrapped marshalled object again.
500 #
501 # If the class of the object is now known locally, the object
502 # will be unmarshalled and returned. Otherwise, a new
503 # but identical DRbUnknown object will be returned.
504 def reload
505 self.class._load(@buf)
506 end
507
508 # Create a DRbUnknownError exception containing this object.
509 def exception
510 DRbUnknownError.new(self)
511 end
512 end
513
514 class DRbArray
515 def initialize(ary)
516 @ary = ary.collect { |obj|
517 if obj.kind_of? DRbUndumped
518 DRbObject.new(obj)
519 else
520 begin
521 Marshal.dump(obj)
522 obj
523 rescue
524 DRbObject.new(obj)
525 end
526 end
527 }
528 end
529
530 def self._load(s)
531 Marshal::load(s)
532 end
533
534 def _dump(lv)
535 Marshal.dump(@ary)
536 end
537 end
538
539 # Handler for sending and receiving drb messages.
540 #
541 # This takes care of the low-level marshalling and unmarshalling
542 # of drb requests and responses sent over the wire between server
543 # and client. This relieves the implementor of a new drb
544 # protocol layer with having to deal with these details.
545 #
546 # The user does not have to directly deal with this object in
547 # normal use.
548 class DRbMessage
549 def initialize(config) # :nodoc:
550 @load_limit = config[:load_limit]
551 @argc_limit = config[:argc_limit]
552 end
553
554 def dump(obj, error=false) # :nodoc:
555 obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
556 begin
557 str = Marshal::dump(obj)
558 rescue
559 str = Marshal::dump(make_proxy(obj, error))
560 end
561 [str.size].pack('N') + str
562 end
563
564 def load(soc) # :nodoc:
565 begin
566 sz = soc.read(4) # sizeof (N)
567 rescue
568 raise(DRbConnError, $!.message, $!.backtrace)
569 end
570 raise(DRbConnError, 'connection closed') if sz.nil?
571 raise(DRbConnError, 'premature header') if sz.size < 4
572 sz = sz.unpack('N')[0]
573 raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
574 begin
575 str = soc.read(sz)
576 rescue
577 raise(DRbConnError, $!.message, $!.backtrace)
578 end
579 raise(DRbConnError, 'connection closed') if str.nil?
580 raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
581 Thread.exclusive do
582 begin
583 save = Thread.current[:drb_untaint]
584 Thread.current[:drb_untaint] = []
585 Marshal::load(str)
586 rescue NameError, ArgumentError
587 DRbUnknown.new($!, str)
588 ensure
589 Thread.current[:drb_untaint].each do |x|
590 x.untaint
591 end
592 Thread.current[:drb_untaint] = save
593 end
594 end
595 end
596
597 def send_request(stream, ref, msg_id, arg, b) # :nodoc:
598 ary = []
599 ary.push(dump(ref.__drbref))
600 ary.push(dump(msg_id.id2name))
601 ary.push(dump(arg.length))
602 arg.each do |e|
603 ary.push(dump(e))
604 end
605 ary.push(dump(b))
606 stream.write(ary.join(''))
607 rescue
608 raise(DRbConnError, $!.message, $!.backtrace)
609 end
610
611 def recv_request(stream) # :nodoc:
612 ref = load(stream)
613 ro = DRb.to_obj(ref)
614 msg = load(stream)
615 argc = load(stream)
616 raise ArgumentError, 'too many arguments' if @argc_limit < argc
617 argv = Array.new(argc, nil)
618 argc.times do |n|
619 argv[n] = load(stream)
620 end
621 block = load(stream)
622 return ro, msg, argv, block
623 end
624
625 def send_reply(stream, succ, result) # :nodoc:
626 stream.write(dump(succ) + dump(result, !succ))
627 rescue
628 raise(DRbConnError, $!.message, $!.backtrace)
629 end
630
631 def recv_reply(stream) # :nodoc:
632 succ = load(stream)
633 result = load(stream)
634 [succ, result]
635 end
636
637 private
638 def make_proxy(obj, error=false)
639 if error
640 DRbRemoteError.new(obj)
641 else
642 DRbObject.new(obj)
643 end
644 end
645 end
646
647 # Module managing the underlying network protocol(s) used by drb.
648 #
649 # By default, drb uses the DRbTCPSocket protocol. Other protocols
650 # can be defined. A protocol must define the following class methods:
651 #
652 # [open(uri, config)] Open a client connection to the server at +uri+,
653 # using configuration +config+. Return a protocol
654 # instance for this connection.
655 # [open_server(uri, config)] Open a server listening at +uri+,
656 # using configuration +config+. Return a
657 # protocol instance for this listener.
658 # [uri_option(uri, config)] Take a URI, possibly containing an option
659 # component (e.g. a trailing '?param=val'),
660 # and return a [uri, option] tuple.
661 #
662 # All of these methods should raise a DRbBadScheme error if the URI
663 # does not identify the protocol they support (e.g. "druby:" for
664 # the standard Ruby protocol). This is how the DRbProtocol module,
665 # given a URI, determines which protocol implementation serves that
666 # protocol.
667 #
668 # The protocol instance returned by #open_server must have the
669 # following methods:
670 #
671 # [accept] Accept a new connection to the server. Returns a protocol
672 # instance capable of communicating with the client.
673 # [close] Close the server connection.
674 # [uri] Get the URI for this server.
675 #
676 # The protocol instance returned by #open must have the following methods:
677 #
678 # [send_request (ref, msg_id, arg, b)]
679 # Send a request to +ref+ with the given message id and arguments.
680 # This is most easily implemented by calling DRbMessage.send_request,
681 # providing a stream that sits on top of the current protocol.
682 # [recv_reply]
683 # Receive a reply from the server and return it as a [success-boolean,
684 # reply-value] pair. This is most easily implemented by calling
685 # DRb.recv_reply, providing a stream that sits on top of the
686 # current protocol.
687 # [alive?]
688 # Is this connection still alive?
689 # [close]
690 # Close this connection.
691 #
692 # The protocol instance returned by #open_server().accept() must have
693 # the following methods:
694 #
695 # [recv_request]
696 # Receive a request from the client and return a [object, message,
697 # args, block] tuple. This is most easily implemented by calling
698 # DRbMessage.recv_request, providing a stream that sits on top of
699 # the current protocol.
700 # [send_reply(succ, result)]
701 # Send a reply to the client. This is most easily implemented
702 # by calling DRbMessage.send_reply, providing a stream that sits
703 # on top of the current protocol.
704 # [close]
705 # Close this connection.
706 #
707 # A new protocol is registered with the DRbProtocol module using
708 # the add_protocol method.
709 #
710 # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
711 # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
712 # drb distribution.
713 module DRbProtocol
714
715 # Add a new protocol to the DRbProtocol module.
716 def add_protocol(prot)
717 @protocol.push(prot)
718 end
719 module_function :add_protocol
720
721 # Open a client connection to +uri+ with the configuration +config+.
722 #
723 # The DRbProtocol module asks each registered protocol in turn to
724 # try to open the URI. Each protocol signals that it does not handle that
725 # URI by raising a DRbBadScheme error. If no protocol recognises the
726 # URI, then a DRbBadURI error is raised. If a protocol accepts the
727 # URI, but an error occurs in opening it, a DRbConnError is raised.
728 def open(uri, config, first=true)
729 @protocol.each do |prot|
730 begin
731 return prot.open(uri, config)
732 rescue DRbBadScheme
733 rescue DRbConnError
734 raise($!)
735 rescue
736 raise(DRbConnError, "#{uri} - #{$!.inspect}")
737 end
738 end
739 if first && (config[:auto_load] != false)
740 auto_load(uri, config)
741 return open(uri, config, false)
742 end
743 raise DRbBadURI, 'can\'t parse uri:' + uri
744 end
745 module_function :open
746
747 # Open a server listening for connections at +uri+ with
748 # configuration +config+.
749 #
750 # The DRbProtocol module asks each registered protocol in turn to
751 # try to open a server at the URI. Each protocol signals that it does
752 # not handle that URI by raising a DRbBadScheme error. If no protocol
753 # recognises the URI, then a DRbBadURI error is raised. If a protocol
754 # accepts the URI, but an error occurs in opening it, the underlying
755 # error is passed on to the caller.
756 def open_server(uri, config, first=true)
757 @protocol.each do |prot|
758 begin
759 return prot.open_server(uri, config)
760 rescue DRbBadScheme
761 end
762 end
763 if first && (config[:auto_load] != false)
764 auto_load(uri, config)
765 return open_server(uri, config, false)
766 end
767 raise DRbBadURI, 'can\'t parse uri:' + uri
768 end
769 module_function :open_server
770
771 # Parse +uri+ into a [uri, option] pair.
772 #
773 # The DRbProtocol module asks each registered protocol in turn to
774 # try to parse the URI. Each protocol signals that it does not handle that
775 # URI by raising a DRbBadScheme error. If no protocol recognises the
776 # URI, then a DRbBadURI error is raised.
777 def uri_option(uri, config, first=true)
778 @protocol.each do |prot|
779 begin
780 uri, opt = prot.uri_option(uri, config)
781 # opt = nil if opt == ''
782 return uri, opt
783 rescue DRbBadScheme
784 end
785 end
786 if first && (config[:auto_load] != false)
787 auto_load(uri, config)
788 return uri_option(uri, config, false)
789 end
790 raise DRbBadURI, 'can\'t parse uri:' + uri
791 end
792 module_function :uri_option
793
794 def auto_load(uri, config) # :nodoc:
795 if uri =~ /^drb([a-z0-9]+):/
796 require("drb/#{$1}") rescue nil
797 end
798 end
799 module_function :auto_load
800 end
801
802 # The default drb protocol.
803 #
804 # Communicates over a TCP socket.
805 class DRbTCPSocket
806 private
807 def self.parse_uri(uri)
808 if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
809 host = $1
810 port = $2.to_i
811 option = $4
812 [host, port, option]
813 else
814 raise(DRbBadScheme, uri) unless uri =~ /^druby:/
815 raise(DRbBadURI, 'can\'t parse uri:' + uri)
816 end
817 end
818
819 public
820
821 # Open a client connection to +uri+ using configuration +config+.
822 def self.open(uri, config)
823 host, port, option = parse_uri(uri)
824 host.untaint
825 port.untaint
826 soc = TCPSocket.open(host, port)
827 self.new(uri, soc, config)
828 end
829
830 def self.getservername
831 host = Socket::gethostname
832 begin
833 Socket::gethostbyname(host)[0]
834 rescue
835 'localhost'
836 end
837 end
838
839 def self.open_server_inaddr_any(host, port)
840 infos = Socket::getaddrinfo(host, nil,
841 Socket::AF_UNSPEC,
842 Socket::SOCK_STREAM,
843 0,
844 Socket::AI_PASSIVE)
845 family = infos.collect { |af, *_| af }.uniq
846 case family
847 when ['AF_INET']
848 return TCPServer.open('0.0.0.0', port)
849 when ['AF_INET6']
850 return TCPServer.open('::', port)
851 else
852 return TCPServer.open(port)
853 end
854 end
855
856 # Open a server listening for connections at +uri+ using
857 # configuration +config+.
858 def self.open_server(uri, config)
859 uri = 'druby://:0' unless uri
860 host, port, opt = parse_uri(uri)
861 if host.size == 0
862 host = getservername
863 soc = open_server_inaddr_any(host, port)
864 else
865 soc = TCPServer.open(host, port)
866 end
867 port = soc.addr[1] if port == 0
868 uri = "druby://#{host}:#{port}"
869 self.new(uri, soc, config)
870 end
871
872 # Parse +uri+ into a [uri, option] pair.
873 def self.uri_option(uri, config)
874 host, port, option = parse_uri(uri)
875 return "druby://#{host}:#{port}", option
876 end
877
878 # Create a new DRbTCPSocket instance.
879 #
880 # +uri+ is the URI we are connected to.
881 # +soc+ is the tcp socket we are bound to. +config+ is our
882 # configuration.
883 def initialize(uri, soc, config={})
884 @uri = uri
885 @socket = soc
886 @config = config
887 @acl = config[:tcp_acl]
888 @msg = DRbMessage.new(config)
889 set_sockopt(@socket)
890 end
891
892 # Get the URI that we are connected to.
893 attr_reader :uri
894
895 # Get the address of our TCP peer (the other end of the socket
896 # we are bound to.
897 def peeraddr
898 @socket.peeraddr
899 end
900
901 # Get the socket.
902 def stream; @socket; end
903
904 # On the client side, send a request to the server.
905 def send_request(ref, msg_id, arg, b)
906 @msg.send_request(stream, ref, msg_id, arg, b)
907 end
908
909 # On the server side, receive a request from the client.
910 def recv_request
911 @msg.recv_request(stream)
912 end
913
914 # On the server side, send a reply to the client.
915 def send_reply(succ, result)
916 @msg.send_reply(stream, succ, result)
917 end
918
919 # On the client side, receive a reply from the server.
920 def recv_reply
921 @msg.recv_reply(stream)
922 end
923
924 public
925
926 # Close the connection.
927 #
928 # If this is an instance returned by #open_server, then this stops
929 # listening for new connections altogether. If this is an instance
930 # returned by #open or by #accept, then it closes this particular
931 # client-server session.
932 def close
933 if @socket
934 @socket.close
935 @socket = nil
936 end
937 end
938
939 # On the server side, for an instance returned by #open_server,
940 # accept a client connection and return a new instance to handle
941 # the server's side of this client-server session.
942 def accept
943 while true
944 s = @socket.accept
945 break if (@acl ? @acl.allow_socket?(s) : true)
946 s.close
947 end
948 self.class.new(nil, s, @config)
949 end
950
951 # Check to see if this connection is alive.
952 def alive?
953 return false unless @socket
954 if IO.select([@socket], nil, nil, 0)
955 close
956 return false
957 end
958 true
959 end
960
961 def set_sockopt(soc) # :nodoc:
962 soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
963 soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
964 end
965 end
966
967 module DRbProtocol
968 @protocol = [DRbTCPSocket] # default
969 end
970
971 class DRbURIOption # :nodoc: I don't understand the purpose of this class...
972 def initialize(option)
973 @option = option.to_s
974 end
975 attr :option
976 def to_s; @option; end
977
978 def ==(other)
979 return false unless DRbURIOption === other
980 @option == other.option
981 end
982
983 def hash
984 @option.hash
985 end
986
987 alias eql? ==
988 end
989
990 # Object wrapping a reference to a remote drb object.
991 #
992 # Method calls on this object are relayed to the remote
993 # object that this object is a stub for.
994 class DRbObject
995
996 # Unmarshall a marshalled DRbObject.
997 #
998 # If the referenced object is located within the local server, then
999 # the object itself is returned. Otherwise, a new DRbObject is
1000 # created to act as a stub for the remote referenced object.
1001 def self._load(s)
1002 uri, ref = Marshal.load(s)
1003
1004 if DRb.here?(uri)
1005 obj = DRb.to_obj(ref)
1006 if ((! obj.tainted?) && Thread.current[:drb_untaint])
1007 Thread.current[:drb_untaint].push(obj)
1008 end
1009 return obj
1010 end
1011
1012 self.new_with(uri, ref)
1013 end
1014
1015 def self.new_with(uri, ref)
1016 it = self.allocate
1017 it.instance_variable_set('@uri', uri)
1018 it.instance_variable_set('@ref', ref)
1019 it
1020 end
1021
1022 # Create a new DRbObject from a URI alone.
1023 def self.new_with_uri(uri)
1024 self.new(nil, uri)
1025 end
1026
1027 # Marshall this object.
1028 #
1029 # The URI and ref of the object are marshalled.
1030 def _dump(lv)
1031 Marshal.dump([@uri, @ref])
1032 end
1033
1034 # Create a new remote object stub.
1035 #
1036 # +obj+ is the (local) object we want to create a stub for. Normally
1037 # this is +nil+. +uri+ is the URI of the remote object that this
1038 # will be a stub for.
1039 def initialize(obj, uri=nil)
1040 @uri = nil
1041 @ref = nil
1042 if obj.nil?
1043 return if uri.nil?
1044 @uri, option = DRbProtocol.uri_option(uri, DRb.config)
1045 @ref = DRbURIOption.new(option) unless option.nil?
1046 else
1047 @uri = uri ? uri : (DRb.uri rescue nil)
1048 @ref = obj ? DRb.to_id(obj) : nil
1049 end
1050 end
1051
1052 # Get the URI of the remote object.
1053 def __drburi
1054 @uri
1055 end
1056
1057 # Get the reference of the object, if local.
1058 def __drbref
1059 @ref
1060 end
1061
1062 undef :to_s
1063 undef :to_a if respond_to?(:to_a)
1064
1065 def respond_to?(msg_id, priv=false)
1066 case msg_id
1067 when :_dump
1068 true
1069 when :marshal_dump
1070 false
1071 else
1072 method_missing(:respond_to?, msg_id, priv)
1073 end
1074 end
1075
1076 # Routes method calls to the referenced object.
1077 def method_missing(msg_id, *a, &b)
1078 if DRb.here?(@uri)
1079 obj = DRb.to_obj(@ref)
1080 DRb.current_server.check_insecure_method(obj, msg_id)
1081 return obj.__send__(msg_id, *a, &b)
1082 end
1083
1084 succ, result = self.class.with_friend(@uri) do
1085 DRbConn.open(@uri) do |conn|
1086 conn.send_message(self, msg_id, a, b)
1087 end
1088 end
1089
1090 if succ
1091 return result
1092 elsif DRbUnknown === result
1093 raise result
1094 else
1095 bt = self.class.prepare_backtrace(@uri, result)
1096 result.set_backtrace(bt + caller)
1097 raise result
1098 end
1099 end
1100
1101 def self.with_friend(uri)
1102 friend = DRb.fetch_server(uri)
1103 return yield() unless friend
1104
1105 save = Thread.current['DRb']
1106 Thread.current['DRb'] = { 'server' => friend }
1107 return yield
1108 ensure
1109 Thread.current['DRb'] = save if friend
1110 end
1111
1112 def self.prepare_backtrace(uri, result)
1113 prefix = "(#{uri}) "
1114 bt = []
1115 result.backtrace.each do |x|
1116 break if /`__send__'$/ =~ x
1117 if /^\(druby:\/\// =~ x
1118 bt.push(x)
1119 else
1120 bt.push(prefix + x)
1121 end
1122 end
1123 bt
1124 end
1125
1126 def pretty_print(q) # :nodoc:
1127 q.pp_object(self)
1128 end
1129
1130 def pretty_print_cycle(q) # :nodoc:
1131 q.object_address_group(self) {
1132 q.breakable
1133 q.text '...'
1134 }
1135 end
1136 end
1137
1138 # Class handling the connection between a DRbObject and the
1139 # server the real object lives on.
1140 #
1141 # This class maintains a pool of connections, to reduce the
1142 # overhead of starting and closing down connections for each
1143 # method call.
1144 #
1145 # This class is used internally by DRbObject. The user does
1146 # not normally need to deal with it directly.
1147 class DRbConn
1148 POOL_SIZE = 16 # :nodoc:
1149 @mutex = Mutex.new
1150 @pool = []
1151
1152 def self.open(remote_uri) # :nodoc:
1153 begin
1154 conn = nil
1155
1156 @mutex.synchronize do
1157 #FIXME
1158 new_pool = []
1159 @pool.each do |c|
1160 if conn.nil? and c.uri == remote_uri
1161 conn = c if c.alive?
1162 else
1163 new_pool.push c
1164 end
1165 end
1166 @pool = new_pool
1167 end
1168
1169 conn = self.new(remote_uri) unless conn
1170 succ, result = yield(conn)
1171 return succ, result
1172
1173 ensure
1174 if conn
1175 if succ
1176 @mutex.synchronize do
1177 @pool.unshift(conn)
1178 @pool.pop.close while @pool.size > POOL_SIZE
1179 end
1180 else
1181 conn.close
1182 end
1183 end
1184 end
1185 end
1186
1187 def initialize(remote_uri) # :nodoc:
1188 @uri = remote_uri
1189 @protocol = DRbProtocol.open(remote_uri, DRb.config)
1190 end
1191 attr_reader :uri # :nodoc:
1192
1193 def send_message(ref, msg_id, arg, block) # :nodoc:
1194 @protocol.send_request(ref, msg_id, arg, block)
1195 @protocol.recv_reply
1196 end
1197
1198 def close # :nodoc:
1199 @protocol.close
1200 @protocol = nil
1201 end
1202
1203 def alive? # :nodoc:
1204 @protocol.alive?
1205 end
1206 end
1207
1208 # Class representing a drb server instance.
1209 #
1210 # A DRbServer must be running in the local process before any incoming
1211 # dRuby calls can be accepted, or any local objects can be passed as
1212 # dRuby references to remote processes, even if those local objects are
1213 # never actually called remotely. You do not need to start a DRbServer
1214 # in the local process if you are only making outgoing dRuby calls
1215 # passing marshalled parameters.
1216 #
1217 # Unless multiple servers are being used, the local DRbServer is normally
1218 # started by calling DRb.start_service.
1219 class DRbServer
1220 @@acl = nil
1221 @@idconv = DRbIdConv.new
1222 @@secondary_server = nil
1223 @@argc_limit = 256
1224 @@load_limit = 256 * 102400
1225 @@verbose = false
1226 @@safe_level = 0
1227
1228 # Set the default value for the :argc_limit option.
1229 #
1230 # See #new(). The initial default value is 256.
1231 def self.default_argc_limit(argc)
1232 @@argc_limit = argc
1233 end
1234
1235 # Set the default value for the :load_limit option.
1236 #
1237 # See #new(). The initial default value is 25 MB.
1238 def self.default_load_limit(sz)
1239 @@load_limit = sz
1240 end
1241
1242 # Set the default value for the :acl option.
1243 #
1244 # See #new(). The initial default value is nil.
1245 def self.default_acl(acl)
1246 @@acl = acl
1247 end
1248
1249 # Set the default value for the :id_conv option.
1250 #
1251 # See #new(). The initial default value is a DRbIdConv instance.
1252 def self.default_id_conv(idconv)
1253 @@idconv = idconv
1254 end
1255
1256 def self.default_safe_level(level)
1257 @@safe_level = level
1258 end
1259
1260 # Set the default value of the :verbose option.
1261 #
1262 # See #new(). The initial default value is false.
1263 def self.verbose=(on)
1264 @@verbose = on
1265 end
1266
1267 # Get the default value of the :verbose option.
1268 def self.verbose
1269 @@verbose
1270 end
1271
1272 def self.make_config(hash={}) # :nodoc:
1273 default_config = {
1274 :idconv => @@idconv,
1275 :verbose => @@verbose,
1276 :tcp_acl => @@acl,
1277 :load_limit => @@load_limit,
1278 :argc_limit => @@argc_limit,
1279 :safe_level => @@safe_level
1280 }
1281 default_config.update(hash)
1282 end
1283
1284 # Create a new DRbServer instance.
1285 #
1286 # +uri+ is the URI to bind to. This is normally of the form
1287 # 'druby://<hostname>:<port>' where <hostname> is a hostname of
1288 # the local machine. If nil, then the system's default hostname
1289 # will be bound to, on a port selected by the system; these value
1290 # can be retrieved from the +uri+ attribute. 'druby:' specifies
1291 # the default dRuby transport protocol: another protocol, such
1292 # as 'drbunix:', can be specified instead.
1293 #
1294 # +front+ is the front object for the server, that is, the object
1295 # to which remote method calls on the server will be passed. If
1296 # nil, then the server will not accept remote method calls.
1297 #
1298 # If +config_or_acl+ is a hash, it is the configuration to
1299 # use for this server. The following options are recognised:
1300 #
1301 # :idconv :: an id-to-object conversion object. This defaults
1302 # to an instance of the class DRb::DRbIdConv.
1303 # :verbose :: if true, all unsuccessful remote calls on objects
1304 # in the server will be logged to $stdout. false
1305 # by default.
1306 # :tcp_acl :: the access control list for this server. See
1307 # the ACL class from the main dRuby distribution.
1308 # :load_limit :: the maximum message size in bytes accepted by
1309 # the server. Defaults to 25 MB (26214400).
1310 # :argc_limit :: the maximum number of arguments to a remote
1311 # method accepted by the server. Defaults to
1312 # 256.
1313 #
1314 # The default values of these options can be modified on
1315 # a class-wide basis by the class methods #default_argc_limit,
1316 # #default_load_limit, #default_acl, #default_id_conv,
1317 # and #verbose=
1318 #
1319 # If +config_or_acl+ is not a hash, but is not nil, it is
1320 # assumed to be the access control list for this server.
1321 # See the :tcp_acl option for more details.
1322 #
1323 # If no other server is currently set as the primary server,
1324 # this will become the primary server.
1325 #
1326 # The server will immediately start running in its own thread.
1327 def initialize(uri=nil, front=nil, config_or_acl=nil)
1328 if Hash === config_or_acl
1329 config = config_or_acl.dup
1330 else
1331 acl = config_or_acl || @@acl
1332 config = {
1333 :tcp_acl => acl
1334 }
1335 end
1336
1337 @config = self.class.make_config(config)
1338
1339 @protocol = DRbProtocol.open_server(uri, @config)
1340 @uri = @protocol.uri
1341
1342 @front = front
1343 @idconv = @config[:idconv]
1344 @safe_level = @config[:safe_level]
1345
1346 @grp = ThreadGroup.new
1347 @thread = run
1348
1349 DRb.regist_server(self)
1350 end
1351
1352 # The URI of this DRbServer.
1353 attr_reader :uri
1354
1355 # The main thread of this DRbServer.
1356 #
1357 # This is the thread that listens for and accepts connections
1358 # from clients, not that handles each client's request-response
1359 # session.
1360 attr_reader :thread
1361
1362 # The front object of the DRbServer.
1363 #
1364 # This object receives remote method calls made on the server's
1365 # URI alone, with an object id.
1366 attr_reader :front
1367
1368 # The configuration of this DRbServer
1369 attr_reader :config
1370
1371 attr_reader :safe_level
1372
1373 # Set whether to operate in verbose mode.
1374 #
1375 # In verbose mode, failed calls are logged to stdout.
1376 def verbose=(v); @config[:verbose]=v; end
1377
1378 # Get whether the server is in verbose mode.
1379 #
1380 # In verbose mode, failed calls are logged to stdout.
1381 def verbose; @config[:verbose]; end
1382
1383 # Is this server alive?
1384 def alive?
1385 @thread.alive?
1386 end
1387
1388 # Stop this server.
1389 def stop_service
1390 DRb.remove_server(self)
1391 if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
1392 Thread.current['DRb']['stop_service'] = true
1393 else
1394 @thread.kill
1395 end
1396 end
1397
1398 # Convert a dRuby reference to the local object it refers to.
1399 def to_obj(ref)
1400 return front if ref.nil?
1401 return front[ref.to_s] if DRbURIOption === ref
1402 @idconv.to_obj(ref)
1403 end
1404
1405 # Convert a local object to a dRuby reference.
1406 def to_id(obj)
1407 return nil if obj.__id__ == front.__id__
1408 @idconv.to_id(obj)
1409 end
1410
1411 private
1412 def kill_sub_thread
1413 Thread.new do
1414 grp = ThreadGroup.new
1415 grp.add(Thread.current)
1416 list = @grp.list
1417 while list.size > 0
1418 list.each do |th|
1419 th.kill if th.alive?
1420 end
1421 list = @grp.list
1422 end
1423 end
1424 end
1425
1426 def run
1427 Thread.start do
1428 begin
1429 while true
1430 main_loop
1431 end
1432 ensure
1433 @protocol.close if @protocol
1434 kill_sub_thread
1435 end
1436 end
1437 end
1438
1439 # List of insecure methods.
1440 #
1441 # These methods are not callable via dRuby.
1442 INSECURE_METHOD = [
1443 :__send__
1444 ]
1445
1446 # Has a method been included in the list of insecure methods?
1447 def insecure_method?(msg_id)
1448 INSECURE_METHOD.include?(msg_id)
1449 end
1450
1451 # Coerce an object to a string, providing our own representation if
1452 # to_s is not defined for the object.
1453 def any_to_s(obj)
1454 obj.to_s + ":#{obj.class}"
1455 rescue
1456 sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
1457 end
1458
1459 # Check that a method is callable via dRuby.
1460 #
1461 # +obj+ is the object we want to invoke the method on. +msg_id+ is the
1462 # method name, as a Symbol.
1463 #
1464 # If the method is an insecure method (see #insecure_method?) a
1465 # SecurityError is thrown. If the method is private or undefined,
1466 # a NameError is thrown.
1467 def check_insecure_method(obj, msg_id)
1468 return true if Proc === obj && msg_id == :__drb_yield
1469 raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
1470 raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
1471
1472 if obj.private_methods.include?(msg_id.to_s)
1473 desc = any_to_s(obj)
1474 raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
1475 elsif obj.protected_methods.include?(msg_id.to_s)
1476 desc = any_to_s(obj)
1477 raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
1478 else
1479 true
1480 end
1481 end
1482 public :check_insecure_method
1483
1484 class InvokeMethod # :nodoc:
1485 def initialize(drb_server, client)
1486 @drb_server = drb_server
1487 @safe_level = drb_server.safe_level
1488 @client = client
1489 end
1490
1491 def perform
1492 @result = nil
1493 @succ = false
1494 setup_message
1495
1496 if $SAFE < @safe_level
1497 info = Thread.current['DRb']
1498 if @block
1499 @result = Thread.new {
1500 Thread.current['DRb'] = info
1501 $SAFE = @safe_level
1502 perform_with_block
1503 }.value
1504 else
1505 @result = Thread.new {
1506 Thread.current['DRb'] = info
1507 $SAFE = @safe_level
1508 perform_without_block
1509 }.value
1510 end
1511 else
1512 if @block
1513 @result = perform_with_block
1514 else
1515 @result = perform_without_block
1516 end
1517 end
1518 @succ = true
1519 if @msg_id == :to_ary && @result.class == Array
1520 @result = DRbArray.new(@result)
1521 end
1522 return @succ, @result
1523 rescue StandardError, ScriptError, Interrupt
1524 @result = $!
1525 return @succ, @result
1526 end
1527
1528 private
1529 def init_with_client
1530 obj, msg, argv, block = @client.recv_request
1531 @obj = obj
1532 @msg_id = msg.intern
1533 @argv = argv
1534 @block = block
1535 end
1536
1537 def check_insecure_method
1538 @drb_server.check_insecure_method(@obj, @msg_id)
1539 end
1540
1541 def setup_message
1542 init_with_client
1543 check_insecure_method
1544 end
1545
1546 def perform_without_block
1547 if Proc === @obj && @msg_id == :__drb_yield
1548 if @argv.size == 1
1549 ary = @argv
1550 else
1551 ary = [@argv]
1552 end
1553 ary.collect(&@obj)[0]
1554 else
1555 @obj.__send__(@msg_id, *@argv)
1556 end
1557 end
1558
1559 end
1560
1561 if RUBY_VERSION >= '1.8'
1562 require 'drb/invokemethod'
1563 class InvokeMethod
1564 include InvokeMethod18Mixin
1565 end
1566 else
1567 require 'drb/invokemethod16'
1568 class InvokeMethod
1569 include InvokeMethod16Mixin
1570 end
1571 end
1572
1573 # The main loop performed by a DRbServer's internal thread.
1574 #
1575 # Accepts a connection from a client, and starts up its own
1576 # thread to handle it. This thread loops, receiving requests
1577 # from the client, invoking them on a local object, and
1578 # returning responses, until the client closes the connection
1579 # or a local method call fails.
1580 def main_loop
1581 Thread.start(@protocol.accept) do |client|
1582 @grp.add Thread.current
1583 Thread.current['DRb'] = { 'client' => client ,
1584 'server' => self }
1585 loop do
1586 begin
1587 succ = false
1588 invoke_method = InvokeMethod.new(self, client)
1589 succ, result = invoke_method.perform
1590 if !succ && verbose
1591 p result
1592 result.backtrace.each do |x|
1593 puts x
1594 end
1595 end
1596 client.send_reply(succ, result) rescue nil
1597 ensure
1598 client.close unless succ
1599 if Thread.current['DRb']['stop_service']
1600 Thread.new { stop_service }
1601 end
1602 break unless succ
1603 end
1604 end
1605 end
1606 end
1607 end
1608
1609 @primary_server = nil
1610
1611 # Start a dRuby server locally.
1612 #
1613 # The new dRuby server will become the primary server, even
1614 # if another server is currently the primary server.
1615 #
1616 # +uri+ is the URI for the server to bind to. If nil,
1617 # the server will bind to random port on the default local host
1618 # name and use the default dRuby protocol.
1619 #
1620 # +front+ is the server's front object. This may be nil.
1621 #
1622 # +config+ is the configuration for the new server. This may
1623 # be nil.
1624 #
1625 # See DRbServer::new.
1626 def start_service(uri=nil, front=nil, config=nil)
1627 @primary_server = DRbServer.new(uri, front, config)
1628 end
1629 module_function :start_service
1630
1631 # The primary local dRuby server.
1632 #
1633 # This is the server created by the #start_service call.
1634 attr_accessor :primary_server
1635 module_function :primary_server=, :primary_server
1636
1637 # Get the 'current' server.
1638 #
1639 # In the context of execution taking place within the main
1640 # thread of a dRuby server (typically, as a result of a remote
1641 # call on the server or one of its objects), the current
1642 # server is that server. Otherwise, the current server is
1643 # the primary server.
1644 #
1645 # If the above rule fails to find a server, a DRbServerNotFound
1646 # error is raised.
1647 def current_server
1648 drb = Thread.current['DRb']
1649 server = (drb && drb['server']) ? drb['server'] : @primary_server
1650 raise DRbServerNotFound unless server
1651 return server
1652 end
1653 module_function :current_server
1654
1655 # Stop the local dRuby server.
1656 #
1657 # This operates on the primary server. If there is no primary
1658 # server currently running, it is a noop.
1659 def stop_service
1660 @primary_server.stop_service if @primary_server
1661 @primary_server = nil
1662 end
1663 module_function :stop_service
1664
1665 # Get the URI defining the local dRuby space.
1666 #
1667 # This is the URI of the current server. See #current_server.
1668 def uri
1669 current_server.uri
1670 end
1671 module_function :uri
1672
1673 # Is +uri+ the URI for the current local server?
1674 def here?(uri)
1675 (current_server.uri rescue nil) == uri
1676 end
1677 module_function :here?
1678
1679 # Get the configuration of the current server.
1680 #
1681 # If there is no current server, this returns the default configuration.
1682 # See #current_server and DRbServer::make_config.
1683 def config
1684 current_server.config
1685 rescue
1686 DRbServer.make_config
1687 end
1688 module_function :config
1689
1690 # Get the front object of the current server.
1691 #
1692 # This raises a DRbServerNotFound error if there is no current server.
1693 # See #current_server.
1694 def front
1695 current_server.front
1696 end
1697 module_function :front
1698
1699 # Convert a reference into an object using the current server.
1700 #
1701 # This raises a DRbServerNotFound error if there is no current server.
1702 # See #current_server.
1703 def to_obj(ref)
1704 current_server.to_obj(ref)
1705 end
1706
1707 # Get a reference id for an object using the current server.
1708 #
1709 # This raises a DRbServerNotFound error if there is no current server.
1710 # See #current_server.
1711 def to_id(obj)
1712 current_server.to_id(obj)
1713 end
1714 module_function :to_id
1715 module_function :to_obj
1716
1717 # Get the thread of the primary server.
1718 #
1719 # This returns nil if there is no primary server. See #primary_server.
1720 def thread
1721 @primary_server ? @primary_server.thread : nil
1722 end
1723 module_function :thread
1724
1725 # Set the default id conv object.
1726 #
1727 # See DRbServer#default_id_conv.
1728 def install_id_conv(idconv)
1729 DRbServer.default_id_conv(idconv)
1730 end
1731 module_function :install_id_conv
1732
1733 # Set the default acl.
1734 #
1735 # See DRb::DRbServer.default_acl.
1736 def install_acl(acl)
1737 DRbServer.default_acl(acl)
1738 end
1739 module_function :install_acl
1740
1741 @server = {}
1742 def regist_server(server)
1743 @server[server.uri] = server
1744 Thread.exclusive do
1745 @primary_server = server unless @primary_server
1746 end
1747 end
1748 module_function :regist_server
1749
1750 def remove_server(server)
1751 @server.delete(server.uri)
1752 end
1753 module_function :remove_server
1754
1755 def fetch_server(uri)
1756 @server[uri]
1757 end
1758 module_function :fetch_server
1759end
1760
1761DRbObject = DRb::DRbObject
1762DRbUndumped = DRb::DRbUndumped
1763DRbIdConv = DRb::DRbIdConv
Note: See TracBrowser for help on using the repository browser.