How to control netty server from spring-shell - java

I try to create a console with "spring shell", I would like to run my own netty socket server command. I have a command:
#CliCommand(value = "server", help = "Start socket server")
public String server(
#CliOption(key = {"port", "p"}, mandatory = true, help = "Port number [49152-65535]") final String port,
#CliOption(key = {"maxclients", "mc"}, mandatory = false, help = "Max clients", unspecifiedDefaultValue = "50") final String maxClients
)
{
NettyServer nettyServer = new NettyServer(Integer.parseInt(port), Integer.parseInt(maxClients));
nettyServer.run();
return "::CliCommand return::";
}
While the server is running, it blocks the console and can not enter other commands. Should not you have a solution? For example, how to let it run alone and only influence the server by the command. For example, I would like to send messages from my console to clients.

I can't answer for the whole architecture of your application, but your server object should not be created inside your command method.
It should be created prior to that (or at the very least, it should not be a local parameter of your command method that dies when your command ends) and acted upon by commands, using a different thread I assume.

Related

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 RMI call slow first time

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

How do I communicate with a specific process in one Erlang node?

I have an Erlang server which is spawning a new process for each client that connects. Then the Pid of this new process is passed to the client (to make a connection to the new process.) Is that enough to make a connection from a jinterface client?
I am using this to connect from the client first:
final String SERVERNAME = "server";
final String SERVERNODE = "bertil#computer";
mbox.send(SERVERNAME, SERVERNODE, connectClient);
And those names is set in the server when it starts:
start() ->
net_kernel:start([bertil, shortnames]),
register(server, self()).
Do I have to register a new name for each spawned process? That would not be so dynamic... How do I solve this? Should I use the main process at the server as a router to send all traffic through?
Once you have a pid, you should be able to send a message directly to it. In Erlang you don't have to specify a node if you got a pid. You only need a node if you are sending to a registered name since names are only unique per nod. Pids are unique in the whole cluster.
If you have a varable my_pid as an OtpErlangPid object you can send like so:
mbox.send(my_pid, message);
See the documentation for the send function and chapter 1.6 Sending and Receiving Messages in the Jinterface User's Guide.

Java WS only responds with tcp/ip monitor turned on

created a very basic WS. i have LoggingHandler, Test, TestImpl, & TestPublisher. my publisher creates my endpoint below:
import javax.xml.ws.Endpoint;
public class TestPublisher {
public static void main(String[] args) {
LoggingHandler handle = new LoggingHandler();
Endpoint ep = Endpoint.publish( "http://localhost:8060/message/hello",
new TestImpl() );
ep.getBindings().getHandlerChain().add( new LoggingHandler() );
System.out.print( handle.toString() );
}
}
my Test interface uses #WebService & #WebMethod and only does "String getHello( String str );"
my TestImpl implements Test and uses #WebService(endpointInterface="my package location") and only has a method "public String getHelloWorld( String str ){ return "Hello " + str; }
my handler has handleMessage, handleFault, & close. i can put this code up too but i don't think that would help.
when i head over to localhost:port/message/hello on the same (dev) RH6 pc, it works great! i get "Hello my name". i even can get a wsdl file at localhost:port/message/hello?wsdl
when i try accessing from any other pc, linux or windows, i get a timeout or "cannot display the webpage" error. so i setup in tcp/ip monitor in eclipse to monitor and route traffic from port 8061 (new port) to my WS port [here][link3] without changing any code.
results: i still can't access 8060 from any other pc except the one running the service (dev). so i point my browser to on the other pc's to port 8061 (tcp/ip monitor) and it successfully returns my message! do i need to run a monitor to forward to my java webservice?
on my RH6 box, i am not running a firewall or a web server & i am on my work network. i can successfully start tomcat6 and get to 8080 from the other pc's without using tcp/ip monitor.
Try Endpoint.publish with the ip address of your machine,
ex: Endpoint.publish("http://192.168.0.1:8060/message/hello")

Access running java program from shell command

I am looking for a way to access running java program from command line. The best woud be something that does the following:
Starting java app:
bash$java -jar MyBundle.jar App
Accessing app:
bash$test.sh param1 param2
So, test.sh calls App from MyBundle.jar somehow and passes values param1 & param2.
Important: I am looking for very fast approach. App hold database connection and it is very expensive to start App every time I need access do DB.
I need solution that will work in Ubuntu and Debian. If it will work on Mac - great.
Any help is appreciated!
I think you need to take a client-server approach. You app is the server, it runs as a background process and listens for connections on some port. And your client makes requests to the server and gets back the response.
A fast and simple way of implementing this in java would be to wrap your app in the Jetty servlet container. You could set it up to return JSON responses for example, which are easy to process.
It would be quite straightforward to open a TCP/IP socket and use netcat from the shell.
Java code
final ServerSocket serverSocket = new ServerSocket(9050);
while (true) {
final Socket socket = serverSocket.accept();
java.util.logging.Logger.getAnonymousLogger().info("Accepted");
final BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
final String input = br.readLine();
final BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
bw.write("You said [" + input + "]");
bw.flush();
socket.close();
}
Shell code
echo 'bla' | nc localhost 9050
You'd need to muck around with threads to keep the sockets open to serve multiple requests.

Categories