summaryrefslogtreecommitdiff
path: root/src/detach/library/scala/runtime/remoting/RegistryDelegate.scala
blob: 1105832ef7a70d12db9dc899a1f96d90c9b7d168 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2002-2013, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: RegistryDelegate.scala 18234 2009-07-07 13:21:57Z michelou $

package scala.runtime.remoting

import java.rmi.{RMISecurityManager, Remote, RemoteException}
import java.rmi.registry.{LocateRegistry, Registry}
import java.rmi.server.UnicastRemoteObject

/**
 * <p>
 *   This class implements the registry delegate concept
 *   (see http://www.genady.net/rmi/v20/docs/delegate/RegistryDelegate.html)
 * </p>
 * <p>
 *   In order to enforce some level of security, the standard RMI registry
 *   implementation (e.g. <code>rmiregistry.exe</code>) only allows processes
 *   on the same host to register objects in the registry (think of a bank
 *   running a registry on one of its servers, and doesn't want anybody
 *   modifying it). So, by design, if a process tries to
 *   <code>bind(String, Remote)</code> an object to a remote registry,
 *   an exception will be thrown.
 * </p>
 * <p>
 *   However, the design of a distributed system may require remote clients to
 *   register themselves in a central registry. If such system is deployed in a
 *   controlled and trusted environment (e.g., a firewalled intranet with tight
 *   access control), the security risk may be acceptable.
 * </p>
 * <p>
 *   The simplest technical solution to the remote registration problem is to
 *   have a registry delegate. A registry delegate is an object that serves as
 *   a proxy for the real registry. The delegate itself usually appears in the
 *   registry under a well known name. It implements the Registry interface and
 *   simply delegates all method calls to the appropriate methods of the real
 *   registry. The delegate is allowed to perform bind and unbind operations
 *   because it is running on the same host as the registry.
 * </p>
 * <p>
 *   The common scenario for starting a registry and creating the delegate is
 *   starting a class with the following <code>main(Array[String])</code> method:
 * </p>
 * <pre>
 *   @throws(classOf[AccessException], classOf[RemoteException], classOf[AlreadyBoundException])
 *   <b>object</b> namingService {
 *     <b>def</b> main(args: Array[String]) {
 *       <b>if</b> (System.getSecurityManager() == <b>null</b>)
 *         System.setSecurityManager(<b>new</b> RMISecurityManager())
 *
 *       <b>val</b> registry = LocateRegistry.createRegistry(REGISTRY_PORT)
 *       registry.bind(DELEGATE_NAME, <b>new</b> RegistryDelegate());
 *
 *       do {
 *         <b>try</b> {
 *           Thread.sleep(Long.MAX_VALUE)
 *         } <b>catch</b> {
 *           <b>case</b> e: InterruptedException => // do nothing
 *           <b>case</b> e: Throwable => e.printStackTrace(); sys.exit(1)
 *         }
 *       } while (<b>true</b>)
 *     }
 *  }</pre>
 * <p>
 *   The common usage scenario looks something like:
 * </p><pre>
 *   Registry remoteRegistry = LocateRegistry.getRegistry("remotehost.mycompany.com");
 *   Registry delegate = (Registry) remoteRegistry.lookup(DELEGATE_NAME);
 *   delegate.bind("someName", <b>new</b> SomeRemoteObject());</pre>
 * <p>
 *   The <code>getRegistryDelegate(String)</code> method is a helper method
 *   that fetches the registry delegate for you.
 * </p>
 * <p>
 *   The <code>main(Array[String])</code> method of this class will create a
 *   local registry on the default port, create a registry delegate and bind
 *   it under the well known name that you chose in the wizard
 *   (<code>DELEGATE_NAME</code>).
 * </p>
 *
 * @author Genady Beryozkin, rmi-info@genady.net
 */

object RMIDelegate {
  /** The name under which the delegate appears in the registry. */
  val DELEGATE_NAME = "foo"

  /** This method retrieves the registry delegate from a registry that is
   *  running on a remote host.
   */
  @throws(classOf[RemoteException])
  def getRegistryDelegate(remoteHost: String): Registry =
    getRegistryDelegate(remoteHost, Registry.REGISTRY_PORT)

  /** This method retrieves the registry delegate from a registry that is
   * running on a remote host.
   */
  @throws(classOf[RemoteException])
  def getRegistryDelegate(remoteHost: String, remotePort: Int): Registry = {
    val registry = LocateRegistry.getRegistry(remoteHost, remotePort)
    (registry lookup DELEGATE_NAME).asInstanceOf[Registry]
  }

  /** A simple way to run a registry and bind a registry delegate. */
  @throws(classOf[RemoteException])
  def main(args: Array[String]) {
    var port = Registry.REGISTRY_PORT

    if (args.length > 0) {
      if (args(0) equals "-help") {
        println("Usage: rmidelegate <options> <port>")
        sys.exit(0)
      }
      try {
        port = args(0).toInt
      } catch {
        case e: NumberFormatException =>
          println("Usage: rmidelegate <options> <port>")
          sys.exit(1)
      }
      val opts = args filter (_ startsWith "-J-D")
      for (opt <- opts) {
        val x = opt.substring(4) split "="
        if (x.length == 2) System.setProperty(x(0), x(1))
        else System.setProperty(x(0), "")
      }
    }

    if (System.getSecurityManager() == null)
      System.setSecurityManager(new RMISecurityManager() {
        override def checkPermission(p: java.security.Permission) {}
      })


    val registry = LocateRegistry.createRegistry(port)
    registry.bind(DELEGATE_NAME, new RegistryDelegate())

    do {
      try {
        Thread.sleep(Long.MaxValue)
      } catch {
       case e: InterruptedException =>
         // do nothing
       case e: Throwable =>
         e.printStackTrace()
         sys.exit(1)
      }
    } while (true)
  }

}

/** Create a delegate for a user provided registry instance. The registry is
 *  assumed to be a local registry, as there is no point in creating a delegate
 *  for a remote registry.
 */
class RegistryDelegate(reg: Registry) extends UnicastRemoteObject with Registry {
  /** The local registry */
  private val localRegistry: Registry = reg

  /** Create a delegate for a local registry that is bound to the default
   *  local port (1099).
   */
  def this() = this(LocateRegistry.getRegistry())

  /** Create a delegate for a local registry that is bound to a user
   *  specified port.
   */
  def this(port: Int) = this(LocateRegistry.getRegistry(port))

  @throws(classOf[RemoteException])
  def bind(name: String, obj: Remote) { localRegistry.bind(name, obj) }

  @throws(classOf[RemoteException])
  def list(): Array[String] = localRegistry.list()

  @throws(classOf[RemoteException])
  def lookup(name: String): Remote = localRegistry.lookup(name)

  @throws(classOf[RemoteException])
  def rebind(name: String, obj: Remote) { localRegistry.rebind(name, obj) }

  @throws(classOf[RemoteException])
  def unbind(name: String) { localRegistry.unbind(name) }

}