001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.remoting.rmi;
018:
019: import java.rmi.RemoteException;
020: import java.rmi.registry.LocateRegistry;
021: import java.rmi.registry.Registry;
022: import java.rmi.server.RMIClientSocketFactory;
023: import java.rmi.server.RMIServerSocketFactory;
024: import java.rmi.server.UnicastRemoteObject;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.beans.factory.DisposableBean;
030: import org.springframework.beans.factory.FactoryBean;
031: import org.springframework.beans.factory.InitializingBean;
032:
033: /**
034: * FactoryBean that locates a {@link java.rmi.registry.Registry} and
035: * exposes it for bean references. Can also create a local RMI registry
036: * on the fly if none exists already.
037: *
038: * <p>Can be used to set up and pass around the actual Registry object to
039: * applications objects that need to work with RMI. One example for such an
040: * object that needs to work with RMI is Spring's {@link RmiServiceExporter},
041: * which either works with a passed-in Registry reference or falls back to
042: * the registry as specified by its local properties and defaults.
043: *
044: * <p>Also useful to enforce creation of a local RMI registry at a given port,
045: * for example for a JMX connector. If used in conjunction with
046: * {@link org.springframework.jmx.support.ConnectorServerFactoryBean},
047: * it is recommended to mark the connector definition (ConnectorServerFactoryBean)
048: * as "depends-on" the registry definition (RmiRegistryFactoryBean),
049: * to guarantee starting up the registry first.
050: *
051: * <p>Note: The implementation of this class mirrors the corresponding logic
052: * in {@link RmiServiceExporter}, and also offers the same customization hooks.
053: * RmiServiceExporter implements its own registry lookup as a convenience:
054: * It is very common to simply rely on the registry defaults.
055: *
056: * @author Juergen Hoeller
057: * @since 1.2.3
058: * @see RmiServiceExporter#setRegistry
059: * @see org.springframework.jmx.support.ConnectorServerFactoryBean
060: * @see java.rmi.registry.Registry
061: * @see java.rmi.registry.LocateRegistry
062: */
063: public class RmiRegistryFactoryBean implements FactoryBean,
064: InitializingBean, DisposableBean {
065:
066: protected final Log logger = LogFactory.getLog(getClass());
067:
068: private String host;
069:
070: private int port = Registry.REGISTRY_PORT;
071:
072: private RMIClientSocketFactory clientSocketFactory;
073:
074: private RMIServerSocketFactory serverSocketFactory;
075:
076: private Registry registry;
077:
078: private boolean alwaysCreate = false;
079:
080: private boolean created = false;
081:
082: /**
083: * Set the host of the registry for the exported RMI service,
084: * i.e. <code>rmi://HOST:port/name</code>
085: * <p>Default is localhost.
086: */
087: public void setHost(String host) {
088: this .host = host;
089: }
090:
091: /**
092: * Return the host of the registry for the exported RMI service.
093: */
094: public String getHost() {
095: return this .host;
096: }
097:
098: /**
099: * Set the port of the registry for the exported RMI service,
100: * i.e. <code>rmi://host:PORT/name</code>
101: * <p>Default is <code>Registry.REGISTRY_PORT</code> (1099).
102: */
103: public void setPort(int port) {
104: this .port = port;
105: }
106:
107: /**
108: * Return the port of the registry for the exported RMI service.
109: */
110: public int getPort() {
111: return this .port;
112: }
113:
114: /**
115: * Set a custom RMI client socket factory to use for the RMI registry.
116: * <p>If the given object also implements <code>java.rmi.server.RMIServerSocketFactory</code>,
117: * it will automatically be registered as server socket factory too.
118: * @see #setServerSocketFactory
119: * @see java.rmi.server.RMIClientSocketFactory
120: * @see java.rmi.server.RMIServerSocketFactory
121: * @see java.rmi.registry.LocateRegistry#getRegistry(String, int, java.rmi.server.RMIClientSocketFactory)
122: */
123: public void setClientSocketFactory(
124: RMIClientSocketFactory clientSocketFactory) {
125: this .clientSocketFactory = clientSocketFactory;
126: }
127:
128: /**
129: * Set a custom RMI server socket factory to use for the RMI registry.
130: * <p>Only needs to be specified when the client socket factory does not
131: * implement <code>java.rmi.server.RMIServerSocketFactory</code> already.
132: * @see #setClientSocketFactory
133: * @see java.rmi.server.RMIClientSocketFactory
134: * @see java.rmi.server.RMIServerSocketFactory
135: * @see java.rmi.registry.LocateRegistry#createRegistry(int, RMIClientSocketFactory, java.rmi.server.RMIServerSocketFactory)
136: */
137: public void setServerSocketFactory(
138: RMIServerSocketFactory serverSocketFactory) {
139: this .serverSocketFactory = serverSocketFactory;
140: }
141:
142: /**
143: * Set whether to always create the registry in-process,
144: * not attempting to locate an existing registry at the specified port.
145: * <p>Default is "false". Switch this flag to "true" in order to avoid
146: * the overhead of locating an existing registry when you always
147: * intend to create a new registry in any case.
148: */
149: public void setAlwaysCreate(boolean alwaysCreate) {
150: this .alwaysCreate = alwaysCreate;
151: }
152:
153: public void afterPropertiesSet() throws Exception {
154: // Check socket factories for registry.
155: if (this .clientSocketFactory instanceof RMIServerSocketFactory) {
156: this .serverSocketFactory = (RMIServerSocketFactory) this .clientSocketFactory;
157: }
158: if ((this .clientSocketFactory != null && this .serverSocketFactory == null)
159: || (this .clientSocketFactory == null && this .serverSocketFactory != null)) {
160: throw new IllegalArgumentException(
161: "Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
162: }
163:
164: // Fetch RMI registry to expose.
165: this .registry = getRegistry(this .host, this .port,
166: this .clientSocketFactory, this .serverSocketFactory);
167: }
168:
169: /**
170: * Locate or create the RMI registry.
171: * @param registryHost the registry host to use (if this is specified,
172: * no implicit creation of a RMI registry will happen)
173: * @param registryPort the registry port to use
174: * @param clientSocketFactory the RMI client socket factory for the registry (if any)
175: * @param serverSocketFactory the RMI server socket factory for the registry (if any)
176: * @return the RMI registry
177: * @throws java.rmi.RemoteException if the registry couldn't be located or created
178: */
179: protected Registry getRegistry(String registryHost,
180: int registryPort,
181: RMIClientSocketFactory clientSocketFactory,
182: RMIServerSocketFactory serverSocketFactory)
183: throws RemoteException {
184:
185: if (registryHost != null) {
186: // Host explictly specified: only lookup possible.
187: if (logger.isInfoEnabled()) {
188: logger.info("Looking for RMI registry at port '"
189: + registryPort + "' of host [" + registryHost
190: + "]");
191: }
192: Registry reg = LocateRegistry.getRegistry(registryHost,
193: registryPort, clientSocketFactory);
194: testRegistry(reg);
195: return reg;
196: }
197:
198: else {
199: return getRegistry(registryPort, clientSocketFactory,
200: serverSocketFactory);
201: }
202: }
203:
204: /**
205: * Locate or create the RMI registry.
206: * @param registryPort the registry port to use
207: * @param clientSocketFactory the RMI client socket factory for the registry (if any)
208: * @param serverSocketFactory the RMI server socket factory for the registry (if any)
209: * @return the RMI registry
210: * @throws RemoteException if the registry couldn't be located or created
211: */
212: protected Registry getRegistry(int registryPort,
213: RMIClientSocketFactory clientSocketFactory,
214: RMIServerSocketFactory serverSocketFactory)
215: throws RemoteException {
216:
217: if (clientSocketFactory != null) {
218: if (this .alwaysCreate) {
219: logger.info("Creating new RMI registry");
220: this .created = true;
221: return LocateRegistry.createRegistry(registryPort,
222: clientSocketFactory, serverSocketFactory);
223: }
224: if (logger.isInfoEnabled()) {
225: logger.info("Looking for RMI registry at port '"
226: + registryPort
227: + "', using custom socket factory");
228: }
229: try {
230: // Retrieve existing registry.
231: Registry reg = LocateRegistry.getRegistry(null,
232: registryPort, clientSocketFactory);
233: testRegistry(reg);
234: return reg;
235: } catch (RemoteException ex) {
236: logger.debug("RMI registry access threw exception", ex);
237: logger
238: .info("Could not detect RMI registry - creating new one");
239: // Assume no registry found -> create new one.
240: this .created = true;
241: return LocateRegistry.createRegistry(registryPort,
242: clientSocketFactory, serverSocketFactory);
243: }
244: }
245:
246: else {
247: return getRegistry(registryPort);
248: }
249: }
250:
251: /**
252: * Locate or create the RMI registry.
253: * @param registryPort the registry port to use
254: * @return the RMI registry
255: * @throws RemoteException if the registry couldn't be located or created
256: */
257: protected Registry getRegistry(int registryPort)
258: throws RemoteException {
259: if (this .alwaysCreate) {
260: logger.info("Creating new RMI registry");
261: this .created = true;
262: return LocateRegistry.createRegistry(registryPort);
263: }
264: if (logger.isInfoEnabled()) {
265: logger.info("Looking for RMI registry at port '"
266: + registryPort + "'");
267: }
268: try {
269: // Retrieve existing registry.
270: Registry reg = LocateRegistry.getRegistry(registryPort);
271: testRegistry(reg);
272: return reg;
273: } catch (RemoteException ex) {
274: logger.debug("RMI registry access threw exception", ex);
275: logger
276: .info("Could not detect RMI registry - creating new one");
277: // Assume no registry found -> create new one.
278: this .created = true;
279: return LocateRegistry.createRegistry(registryPort);
280: }
281: }
282:
283: /**
284: * Test the given RMI registry, calling some operation on it to
285: * check whether it is still active.
286: * <p>Default implementation calls <code>Registry.list()</code>.
287: * @param registry the RMI registry to test
288: * @throws RemoteException if thrown by registry methods
289: * @see java.rmi.registry.Registry#list()
290: */
291: protected void testRegistry(Registry registry)
292: throws RemoteException {
293: registry.list();
294: }
295:
296: public Object getObject() throws Exception {
297: return this .registry;
298: }
299:
300: public Class getObjectType() {
301: return (this .registry != null ? this .registry.getClass()
302: : Registry.class);
303: }
304:
305: public boolean isSingleton() {
306: return true;
307: }
308:
309: /**
310: * Unexport the RMI registry on bean factory shutdown,
311: * provided that this bean actually created a registry.
312: */
313: public void destroy() throws RemoteException {
314: if (this .created) {
315: logger.info("Unexporting RMI registry");
316: UnicastRemoteObject.unexportObject(this .registry, true);
317: }
318: }
319:
320: }
|