Java RMI call slow first time - java

I'm working on a personal project for school where I have to user RMI to communicate between server and client.
Project info
The goal of my project is to retrieve stock info (from NYSE) for each day on the server at a specific time (after NYSE is closed). Each stock object is saved in a database. The information is retrieved over http and has nothing to do with RMI.
For the client it is also possible to fetch the stocks. When a user wants to fetch the stock object for the current day, it is directly fetched from the 3th party service. When a user, for example, wants to fetch Google's stock from last month, it is requested on the server over RMI. The server will the look for the stock object in the database and retrieve a Stock object and send it to the client.
Problem
When I start the client application, I have to login. The client will create a User object containing the username and password.
When I press the login button, it will take around 2 minutes before the main screen will be shown.
Below the source code where I setup the RMI connection.
Server (main.java)
public static void main(String[] args) throws UnknownHostException {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
System.out.println("Running on localhost");
System.setProperty("java.rmi.server.hostname", IP.getHostAddress());
} else {
System.out.println("rmi hostname is set to 37.97.223.70");
System.setProperty("java.rmi.server.hostname", "37.97.223.70");
}
try {
Registry reg = LocateRegistry.createRegistry(1099);
StockAppServer server = StockAppServer.getInstance();
reg.rebind("StockApp", server);
System.out.println("StockApp bound for StockAppServer object.");
} catch (RemoteException e) {
e.printStackTrace();
}
}
Based on the arguments that are passed to the application when it starts, I set the RMI hostname to my current IP address, or to the remote server address. The remote server address is a static IP, so this won't change.
Server (StockAppServer.java)
This class implements the interfaces that is used by the client to call methods on the server. So this class extends UnicastRemoteObject. When I start the server, registerStockTask() will be called. This method will fetch the ticker symbols (What are ticker symbols?) and then schedule a task to fetch all stock objects at a specific time.
private static StockAppServer _instance;
private List<User> loggedInUsers;
private List<Group> activeGroups;
private List<Notification> registeredNotifications;
private StockAppServer() throws IOException {
_instance = this;
this.loggedInUsers = new ArrayList<>();
this.activeGroups = new ArrayList<>();
this.registeredNotifications = new ArrayList<>();
this.registerStockTask();
clearActiveGroups();
checkForCompletedNotifications();
// Start the restful framework to allow incoming connections from the NodeJS server to manage new notification
Router.getInstance();
}
public static StockAppServer getInstance() {
try{
return _instance == null ? new StockAppServer() : _instance;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Client (main.java)
public static void main(String[] arguments) throws Exception {
args = arguments;
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
// Program started with local command, expect that server is running on local host
reg = LocateRegistry.getRegistry(IP.getHostAddress(), 1099);
System.out.println("Attempting to connect to RMI server over 127.0.0.1");
} else {
// Program started without additional commands. Except that "the server" is available;
reg = LocateRegistry.getRegistry("37.97.223.70", 1099);
System.out.println("Attempting to connect to RMI server over 37.97.223.70");
}
try {
StockApp.getInstance().setServerInterfaces((IStockSend) reg.lookup("StockApp"), (IUserHandling) reg.lookup("StockApp"));
} catch(RemoteException e) {
AlertMessage.showException("Unable to connect to server.", e);
} catch (NotBoundException e) {
AlertMessage.showException("No server has been found with the name \"StockApp\" on the remote host.\nPlease try again later", e);
}
LoginController.showMenu();
//FileNotFoundException e = new FileNotFoundException("Couldn't find file blabla.txt");
//AlertMessage.showException("Something went wrong. Please try again later.", e);
}
How I tried to solve my problem
When I test my applications local, there is no problem. The login method will be finished within a few milliseconds and I will be represented the main screen.
I started by turning of my firewall on my macbook. No result, login method still takes around 2 seconds.
I turned off the firewall om my Ubuntu server. No result, both firewalls on server and macbook are turned off. Login method still takes around 2 seconds.
On the server runs (thanks to jenkins) another (unrelated) program. This program uses sockets instead of RMI. When this program is not running, the login method still takes around 2 minutes.
In StockAppServer.java, I called the following method:
super(1099);
This has the same outcome as the above steps I took.
I don't know what else I can try to solve my problem.
I tried to give as much code as possible for the RMI part. I you need any other source code, just ask and I can update this question. Also, the source code is available via github: https://github.com/juleskreutzer/GSO-Maatwerk. Make sure to run the program with -remote param.
Update 1 (9-1-2017)
As yanys requested in the comments, I should run the following command:
dscacheutil -q host -a name localhost
this returns the following output:
Mac:
name: localhost
ip_address: 127.0.0.1
Ubuntu:
dscacheutil: command not found
Update 2 (9-1-2017)
I checked with the provider of my VPS where I run the java server on. On their side everything should be OK. According to them, it shouldn't be a dns problem. After some research, I found out that RMI uses both DNS and reverse DNS. It this case, reverse DNS was the issue. Please see my answer on how I solved my problem.

As EJP pointed out in the comments on the question, it was an DNS problem.
I contacted the support of my hosting provider to see if I had some wrong settings. They helped me a lot in solving this problem.
First we tested the speed of my VPS, this is around 1000mbit download and upload speed. After we checked this, they said there was nothing wrong on their side.
After doing some research, I found out that RMI uses both DNS and Reverse DNS. The problem was that I didn't setup the reverse DNS on my server. I already have a domain name to use for reverse DNS.
I than did the following:
Create a A-record on my website that points to the IP address of the server. I named it vps.mydomain.com
Add the reverse DNS in the control panel of my server
Change the hostname of my server to vps.mydomain.com*
*My server runs Ubuntu 16.04, on ubuntu machines with systemd, you can use the command
sudo hostnamectl set-hostname new-name
to change the hostname

Related

Cannot Get DNS Requests via Java Code in Windows 10 and DLINK DIR-615 router

So I am working on a software that will monitor(and may alter by acting as a Forrowder) all the DNS requests made by my router.
What I did?
So for first I wrote a Java code that can listens to a specific port and prints all the requests to the console[For now I just want to test with the requests].
The code is:
import java.net.*;
import java.io.*;
public class PortLogger{
public static void main(String[] args) {
LoggerServer loggerServer = new LoggerServer(53);
loggerServer.start();
}
}
class LoggerServer extends Thread{
private int port;
public LoggerServer(int port){
this.port = port;
}
#Override
public void run(){
try{
int id = 1;
ServerSocket server = new ServerSocket(port);
System.out.println("Server Listening at port " + port);
Socket client;
while(true){
client = server.accept();
ClientHandler clientHandler = new ClientHandler(client, id++);
clientHandler.start();
}
}catch(Exception ex){
System.out.println("Exception at Server : 1 :: EX = " + ex);
}
}
}
class ClientHandler extends Thread{
private Socket client;
private int id;
public ClientHandler(Socket client, int id){
this.client = client;
this.id = id;
}
#Override
public void run(){
try {
String data = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
while(true){
data = reader.readLine();
if(data.length() > 0){
System.out.println("Client : " + id + " :: " + data);
}
}
}catch(Exception ex){
System.out.println("Exception at Client : " + id + " :: EX = " + ex);
}
}
}
The sole propose of this code for now is to Show me all the requests made to the server.
I know that I also have to change the DNS Server in my router for this.
So, for that I first tried by going to internet setup and put the local IP of my computer as DNS server.
But it was showing :
DNS IP and LAN IP must be on different networks!
But I found another way to do it.
It is as follows:
I went to the setup wizard of the router and the set the DNS Server to the same IP.
Surprisingly this worked!
[I have no idea whether this is a bug in the D-Link Firmware or not.
I have also added an exception to allow all request both inbound and outbound to port 53.
What is the problem?
So now the problem is that even after successfully changing the DNS to my servers. There seemed to be no requests at all to the console. I tried a lot but nothing.
I checked that the program was working fine by voluntarily sending request to it using telnet?
Now am I doing anything wrong or there is some bug with the router(its is a old one).
NOTE: The black lines on the images are just to hide my public IP address nothing special.
EDIT: I tried a few more times then found that websites were not opening when I changed the DNS in my router but still nothing in the console!
While it is difficult to give you a complete answer why your application doesn't work I can suggest some ways to investigate:
Port 53 is a privileged port. This means on Linux binding to that port requires root privileges and the application will throw an exception due to 'permission denied' if executed as a 'normal' user. As you are using Windows I don't know what it does if you try to bind as a 'normal' user, or you might be executing as an Admin user (or whatever the equivalent of 'root' is in Windows) and you don't know it. It might even just silently fail i.e. appear to bind when in fact it hasn't and no data is passed through you your application. As an aside, defaulting to 'root' as the default execution user in Linux is not the norm because it's insecure and most Linux distributions if not all do not allow this by default i.e. you can have this but you have to tell the distribution this is what you intend during installation. I'll let you come to your own conclusions what stance Windows takes for making users 'admin'...
In a scenario such as this if it were me I would immediately go to some networking tools to see what is happening. On Linux this is tcpdump or Wireshark. You can also get Wireshark for Windows as it's a GUI application. This will let you monitor and filter network traffic and so will be independent of your application. You can filter by source or destination address and/or port number.
I would leave the DNS setting alone in the router and change the DNS settings in one machine first, call it the test client, and set its DNS address to the machine where your application is running. Using tcpdump or Wireshark you can then make requests on your test_client e.g. browser requests and see the resulting network traffic.
You never mentioned if after changing your router's DNS settings all browser requests from clients fail. This is what I would expect to see if your router can no longer get a name resolution. However there maybe some DNS caching going on in your clients so you may appear to get successful DNS requests on your test_client. Again look at network traffic or use a Linux client which will provide you with much better networking tools.

How to make a local multiplayer java game runs online?

I've developed a decent amount of multiplayer games lately in java (board/turn based games running on a tcp connection with data in/out streams) but they only work locally, I want to go a step further and make them run online, what do I need to change to make it work?
It seems like there are many ways to accomplish that yet i don't know where to start, as I don't know much about networking.
Besides that I don't have a real server, I'm only using my home router and my pc.
So here is what I've tried so far:
I enabled port forwarding in my router and I think it works (I used a port forwarding checking tool online)
I created a dynamic DNS for my public ip using noip.com
So the server side should be fine at least, my problem is with the client side, the client's socket won't connect to my public ip, when I searched for a solution I concluded that the client shouldn't be in the same LAN where the server is, so I used a mobile hotspot as a second network and tried again, but got the same results. (connection refused exception)
is it because of the mobile hotspot (should I use another router) ?
or is it just some coding tweaks ?
This is a minimal server class example
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(50895);
Socket cs = ss.accept();
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
DataInputStream in = new DataInputStream(cs.getInputStream());
out.writeInt(123456);
} catch (IOException e) {
e.printStackTrace();
}
}
And this a minimal client class example
public static void main(String[] args) {
try {
String ip = Inet4Address.getByName("my Dynamic IP address").getCanonicalHostName();
System.out.println(ip);
InetSocketAddress sa = new InetSocketAddress(ip, 50895);
Socket cs = new Socket();
cs.connect(sa);
DataOutputStream out = new DataOutputStream(cs.getOutputStream());
DataInputStream in = new DataInputStream(cs.getInputStream());
System.out.println("received : " + in.readInt());
} catch (IOException e) {
e.printStackTrace();
}
}
The method that i've tried always gives me a connection refused exception, so any solution would be appreciated.
I found a software solution to the problem through virtual lan (using LogMeIn Hamachi) which is basicly used for creating game servers, you just need to create an account and a network so your friends can join it, after that everything should be fine and runs as if it's locally hosted.
You can download the software through here but there are many alternatives to choose from, that one gives you up to 5 people on the network for free, it's not the best, but still a free solution.
I still want more of a java solution to this problem that doesn't require a third-party software.

How to determine if tests are running local or on the remote server

According to this tutorial, I am able to upload files on the website while running my tests locally and on the remote server.
As in the tutorial is:
For those of you doing this locally, all you need to do is use the
sendKeys command to type the local path of the file in any file field.
This works like a charm in all drivers. When moving this test to a
remote server (such as, for example, our Selenium 2 Cloud), all you
need to do is use the setFileDetector method to let WebDriver know
that you’re uploading files from your local computer to a remote
server instead of just typing a path.
on the remote server I have to use:
driver.setFileDetector(new LocalFileDetector());
...
upload.sendKeys("/Path/to/image.jpg");
and local just:
upload.sendKeys("/Path/to/image.jpg");
And this all works fine. Only the problem is, that there is no information how to determine if my tests are running local or on the remote server.
I have tried to determine instance of the webDriver:
WebDriver proxiedWebDriver = ((WebDriverFacade) getDriver()).getProxiedDriver();
if (proxiedWebDriver instanceof RemoteWebDriver) {
((RemoteWebDriver)proxiedWebDriver).setFileDetector(new LocalFileDetector());
}
but it seems like both(local and remote) cases are using RemoteWebDriver while running, because in every case I'm passing if condition.
How can I determine if my tests are running local or remote?
To get the address of the remote server you can use HttpCommandExecutor like this:
HttpCommandExecutor ce = (HttpCommandExecutor) ((RemoteWebDriver)driver).getCommandExecutor();
String remoteAddress = ce.getAddressOfRemoteServer().toString();
String localAddress = null;
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress("google.com", 80));
localAddress = socket.getLocalAddress().getHostAddress();
} catch (IOException e) {
e.printStackTrace();
}
if (remoteAddress.contains("localhost") || remoteAddress.contains(localAddress)) System.out.println("Local machine");
else System.out.println("Remote machine");
The above code gets the Remote Server address (HUB) and compares it with your public IP address. It should give you the information if you are running local or remote server

takeLeadership() method of LeaderSelectorListener not being called

I have a standalone zookeeper server running.
client = CuratorFrameworkFactory.newClient(zkHostPorts, retryPolicy);
client.start();
assertThat(client.checkExists().forPath("/")).isNotNull(); // working
listener = new LeaderSelectorListenerAdapter() {
#Override
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println("This method is never called! :( ");
Thread.sleep(5000);
}
};
String path = "/somepath";
leaderSelector = new LeaderSelector(client, path, listener);
leaderSelector.autoRequeue();
leaderSelector.start();
I am connecting to the server successfully, defining a listener and starting leader election.
Note: There is only 1 client.
But my client app is never taking leadership. I am not able to figure out what I am doing wrong. Also this is a trivial single client scenario. Shouldn't the client already be a leader
EDIT:
It works if I use TestingServer from curator-test library instead of starting my Zookeeper server, like below -
TestingServer server = new TestingServer();
client = CuratorFrameworkFactory.newClient(server.getConnectString(), retryPolicy);
...
Does this mean there is something wrong with my zookeeper server.
This is my zoo.cfg -
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper/ex1
clientPort=2181
Also, the server appears to be working fine as I am able to connect to it using cli and am able to create/delete zNodes.

Java DatagramChannel - Cannot ping port from external network

I am writing a server which uses a DatagramChannel (non-blocking) to send/receive data. It works flawlessly on my local network, but if I try to connect to it using the client application from a different network (i.e. over the internet), I cannot reach it.
Whilst running, if I use http://ping.eu/port-chk/ to check the port, it says it's closed. I have forwarded the appropriate ports and adjusted firewalls to appropriate levels.
My code is as follows:
public void runServer(int portNo)
{
try
{
serverChannel = DatagramChannel.open();
ipAddress = InetAddress.getLocalHost();
//ipAddress = InetAddress.getByName(getPublicIP());
//serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); //Added in to try to fix
serverChannel.socket().bind(new InetSocketAddress(portNo));
serverChannel.configureBlocking(false);
serverChannel.socket().setReceiveBufferSize(receiveBufferSize);
serverChannel.socket().setSendBufferSize(sendBufferSize);
//serverChannel.connect(new InetSocketAddress(ipAddress,portNo)); //Added in to try to fix
serverRunning = true;
}
catch(IOException e)
{
e.printStackTrace();
}
}
The parts which are commented out have had no effect. The ipAddress variable in the example will fetch the local IP, where as the commented-out version will get the public IP of the computer.
If you could help me find out why I cannot connect to this port over the internet, I would be very grateful.
You haven't forwarded the ports correctly. Nothing to do with the code.
As #EJP has suggested, there doesn't seem to be anything wrong with my code. I have since hosted this server application using Amazon EC2, and it has worked flawlessly.
There is an issue with the firmware of my router which is preventing port forwarding.

Categories