1 /*
2 * Copyright (C) 2012-2025 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 }