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

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.

Related

Access field in a Rabbitmq testContainer extension class in junit5

I am using TestContainer to run RabbitMQ instance in order to use it in my integration tests.
I create a Junit 5 extension class that implement the BeforeAllCallback interface to run the container only once before my tests, to connect to the container i need to retrieve the mapped port that is exposed in my host machine, so i am wandering if there is any solution in order to access the extension class field from my integration test class.
The Extension
public class RmqExtension implements BeforeAllCallback {
private static final String DEFAULT_USER = "guest";
private static final String DEFAULT_PASS = "guest";
public static final int RABBIT_HTTP_API_PORT = 15672;
private static final String RABBIT_MQ_IMAGE_NAME = "rmqImage";
private static final String RABBIT_MQ_OVERVIEW_PATH = "/api/overview";
private static final GenericContainer rabbitMqContainer = new GenericContainer(DockerImageName.parse(RABBIT_MQ_IMAGE_NAME))
.withExposedPorts(RABBIT_HTTP_API_PORT)
.waitingFor(Wait.forHttp(RABBIT_MQ_OVERVIEW_PATH).withBasicCredentials(DEFAULT_USER, DEFAULT_PASS).forStatusCode(HttpStatus.SC_OK));
#Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
rabbitMqContainer.start();
}
}
My test Class
#ExtendWith(RmqExtension.class)
class RabbitMqIT {
private int myPort;
#Test
void myTest(){
// What i need to do
myPort = rabbitMqContainer.getMappedPort(15672);
}
}
I am unsure what is the most elegant JUnit-Jupiter-idiomatic way to do this, but if there is only 1 instance of the container per JVM process, you could either use a public static field or save it System Properties.
Also, see the Singleton Container Pattern for another example of how to do this without JUnit:
https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers

Mapping Openfire Custom plugin with aSmack Client

I'm a newbie to XMPP so forgive me if this question sounds silly. I want to create a custom plugin and map it with my aSmack client on Android. I'm trying to apply my knowledge of Web Services but I'm not winning. So please guide my thinking toward the best approach, an example will be really helpful. Thanx in advance.
There are many types of plugins, let's talk in general pourpose.
Igniterealtime Plugin guide
You want to define a brand new IQ Stanza to manage an UserCustomParam.
Let's say:
<iq from="user1#myserver" to="myserver" type="get">
<usercustomparam xmls:"com.records.iq" retrive="favouritecolor">
</iq>
What you have to:
step 1:
define a plugin (class that implemements Plugin)
that adds a new handler
MyCustomHandler colorshandler;
IQRouter iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(colorshandler);
Step2: implements MyCustomHandler as you need (read on database, write on database, read server side and so on).
public class MyCustomHandler extends IQHandler {
public static final String NAMESPACE_TICKET_IQ = "com.records.iq";
public static final String TAG_TICKET_IQ = "usercustomparam ";
Now your server it's ready to manage your custom IQ request.
Time to go client side:
Step3: register to your ProviderManager an IQProvider
ProviderManager.addIQProvider("usercustomparam ","com.records.iq", new IQUserCustomParamProvider());
Step4: implements your IQUserCustomParamProvider as you need
public class IQUserCustomParamProvider extends IQProvider<IQUserCustomParam>
into Provider you'll parse the incoming IQ from server and you'll create a IQUserCustomParam with an instance param like
String favouriteColor
Step5: you need to implement IQUserCustomParam
public class IQUserCustomParam extends IQ
private final static String childElementName = "usercustomparam";
private final static String childElementNamespace = "com.records.iq";
public IQUserCustomParam (String color)
{
this(childElementName , childElementNamespace );
this.setType(IQ.Type.result);
this.setFavouriteColor(color);
}
Step 6: now set up it's completed, but you haven't defined yet when to accept IQUserCustomParam when it comes from server. So you need a StanzaFilter
public class IQUserCustomParamFilter implements StanzaFilter
Step 7: and you haven't defined yet what to do with IQUserCustomParam when it comes from server. So you need a StanzaListner
public class IQUserCustomParamListner implements StanzaListener
Step 8: finally you'll have to register the combo filter/listner on your connection:
AbstractXMPPConnection connection = ...;
connection.addAsyncStanzaListener(new PersonalConfigListner(this), new IQMUCConfigTicketFIlter();
if that helped, please don't forget to accept the answer!
This is a sample of plugin implementation:
First, you should implement the Plugin interface:
public class MotDPlugin implements Plugin
Then, this requires implementation of the intitalizePlugin and destroyPlugin methods, as shown below:
public void initializePlugin(PluginManager manager, File pluginDirectory) {
serverAddress = new JID(XMPPServer.getInstance().getServerInfo().getName());
router = XMPPServer.getInstance().getMessageRouter();
SessionEventDispatcher.addListener(listener);
}
public void destroyPlugin() {
SessionEventDispatcher.removeListener(listener);
listener = null;
serverAddress = null;
router = null;
}
The more about this sample, you may refer to the Openfire Plugin Development: Message of the Day.
Hope it helps.
There is a simple instance about plugin:
public class TestIQHandle extends IQHandler {
private static final String MODULE_NAME = "test plugin";
private static final String NAME_SPACE = "com:test:testplug";
private IQHandlerInfo info;
public TestIQHandle(){
super(MODULE_NAME);
info = new IQHandlerInfo("query", NAME_SPACE);
}
public TestIQHandle(String moduleName) {
super(moduleName);
info = new IQHandlerInfo("query", NAME_SPACE);
}
#Override
public IQ handleIQ(IQ packet) throws UnauthorizedException {
IQ reply = IQ.createResultIQ(packet);
Element groups = packet.getChildElement();
if(true){
System.out.println("=======invalid========");
}
if(!IQ.Type.get.equals(packet.getType())){
reply.setChildElement(groups.createCopy());
reply.setError(PacketError.Condition.bad_request);
return reply;
}
//StringUtils.substringBefore(packet.getFrom().toString(), "#");
return reply;
}
#Override
public IQHandlerInfo getInfo() {
// TODO Auto-generated method stub
return info;
}
}

Binding proxy for remote Object in java RMI

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.

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.

Calling EJB in JBoss from Tomcat & passing an object as an argument

I have the following EJB class instantiated in an application running in JBoss 5
public interface ISlaveServer {
public String getId();
public String getName();
}
#Remote
public interface IMasterServer {
public String getId();
public void addSlave(ISlaveServer slaveServer);
public void removeSlave(ISlaveServer slaveServer);
}
#Stateless
#RemoteBinding(jndiBinding = "MasterServer")
public class MasterServer implements IMasterServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public void addSlave(ISlaveServer slaveServer) { ... }
public void removeSlave(ISlaveServer slaveServer) { ... }
}
I Have the following class instantiated in an application running in Tomcat 6
public static class SlaveServer implements ISlaveServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public String getName() { return "SlaveServer"; }
}
Finally I have the following code also running in the Tomcat based application...
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer(new SlaveServer());
}
catch(NamingException e) {
e.printStackTrace();
}
Everything is working fine up until the call to
masterServer.addVideoServer(new SlaveServer());
at which time I get the following exception...
java.lang.ClassNotFoundException: org.test.SlaveServerTest$SlaveServer (no security manager: RMI class loader disabled)
From what I can tell this exception might be originating from the remote JBoss server because the remote calls are working ( masterServer.getId() works fine ). Just the call where I am passing a locally implemented object is failing.
What do I need to do to get this working?
The SlaveServer class is Serializable. This means that this class must be made available to both the client (the JNDI snippet) and the server (MasterServer). When a class cannot be found on the server, RMI has the capability of downloading code from a remote location. However, executing code downloaded from a remote client is a potentially dangerous operation, this is only allowed if a security manager has been installed.
You'll need to either include the SlaveServer class in the application containing MasterServer (or some server class path), or you'll need to stop using Serializable.
The "static" was there because the original SlaveServer class was a nested class.
I have moved the class to the top-level (thus removing the static) and it is still a no go; I get the same exception.
It seems to me I need to do something like CORBA's "activation" to my SlaveServer. That way the JBoss server should be able to get the stubs for my SlaveServer inside Tomcat.
/Edit
There is no implementation of ISlaveServer in the JBoss application. I want it to pass a "remote reference" from the Tomcat application to the JBoss application so it shouldn't really be serializing it anyway (just a reference to it).
To get this working I needed to implement the ISlaveServer interface as an RMI remote interface.
public interface ISlaveServer extends java.rmi.Remote {
...
}
and make sure that the SlaveServer class was a proper RemoteObject...
public class SlaveServer extends java.rmi.RemoteObject implements ISlaveServer {
}
Finally I had to make sure the SlaveServer was exported through RMI before using it...
static SlaveServer slaveServer = new SlaveServer();
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer((ISlaveServer)UnicastRemoteObject.exportObject(slaveServer, 0));
}
catch(NamingException e) {
e.printStackTrace();
}
This correctly communicates with the Remote EJB and passes a reference to my SlaveServer object which the EJB can use to communicate back with the calling VM.
FYI SlaveServer is static because in RMI it is your responsibility to hold on to a reference to the real object since RMI only holds weak-references. If you don't you will get "Object not in table" errors from RMI.

Categories

Resources