RmiRequestProcessorImpl.java
/*
* Copyright (C) 2012-2024 RRiBbit.org
*
* 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
*
* https://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.rribbit.processing;
import java.io.Serializable;
import java.rmi.Remote;
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 javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
import org.rribbit.Request;
import org.rribbit.Response;
import org.rribbit.dispatching.RmiRequestDispatcher;
import org.rribbit.execution.ListenerObjectExecutor;
import org.rribbit.retrieval.ListenerObjectRetriever;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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},
* all parameters and return values must implement {@link Serializable}. Note that an {@link RmiRequestProcessorImpl} is not actually an {@link RequestProcessor}, because Java
* 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
* interface.
* <p />
* Users of this class must call {@link #shutdown()} after use, to clean up the RMI {@link Registry}.
*
* @author G.J. Schouten
*
*/
public class RmiRequestProcessorImpl implements RmiRequestProcessor {
private static final Logger log = LoggerFactory.getLogger(RmiRequestProcessorImpl.class);
protected LocalRequestProcessor requestProcessor;
protected Registry registry;
/**
* Sets up a {@link Registry} on the specified portnumber that does NOT use SSL.
* <p />
* Whenever you use this constructor, be sure to set the {@link ListenerObjectRetriever} AND the {@link ListenerObjectExecutor} with the setters provided by this class.
* If you don't, runtime {@link NullPointerException}s will occur.
*
* @param portnumber The portnumber to use
*/
public RmiRequestProcessorImpl(int portnumber) {
this(portnumber, null, null);
}
/**
* Sets up a {@link Registry} on the specified portnumber that does NOT use SSL.
* <p />
* This constructor is recommended, since it forces you to specify the {@link ListenerObjectRetriever} and {@link ListenerObjectExecutor}. Passing a null value for either
* of these will result in a runtime {@link NullPointerException} whenever the {@link RmiRequestProcessorImpl} is used.
*
* @param portnumber The portnumber to use
* @param listenerObjectRetriever
* @param listenerObjectExecutor
*/
public RmiRequestProcessorImpl(int portnumber, ListenerObjectRetriever listenerObjectRetriever, ListenerObjectExecutor listenerObjectExecutor) {
try {
log.info("Creating RMI Registry");
registry = LocateRegistry.createRegistry(portnumber);
Remote stub = UnicastRemoteObject.exportObject(this, 0);
registry.bind(REGISTRY_KEY, stub);
} catch(Exception e) {
throw new RuntimeException(e);
}
requestProcessor = new LocalRequestProcessor(listenerObjectRetriever, listenerObjectExecutor);
}
/**
* Sets up a {@link Registry} on the specified portnumber that uses SSL with the supplied parameters. The following system properties will be set:
*
* <ul>
* <li>javax.net.ssl.keyStore</li>
* <li>javax.net.ssl.keyStorePassword</li>
* <li>javax.net.ssl.trustStore</li>
* </ul>
*
* Whenever you use this constructor, be sure to set the {@link ListenerObjectRetriever} AND the {@link ListenerObjectExecutor} with the setters provided by this class.
* If you don't, runtime {@link NullPointerException}s will occur.
*
* @param portnumber The portnumber to use
* @param keystoreLocation The filepath that contains the SSL keystore, without file://
* @param keystorePassword The password of the SSL keystore
* @param truststoreLocation The filepath that contains the SSL truststore, without file://
* @param truststorePassword The password of the SSL truststore
*/
public RmiRequestProcessorImpl(int portnumber, String keystoreLocation, String keystorePassword, String truststoreLocation, String truststorePassword) {
this(portnumber, keystoreLocation, keystorePassword, truststoreLocation, truststorePassword, null, null);
}
/**
* Sets up a {@link Registry} on the specified portnumber that uses SSL with the supplied parameters. The following system properties will be set:
*
* <ul>
* <li>javax.net.ssl.keyStore</li>
* <li>javax.net.ssl.keyStorePassword</li>
* <li>javax.net.ssl.trustStore</li>
* </ul>
*
* This constructor is recommended, since it forces you to specify the {@link ListenerObjectRetriever} and {@link ListenerObjectExecutor}. Passing a null value for either
* of these will result in a runtime {@link NullPointerException} whenever the {@link RmiRequestProcessorImpl} is used.
*
* @param portnumber The portnumber to use
* @param keystoreLocation The filepath that contains the SSL keystore, without file://
* @param keystorePassword The password of the SSL keystore
* @param truststoreLocation The filepath that contains the SSL truststore, without file://
* @param truststorePassword The password of the SSL truststore
* @param listenerObjectRetriever
* @param listenerObjectExecutor
*/
public RmiRequestProcessorImpl(int portnumber, String keystoreLocation, String keystorePassword, String truststoreLocation, String truststorePassword,
ListenerObjectRetriever listenerObjectRetriever, ListenerObjectExecutor listenerObjectExecutor) {
try {
log.info("Setting SSL properties");
System.setProperty("javax.net.ssl.keyStore", keystoreLocation);
System.setProperty("javax.net.ssl.keyStorePassword", keystorePassword);
System.setProperty("javax.net.ssl.trustStore", truststoreLocation);
System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
log.info("Creating SSL RMI Registry");
RMIClientSocketFactory csf = new SslRMIClientSocketFactory();
RMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
registry = LocateRegistry.createRegistry(portnumber, csf, ssf);
Remote stub = UnicastRemoteObject.exportObject(this, 0, csf, ssf);
registry.bind(REGISTRY_KEY, stub);
} catch(Exception e) {
throw new RuntimeException(e);
}
requestProcessor = new LocalRequestProcessor(listenerObjectRetriever, listenerObjectExecutor);
}
/**
* 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.
*/
public void shutdown() {
try {
log.info("Unbinding this RmiRequestProcessorImpl");
registry.unbind(REGISTRY_KEY);
UnicastRemoteObject.unexportObject(this, false);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
@Override
public <T> Response<T> processRequestViaRMI(Request request) {
log.info("Processing RMI Request");
Response<T> response = requestProcessor.processRequest(request);
log.info("Returning Response");
return response;
}
public ListenerObjectRetriever getListenerObjectRetriever() {
return requestProcessor.getListenerObjectRetriever();
}
public void setListenerObjectRetriever(ListenerObjectRetriever listenerObjectRetriever) {
requestProcessor.setListenerObjectRetriever(listenerObjectRetriever);
}
public ListenerObjectExecutor getListenerObjectExecutor() {
return requestProcessor.getListenerObjectExecutor();
}
public void setListenerObjectExecutor(ListenerObjectExecutor listenerObjectExecutor) {
requestProcessor.setListenerObjectExecutor(listenerObjectExecutor);
}
}