KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > remoting > rmi > RmiServiceExporter


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.remoting.rmi;
18
19 import java.rmi.NoSuchObjectException JavaDoc;
20 import java.rmi.NotBoundException JavaDoc;
21 import java.rmi.Remote JavaDoc;
22 import java.rmi.RemoteException JavaDoc;
23 import java.rmi.registry.LocateRegistry JavaDoc;
24 import java.rmi.registry.Registry JavaDoc;
25 import java.rmi.server.RMIClientSocketFactory JavaDoc;
26 import java.rmi.server.RMIServerSocketFactory JavaDoc;
27 import java.rmi.server.UnicastRemoteObject JavaDoc;
28
29 import org.springframework.beans.factory.DisposableBean;
30 import org.springframework.beans.factory.InitializingBean;
31
32 /**
33  * RMI exporter that exposes the specified service as RMI object with the specified name.
34  * Such services can be accessed via plain RMI or via {@link RmiProxyFactoryBean}.
35  * Also supports exposing any non-RMI service via RMI invokers, to be accessed via
36  * {@link RmiClientInterceptor} / {@link RmiProxyFactoryBean}'s automatic detection
37  * of such invokers.
38  *
39  * <p>With an RMI invoker, RMI communication works on the {@link RmiInvocationHandler}
40  * level, needing only one stub for any service. Service interfaces do not have to
41  * extend <code>java.rmi.Remote</code> or throw <code>java.rmi.RemoteException</code>
42  * on all methods, but in and out parameters have to be serializable.
43  *
44  * <p>The major advantage of RMI, compared to Hessian and Burlap, is serialization.
45  * Effectively, any serializable Java object can be transported without hassle.
46  * Hessian and Burlap have their own (de-)serialization mechanisms, but are
47  * HTTP-based and thus much easier to setup than RMI. Alternatively, consider
48  * Spring's HTTP invoker to combine Java serialization with HTTP-based transport.
49  *
50  * <p>Note: RMI makes a best-effort attempt to obtain the fully qualified host name.
51  * If one cannot be determined, it will fall back and use the IP address. Depending
52  * on your network configuration, in some cases it will resolve the IP to the loopback
53  * address. To ensure that RMI will use the host name bound to the correct network
54  * interface, you should pass the <code>java.rmi.server.hostname</code> property to the
55  * JVM that will export the registry and/or the service using the "-D" JVM argument.
56  * For example: <code>-Djava.rmi.server.hostname=myserver.com</code>
57  *
58  * @author Juergen Hoeller
59  * @since 13.05.2003
60  * @see RmiClientInterceptor
61  * @see RmiProxyFactoryBean
62  * @see java.rmi.Remote
63  * @see java.rmi.RemoteException
64  * @see org.springframework.remoting.caucho.HessianServiceExporter
65  * @see org.springframework.remoting.caucho.BurlapServiceExporter
66  * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
67  */

68 public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
69
70     private String JavaDoc serviceName;
71
72     private int servicePort = 0; // anonymous port
73

74     private RMIClientSocketFactory JavaDoc clientSocketFactory;
75
76     private RMIServerSocketFactory JavaDoc serverSocketFactory;
77
78     private Registry JavaDoc registry;
79
80     private String JavaDoc registryHost;
81
82     private int registryPort = Registry.REGISTRY_PORT;
83
84     private RMIClientSocketFactory JavaDoc registryClientSocketFactory;
85
86     private RMIServerSocketFactory JavaDoc registryServerSocketFactory;
87
88     private boolean alwaysCreateRegistry = false;
89
90     private Remote JavaDoc exportedObject;
91
92
93     /**
94      * Set the name of the exported RMI service,
95      * i.e. <code>rmi://host:port/NAME</code>
96      */

97     public void setServiceName(String JavaDoc serviceName) {
98         this.serviceName = serviceName;
99     }
100
101     /**
102      * Set the port that the exported RMI service will use.
103      * <p>Default is 0 (anonymous port).
104      */

105     public void setServicePort(int servicePort) {
106         this.servicePort = servicePort;
107     }
108
109     /**
110      * Set a custom RMI client socket factory to use for exporting the service.
111      * <p>If the given object also implements <code>java.rmi.server.RMIServerSocketFactory</code>,
112      * it will automatically be registered as server socket factory too.
113      * @see #setServerSocketFactory
114      * @see java.rmi.server.RMIClientSocketFactory
115      * @see java.rmi.server.RMIServerSocketFactory
116      * @see UnicastRemoteObject#exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory)
117      */

118     public void setClientSocketFactory(RMIClientSocketFactory JavaDoc clientSocketFactory) {
119         this.clientSocketFactory = clientSocketFactory;
120     }
121
122     /**
123      * Set a custom RMI server socket factory to use for exporting the service.
124      * <p>Only needs to be specified when the client socket factory does not
125      * implement <code>java.rmi.server.RMIServerSocketFactory</code> already.
126      * @see #setClientSocketFactory
127      * @see java.rmi.server.RMIClientSocketFactory
128      * @see java.rmi.server.RMIServerSocketFactory
129      * @see UnicastRemoteObject#exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory)
130      */

131     public void setServerSocketFactory(RMIServerSocketFactory JavaDoc serverSocketFactory) {
132         this.serverSocketFactory = serverSocketFactory;
133     }
134
135     /**
136      * Specify the RMI registry to register the exported service with.
137      * Typically used in combination with RmiRegistryFactoryBean.
138      * <p>Alternatively, you can specify all registry properties locally.
139      * This exporter will then try to locate the specified registry,
140      * automatically creating a new local one if appropriate.
141      * <p>Default is a local registry at the default port (1099),
142      * created on the fly if necessary.
143      * @see RmiRegistryFactoryBean
144      * @see #setRegistryHost
145      * @see #setRegistryPort
146      * @see #setRegistryClientSocketFactory
147      * @see #setRegistryServerSocketFactory
148      */

149     public void setRegistry(Registry JavaDoc registry) {
150         this.registry = registry;
151     }
152
153     /**
154      * Set the host of the registry for the exported RMI service,
155      * i.e. <code>rmi://HOST:port/name</code>
156      * <p>Default is localhost.
157      */

158     public void setRegistryHost(String JavaDoc registryHost) {
159         this.registryHost = registryHost;
160     }
161
162     /**
163      * Set the port of the registry for the exported RMI service,
164      * i.e. <code>rmi://host:PORT/name</code>
165      * <p>Default is <code>Registry.REGISTRY_PORT</code> (1099).
166      * @see java.rmi.registry.Registry#REGISTRY_PORT
167      */

168     public void setRegistryPort(int registryPort) {
169         this.registryPort = registryPort;
170     }
171
172     /**
173      * Set a custom RMI client socket factory to use for the RMI registry.
174      * <p>If the given object also implements <code>java.rmi.server.RMIServerSocketFactory</code>,
175      * it will automatically be registered as server socket factory too.
176      * @see #setRegistryServerSocketFactory
177      * @see java.rmi.server.RMIClientSocketFactory
178      * @see java.rmi.server.RMIServerSocketFactory
179      * @see LocateRegistry#getRegistry(String, int, RMIClientSocketFactory)
180      */

181     public void setRegistryClientSocketFactory(RMIClientSocketFactory JavaDoc registryClientSocketFactory) {
182         this.registryClientSocketFactory = registryClientSocketFactory;
183     }
184
185     /**
186      * Set a custom RMI server socket factory to use for the RMI registry.
187      * <p>Only needs to be specified when the client socket factory does not
188      * implement <code>java.rmi.server.RMIServerSocketFactory</code> already.
189      * @see #setRegistryClientSocketFactory
190      * @see java.rmi.server.RMIClientSocketFactory
191      * @see java.rmi.server.RMIServerSocketFactory
192      * @see LocateRegistry#createRegistry(int, RMIClientSocketFactory, RMIServerSocketFactory)
193      */

194     public void setRegistryServerSocketFactory(RMIServerSocketFactory JavaDoc registryServerSocketFactory) {
195         this.registryServerSocketFactory = registryServerSocketFactory;
196     }
197
198     /**
199      * Set whether to always create the registry in-process,
200      * not attempting to locate an existing registry at the specified port.
201      * <p>Default is "false". Switch this flag to "true" in order to avoid
202      * the overhead of locating an existing registry when you always
203      * intend to create a new registry in any case.
204      */

205     public void setAlwaysCreateRegistry(boolean alwaysCreateRegistry) {
206         this.alwaysCreateRegistry = alwaysCreateRegistry;
207     }
208
209
210     public void afterPropertiesSet() throws RemoteException JavaDoc {
211         prepare();
212     }
213
214     /**
215      * Initialize this service exporter, registering the service as RMI object.
216      * <p>Creates an RMI registry on the specified port if none exists.
217      * @throws RemoteException if service registration failed
218      */

219     public void prepare() throws RemoteException JavaDoc {
220         checkService();
221
222         if (this.serviceName == null) {
223             throw new IllegalArgumentException JavaDoc("Property 'serviceName' is required");
224         }
225
226         // Check socket factories for exported object.
227
if (this.clientSocketFactory instanceof RMIServerSocketFactory JavaDoc) {
228             this.serverSocketFactory = (RMIServerSocketFactory JavaDoc) this.clientSocketFactory;
229         }
230         if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||
231                 (this.clientSocketFactory == null && this.serverSocketFactory != null)) {
232             throw new IllegalArgumentException JavaDoc(
233                     "Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
234         }
235
236         // Check socket factories for RMI registry.
237
if (this.registryClientSocketFactory instanceof RMIServerSocketFactory JavaDoc) {
238             this.registryServerSocketFactory = (RMIServerSocketFactory JavaDoc) this.registryClientSocketFactory;
239         }
240         if (this.registryClientSocketFactory == null && this.registryServerSocketFactory != null) {
241             throw new IllegalArgumentException JavaDoc(
242                     "RMIServerSocketFactory without RMIClientSocketFactory for registry not supported");
243         }
244
245         // Determine RMI registry to use.
246
if (this.registry == null) {
247             this.registry = getRegistry(this.registryHost, this.registryPort,
248                 this.registryClientSocketFactory, this.registryServerSocketFactory);
249         }
250
251         // Initialize and cache exported object.
252
this.exportedObject = getObjectToExport();
253
254         if (logger.isInfoEnabled()) {
255             logger.info("Binding service '" + this.serviceName + "' to RMI registry: " + this.registry);
256         }
257
258         // Export RMI object.
259
if (this.clientSocketFactory != null) {
260             UnicastRemoteObject.exportObject(
261                     this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
262         }
263         else {
264             UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
265         }
266
267         // Bind RMI object to registry.
268
try {
269             this.registry.rebind(this.serviceName, this.exportedObject);
270         }
271         catch (RemoteException JavaDoc ex) {
272             // Registry binding failed: let's unexport the RMI object as well.
273
unexportObjectSilently();
274             throw ex;
275         }
276     }
277
278
279     /**
280      * Locate or create the RMI registry for this exporter.
281      * @param registryHost the registry host to use (if this is specified,
282      * no implicit creation of a RMI registry will happen)
283      * @param registryPort the registry port to use
284      * @param clientSocketFactory the RMI client socket factory for the registry (if any)
285      * @param serverSocketFactory the RMI server socket factory for the registry (if any)
286      * @return the RMI registry
287      * @throws RemoteException if the registry couldn't be located or created
288      */

289     protected Registry JavaDoc getRegistry(String JavaDoc registryHost, int registryPort,
290             RMIClientSocketFactory JavaDoc clientSocketFactory, RMIServerSocketFactory JavaDoc serverSocketFactory)
291             throws RemoteException JavaDoc {
292
293         if (registryHost != null) {
294             // Host explictly specified: only lookup possible.
295
if (logger.isInfoEnabled()) {
296                 logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
297             }
298             Registry JavaDoc reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
299             testRegistry(reg);
300             return reg;
301         }
302
303         else {
304             return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
305         }
306     }
307
308     /**
309      * Locate or create the RMI registry for this exporter.
310      * @param registryPort the registry port to use
311      * @param clientSocketFactory the RMI client socket factory for the registry (if any)
312      * @param serverSocketFactory the RMI server socket factory for the registry (if any)
313      * @return the RMI registry
314      * @throws RemoteException if the registry couldn't be located or created
315      */

316     protected Registry JavaDoc getRegistry(
317             int registryPort, RMIClientSocketFactory JavaDoc clientSocketFactory, RMIServerSocketFactory JavaDoc serverSocketFactory)
318             throws RemoteException JavaDoc {
319
320         if (clientSocketFactory != null) {
321             if (this.alwaysCreateRegistry) {
322                 logger.info("Creating new RMI registry");
323                 return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
324             }
325             if (logger.isInfoEnabled()) {
326                 logger.info("Looking for RMI registry at port '" + registryPort + "', using custom socket factory");
327             }
328             try {
329                 // Retrieve existing registry.
330
Registry JavaDoc reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory);
331                 testRegistry(reg);
332                 return reg;
333             }
334             catch (RemoteException JavaDoc ex) {
335                 logger.debug("RMI registry access threw exception", ex);
336                 logger.info("Could not detect RMI registry - creating new one");
337                 // Assume no registry found -> create new one.
338
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
339             }
340         }
341
342         else {
343             return getRegistry(registryPort);
344         }
345     }
346
347     /**
348      * Locate or create the RMI registry for this exporter.
349      * @param registryPort the registry port to use
350      * @return the RMI registry
351      * @throws RemoteException if the registry couldn't be located or created
352      */

353     protected Registry JavaDoc getRegistry(int registryPort) throws RemoteException JavaDoc {
354         if (this.alwaysCreateRegistry) {
355             logger.info("Creating new RMI registry");
356             return LocateRegistry.createRegistry(registryPort);
357         }
358         if (logger.isInfoEnabled()) {
359             logger.info("Looking for RMI registry at port '" + registryPort + "'");
360         }
361         try {
362             // Retrieve existing registry.
363
Registry JavaDoc reg = LocateRegistry.getRegistry(registryPort);
364             testRegistry(reg);
365             return reg;
366         }
367         catch (RemoteException JavaDoc ex) {
368             logger.debug("RMI registry access threw exception", ex);
369             logger.info("Could not detect RMI registry - creating new one");
370             // Assume no registry found -> create new one.
371
return LocateRegistry.createRegistry(registryPort);
372         }
373     }
374
375     /**
376      * Test the given RMI registry, calling some operation on it to
377      * check whether it is still active.
378      * <p>Default implementation calls <code>Registry.list()</code>.
379      * @param registry the RMI registry to test
380      * @throws RemoteException if thrown by registry methods
381      * @see java.rmi.registry.Registry#list()
382      */

383     protected void testRegistry(Registry JavaDoc registry) throws RemoteException JavaDoc {
384         registry.list();
385     }
386
387
388     /**
389      * Unbind the RMI service from the registry on bean factory shutdown.
390      */

391     public void destroy() throws RemoteException JavaDoc {
392         if (logger.isInfoEnabled()) {
393             logger.info("Unbinding RMI service '" + this.serviceName +
394                     "' from registry at port '" + this.registryPort + "'");
395         }
396         try {
397             this.registry.unbind(this.serviceName);
398         }
399         catch (NotBoundException JavaDoc ex) {
400             if (logger.isWarnEnabled()) {
401                 logger.warn("RMI service '" + this.serviceName + "' is not bound to registry at port '" +
402                         this.registryPort + "' anymore", ex);
403             }
404         }
405         finally {
406             unexportObjectSilently();
407         }
408     }
409
410     /**
411      * Unexport the registered RMI object, logging any exception that arises.
412      */

413     private void unexportObjectSilently() {
414         try {
415             UnicastRemoteObject.unexportObject(this.exportedObject, true);
416         }
417         catch (NoSuchObjectException JavaDoc ex) {
418             if (logger.isWarnEnabled()) {
419                 logger.warn("RMI object for service '" + this.serviceName + "' isn't exported anymore", ex);
420             }
421         }
422     }
423
424 }
425
Popular Tags