Wednesday, July 15, 2009

Eclipse RCP : Single instance of RCP Application

There are methods of maintaining a single instance of an application. Most often the application creates a lock file and holds on to it during the lifecycle of the application.

A new instance of the application will try to find this file and if it exists, the application will try to show the existing instance to the user, preventing from creating whole new instance of the application. One drawback of this approach will be if the application crashes leaving the lock file. The user has to manually delete the file (or application can try deleting this file )
The same can be achieved via opening a server socket when the application is opened. Now how do we do it for a simple RCP Application.



/*
* (non-Javadoc)
* @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
*/
public Object start(final IApplicationContext context)
{
if (!checkServerPort())
{
try
{
final ServerSocket server = new ServerSocket(5000);

serverThread = new Thread(new Runnable()
{
@Override
public void run()
{
boolean socketClosed = false;
while (!socketClosed)
{
if (server.isClosed())
{
socketClosed = true;
}
else
{
try
{
Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client
.getInputStream()));
new UIJob("Reopening the app...")
{
@Override
public IStatus runInUIThread(IProgressMonitor monitor)
{
advisor.getWindowAdvisor().handleEvent(null);
return null;
}
}.schedule();

/**
* if (SINGLE_INSTANCE_SHARED_KEY.trim().equals(message.trim())) {
* System.out.println("Receiving message"); }
*/
in.close();
client.close();
}
catch (IOException ex)
{
socketClosed = false;
}
}
}
}
});
serverThread.start();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
else
{
try
{
Socket clientSocket = new Socket(InetAddress.getLocalHost(), 5000);
OutputStream out = clientSocket.getOutputStream();
out.write(SINGLE_INSTANCE_SHARED_KEY.getBytes());
out.close();
clientSocket.close();
}
catch (UnknownHostException ex)
{
ex.printStackTrace();
}
catch (IOException ex)
{
ex.printStackTrace();
}
return IApplication.EXIT_OK;
}
final Display display = PlatformUI.createDisplay();
try
{

final int returnCode = PlatformUI.createAndRunWorkbench(display, advisor);
if (returnCode == PlatformUI.RETURN_RESTART)
{
return IApplication.EXIT_RESTART;
}
return IApplication.EXIT_OK;
}
finally
{
display.dispose();
}
}

private boolean checkServerPort()
{
try
{
new Socket("localhost", 5000);
}
catch (IOException ex)
{
return false;
}
return true;
}

/*
* (non-Javadoc)
* @see org.eclipse.equinox.app.IApplication#stop()
*/
public void stop()
{
final IWorkbench workbench = PlatformUI.getWorkbench();
if (workbench == null)
{
return;
}
final Display display = workbench.getDisplay();
display.syncExec(new Runnable()
{
public void run()
{
if (!display.isDisposed())
{
workbench.close();
}
}
});
}



Now when the application is launched for the first time, it will open a server socket 5000 and will start listening on it. The next instance of the application will check the server port and if it is used already, it will create a client for that server socket and send a message to the server.

When the server receives any message from the client, it will reopen the instance which created the server socket.

The handleEvent method on the WorkbenchWindowAdvisor will have to have the following code..


/*
* (non-Javadoc)
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
@Override
public void handleEvent(Event event)
{
Shell workbenchWindowShell = getWindowConfigurer().getWindow().getShell();
workbenchWindowShell.setVisible(true);
workbenchWindowShell.setActive();
workbenchWindowShell.setFocus();
workbenchWindowShell.setMinimized(false);
}


Now enjoy the single instance of the RCP application. This approach can be used for any java application.

2 comments:

Mário Marinato said...

On our company, we use the JUnique library (http://www.sauronsoftware.it/projects/junique/index.php).

But it's nice to have another solution at hand. Thanks for the tip.

SaiRaghava K said...

what is the advisor type in the post

and also SINGLE_INSTANCE_SHARED_KEY ??