I have a java rmi server and a java rmi client in two separate and different machines.
The server is basically a fibonacy calculator. It receives a bunch of numbers and calculates a Fibonacci sequence based on them.
The client simply sends a bunch of numbers for the server to calculate.
The FiboServer project consists of three classes:
IFibonacci.java: an interface
Fibonacci.java: implements the previously defined interface
FibonacciServer.java: Has the main, and runs the server
FibonacciServer.java:
package fiboserver;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class FibonacciServer {
public static void main(String args[]){
if (System.getSecurityManager() == null) {
System.setProperty("java.security.policy", "server.policy");
System.setSecurityManager(new SecurityManager());
}
try{
IFibonacci fib = new Fibonacci();
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("fibonacci", fib);
System.out.println("Fibonacci Server ready.");
}catch(RemoteException rex){
System.err.println("Exception in Fibonacci.main " + rex);
}
}
}
The client project merely has one class: FibonacciClient.java.
FibonacciClient.java:
package fiboclient;
import fiboserver.IFibonacci;
import java.math.BigInteger;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class FibonacciClient {
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setProperty("java.security.policy", "client.policy");
System.setSecurityManager(new SecurityManager());
}
try{
//args[0] = server Public IP
Registry registry = LocateRegistry.getRegistry(args[0]);
IFibonacci calculator = (IFibonacci) registry.lookup("fibonacci");
//the rest of the args are just numbers to calculate
for(int i = 1; i < args.length; i++){
try{
BigInteger index = new BigInteger(args[i]);
BigInteger f = calculator.getFibonacci(index);
System.out.println("The " + args[i] + "th Fibonacci number "
+ "is " + f);
}catch(NumberFormatException e){
System.err.println(args[i] + " is not an integer.");
}
}
}catch(RemoteException e){
System.err.println("Remote object threw exception " + e);
} catch (NotBoundException e) {
System.err.println("Could not find the requested remote object on "
+ "the server: " + e);
}
}
}
Both projects have a policy file, the server has a server.policy and the client has a client.policy file. Both files have the same content:
grant{
permission java.security.AllPermission;
};
I am launching the FiboServer in the server machine using java -jar FiboServer.jar -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=12.34.56.789
I am launching the client by using java -jar FiboClient.jar 12.34.56.789 1 2 3 4 5.
The server launches without a problem. But when I launch the client I get the error:
Remote object threw exception java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: fiboserver.IFibonacci
Searches on StackOverflow convinced me that this has to do with RMI registry errors or policy file errors. However I know the the policy files are being read correctly and I do not think they have any errors.
What am I doing wrong? Why is this not working?
EDIT:
IFibonacci.java:
package fiboserver;
import java.math.BigInteger;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IFibonacci extends Remote{
public BigInteger getFibonacci(int n) throws RemoteException;
public BigInteger getFibonacci(BigInteger n) throws RemoteException;
}
JAR Files:
Contents of FibonacciClient.jar:
|META-INF
|----Manifest.mf
|fiboclient
|----FibonacciClient.class
Contents of FibonacciServer.jar:
|META-INF
|----manifest.mf
|fiboserver
|----IFibonacci.class
|----FibonacciServer.class
|----Fibonacci.class
The client doesn't have the class named in the exception available on its classpath. You need to deploy it, and any class it depends in, and so on recursively until closure.
Possibly you've renamed/copied the remote interface into another package for the client. You can't do that. It has to be the same at server and client. Same name, same methods, same package.
NB The message in your client 'remote object threw exception' isn't correct. It could be the lookup() throwing the exception. Don't confuse yourself by assuming things that may not be so. Just print the actual error message, exception, and stack trace.
Related
I am working with Jetty embedded server, building a REST api out of a legacy jar, which has a lot of critically useful calls to println (I run its classes and it prints stuff in console). I am trying now to have these printlns in a file, along with the requests status, but the NCSARequestLog only logs in the file date and code of the responses. Is there a way to log everything in a file then? I'm pretty sure it is possible because before we were wrapping the legacy jar in a war file deployed into Glassfish, and all prints used to show up in the server log.
Thanks
In the jetty-util-<ver>.jar there is a class called RolloverFileOutputStream which can be instantiated and then set to take over the roll of System.out and System.err
An example of this:
package demo;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.TimeZone;
import org.eclipse.jetty.util.RolloverFileOutputStream;
public class ConsoleCaptureDemo
{
public static void main(String[] args) throws IOException
{
File loggingDir = new File("logs");
if (!loggingDir.exists())
{
if (!loggingDir.mkdirs())
{
throw new RuntimeException("Unable to create directory: " + loggingDir);
}
}
String loggingFile = new File(loggingDir, "yyyy_mm_dd.jetty.log").getAbsolutePath();
boolean append = false;
int retainDays = 90;
TimeZone zone = TimeZone.getTimeZone("GMT");
RolloverFileOutputStream logStream = new RolloverFileOutputStream(loggingFile,
append, retainDays, zone);
System.out.println("Look at " + logStream.getFilename());
PrintStream logWriter = new PrintStream(logStream);
System.setOut(logWriter);
System.setErr(logWriter);
System.out.println("From System.out - hi there");
System.err.println("From System.err - hello again");
}
}
i wrote a program in netbeans with RMI that client has error
error :
java.rmi.UnmarshalException: error unmarshalling return; nested
exception is: java.lang.ClassNotFoundException: rmiserver.Message
(no security manager: RMI class loader disabled) at
sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
but sever does not any error!
interfaace code:
package rmiclient;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Message extends Remote {
void sayHello(String name) throws RemoteException;
}
interface implementation is:
package rmiserver;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class MessageImpl extends UnicastRemoteObject implements Message {
public MessageImpl() throws RemoteException {
}
#Override
public void sayHello(String name) throws RemoteException {
System.out.println("hello "+name);
}
}
server code is:
package rmiserver;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Main {
private void startServer(){
try {
// create on port 1099
Registry registry = LocateRegistry.createRegistry(1099);
// create a new service named myMessage
registry.rebind("myMessage", new MessageImpl());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("system is ready");
}
public static void main(String[] args) {
Main main = new Main();
main.startServer();
}
}
client code is:
package rmiclient;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Main {
private void doTest(){
try {
// fire to localhost port 1099
Registry myRegistry = LocateRegistry.getRegistry("127.0.0.1", 1099);
// search for myMessage service
Message impl = (Message) myRegistry.lookup("myMessage");
// call server's method
impl.sayHello("edwin");
System.out.println("Message Sent");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Main main = new Main();
main.doTest();
}
}
thanks :).
In your stacktrace:
java.lang.ClassNotFoundException: rmiserver.Message
but as per data you have provided, your Message interface is declated with package package rmiclient; and You haven't created any rmiserver.Message class.
Correct the package name.
I have two package. rmiclient and rmiserver. that in rmiclient are "message" and "main" . in rmiserver are "message" and "main" and "messageimpl"
That's your problem. This doesn't satisfy the contract. You can't just copy Message to another package and expect to have it be treated the same as the original. The remote stub implements the same remote interfaces that the remote object does. Not another interface with the same name in another package.
You have to deploy rmiserver.Message to the client. Just like the error message says, really.
I wish to create an agent to attach to our live Tomcat & Weblogic servers which will intercept all method calls to all classes declared in my companies package and do some logging of metrics such as execution time.
I came across the Byte Buddy library which seems to cater for this. However, I am not 100% clear on the approach to creating an agent using Byte Buddy:
The following article suggests that one creates an own agent and makes no mention of the byte-buddy-agent: http://mydailyjava.blogspot.ie/2015/01/make-agents-not-frameworks.html
However, I do see someone has created his/her own byte-buddy-agent so I am
not sure if I am meant to use this.
https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent
I went with the approach of creating my own agent and packaged it up using Maven to include Byte Buddy as a fat jar (so that the Byte Buddy code is on the class path) which I reference from my catalina.bat.
Edit: I have since downloaded the source and figured out that the AgentBuilder relies on the byte-buddy-agent package so the above question is irrelevant.
Tomcat starts up fine and I can see that the agent is called as I see the "Entered premain" System.out.
However I never see the "Intercepted" System.out when I execute the code on a separate war file deployed to Tomcat.
Edit: Code below updated based on Rafael's response and this is now working.
Can somebody tell me what I might be doing wrong here? I've included the agent code below.
Also, can someone tell me which ElementMatchers is best for package matching? I tried nameStartsWith but it had no effect and the API documentation does not state if it is the fully qualified class name.
*Edit: The nameStartsWith does check the package. *
Anyway, thanks in advance for any help!
package com.mycompany.agent;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.matcher.ElementMatchers;
public class MyAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("Entered premain");
try{
new AgentBuilder.Default()
.withListener( new AgentBuilder.Listener() {
public void onComplete(String arg0) {
System.out.println("Completed - " + arg0);
}
public void onError(String arg0, Throwable arg1) {
System.out.println("Error - " + arg0+", "+arg1.getMessage());
arg1.printStackTrace();
}
public void onIgnored(String arg0) {
System.out.println("Ignored - " + arg0);
}
public void onTransformation(TypeDescription arg0, DynamicType arg1) {
System.out.println("Transformed - " + arg0+", type = "+arg1);
}
})
.rebase(ElementMatchers.nameStartsWith("com.mycompany"))
.transform(new AgentBuilder.Transformer() {
public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription) {
return builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(new Interceptor()));
}
}).installOn(instrumentation);
}
catch (RuntimeException e) {
System.out.println("Exception instrumenting code : "+e);
e.printStackTrace();
}
}
package com.mycompany.agent;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
#SuppressWarnings("rawtypes")
public class Interceptor {
#RuntimeType
public Object intercept( #SuperCall Callable<?> callable, #AllArguments Object[] allArguments, #Origin Method method, #Origin Class clazz) throws Exception {
long startTime = System.currentTimeMillis();
Object response;
try{
response = callable.call();
}
catch(Exception e) {
System.out.println("Exception occurred in method call: " + methodName(clazz, method, allArguments) + " Exception = " + e);
throw e;
}
finally{
System.out.println("Method " + methodName(clazz, method) + " completed in " + (System.currentTimeMillis() - startTime) + " miliseconds");
}
return response;
}
private String methodName(Class clazz, Method method){
return methodName(clazz, method, null);
}
private String methodName(Class clazz, Method method, Object[] allArguments){
StringBuilder builder = new StringBuilder();
builder.append(clazz.getName());
builder.append(".");
builder.append(method.getName());
builder.append("(");
for(int i = 0; i < method.getParameters().length; i++) {
builder.append(method.getParameters()[i].getName());
if(allArguments != null) {
Object arg = allArguments[i];
builder.append("=");
builder.append(arg != null ? arg.toString() : "null");
}
if(i < method.getParameters().length - 1) {
builder.append(", ");
}
}
builder.append(")");
return builder.toString();
}
Everything seems to be right. You should always try registering an AgentBuider.Listener which will expose stack traces of unsuccessful instrumentations if Byte Buddy causes an exception for signaling an illegal instrumentation attempt.
I assume that your class's package-private definition of your Interceptor is the cause of this exception. Your interceptor must be visible to all instrumented code. Otherwise, the class is not invokable.
I created a simple Java RMI application in netbeans 7.0.1 ( win 7 + jdk 7u1). I have two separate projects:
RMI_Client contains:
MyClient class:
package rmi_client;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class MyClient
{
public static void main(String[] args)
{
String msg = "Hello RMI";
rmiInterface stub;
Registry reg;
try
{
reg = LocateRegistry.getRegistry("localhost");
stub = (rmiInterface) reg.lookup("Hello");
try
{
stub.hello(msg);
}
catch(Exception e)
{
System.out.println("Remote method exception thrown: " +e.getMessage());
}
}
catch(Exception e)
{
System.err.println("Client exception thrown: " + e.toString());
e.printStackTrace();
}
}
}
rmiInterface interface:
package rmi_client;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface rmiInterface extends Remote
{
void hello(String message) throws RemoteException;
}
RMI_Server contains:
rmiInterface interface - exactly the same as above
rmiImpl class:
package rmi_server;
import java.rmi.RemoteException;
public class rmiImpl implements rmiInterface
{
#Override
public void hello(String message) throws RemoteException
{
System.out.println("Server:" + message);
}
}
MyServer class:
package rmi_server;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class MyServer
{
public static void main(String[] args)
{
try
{
rmiImpl robj = new rmiImpl();
rmiInterface stub = (rmiInterface) UnicastRemoteObject.exportObject(robj, 0);
Registry reg = LocateRegistry.createRegistry(1099);
reg.rebind("Hello", stub);
System.out.println("Server is ready to listen: ");
}
catch (Exception e)
{
System.err.println("Server exception thrown: " + e.toString());
}
}
}
If I'm doing something wrong above please let me know. First I start RMI_Server application, then when I run RMI_Client I get errors:
Client exception thrown: java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: rmi_server.rmiInterface (no security manager: RMI class loader disabled)
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: rmi_server.rmiInterface (no security manager: RMI class loader disabled)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at rmi_client.MyClient.main(MyClient.java:28)
Caused by: java.lang.ClassNotFoundException: rmi_server.rmiInterface (no security manager: RMI class loader disabled)
at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:554)
at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:646)
at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:311)
at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:257)
at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1549)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1511)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
... 2 more
Where is the problem ?
RMI_Server contains:
rmiInterface interface - exactly the same as above
No it isn't. This message:
java.lang.ClassNotFoundException: rmi_server.rmiInterface
says that rmiinterface is in the rmi_server package, which isn't 'the same as above'.
It has to be exactly the same class. Not a similar class in a different package.
So what you need is three packages: server, client, and shared. The shared package needs to contain the remote interface and any application classes it relies on.
Any idea why do I get RemoteException while trying to invoke methods on Unix machine from Windows?
I am inside the network and don't think this is because of firewall problem as I can do "telnet" from Windows to Unix box after starting the RMI server at the unix box. I also could not understand why is it going to local loopback IP?
Stack Trace:
RemoteException occured, details java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused: connect
java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused: connect
Many thanks in advance.
You probably don't have your hostname configured properly on your Linux box. I bet if you ping $(hostname) from your Linux box, it will ping 127.0.0.1. Usually this is because of an entry in your /etc/hosts file.
There's a couple of ways to solve the problem. The hard way would be to get your Linux box to resolve its own hostname to its IP address properly. You can edit your /etc/hosts file, setup your DNS server, whatever you've got to do. The challenge is that while this may make things more technically correct, you run the risk of breaking things that relied on the old behavior.
The path of least change would be to set the system property java.rmi.server.hostname to the hostname or IP address of your Linux box. (i.e. java -Djava.rmi.server.hostname=$(hostname) ...).
Why?
The Java RMI registration server is actually a network wide registration server. Objects on other machines can bind themselves to this registry.
When a remote object is registered, the registration includes the network address as part of the registration. By default, the address it uses is 'the IP address of the local host, in "dotted-quad" format.' In your setup, this address is 127.0.0.1.
When your Windows box contacts the registration service for the address of the remote object, it gets back 127.0.0.1. It then tries to contact the remote object at that address. That's why it's going to the loopback address.
I suggest a solution based on customized RMISocketFactory.
Like explained on Sun Site, you can provide your own SocketFactory :
http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/socketfactory/
My solution use this mecanism for intercept client socket creation, and replace the host received (127.0.0.1) by the good IP, well known by the client.
Th rest of the communication mechanism is still based on java rmi standards.
With this implementation, the exporter does not have to know it's own IP, which is sometimes no easy (multiple network interfaces ...)
Here are the tree classes, the Factory, the Server and the Client. The Hello class and interface are also uploaded to be exhaustive.
Hope it should be utile
SocketFactory:
import java.io.IOException;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.server.RMISocketFactory;
/**
* Socket Factory for RMI calls.
*
* This classe, instanciated from server when RMI objects are exported, is send
* to the client who use it (transparently) for create sockets which call remote objects.
*
* This implementation give the ability to modify dynamically the target host cible.
*
* The host will not be aware of it's own IP.
*/
public class MySocketFactory extends RMISocketFactory implements Serializable {
/**Target host for RMI calls, setted by caller. */
private static String server = "localhost";
/**
* Create a client socket, replacing required host by the host setted when the service is called,
* via {#link #setServer(String)}.
* The host received is usually 127.0.0.1, depending on property java.rmi.server.hostname on the exporter.
*/
#Override
public Socket createSocket(String host, int port) throws IOException {
System.out.println("change host from " + host + " to " + server);
return getFactory().createSocket(server, port);
}
/**
* Create a server socket.
*/
#Override
public ServerSocket createServerSocket(int port) throws IOException {
return getFactory().createServerSocket(port);
}
/**
* Use default RMI factory.
*/
private RMISocketFactory getFactory() {
return RMISocketFactory.getDefaultSocketFactory();
}
/**
* Save the target host. This method must be called before use of a service (before Naming.lookup).
*/
public static void setServer(String host) {
server = host;
}
}
Exporter :
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.rmi.server.UnicastRemoteObject;
/**
* RmiExport
*/
public class MyRmiExporter {
/**
* java -Djava.security.policy=java.policy MyRmiExporter
*/
public static void main(String[] args) throws RemoteException, IOException {
System.setSecurityManager(new RMISecurityManager());
Hello export = new HelloImpl();
RMISocketFactory sf = new MySocketFactory();
UnicastRemoteObject.unexportObject(export, true);
Remote stub = UnicastRemoteObject.exportObject(export, 0, sf, sf);
String url = "rmi://0.0.0.0:" + Registry.REGISTRY_PORT + "/Hello";
LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Naming.rebind(url, stub);
System.out.println("Exported " + url);
}
}
Client :
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.registry.Registry;
public class MyClient {
/**
* java MyClient localhost
*/
public static void main(String[] args) throws IOException, NotBoundException, InterruptedException {
String host = args[0];
MySocketFactory.setServer(host);
String url = "rmi://" + host + ":" + Registry.REGISTRY_PORT + "/Hello";;
System.out.println("look up " + url);
Hello proxy = (Hello) Naming.lookup(url);
System.out.println("OK, remote getted !");
System.out.println(proxy.hello("bonjour"));
}
}
Bean :
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote, Serializable {
String hello(String mess) throws RemoteException;
}
Impl :
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException {
}
#Override
public String hello(String mess) throws RemoteException {
return "hello : " + mess;
}
}
last and least, java.policy :
grant {
permission java.security.AllPermission;
};