Binding proxy for remote Object in java RMI - java

I would implement a security layer for java RMI, with dynamic proxy mechanism.
I've some class with remote interface that bind in rmi registry, now I'm coding a class SecurityInvocationHandler, code below:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
/**
*
* #author andrew
* #param <T>
*/
public class SecurityInvocationHandler<T> extends SuperRemoteInterface implements InvocationHandler {
final T remoteInterface;
public static <T> T newInstance(final T obj, RMIClientSocketFactory rcsf, RMIServerSocketFactory rssf) throws RemoteException {
return (T) java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new SecurityInvocationHandler(obj, rcsf, rssf));
}
private SecurityInvocationHandler(T remoteInterface, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException {
super(csf, ssf);
this.remoteInterface = remoteInterface;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Invoke method -> " + method.getName());
//TODO
return method.invoke(remoteInterface, args);
}
}
SuperRemoteInterface is parent of all classes with Interface "Remote":
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import Config.SysConfiguration;
import java.rmi.server.UnicastRemoteObject;
public class SuperRemoteInterface extends UnicastRemoteObject {
protected SysConfiguration conf;
protected SuperRemoteInterface() throws RemoteException {
super();
}
protected SuperRemoteInterface(RMIClientSocketFactory clientFactory, RMIServerSocketFactory serverFactory) throws RemoteException {
super(0, clientFactory, serverFactory);
}
}
In the main of Server RMI I proxy Object and bind it in rmiregistry:
import /****/
public class ServerRMI extends UnicastRemoteObject {
public ServerRMI() throws RemoteException {
}
/*...*/
public static void main(String[] args) {
/*.....*/
try {
//Registry r = LocateRegistry.getRegistry();
Registry r = LocateRegistry.createRegistry(port);
RMIClientSocketFactory clientFactory = new RMISSLClientSocketFactory();
RMIServerSocketFactory serverFactory = new RMISSLServerSocketFactory();
AInterface proxy = (AInterface)SecurityInvocationHandler.newInstance(new AObject(conf), clientFactory, serverFactory);
r.bind("AObject", proxy);
/* ..... */
} catch (Exception e) {
//e.printStackTrace();
System.exit(-1);
}
}
}
Binding it's ok, but in the client side when lookup "AObject", I have this error:
java.lang.ClassCastException: cannot assign instance of $Proxy80 to field java.lang.reflect.Proxy.h of type java.lang.reflect.InvocationHandler in instance of $Proxy79
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2039)
at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1212)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1952)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1870)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at java.rmi.Naming.lookup(Naming.java:84)
at login_web.GetRemoteInterface.getAInterface(GetRemoteInterface.java:35)
.....
Client code is:
public class GetRemoteInterface {
private static final String _port = ":nnnn";
private String hostAddress;
public GetRemoteInterface() throws UnknownHostException {
/*....*/
public AInterface getAInterface() throws MalformedURLException, RemoteException, NotBoundException{
return (AInterface) Naming.lookup("//"+hostAddress+_port+"/AObject");
}
}
Without proxy mechanism lookup ok, with these codes not work.
Maybe it isn't possible binding a proxed object with java rmi??
Thanks in advance.
P.S. sorry for my English

The basic problem here is that you need to export the proxy object itself, not the invocation handler. Otherwise the proxy object gets serialized to the Registry, instead of its stub, with the consequences we see.
So you need to make the following adjustments:
SecureRemoteInvocationHandler doesn't need to extend UnicastRemoteObject either directly or indirectly.
You need to add Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf); before r.bind() in ServerRMI, where csf and ssf are the socket factories. (I renamed them in my code.)
There are other improvements you can make:
public class SecurityInvocationHandler<T extends Remote>
for better type-safety, and similarly:
public static <T extends Remote> T newInstance(...)
You need to make the variable containing the result of LocateRegistry.createRegistry() static so it doesn't get garbage-collected.
You need to get adjust all remote object contructors to call super() with a port number, so you get dynamic stubs.
You won't get much further than this until you sort out what is required for the SSL handshake to complete. You need to define javax.net.ssl.keyStore/keyStorePassword in the server, and javax.net.ssl.trustStore in the client if you aren't using the default one (i.e. if the server has a self-signed certificate).
The reason it doesn't work your way is that your exported SecurityInvocationHandler replaces itself with its stub during serialization, and that stub isn't an InvocationHandler, because InvocationHandler isn't a remote interface, so when the object gets deserialized it can't be reassembled, as there is no InvocationHandler to store in the dynamic proxy, just this stub, which the dynamic proxy doesn't know from Adam.

Thanks for EJP's advice.
I have try this solution, UnicastRemoteObject.exportObject really helps that proxy code is now run in server side but not client side.
UnicastRemoteObject.exportObject(proxy, 0) works as expected, I do not have to modify the remote object constructor to call super() because the default super constructor is calling UnicastRemoteObject(0)
I have to wrap the invoke call to handle the exception carefully like
#Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
try {
return method.invoke(remote, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
or else client side would got a java.lang.reflect.UndeclaredThrowableException instead of the correct one.

Related

How to pass data to EJBContext on client side?

I have following interceptor on server:
#Resource
EJBContext ejbContext;
#AroundInvoke
public Object onInvocation( InvocationContext aInvocationContext ) throws Exception
{
final Object myValue = ejbContext.getContextData().get("MyKey");
...
Object proceed = aInvocationContext.proceed();
...
return proceed;
}
How to pass data ("MyKey") to EJBContext on client side?
I tried to lookup it but I get javax.naming.InvalidNameException' exception during lookup or EJBCLIENT000409: No more destinations are available when I call getContextData(). I tried several ways I do not know If I do something wrong or EJBContext is some special resource and It is not possible to modify it on client.
How lookup name should look like?I tried java:comp/EJBContext, appName/moduleName/EJBContext.
I created client interceptor.
EJBClientContext.requireCurrent().registerInterceptor( 0, new MyClientInterceptor() );
There is important note in registerInterceptor method javadoc:
Note: If an interceptor is added or removed after a proxy is used,
this will not affect the proxy interceptor list.
MyClientInterceptor:
import org.jboss.ejb.client.EJBClientInterceptor;
import org.jboss.ejb.client.EJBClientInvocationContext;
public class MyClientInterceptor implements EJBClientInterceptor
{
#Override
public void handleInvocation( EJBClientInvocationContext aContext ) throws Exception
{
Object myValue =...
aContext.getContextData().put( "MyKey", myValue );
aContext.sendRequest();
}
#Override
public Object handleInvocationResult( EJBClientInvocationContext aContext ) throws Exception
{
return aContext.getResult();
}
}

Invoke of a method - logging as method declaring class

I'm currently using JBoss interceptors and Proxy classes for wrapping method invoking at runtime and log some statistics.
So said, having this code:
public class ProxyLoggingInterceptor <T> implements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(ProxyLoggingInterceptor.class);
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("%s.%s", t.getClass().getSimpleName(), method.getName());
}
}
the log will produce something like this:
12-11-2018 11:41.09,728 INFO (ProxyLoggingInterceptor) - [ANALYTICS]: MyClass.myMethod
However I'd like to show the logging declaring class as the logger entry, that is MyClass.
The desired result would be like:
12-11-2018 11:41.09,728 INFO (MyClass) - [ANALYTICS]: MyClass.myMethod
Is there any way that would not considered as a bad practice ?
Actually I am not into AOP based on dynamic proxies, I always use AspectJ where this kind of problem does not exist and it is easy to get the information you want because the original classes get modified. But having found the question anyway due to its aop tag and having played around a bit, I am trying to answer it:
package de.scrum_master.app;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.function.Function;
public class ProxyLoggingInterceptor<T> implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf(
"%s.%s%n",
proxy.getClass().getGenericInterfaces()[0].getTypeName(),
method.getName()
);
return null;
}
public static void main(String[] args) {
ClassLoader classLoader = ProxyLoggingInterceptor.class.getClassLoader();
Map mapProxy = (Map) Proxy.newProxyInstance(
classLoader,
new Class[] { Map.class },
new ProxyLoggingInterceptor<Map>()
);
mapProxy.put("foo", 11);
Function functionProxy = (Function) Proxy.newProxyInstance(
classLoader,
new Class[] { Function.class },
new ProxyLoggingInterceptor<Function>()
);
functionProxy.apply("x");
Runnable runnableProxy = (Runnable) Proxy.newProxyInstance(
classLoader,
new Class[] { Runnable.class },
new ProxyLoggingInterceptor<Runnable>()
);
runnableProxy.run();
}
}
Console output:
java.util.Map.put
java.util.function.Function.apply
java.lang.Runnable.run
Is that what you want?

Wrapping a service in a SleepyProxy to simulate lag

I am attempting to wrap a service in a proxy to simulate lag during tests. The following class is meant to wrap an object and sleep the thread for 100ms for any invoked method.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SleepyProxy<T> implements InvocationHandler {
private T wrapped;
private SleepyProxy(T toWrap) {
this.wrapped = toWrap;
}
#SuppressWarnings({"unchecked", "rawtypes"})
public static <T> T createProxy(T toWrap) {
Object proxy = Proxy.newProxyInstance(
toWrap.getClass().getClassLoader(),
toWrap.getClass().getInterfaces(),
new SleepyProxy(toWrap));
return (T) proxy;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(wrapped, args);
nap();
return result;
}
private void nap() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
From my test class:
private MyService service = SleepyProxy.createProxy(ServiceProvider.getMyService());
Produces the following error:
java.lang.ClassCastException: com.sun.proxy.$Proxy33 cannot be cast to com.example.service.MyService;
Please Note:
I am using Spring Framework and JUnit4
Test class annotated with #RunWith(SpringJUnit4ClassRunner.class)
I'm learning Spring; I'm unsure if I need to be using a Spring InvocationHandler / Proxy service
Why am I having issues casting to MyService? All object values seem to line up when debugging. Is there a better way I can go about simulating lag on my services? (Aside from making a 'test service' for each).
Thanks for your help!
Problem is that MyService is a class, and Proxy.newProxyInstance creates object that implements interface you provide in second argument. To have it working MyService needs to implement interface so:
class MyService implements ServiceInterface
And later use your proxy like this:
ServiceInterface service = SleepyProxy.createProxy(ServiceProvider.getMyService());
Proxy.newProxyInstance has nothing to do with MyService class, it will only create object that will run InvocationHandler.invoke when you will call method on it.

Java RMI - When to create a Stub, start Registry and specify Codebase?

When to create a Stub, start Registry and specify Codebase?
I have created a RMI application. My simple application works. I have the RemoteObjInterface.class's package in my buildpath for the Client and the Server packages. I first start the Server application and then the Client application.
However, I have looked at other examples in the internet and I see them starting the registry, creating a Stub and specifying a codebase.
The following is my program:
The "RemoteObjInterface.class" is my Interface, "RemoteObjImplementation.class" is my Server and "Client.class" is my Client.
public interface RemoteObjInterface extends Remote {
public String someMethod() throws RemoteException;
}
public class RemoteObjImplementation extends UnicastRemoteObject implements
RemoteObjInterface {
private static final long serialVersionUID = 1L;
private static final int PORT = 1099;
private static Registry registry;
public RemoteObjImplementation() throws RemoteException {
super();
}
#Override
public String someMethod() throws RemoteException {
return new String("Hello");
}
public static void main(String args[]) {
Registry registry = LocateRegistry.createRegistry(PORT);
registry.bind(RemoteObjInterface.class.getSimpleName(),
new RemoteObjImplementation());
}
}
public class Client {
private static final String HOST = "localhost";
private static final int PORT = 1099;
private static Registry registry;
public static void main(String[] args) throws Exception {
registry = LocateRegistry.getRegistry(HOST, PORT);
RemoteObjInterface remoteApi = (RemoteObjInterface) registry.lookup(RemoteObjInterface.class.getSimpleName());
System.out.println("Message = " +
remoteApi.someMethod();
}
}
When to create a Stub
Creating a stub is a side-effect of exporting the remote object, which in turn is a side-effect of constructing it if it extends UnicastRemoteObject.
start Registry
When you want to start it. Before you start calling bind() or rebind() for example.
and specify Codebase?
You don't need to use this feature at all, it is optional. If you want your clients to be able to download classes dynamically rather than distributing them to the client ahead of time, specify the java.rmi.server.codebase system property in the server JVM before you export any remote objects (including Registries), and make sure it points to a URL that is accessible by both the Registry and the client.

The correct way to pass data to/from a java HttpHandler class for java HttpServer class

I have a java HttpHandler that I am using to test an application. In the HttpHandler each http request is handled in a separate thread and is passed the HttpExchange. But I need to access data from the main thread and class (the one that setup the HttpServer and HttpHandler) so that HttpHandler can send back the correct response for the current test being run. How is the best way to get this data passed in or accessible by the HttpHandler class? I cannot add another parameter to the HttpHandler#handle since that is defined by the HttpHandler & used by the HttpServer, and I can not access none static methods in the main class. I will also need to push messages from the HttpHandler back to the main class to log.
Thanks
A sample of my code:
class RequestHandler implements HttpHandler {
#Override
public void handle(HttpExchange exchange)
{
// do a bunch of stuff with the request that come in.
}
}
public class MainClass
{
public static void main(String[] args)
{
HttpServer server;
ExecutorService excutor;
InetSocketAddress addr = new InetSocketAddress(ipAdd, ipPort);
server = HttpServer.create(addr, 0);
server.createContext("/", new RequestHandler());
excutor = Executors.newCachedThreadPool();
server.setExecutor(excutor);
server.start();
// do a bunch of stuff that uses the server
}
From the comments you say that you are constructing the handlers yourself. A typical way that you can inject objects into the handlers is just to pass them in as arguments to the constructor.
For example:
public class RequestHandler implements HttpHandler {
private final Object someObject;
public RequestHandler(Object someObject) {
// there is an implied super() here
this.someObject = someObject;
}
public void handle(HttpExchange exchange) throws IOException {
...
// you can then use someObject here
...
}
}
Then you can pass in the object into your handler like:
server.createContext("/", new RequestHandler(someObject));
In terms of passing information around between handlers, you should be able to use the HttpExchange.setAttribute(...) method to do this. That is a typical way. I'd suggest using attribute names that start with "_" to differentiate them from HTTP attributes.

Categories

Resources