Java tutorial
/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.remoting.rmi; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.lang.Nullable; /** * {@link FactoryBean} that locates a {@link java.rmi.registry.Registry} and * exposes it for bean references. Can also create a local RMI registry * on the fly if none exists already. * * <p>Can be used to set up and pass around the actual Registry object to * applications objects that need to work with RMI. One example for such an * object that needs to work with RMI is Spring's {@link RmiServiceExporter}, * which either works with a passed-in Registry reference or falls back to * the registry as specified by its local properties and defaults. * * <p>Also useful to enforce creation of a local RMI registry at a given port, * for example for a JMX connector. If used in conjunction with * {@link org.springframework.jmx.support.ConnectorServerFactoryBean}, * it is recommended to mark the connector definition (ConnectorServerFactoryBean) * as "depends-on" the registry definition (RmiRegistryFactoryBean), * to guarantee starting up the registry first. * * <p>Note: The implementation of this class mirrors the corresponding logic * in {@link RmiServiceExporter}, and also offers the same customization hooks. * RmiServiceExporter implements its own registry lookup as a convenience: * It is very common to simply rely on the registry defaults. * * @author Juergen Hoeller * @since 1.2.3 * @see RmiServiceExporter#setRegistry * @see org.springframework.jmx.support.ConnectorServerFactoryBean * @see java.rmi.registry.Registry * @see java.rmi.registry.LocateRegistry */ public class RmiRegistryFactoryBean implements FactoryBean<Registry>, InitializingBean, DisposableBean { protected final Log logger = LogFactory.getLog(getClass()); private String host; private int port = Registry.REGISTRY_PORT; private RMIClientSocketFactory clientSocketFactory; private RMIServerSocketFactory serverSocketFactory; private Registry registry; private boolean alwaysCreate = false; private boolean created = false; /** * Set the host of the registry for the exported RMI service, * i.e. {@code rmi://HOST:port/name} * <p>Default is localhost. */ public void setHost(String host) { this.host = host; } /** * Return the host of the registry for the exported RMI service. */ public String getHost() { return this.host; } /** * Set the port of the registry for the exported RMI service, * i.e. {@code rmi://host:PORT/name} * <p>Default is {@code Registry.REGISTRY_PORT} (1099). */ public void setPort(int port) { this.port = port; } /** * Return the port of the registry for the exported RMI service. */ public int getPort() { return this.port; } /** * Set a custom RMI client socket factory to use for the RMI registry. * <p>If the given object also implements {@code java.rmi.server.RMIServerSocketFactory}, * it will automatically be registered as server socket factory too. * @see #setServerSocketFactory * @see java.rmi.server.RMIClientSocketFactory * @see java.rmi.server.RMIServerSocketFactory * @see java.rmi.registry.LocateRegistry#getRegistry(String, int, java.rmi.server.RMIClientSocketFactory) */ public void setClientSocketFactory(RMIClientSocketFactory clientSocketFactory) { this.clientSocketFactory = clientSocketFactory; } /** * Set a custom RMI server socket factory to use for the RMI registry. * <p>Only needs to be specified when the client socket factory does not * implement {@code java.rmi.server.RMIServerSocketFactory} already. * @see #setClientSocketFactory * @see java.rmi.server.RMIClientSocketFactory * @see java.rmi.server.RMIServerSocketFactory * @see java.rmi.registry.LocateRegistry#createRegistry(int, RMIClientSocketFactory, java.rmi.server.RMIServerSocketFactory) */ public void setServerSocketFactory(RMIServerSocketFactory serverSocketFactory) { this.serverSocketFactory = serverSocketFactory; } /** * Set whether to always create the registry in-process, * not attempting to locate an existing registry at the specified port. * <p>Default is "false". Switch this flag to "true" in order to avoid * the overhead of locating an existing registry when you always * intend to create a new registry in any case. */ public void setAlwaysCreate(boolean alwaysCreate) { this.alwaysCreate = alwaysCreate; } @Override public void afterPropertiesSet() throws Exception { // Check socket factories for registry. if (this.clientSocketFactory instanceof RMIServerSocketFactory) { this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory; } if ((this.clientSocketFactory != null && this.serverSocketFactory == null) || (this.clientSocketFactory == null && this.serverSocketFactory != null)) { throw new IllegalArgumentException( "Both RMIClientSocketFactory and RMIServerSocketFactory or none required"); } // Fetch RMI registry to expose. this.registry = getRegistry(this.host, this.port, this.clientSocketFactory, this.serverSocketFactory); } /** * Locate or create the RMI registry. * @param registryHost the registry host to use (if this is specified, * no implicit creation of a RMI registry will happen) * @param registryPort the registry port to use * @param clientSocketFactory the RMI client socket factory for the registry (if any) * @param serverSocketFactory the RMI server socket factory for the registry (if any) * @return the RMI registry * @throws java.rmi.RemoteException if the registry couldn't be located or created */ protected Registry getRegistry(String registryHost, int registryPort, @Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory) throws RemoteException { if (registryHost != null) { // Host explicitly specified: only lookup possible. if (logger.isInfoEnabled()) { logger.info( "Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]"); } Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory); testRegistry(reg); return reg; } else { return getRegistry(registryPort, clientSocketFactory, serverSocketFactory); } } /** * Locate or create the RMI registry. * @param registryPort the registry port to use * @param clientSocketFactory the RMI client socket factory for the registry (if any) * @param serverSocketFactory the RMI server socket factory for the registry (if any) * @return the RMI registry * @throws RemoteException if the registry couldn't be located or created */ protected Registry getRegistry(int registryPort, @Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory) throws RemoteException { if (clientSocketFactory != null) { if (this.alwaysCreate) { logger.info("Creating new RMI registry"); this.created = true; return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); } if (logger.isInfoEnabled()) { logger.info("Looking for RMI registry at port '" + registryPort + "', using custom socket factory"); } synchronized (LocateRegistry.class) { try { // Retrieve existing registry. Registry reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory); testRegistry(reg); return reg; } catch (RemoteException ex) { logger.debug("RMI registry access threw exception", ex); logger.info("Could not detect RMI registry - creating new one"); // Assume no registry found -> create new one. this.created = true; return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory); } } } else { return getRegistry(registryPort); } } /** * Locate or create the RMI registry. * @param registryPort the registry port to use * @return the RMI registry * @throws RemoteException if the registry couldn't be located or created */ protected Registry getRegistry(int registryPort) throws RemoteException { if (this.alwaysCreate) { logger.info("Creating new RMI registry"); this.created = true; return LocateRegistry.createRegistry(registryPort); } if (logger.isInfoEnabled()) { logger.info("Looking for RMI registry at port '" + registryPort + "'"); } synchronized (LocateRegistry.class) { try { // Retrieve existing registry. Registry reg = LocateRegistry.getRegistry(registryPort); testRegistry(reg); return reg; } catch (RemoteException ex) { logger.debug("RMI registry access threw exception", ex); logger.info("Could not detect RMI registry - creating new one"); // Assume no registry found -> create new one. this.created = true; return LocateRegistry.createRegistry(registryPort); } } } /** * Test the given RMI registry, calling some operation on it to * check whether it is still active. * <p>Default implementation calls {@code Registry.list()}. * @param registry the RMI registry to test * @throws RemoteException if thrown by registry methods * @see java.rmi.registry.Registry#list() */ protected void testRegistry(Registry registry) throws RemoteException { registry.list(); } @Override public Registry getObject() throws Exception { return this.registry; } @Override public Class<? extends Registry> getObjectType() { return (this.registry != null ? this.registry.getClass() : Registry.class); } @Override public boolean isSingleton() { return true; } /** * Unexport the RMI registry on bean factory shutdown, * provided that this bean actually created a registry. */ @Override public void destroy() throws RemoteException { if (this.created) { logger.info("Unexporting RMI registry"); UnicastRemoteObject.unexportObject(this.registry, true); } } }