001/* 002 * Copyright 2002-2012 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.NoSuchObjectException; 020import java.rmi.Remote; 021import java.rmi.RemoteException; 022import java.util.Properties; 023import javax.naming.NamingException; 024import javax.rmi.PortableRemoteObject; 025 026import org.springframework.beans.factory.DisposableBean; 027import org.springframework.beans.factory.InitializingBean; 028import org.springframework.jndi.JndiTemplate; 029 030/** 031 * Service exporter which binds RMI services to JNDI. 032 * Typically used for RMI-IIOP (CORBA). 033 * 034 * <p>Exports services via the {@link javax.rmi.PortableRemoteObject} class. 035 * You need to run "rmic" with the "-iiop" option to generate corresponding 036 * stubs and skeletons for each exported service. 037 * 038 * <p>Also supports exposing any non-RMI service via RMI invokers, to be accessed 039 * via {@link JndiRmiClientInterceptor} / {@link JndiRmiProxyFactoryBean}'s 040 * automatic detection of such invokers. 041 * 042 * <p>With an RMI invoker, RMI communication works on the {@link RmiInvocationHandler} 043 * level, needing only one stub for any service. Service interfaces do not have to 044 * extend {@code java.rmi.Remote} or throw {@code java.rmi.RemoteException} 045 * on all methods, but in and out parameters have to be serializable. 046 * 047 * <p>The JNDI environment can be specified as "jndiEnvironment" bean property, 048 * or be configured in a {@code jndi.properties} file or as system properties. 049 * For example: 050 * 051 * <pre class="code"><property name="jndiEnvironment"> 052 * <props> 053 * <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop> 054 * <prop key="java.naming.provider.url">iiop://localhost:1050</prop> 055 * </props> 056 * </property></pre> 057 * 058 * @author Juergen Hoeller 059 * @since 1.1 060 * @see #setService 061 * @see #setJndiTemplate 062 * @see #setJndiEnvironment 063 * @see #setJndiName 064 * @see JndiRmiClientInterceptor 065 * @see JndiRmiProxyFactoryBean 066 * @see javax.rmi.PortableRemoteObject#exportObject 067 */ 068public class JndiRmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean { 069 070 private JndiTemplate jndiTemplate = new JndiTemplate(); 071 072 private String jndiName; 073 074 private Remote exportedObject; 075 076 077 /** 078 * Set the JNDI template to use for JNDI lookups. 079 * You can also specify JNDI environment settings via "jndiEnvironment". 080 * @see #setJndiEnvironment 081 */ 082 public void setJndiTemplate(JndiTemplate jndiTemplate) { 083 this.jndiTemplate = (jndiTemplate != null ? jndiTemplate : new JndiTemplate()); 084 } 085 086 /** 087 * Set the JNDI environment to use for JNDI lookups. 088 * Creates a JndiTemplate with the given environment settings. 089 * @see #setJndiTemplate 090 */ 091 public void setJndiEnvironment(Properties jndiEnvironment) { 092 this.jndiTemplate = new JndiTemplate(jndiEnvironment); 093 } 094 095 /** 096 * Set the JNDI name of the exported RMI service. 097 */ 098 public void setJndiName(String jndiName) { 099 this.jndiName = jndiName; 100 } 101 102 103 @Override 104 public void afterPropertiesSet() throws NamingException, RemoteException { 105 prepare(); 106 } 107 108 /** 109 * Initialize this service exporter, binding the specified service to JNDI. 110 * @throws NamingException if service binding failed 111 * @throws RemoteException if service export failed 112 */ 113 public void prepare() throws NamingException, RemoteException { 114 if (this.jndiName == null) { 115 throw new IllegalArgumentException("Property 'jndiName' is required"); 116 } 117 118 // Initialize and cache exported object. 119 this.exportedObject = getObjectToExport(); 120 PortableRemoteObject.exportObject(this.exportedObject); 121 122 rebind(); 123 } 124 125 /** 126 * Rebind the specified service to JNDI, for recovering in case 127 * of the target registry having been restarted. 128 * @throws NamingException if service binding failed 129 */ 130 public void rebind() throws NamingException { 131 if (logger.isInfoEnabled()) { 132 logger.info("Binding RMI service to JNDI location [" + this.jndiName + "]"); 133 } 134 this.jndiTemplate.rebind(this.jndiName, this.exportedObject); 135 } 136 137 /** 138 * Unbind the RMI service from JNDI on bean factory shutdown. 139 */ 140 @Override 141 public void destroy() throws NamingException, NoSuchObjectException { 142 if (logger.isInfoEnabled()) { 143 logger.info("Unbinding RMI service from JNDI location [" + this.jndiName + "]"); 144 } 145 this.jndiTemplate.unbind(this.jndiName); 146 PortableRemoteObject.unexportObject(this.exportedObject); 147 } 148 149}