How to set various parameters for Java RMI based communication? - java

While performing a client-server communication with various forums, I am unable to perform Remote-object's lookup on the client machine.
The errors which I receive are ConnectIOException(NoRouteToHostException), and sometimes ConnectException and sometimes someother.
This is not what I want to ask. But, the main concern is how should I setup client platform and server platform --- talking about networking details --- this is what I doubt interferes with my connection.
My questions :-
How should I edit my /etc/hosts file on both client-side and server-side? Server's IP- 192.168.1.8 & Client's IP-192.168.1.100. Means, should I add the system name in both the files:
192.168.1.8 SERVER-1 # on the server side
192.168.1.100 CLIENT-1 # on the client side
Should I edit like this? Can this be one of the possible concerns? I just want to remove any doubts left over to perform the rmi-communication!
Also, I am also setting Server's hostname property using System.setProperty("java.rmi.server.hostname",192.168.1.8); on the server side. Should I do the same on the client-side too?
I've read about setting classpath while running the java program on both server-side as well as the client-side. I did this too,but,again the same exceptions. No difference at all. I've read that since Java update 6u45, classpaths aren't necessary to include! Please throw some light on this too...
If I am missing something, Please enlighten about the same too. A brief idea/link to resources are most preferred.

You don't need any of this unless you have a problem. The most usual problem is the one described in the RMI FAQ #A.1, and editing the hosts file of the server or setting java.rmi.server.hostname in the server JVM is the solution to that.
'No route to host' is a network connectivity problem, not an RMI problem, and not one you'll solve with code or system property settings.
Setting the classpath has nothing to do with network problems.

Here is server example of which transfers an concrete class. This class must be exist in server and client classpath with same structure
Message:
public class MyMessage implements Serializable {
private static final long serialVersionUID = -696658756914311143L;
public String Title;
public String Body;
public MyMessage(String strTitle) {
Title = strTitle;
Body = "";
}
public MyMessage() {
Title = "";
Body = "";
}
}
And here is the server code that gets an message and returns another message:
public class SimpleServer {
public String ServerName;
ServerRemoteObject mRemoteObject;
public SimpleServer(String pServerName) {
ServerName = pServerName;
}
public void bindYourself() {
try {
mRemoteObject = new ServerRemoteObject(this);
java.rmi.registry.Registry iRegistry = LocateRegistry.getRegistry(RegistryContstants.RMIPort);
iRegistry.rebind(RegistryContstants.CMName, mRemoteObject);
} catch (Exception e) {
e.printStackTrace();
mRemoteObject = null;
}
}
public MyMessage handleEvent(MyMessage mMessage) {
MyMessage iMessage = new MyMessage();
iMessage.Body = "Response body";
iMessage.Title = "Response title";
return iMessage;
}
public static void main(String[] server) {
SimpleServer iServer = new SimpleServer("SERVER1");
iServer.bindYourself();
while (true) {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
and here is the remote interface of server remote object:
public interface ISimpleServer extends java.rmi.Remote{
public MyMessage doaction(MyMessage message) throws java.rmi.RemoteException;
}
all you need is adding MyMessage class both in server and client classpath.

Related

how to create a connection pool/only once for a third party Application?

I'm using JAVA/Spring MVC and I need to make a Connection Pool for a Third Party Application integration in my application becouse when i try to connect it multiple time my application and server System utilize 100% RAM.
here i have to problem, when users start to hit a specific method (callGenerationService()) multiple time, my Heap memory(RAM space) increases and becomes 100% and application going to slow becouse of it connect third party application multiple times ? here i need to create a connection only once and get it multiple times. where my connection like,
public class ClickToCallServiceImpl implements ClickToCallServiceInterface {
Client client = null;
#Override
public ClickToCall callGenerationService(ClickToCall clickToCall) {
client = new Client();
client.connect("127.0.0.1", 8021 , "password", 10); //Every time Connection Connect.
client.setEventSubscriptions("plain", "all");
// client.sendSyncApiCommand("",""); //here i run command on every hit like.
client.sendSyncApiCommand(clickToCall.command1, clickToCall.command2);
client.close();
}
}
and here 'ClickToCall' is a #Component Bean/POJO Class with variables setters and getters.
Is there, how to we create a connection (either pool or only once connect) for above connection where i connect only once and hit clickToCall.Command1 and clickToCall.Command2 multiple times and utilize less RAM? Thanks in advance.
Please note that I'm not an expert of freeswitch esl so you must check the code properly. Anyway this is what I would do.
First I create a Factory for Client
public class FreeSwitchEslClientFactory extends BasePooledObjectFactory<Client> {
#Override
public Client create() throws Exception {
//Create and connect: NOTE I'M NOT AN EXPERT OF ESL FREESWITCH SO YOU MUST CHECK IT PROPERLY
Client client = new Client();
client.connect("127.0.0.1", 8021 , "password", 10);
client.setEventSubscriptions("plain", "all");
return client;
}
#Override
public PooledObject<Client> wrap(Client obj) {
return new DefaultPooledObject<Client>(obj);
}
}
Then I create a shareable GenericObjectPool:
#Configuration
#ComponentScan(basePackages= {"it.olgna.spring.pool"})
public class CommonPoolConfig {
#Bean("clientPool")
public GenericObjectPool<Client> clientPool(){
GenericObjectPool<Client> result = new GenericObjectPool<Client>(new FreeSwitchEslClientFactory());
//Pool config e.g. max pool dimension
result.setMaxTotal(20);
return result;
}
}
Finally I use the created pool in order to get the Client obj:
#Component
public class FreeSwitchEslCommandSender {
#Autowired
#Qualifier("clientPool")
private GenericObjectPool<Client> pool;
public void sendCommand(String command, String param) throws Exception{
Client client = null;
try {
client = pool.borrowObject();
client.sendSyncApiCommand(command, param);
} finally {
if( client != null ) {
client.close();
}
pool.returnObject(client);
}
}
}
I didn't test (also because I can't) it but it should work. In any case I pray you to properly check the configuration. I don't know if it's OK to always create a Client object and connect or if it's better to connect when you want to send command
I hope it can be useful
EDIT INFORMATION
Sorry I made an error early. You must return the client to the pool
I updated my FreeSwitchEslCommandSender class
Angelo

RMI implementing Session management

I want to build system so that :
1. Client connects to Server
2. Client asks for a port to server him
3. Server creates a remote object to serve the Client and binds it to a port
4. return the port number to client
5. Client connects to the port
My session/connection manager
public class ConnectionManager extends UnicastRemoteObject implements Server
{
public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
public ConnectionManager() throws RemoteException
{
super();
}
public static final String RMI_ID = "Server";
#Override
public boolean checkConnection() throws RemoteException
{
return true;
}
#Override
public int getPort(String ip) throws RemoteException
{
int i = 10000+clients.size()*2;
clients.add(new ClientHandler(ip, i));
return i;
}
}
Session implementation
public class ClientHandler extends UnicastRemoteObject implements Transfer
{
Registry rmi;
Registry reg;
PrintWriter log;
public Client client;
public ClientHandler(String ip, int port) throws RemoteException
{
super();
try
{
File f = new File(ip+" "+new Date()+".txt");
log = new PrintWriter(f);
}catch (Exception e)
{
e.printStackTrace();
}
rmi = LocateRegistry.createRegistry(port);
try
{
rmi.bind(String.valueOf(port),this);
}catch(Exception e)
{
e.printStackTrace();
}
}
The problem that if the object is created in a remote call , it is considered of remote origin and so unless you are on the same host it is not allowed to bind one to the LocalRegistry. and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.
The problem that if the object is created in a remote call , it is considered of remote origin
Untrue. This is simply not correct. You've just made this up. Don't do that.
and so unless you are on the same host it is not allowed to bind one to the LocalRegistry.
But you are on the same host. The constructor ClientHandler runs in the same host as the ConnectionManager and it creates a Registry, also in the same host. Indeed all this takes place within the same JVM.
and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.
No it doesn't. I tested your code. After fixing a compilation error it worked. Cannot reproduce.
HOWEVER
You don't need the part about the port, or the extra bind. All remote objects can share the same port. The server only needs to return a new instance of the remote session object to the client, directly.
A simple implementation of your requirement, using your classnames, looks like this, with a certain amount of guesswork as you didn't provide the Server interface:
public interface Server extends Remote
{
boolean checkConnection() throws RemoteException;
Transfer getSession(String ip) throws RemoteException;
}
public class ConnectionManager extends UnicastRemoteObject implements Server
{
public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
public ConnectionManager() throws RemoteException
{
super();
}
public static final String RMI_ID = "Server";
#Override
public boolean checkConnection() throws RemoteException
{
return true;
}
#Override
public Transfer getSession(String ip) throws RemoteException
{
ClientHandler ch = new ClientHandler(ip);
clients.add(ch);
return ch;
}
}
public class ClientHandler extends UnicastRemoteObject implements Transfer
{
PrintWriter log;
public Client client;
public ClientHandler(String ip) throws RemoteException
{
super();
try
{
File f = new File(ip+" "+new Date()+".txt");
log = new PrintWriter(f);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
EDIT
It is clear from your post and your comment below that you are suffering from a number of major misconceptions about Java RMI:
if a remote object is created in a remote call , it is not 'considered of remote origin'
The port on which an object is exported has nothing to do with the Registry.
It is determined when you construct an object that extends UnicastRemoteObject, via the super(), call, or when you call UnicastRemoteObject.exportObject() for other objects. If you supply a non-zero port number, that port is used, and can normally shared with other remote objects. Otherwise if there is already an RMI port in use it is shared with this object, otherwise a system-allocated port number is obtained.
Note that the export step precedes the bind step, so it is quite impossible for your misconception to be correct.
You don't need multiple Registries running in the same host. You can use a single one and bind multiple names to it.
If you first call LocateRegistry.createRegistry(), all other remote objects you export from that JVM can share the port with the Registry.
Remote methods can return remote objects.
The Registry is an example: it is actually very little more than a remote hash-map. Your methods can do it too. The objects are replaced by their stubs during the return process.
For those reasons, your intended design is completely fallacious and your comment below complete nonsense.
Further notes:
A do-nothing method doesn't really check a connection. It is just as likely to create a new connection and check that. Connections don't really exist in RMI, or at least they are very well hidden from you, pooled, expired, etc.
You don't need to pass the client's IP address. You can get it at the server from RemoteServer.getClientHost().
The constructor for ClientHandler should not catch IOExceptions internally: it should throw them to the caller, so the caller can be aware of the problem.

Jetty WebSocket Client Doesn't work for Binary Listener

I have a Java app that uses the Jetty WebSocket Client, version 9.x. It works fine for text messages sent from the server, but the binary listener is never invoked. I have a Javascript client implementation which I'm basically duplicating. I'm doing the same exact thing in Javascript that I do in Java, calling the same server. The Javascript works, and the Java doesn't. So I'm thinking that something is not configured properly in Jetty for binary listeners.
For example, the server is sending blob data. I know that in the Javascript client, I can set the binarytype to either arraybuffer or blob. I figured there may be a similar setting required in Jetty, but I've looked all through the API and searched many examples online. There are precious few examples of binary listeners online, and no mention anywhere of setting the binarytype, or any other special setting required to make binary llisteners work.
Here's a consolidated representation of my code. The code is spread throughout various classes, so this is not a stand-alone app, but I think it shows what I'm doing. The server is implemented with libwebsockets.
Client implementation
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
client = new WebSocketClient();
client.start();
client.setMaxBinaryMessageBufferSize((int) 500e6);//just to be sure
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setSubProtocols("pipe-data");
client = new SimpleSocket();
client.connect(socket, uri, request);
Socket implementation
#WebSocket
public class SimpleSocket {
#SuppressWarnings("unused")
private Session session;
private SocketHandlerBase handler;
private boolean connected = false;
public SimpleSocket(SocketHandlerBase listener) {
this.handler = listener;
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
this.handler.onClose(statusCode, reason);
this.connected = false;
}
#OnWebSocketConnect
public void onConnect(Session session) {
this.handler.onConnect(session);
this.connected = true;
}
//invoked when text messages are sent
#OnWebSocketMessage
public void onMessage(String msg) {
this.handler.onMessage(msg);
}
//does not get invoked when binary data is sent
#OnWebSocketMessage
public void onMessage(byte buf[], int offset, int length) {
this.handler.onMessage(buf, offset, length);
}
public boolean isConnected() {
return this.connected;
}
public SocketHandlerBase getHandler() {
return this.handler;
}
}
There was a hard to find problem with the server I was calling. A very specific configuration of invocation arguments was causing the binary listener to not be called. Nothing about the Jetty client or WebSockets in general involved here.

RMI : NotBoundException

I've implemented an RMI interface with these current files;
MyClient.java - Clientside code
MyServer.java - Serverside code
Adder.java - Interface
AdderRemote.java - Remote _implements Adder_
DataAccess.java - Contains all the methods to interveen between server and client
I have a vps which contains all files except the Client file in the directory
vps:~/rmi#
When testing this on it's own, ie: compiling on the server, doing
rmic AdderRemote
rmiregistry 5000&
(the port I have chosen)
java -classpath .:mysql-connector... MyServer
and then locally doing the same process but running the MyClient java instead, it works. The problem I am facing is now implementing this into a project I have running in eclipse as a part of this;
I have an instance of MyClient in the main file which is then passed as parameters to the certain classes (This project implements the MVC pattern and is passed to fellow model class'), and I am now getting the error
java.rmi.NotBoundException: xox
after googling, the only response I could find was "Attempt to look up a name that is not bound.", But I'm not really sure what this means? I'll attach my code, and any help would be much appreciated.
MyClient.java
public class MyClient
{
public Adder stub;
public MyClient ()
{
try
{
stub = (Adder)Naming.lookup("rmi://92.222.2.96:5000/xox");
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
public static void connect(Adder stub) throws RemoteException
{
System.out.println(stub.connect());
}
Adder.java
public interface Adder extends Remote
{
public String connect() throws RemoteException;
}
AdderRemote.java
public class AdderRemote extends UnicastRemoteObject implements Adder
{
public AdderRemote() throws RemoteException
{
super();
da = new DataAccess();
}
DataAccess da;
public String connect() throws RemoteException
{
return da.connect();
}
DataAccess.java
public class DataAccess
{
// Connection info
static final String URL = "jdbc:mysql://92.222.2.96:3306/groupproj";
static final String DRIVER = "com.mysql.jdbc.Driver";
static final String USERNAME = "root";
static final String PASSWORD = "*****";
Connection c = null;
public String connect()
{
try
{
Class.forName(DRIVER).newInstance(); //Load DB driver
c = DriverManager.getConnection(URL, USERNAME, PASSWORD); //Establish connection to DB2
return "Connected.";
}
catch(Exception e)
{
return e.toString();
}
}
MyServer.java
public class MyServer
{
public static void main ( String args[] )
{
try
{
Adder stub = new AdderRemote();
Naming.rebind("rmi://92.222.2.96:5000/xox", stub);
}
catch ( Exception e )
{
System.out.println(e);
}
}
public static void connect(Adder stub) throws RemoteException
{
try
{
stub.connect();
}
catch(Exception e)
{
System.out.println("Could not connect to the DB.");
}
}
I gathered that because the files on the server are located in the directory "rmi" I renamed the xox to this, but this did not solve the problem, so I reverted it back to xox, which it worked before putting it into a java project.
Thank you
You must have got an exception doing the bind.
If you got a NotBoundException when looking up the same name in the same Registry you're supposed to have bound it to, you didn't bind it at all.
Notes:
You can only bind to a Registry that is running in the same host as yourself. For that reason it is convenient to always use "localhost" as the hostname when calling bind(), rebind(), or unbind().
You'd be better off letting the RemoteException and NotBoundException be thrown from the constructor of MyClient.
MyClient.connect() should not be static. In fact it cannot be static. Ergo this cannot be the real code.
From what we can see so far, your system isn't correctly designed. Your server should get a DBMS connection when it needs one, inside a remote method, on behalf of the client that is calling that method, and release it before exiting the method. Opening a new connection every time a client asks for one explicity and storing it into an instance variable of the remote object (a) will leak connections and (b) won't work when concurrent clients come to execute a query or update on the same connection.

What can be the best approach to handle java.net.UnknownHostException for AWS users?

My application sends message to Amazon Simple Notification Service (SNS) topic but sometime (6/10) I get java.net.UnknownHostException:sqs.ap-southeast-1.amazonaws.com. The reason of exception is described in the amazon web services discussion forums, please look: https://forums.aws.amazon.com/thread.jspa?messageID=499290&#499290.
My problem is similar to what described in forums of amazon but my rate of publishing messages to topic is very dynamic. It can be 1 message/second or 1 message/minute or no message in an hour. I am looking for a cleaner, better and safe approach, which guaranties sending of message to SNS topic.
Description of problem in detail:
Topic_Arn= arn of SNS topic where application wants to publish message
msg = Message to send in topic
// Just a sample example which publish message to Amazon SNS topic
class SimpleNotificationService {
AmazonSNSClient mSnsClient = null;
static {
createSnsClient()
}
private void static createSnsClient() {
Region region = Region.getRegion(Regions.AP_SOUTHEAST_1);
AWSCredentials credentials = new
BasicAWSCredentials(AwsPropertyLoader.getInstance().getAccessKey(),
AwsPropertyLoader.getInstance().getSecretKey());
mSqsClient = new AmazonSQSClient(credentials);
mSqsClient.setRegion(region);
}
public void static publishMessage(String Topic_Arn, String msg) {
PublishRequest req = new PublishRequest(Topic_Arn, msg);
mSnsClient.publish(req);
}
}
class which calls SimpleNotificationService
class MessagingManager {
public void sendMessage(String message) {
String topic_arn = "arn:of:amazon:sns:topic";
SimpleNotificationService.publishMessage(topic_arn, message);
}
}
Please note that this is a sample code, not my actual code. Here can be class design issue but please ignore those if they are not related to problem.
My thought process says to have try-catch block inside sendMessage, so when we catch UnknownHostException then again retry but I am not sure how to write this in safer, cleaner and better way.
So MessagingManager class will look something like this:
class MessagingManager {
public void sendMessage(String message) {
String topic_arn = "arn:of:amazon:sns:topic";
try {
SimpleNotificationService.publishMessage(topic_arn, message);
} catch (UnknownHostException uhe) {
// I need to catch AmazonClientException as aws throws
//AmazonClientException when sees UnknownHostException.
// I am mentioning UnknownHostException for non-aws user to understand
// my problem in better way.
sendMessage(message); // Isn't unsafe? - may falls into infinite loop
}
}
}
I am open for answers like this: java.net.UnknownHostException: Invalid hostname for server: local but my concern is to dependent on solution at application code-level and less dependent on changes to machine. As my server application is going to run in many boxes (developer boxes, testing boxes or production boxes). If changes in machine host-files or etc is only guaranted solution then I prefer that to include with code level changes.
Each AWS SDK implements automatic retry logic. The AWS SDK for Java automatically retries requests, and you can configure the retry settings using the ClientConfiguration class.
Below is the sample example to create SNS client. It retries for 25 times if encounters UnKnownHostException. It uses default BackOff and retry strategy. If you want to have your own then you need to implement these two interfaces: http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/retry/RetryPolicy.html
private void static createSnsClient() {
Region region = Region.getRegion(Regions.AP_SOUTHEAST_1);
AWSCredentials credentials = new
BasicAWSCredentials(AwsPropertyLoader.getInstance().getAccessKey(),
AwsPropertyLoader.getInstance().getSecretKey());
ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setMaxErrorRetry(25);
clientConfiguration.setRetryPolicy(new RetryPolicy(null, null, 25, true));
mSnsClient = new AmazonSNSClient(credentials, clientConfiguration);
mSnsClient.setRegion(region);
}
Have you considering looking into the JVM TTL for the DNS Cache?
http://docs.aws.amazon.com/AWSSdkDocsJava/latest//DeveloperGuide/java-dg-jvm-ttl.html

Categories

Resources