View Javadoc
1   /*
2    * Copyright (C) 2012-2024 RRiBbit.org
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    * https://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  package org.rribbit.processing;
17  
18  import java.io.Serializable;
19  import java.rmi.Remote;
20  import java.rmi.registry.LocateRegistry;
21  import java.rmi.registry.Registry;
22  import java.rmi.server.RMIClientSocketFactory;
23  import java.rmi.server.RMIServerSocketFactory;
24  import java.rmi.server.UnicastRemoteObject;
25  
26  import javax.rmi.ssl.SslRMIClientSocketFactory;
27  import javax.rmi.ssl.SslRMIServerSocketFactory;
28  
29  import org.rribbit.Request;
30  import org.rribbit.Response;
31  import org.rribbit.dispatching.RmiRequestDispatcher;
32  import org.rribbit.execution.ListenerObjectExecutor;
33  import org.rribbit.retrieval.ListenerObjectRetriever;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * This {@link RequestProcessor} processes requests that it receives from an {@link RmiRequestDispatcher} and returns the result via RMI. In order to use this {@link RequestProcessor},
39   * all parameters and return values must implement {@link Serializable}. Note that an {@link RmiRequestProcessorImpl} is not actually an {@link RequestProcessor}, because Java
40   * RMI does not allow non-remote methods in a remote interface. This is not a problem though, since an {@link RmiRequestProcessorImpl} receives its requests via RMI and not via a Java
41   * interface.
42   * <p />
43   * Users of this class must call {@link #shutdown()} after use, to clean up the RMI {@link Registry}.
44   *
45   * @author G.J. Schouten
46   *
47   */
48  public class RmiRequestProcessorImpl implements RmiRequestProcessor {
49  
50  	private static final Logger log = LoggerFactory.getLogger(RmiRequestProcessorImpl.class);
51  
52  	protected LocalRequestProcessor requestProcessor;
53  	protected Registry registry;
54  
55  	/**
56  	 * Sets up a {@link Registry} on the specified portnumber that does NOT use SSL.
57  	 * <p />
58  	 * Whenever you use this constructor, be sure to set the {@link ListenerObjectRetriever} AND the {@link ListenerObjectExecutor} with the setters provided by this class.
59  	 * If you don't, runtime {@link NullPointerException}s will occur.
60  	 *
61  	 * @param portnumber			The portnumber to use
62  	 */
63  	public RmiRequestProcessorImpl(int portnumber) {
64  		this(portnumber, null, null);
65  	}
66  
67  	/**
68  	 * Sets up a {@link Registry} on the specified portnumber that does NOT use SSL.
69  	 * <p />
70  	 * This constructor is recommended, since it forces you to specify the {@link ListenerObjectRetriever} and {@link ListenerObjectExecutor}. Passing a null value for either
71  	 * of these will result in a runtime {@link NullPointerException} whenever the {@link RmiRequestProcessorImpl} is used.
72  	 *
73  	 * @param portnumber				The portnumber to use
74  	 * @param listenerObjectRetriever
75  	 * @param listenerObjectExecutor
76  	 */
77  	public RmiRequestProcessorImpl(int portnumber, ListenerObjectRetriever listenerObjectRetriever, ListenerObjectExecutor listenerObjectExecutor) {
78  
79  		try {
80  			log.info("Creating RMI Registry");
81  			registry = LocateRegistry.createRegistry(portnumber);
82  			Remote stub = UnicastRemoteObject.exportObject(this, 0);
83  			registry.bind(REGISTRY_KEY, stub);
84  		} catch(Exception e) {
85  			throw new RuntimeException(e);
86  		}
87  		requestProcessor = new LocalRequestProcessor(listenerObjectRetriever, listenerObjectExecutor);
88  	}
89  
90  	/**
91  	 * Sets up a {@link Registry} on the specified portnumber that uses SSL with the supplied parameters. The following system properties will be set:
92  	 *
93  	 * <ul>
94  	 * 	<li>javax.net.ssl.keyStore</li>
95  	 * 	<li>javax.net.ssl.keyStorePassword</li>
96  	 * 	<li>javax.net.ssl.trustStore</li>
97  	 * </ul>
98  	 *
99  	 * Whenever you use this constructor, be sure to set the {@link ListenerObjectRetriever} AND the {@link ListenerObjectExecutor} with the setters provided by this class.
100 	 * If you don't, runtime {@link NullPointerException}s will occur.
101 	 *
102 	 * @param portnumber			The portnumber to use
103 	 * @param keystoreLocation		The filepath that contains the SSL keystore, without file://
104 	 * @param keystorePassword		The password of the SSL keystore
105 	 * @param truststoreLocation	The filepath that contains the SSL truststore, without file://
106 	 * @param truststorePassword	The password of the SSL truststore
107 	 */
108 	public RmiRequestProcessorImpl(int portnumber, String keystoreLocation, String keystorePassword, String truststoreLocation, String truststorePassword) {
109 		this(portnumber, keystoreLocation, keystorePassword, truststoreLocation, truststorePassword, null, null);
110 	}
111 
112 	/**
113 	 * Sets up a {@link Registry} on the specified portnumber that uses SSL with the supplied parameters. The following system properties will be set:
114 	 *
115 	 * <ul>
116 	 * 	<li>javax.net.ssl.keyStore</li>
117 	 * 	<li>javax.net.ssl.keyStorePassword</li>
118 	 * 	<li>javax.net.ssl.trustStore</li>
119 	 * </ul>
120 	 *
121 	 * This constructor is recommended, since it forces you to specify the {@link ListenerObjectRetriever} and {@link ListenerObjectExecutor}. Passing a null value for either
122 	 * of these will result in a runtime {@link NullPointerException} whenever the {@link RmiRequestProcessorImpl} is used.
123 	 *
124 	 * @param portnumber			The portnumber to use
125 	 * @param keystoreLocation		The filepath that contains the SSL keystore, without file://
126 	 * @param keystorePassword		The password of the SSL keystore
127 	 * @param truststoreLocation	The filepath that contains the SSL truststore, without file://
128 	 * @param truststorePassword	The password of the SSL truststore
129 	 * @param listenerObjectRetriever
130 	 * @param listenerObjectExecutor
131 	 */
132 	public RmiRequestProcessorImpl(int portnumber, String keystoreLocation, String keystorePassword, String truststoreLocation, String truststorePassword,
133 		ListenerObjectRetriever listenerObjectRetriever, ListenerObjectExecutor listenerObjectExecutor) {
134 
135 		try {
136 			log.info("Setting SSL properties");
137 			System.setProperty("javax.net.ssl.keyStore", keystoreLocation);
138 			System.setProperty("javax.net.ssl.keyStorePassword", keystorePassword);
139 			System.setProperty("javax.net.ssl.trustStore", truststoreLocation);
140 			System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
141 
142 			log.info("Creating SSL RMI Registry");
143 			RMIClientSocketFactory csf = new SslRMIClientSocketFactory();
144 			RMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
145 			registry = LocateRegistry.createRegistry(portnumber, csf, ssf);
146 			Remote stub = UnicastRemoteObject.exportObject(this, 0, csf, ssf);
147 			registry.bind(REGISTRY_KEY, stub);
148 		} catch(Exception e) {
149 			throw new RuntimeException(e);
150 		}
151 		requestProcessor = new LocalRequestProcessor(listenerObjectRetriever, listenerObjectExecutor);
152 	}
153 
154 	/**
155 	 * Call this method at application shutdown to make sure that the RMI Registry gets closed properly. Pending requests will be allowed to finish before the {@link Registry} is killed.
156 	 */
157 	public void shutdown() {
158 
159 		try {
160 			log.info("Unbinding this RmiRequestProcessorImpl");
161 			registry.unbind(REGISTRY_KEY);
162 			UnicastRemoteObject.unexportObject(this, false);
163 		} catch(Exception e) {
164 			throw new RuntimeException(e);
165 		}
166 	}
167 
168 	@Override
169 	public <T> Response<T> processRequestViaRMI(Request request) {
170 
171 		log.info("Processing RMI Request");
172 		Response<T> response =  requestProcessor.processRequest(request);
173 		log.info("Returning Response");
174 		return response;
175 	}
176 
177 	public ListenerObjectRetriever getListenerObjectRetriever() {
178 		return requestProcessor.getListenerObjectRetriever();
179 	}
180 
181 	public void setListenerObjectRetriever(ListenerObjectRetriever listenerObjectRetriever) {
182 		requestProcessor.setListenerObjectRetriever(listenerObjectRetriever);
183 	}
184 
185 	public ListenerObjectExecutor getListenerObjectExecutor() {
186 		return requestProcessor.getListenerObjectExecutor();
187 	}
188 
189 	public void setListenerObjectExecutor(ListenerObjectExecutor listenerObjectExecutor) {
190 		requestProcessor.setListenerObjectExecutor(listenerObjectExecutor);
191 	}
192 }