I have set up a client/server project using Java RMI. Below I show parts of the implementation. I
launch the server using
ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/C", "start /B java -jar myServer.jar --start);
this.myProcess = processBuilder.start();
I have added a main method to the server which handles the command line call.
The server starts and runs perfectly. The client is able to connect and perform as I expect it to.
My problems arise when I try to kill the server. This needs to be done from outside. The object which
previously started the process is not available anymore. To actually stop the server, the main method
of the server class calls a stop method (see below). This method now kills the RMI but it does not
the least stop the JVM from running. The process is still available and needs to be killed from the
task manager (on Windows).
Do I miss some fact in my implementation that yields this behavior. Why does the process not stop running?
public class MyServer {
// ...
public void startServer() throws RemoteException {
// the RMI registry
Registry registry;
try {
// find the registry
registry = LocateRegistry.createRegistry(portRMI);
} catch (Exception e) {
// if the registry does not yet exist, try to create a new registry
// entry or use an existing one
registry = LocateRegistry.getRegistry(MyConstants.HOST_NAME, portRMI);
}
// create the object
servantObject = new MyServant();
// bind the object to the server
registry.rebind(MyConstants.SERVER_NAME, servantObject);
}
public void stopServer() throws RemoteException {
try {
// access the service
Registry rmiRegistry = LocateRegistry.getRegistry(MyConstants.HOST_NAME, portRMI);
MyService myService = (MyService) rmiRegistry.lookup(MyConstants.SERVER_NAME);
rmiRegistry.unbind(MyConstants.SERVER_NAME);
// get rid of the service object
UnicastRemoteObject.unexportObject(myService, true);
// get rid of the rmi registry
UnicastRemoteObject.unexportObject(rmiRegistry, true);
} catch (NoSuchObjectException e) {
// ...
} catch (NotBoundException e) {
// ...
}
}
public static void main(String[] args) {
// handle arguments and call either startServer or stopServer
}
}
public class MyServant extends UnicastRemoteObject implements MyService {
// ...
}
public interface MyService extends Remote {
// ...
}
You should add a self writtem RMIService interface available in your RMIServer so that a program that wishes to stop the running server instructs it to stop.
Your app that tries to stop the server just unbinds some object, as it is not the running rmi server process itself it will not have a big effect.
if your rmi sever is process a, you should write an app (using rmi) running as process b to send a message to process a to stop it.
Related
I'm currently developing a Vaadin-based program in Java which extracts documents from Domino databases and writes them to a MongoDB collection. The program works perfectly but has one small flaw:
Currently i've found no way to stop the program other then send "KILL" to the process. My shutdown hook/signal handler is totally ignored. I've narrowed down the problem to a single line of code:
NotesThread.sinitThread();
When i remove this line, the hook works perfectly and my program is shutdown properly. When the line is inserted, then the hook is never called.
Here is some example code:
private boolean running = true;
...
#Override
public void run() {
try {
NotesGC.runWithAutoGC(() -> {
NotesThread.sinitThread() // --> "Kills" all signal handling
Session session = NotesFactory.createSession();
while (running) {
Thread.sleep(1000);
System.out.println("Running ...");
}
session.recycle();
return null;
});
} catch (Exception e) {
} finally {
NotesThread.stermThread();
}
}
public void kill() {
System.out.println("Killed!");
this.running = false;
}
...
Signal.handle(new Signal("TERM"), sig -> runner.kill()); // Signal handler from main-method
I've asks friends and colleagues and nobody ever had the same problem.
NotesGC.runWithAutoGC
As I see you are using Domino JNA, a side project.
It use the domino CAPI.
It's open source: https://github.com/klehmann/domino-jna
You can create issue ticket or ask question.
BTW, func "runWithAutoGC" call initThread() in his body.
This link for source code: https://github.com/klehmann/domino-jna/blob/master/domino-jna/src/main/java/com/mindoo/domino/jna/gc/NotesGC.java
In one package I have two different classes Client.java and Server.java
I want to make this package jar, i mean executable.
First I want the Server class to run and after 2-3 seconds I want Client method to run. Is it possible?
Thank you
You have to leave only one main method and run your server and client in separate threads from it.
To do it, take a look at Runnable interface. Your server class and client class should implement it. Then you have to move the logic, used to start server and client to it's run() method.
class Server implements Runnable {
#Override
public void run() {
//your server starting logic here
}
}
class Client implements Runnable {
#Override
public void run() {
//your client starting logic here
}
}
After that, you can modify your main method, to start server and client, like:
public static void main(String args[]) throws InterruptedException {
Server server = new Server();
Client client = new Client();
Thread tServer = new Thread(server);
tServer.start();
//here you can wait some time to Server started
Thread tClient = new Thread(client);
tClient.start();
}
I have the following server class that export himself for JRMP and IIOP:
public class FooServer implements RemoteInt{
FooServer(){
UnicastRemoteObject.exportObject(this);
PortableRemoteObject.exportObject(this);
}
public boolean remoteMethod() throws RemoteException{
// some stuff
return false;
}
}
and the following setup class which creates the server and register it to a running register:
public class Setup{
public static void main(String[] args){
RemoteInt serv = new FooServer();
Naming.rebind("//localhost/server", this);
}
}
The problem is that when Setup finishes its job, it waits for the FooServer to terminate. Instead I would like to exit from Setup class, leaving the FooServer running.
How can I do?
You can't. The remote object is exported from the current JVM and it keeps it running until you unexport it. Note that the main() method does exit, but the RMI/JRMP and RMi/IIOP listening threads are still running so the JVM doesn't exit.
How do I keep an RMI server running? It currently, just binds and object, then exits..
public class WutServer {
public static void main(String[] args) throws RemoteException {
Registry registry = LocateRegistry.createRegistry(1099);
try {
registry.bind("WutManager", new WutManager());
System.out.println("Ready..");
} catch (Exception e) {
e.printStackTrace();
}
}
}
I am simply running this class. I didn't run rmic or anything..
How do I force it to stay running?
Try this:
Remote stub = UnicastRemoteObject.exportObject(new WutManager(), 0);
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("WutManager", stub);
Note: WutManager should implement java.rmi.Remote.
Your server is being DGC'd and then GC'd, which causes it to be unexported, which eventually causes the JVM to exit if it has nothing else to do. To stop that, if you are creating the Registry via LocateRegistry.createRegistry(), keep the return value of that method in a static variable. Otherwise keep a static reference to your server object.
This is an old question, but here is a new answer.
On OSX, using latest Java 9.0.4 I find that the program exits. If I use latest Java 1.8.0.162 then it does not exit and the server remains running.
You need to make WutServer implement the interface that clients will access it by, which in turn should inherit from the marker interface Remote. You also probably want to make the WutServer class inherit from UnicastRemoteObject; while there are other ways to build the remoting support, inheriting from UnicastRemoteObject is definitely the easiest way to get something going.
Try this instead (though you should probably separate the remote interface into another file and have it be redistributed separately):
public class WutServer extends UnicastRemoteObject implements WutServer.Wut {
interface Wut extends Remote {
String wut() throws RemoteException;
}
// Because of the exception...
public WutServer() throws RemoteException {}
public String wut() { return "wut"; }
public static void main(String[] args) throws RemoteException {
LocateRegistry.createRegistry(1099).rebind("WutManager",new WutServer());
System.out.println("Ready...");
}
}
Create an object and call wait of the object at the end of the main function. That is;
public static void main(String[] args) throws RemoteException {
Registry registry = LocateRegistry.createRegistry(1099);
//your object to wait
Object lockObject=new Object();
try {
registry.bind("WutManager", new WutManager());
System.out.println("Ready..");
//here makes your rmi server non-stop
synchronized(lockObject){
lockObject.wait();
}
}catch (Exception e) {
e.printStackTrace();
}
}
I'm using Beanshell as an embedded debugging tool in my app. It means I can telnet to my app and poke around with its internals while it is running (I typically wrap the telnet session with rlwrap).
The problem is that the only way I've found to print to the Beanshell console, rather than stdout of the application itself, is the print() method within Beanshell.
But I'd like to write code in Java that I can call from Beanshell, which will output to the Beanshell console - ie. it will be shown in my telnet session, not sent to stdout of the application, as happens if you try to use System.out or System.err.
Is this possible?
edit: To further clarify, I'm setting up a Beanshell server as follows:
public static void setUpBeanshell() {
try {
i.setShowResults(true);
i.eval(new InputStreamReader(Bsh.class.getResourceAsStream("init.bsh")));
i.eval("server(" + Main.globalConfig.beanShellPort + ");");
} catch (final EvalError e) {
Main.log.error("Error generated while starting BeanShell server", e);
}
}
How would I modify this such that I can write a Java function that outputs to the telnet session (rather than to System.out of my application)
I'll copy it there as it seems that comments are disregarded this days.
you can:
instead of having a method which print debug information to the standard output returns that debug information:
class ClientList {
Integer clients = 0;
public String debugClientList() {
return clients.toString();
}
and then calling it from beanshell
print(clients.debugCientList());
will give you an output on your telnet
or if you need it more logger like, you need to interact with the Interpreter object directly
InterpreterSingleton {
public static final void Console console = new Interpreter();
}
....
class ClientList {
Integer clients = 0;
public void addClient(Client c) {
....
InterpreterSingleton.console.print("Client added, clients now are " + clients);
}
I'm replying there to the comment as it will need some more coding; the telnet implementation uses a different interpreter for each connection, so you have to expose that interpreter to the objects for printing to the telnet client. The quickest way is to change some bit in the default telnet server and use the modified one to start your server, instead of using the server() scripted command (it's under lgpl or sun license terms)
note that this way have an interpreter started for each connection; the easy and quick fix is to maintain a list of all the running interpreters and print to each one the debugging information, so:
class InterpreterSingletonList {
public static final void Set<Interpreter> is = new HashSet();
void printToAll(String s) {
for (Interpreter i: is) {
i.print(s);
}
}
}
package bsh.util;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;
import bsh.*;
/**
BeanShell remote session server.
Starts instances of bsh for client connections.
Note: the sessiond effectively maps all connections to the same interpreter
(shared namespace).
*/
public class Sessiond extends Thread
{
private ServerSocket ss;
NameSpace globalNameSpace;
public Sessiond(NameSpace globalNameSpace, int port) throws IOException
{
ss = new ServerSocket(port);
this.globalNameSpace = globalNameSpace;
}
public void run()
{
try
{
while(true)
new SessiondConnection(globalNameSpace, ss.accept()).start();
}
catch(IOException e) { System.out.println(e); }
}
}
class SessiondConnection extends Thread
{
NameSpace globalNameSpace;
Socket client;
SessiondConnection(NameSpace globalNameSpace, Socket client)
{
this.client = client;
this.globalNameSpace = globalNameSpace;
}
public void run()
{
try
{
InputStream in = client.getInputStream();
PrintStream out = new PrintStream(client.getOutputStream());
/* this is the one you're looking for */
Interpreter i = new Interpreter(
new InputStreamReader(in), out, out, true, globalNameSpace);
i.setExitOnEOF( false ); // don't exit interp
/*store the interpreter on the list*/
InterpreterSingletonList.is.add(i);
i.run();
/*remove it (i.run() blocks)*/
InterpreterSingletonList.is.remove(i);
}
catch(IOException e) { System.out.println(e); }
}
}
I think it's not possible with out hack..., sorry, adapting the telnet server implementation of BSH.
The class we're looking at is bsh.util.Sessiond. Once started, it opens and maintains a telnet server. When ever it receives a command, it creates a new worker thread, this on creates a new bsh.Interpreter with the correct input and output streams (derived from the socket) and runs the interpreter.
So it makes sense, that only output of interpreted commands is send to the telnet client, because System.out and System.err are not redirected.
But that exactly is what has to be done in your case: redirect System.out and System.err to the sockets output stream before the interpreter runs the command and reset the streams after completion.
I'd suggest, you copy the bsh.util.Sessiond class to something like mybsh.util.DebuggerSessiond, apply the redirection code to the run method of the inner class SessiondConnection and modify bsh/commands/server.bsh to start this 'new' telnet server in addition (or instead of the original one). (I guess, this script starts the servers...)
Source code can be found here: beanshell repository
If all your application's output is written using some logging framework anyways, you could write a custom appender/handler which besides logging to say a file would write to the beanshell console in addition? Possibly enabling and disabling the console-logging after executing some beanshell command.
(I wasn't aware of beanshell before, but it seems useful!)