001/* 002 * Copyright 2002-2013 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 * https://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 017package org.springframework.remoting.rmi; 018 019import java.rmi.RemoteException; 020import java.rmi.registry.LocateRegistry; 021import java.rmi.registry.Registry; 022import java.rmi.server.RMIClientSocketFactory; 023import java.rmi.server.RMIServerSocketFactory; 024import java.rmi.server.UnicastRemoteObject; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import org.springframework.beans.factory.DisposableBean; 030import org.springframework.beans.factory.FactoryBean; 031import org.springframework.beans.factory.InitializingBean; 032 033/** 034 * {@link 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 */ 063public class RmiRegistryFactoryBean implements FactoryBean<Registry>, InitializingBean, DisposableBean { 064 065 protected final Log logger = LogFactory.getLog(getClass()); 066 067 private String host; 068 069 private int port = Registry.REGISTRY_PORT; 070 071 private RMIClientSocketFactory clientSocketFactory; 072 073 private RMIServerSocketFactory serverSocketFactory; 074 075 private Registry registry; 076 077 private boolean alwaysCreate = false; 078 079 private boolean created = false; 080 081 082 /** 083 * Set the host of the registry for the exported RMI service, 084 * i.e. {@code rmi://HOST:port/name} 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} 101 * <p>Default is {@code Registry.REGISTRY_PORT} (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}, 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(RMIClientSocketFactory clientSocketFactory) { 124 this.clientSocketFactory = clientSocketFactory; 125 } 126 127 /** 128 * Set a custom RMI server socket factory to use for the RMI registry. 129 * <p>Only needs to be specified when the client socket factory does not 130 * implement {@code java.rmi.server.RMIServerSocketFactory} already. 131 * @see #setClientSocketFactory 132 * @see java.rmi.server.RMIClientSocketFactory 133 * @see java.rmi.server.RMIServerSocketFactory 134 * @see java.rmi.registry.LocateRegistry#createRegistry(int, RMIClientSocketFactory, java.rmi.server.RMIServerSocketFactory) 135 */ 136 public void setServerSocketFactory(RMIServerSocketFactory serverSocketFactory) { 137 this.serverSocketFactory = serverSocketFactory; 138 } 139 140 /** 141 * Set whether to always create the registry in-process, 142 * not attempting to locate an existing registry at the specified port. 143 * <p>Default is "false". Switch this flag to "true" in order to avoid 144 * the overhead of locating an existing registry when you always 145 * intend to create a new registry in any case. 146 */ 147 public void setAlwaysCreate(boolean alwaysCreate) { 148 this.alwaysCreate = alwaysCreate; 149 } 150 151 152 @Override 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, this.clientSocketFactory, this.serverSocketFactory); 166 } 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, int registryPort, 180 RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory) 181 throws RemoteException { 182 183 if (registryHost != null) { 184 // Host explicitly specified: only lookup possible. 185 if (logger.isInfoEnabled()) { 186 logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]"); 187 } 188 Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory); 189 testRegistry(reg); 190 return reg; 191 } 192 193 else { 194 return getRegistry(registryPort, clientSocketFactory, serverSocketFactory); 195 } 196 } 197 198 /** 199 * Locate or create the RMI registry. 200 * @param registryPort the registry port to use 201 * @param clientSocketFactory the RMI client socket factory for the registry (if any) 202 * @param serverSocketFactory the RMI server socket factory for the registry (if any) 203 * @return the RMI registry 204 * @throws RemoteException if the registry couldn't be located or created 205 */ 206 protected Registry getRegistry( 207 int registryPort, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory) 208 throws RemoteException { 209 210 if (clientSocketFactory != null) { 211 if (this.alwaysCreate) { 212 logger.info("Creating new RMI registry"); 213 this.created = true; 214 return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); 215 } 216 if (logger.isInfoEnabled()) { 217 logger.info("Looking for RMI registry at port '" + registryPort + "', using custom socket factory"); 218 } 219 synchronized (LocateRegistry.class) { 220 try { 221 // Retrieve existing registry. 222 Registry reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory); 223 testRegistry(reg); 224 return reg; 225 } 226 catch (RemoteException ex) { 227 logger.debug("RMI registry access threw exception", ex); 228 logger.info("Could not detect RMI registry - creating new one"); 229 // Assume no registry found -> create new one. 230 this.created = true; 231 return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); 232 } 233 } 234 } 235 236 else { 237 return getRegistry(registryPort); 238 } 239 } 240 241 /** 242 * Locate or create the RMI registry. 243 * @param registryPort the registry port to use 244 * @return the RMI registry 245 * @throws RemoteException if the registry couldn't be located or created 246 */ 247 protected Registry getRegistry(int registryPort) throws RemoteException { 248 if (this.alwaysCreate) { 249 logger.info("Creating new RMI registry"); 250 this.created = true; 251 return LocateRegistry.createRegistry(registryPort); 252 } 253 if (logger.isInfoEnabled()) { 254 logger.info("Looking for RMI registry at port '" + registryPort + "'"); 255 } 256 synchronized (LocateRegistry.class) { 257 try { 258 // Retrieve existing registry. 259 Registry reg = LocateRegistry.getRegistry(registryPort); 260 testRegistry(reg); 261 return reg; 262 } 263 catch (RemoteException ex) { 264 logger.debug("RMI registry access threw exception", ex); 265 logger.info("Could not detect RMI registry - creating new one"); 266 // Assume no registry found -> create new one. 267 this.created = true; 268 return LocateRegistry.createRegistry(registryPort); 269 } 270 } 271 } 272 273 /** 274 * Test the given RMI registry, calling some operation on it to 275 * check whether it is still active. 276 * <p>Default implementation calls {@code Registry.list()}. 277 * @param registry the RMI registry to test 278 * @throws RemoteException if thrown by registry methods 279 * @see java.rmi.registry.Registry#list() 280 */ 281 protected void testRegistry(Registry registry) throws RemoteException { 282 registry.list(); 283 } 284 285 286 @Override 287 public Registry getObject() throws Exception { 288 return this.registry; 289 } 290 291 @Override 292 public Class<? extends Registry> getObjectType() { 293 return (this.registry != null ? this.registry.getClass() : Registry.class); 294 } 295 296 @Override 297 public boolean isSingleton() { 298 return true; 299 } 300 301 302 /** 303 * Unexport the RMI registry on bean factory shutdown, 304 * provided that this bean actually created a registry. 305 */ 306 @Override 307 public void destroy() throws RemoteException { 308 if (this.created) { 309 logger.info("Unexporting RMI registry"); 310 UnicastRemoteObject.unexportObject(this.registry, true); 311 } 312 } 313 314}