JAVA RMI Observer Slider
How To Make A Slider With RMI Server
First of all, let’s take a look at the RMI structure.
RMI is can be used to run a server and to connect several clients to the given server. It can be loosely coupled with the design patterns to optimise scalability.
The thing is, for this architecture to work well, you should loosely couple all classes.
I have used MVC design pattern to design this project. So the client will have a Controller to get the slider position and a JFrame to view.
The controller will have two methods getValue and setValue to deal with slider value of clients. For RMI structure, controller should be implementing am interface to implement these methods
- public int getValue();
- public voidsetValue(int value);
So this will be the standalone structure of the client-side. So now we can add RMI Architecture to this
How to add RMI Observers
Observer design pattern can be used in any application with Observer and Observable classes in java.util package. But these cannot be used with RMI. So we have to create Observers with RMI Architecture.
Let’s see how to use RMI with observers. First, let’s create the RemoteObserver class the Here Remote class is found in java.rmi package.
REMOTEOBSERVER
package rmi; import java.rmi.Remote; import java.rmi.RemoteException; public interface RemoteObserver extends Remote { public void update(RemoteObservable o, Object arg) throws RemoteException; }
REMOTEOBSERVERIMPL
public class RemoteObserverImpl extends UnicastRemoteObject implements RemoteObserver { private RemoteObserver ob; public RemoteObserverImpl(JFrame o) throws ClassCastException, RemoteException { this.ob = (RemoteObserver) o; } @Override public void update(RemoteObservable o, Object arg) throws RemoteException { ob.update(o, arg); } }
REMOTEOBSERVERIMPL
Note: Here we have used a bit different way to use observers. As swings cannot be casted for Remote, it cannot be used with RMI. So we use RemoteObserver implemented class . Observable class should be coded with extending UnicastRemoteObject in java.rmi. And also it must be implementing an interface throwing RemoteException. Therefore we will have to code a RemoteObservable class
Note: Here RemoteObservable is an interface extending Remote. Its code is below.
import java.rmi.Remote; import java.rmi.RemoteException; public interface RemoteObservable extends Remote { public void addObserver (RemoteObserver o) throws RemoteException; public void deleteObserver(RemoteObserver o)throws RemoteException; public void notifyObservers() throws RemoteException; public void notifyObservers(Object arg) throws RemoteException; public boolean hasChanged()throws RemoteException; public int countObservers()throws RemoteException; public void deleteObservers() throws RemoteException; public void setChanged() throws RemoteException; }
Note: Below is the RemoteObservableImpl class that implements and does the job.
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.Observer; public class RemoteObservableImpl extends UnicastRemoteObject implements RemoteObservable{ private boolean changed = false; private ArrayList obs; public RemoteObservableImpl() throws RemoteException { obs = new ArrayList (); } @Override public synchronized void addObserver(RemoteObserver o) throws RemoteException { if (o == null) throw new NullPointerException (); if (!obs.contains(o)) { obs.add(o); System.out.println("Observer Added size "+obs.size()); } } @Override public synchronized void deleteObserver(RemoteObserver o) throws RemoteException { obs.remove(o); } @Override public void notifyObservers() throws RemoteException { notifyObservers(null); } @Override public void notifyObservers (Object arg) throws RemoteException { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); System.out.println ("obs arr size"+obs.size()); clearChanged(); } System.out.println(""+arg+" arrSize "+ arrLocal.length ); for (int i = arrLocal.length-1; i>=0; i--){ RemoteObservable impl=this; ((RemoteObserver)arrLocal[i]).update (impl, arg); } } @Override public synchronized void deleteObservers() throws RemoteException { obs.clear(); } @Override public synchronized void setChanged() throws RemoteException { changed = true; } protected synchronized void clearChanged() { changed = false; } @Override public synchronized boolean hasChanged() throws RemoteException { return changed; } @Override public synchronized int countObservers() throws RemoteException{ return obs.size(); } }
This is basically the original Remote Observer just edited to be Remote.
Let’s see how to make use of them in our project.
What we have to do is making our controller class extending RemoteObservableImpl overriding methods that we want to use to invoke observers.
package controller; import java.rmi.RemoteException; import rmi.RemoteObservableImpl; import rmi.RemoteObserver; public class Controller extends RemoteObservableImpl { private int value; public Controller() throws RemoteException { } public int getValue() throws RemoteException{ return value; } public void setValue (int value) throws RemoteException{ this.value = value; } @Override public synchronized void setChanged() throws RemoteException{ super.setChanged(); } @Override public void notifyObservers () throws RemoteException{ super.notifyObservers(); } @Override public void notifyObservers (Object arg) throws RemoteException{ super.notifyObservers(arg); } @Override public synchronized void addObserver(RemoteObserver o) throws RemoteException{ super.addObserver(o); } }
Now make a RemoteController that have UnicastRemoteObject. We can do this by keeping an object in a RemoteControllerImpl class.
The classes are as follows
REMOTECONTROLLER
What we do here is make an interface to cast RemoteContollerImpl class from the registry. Don’t forget to throw RemoteException unless it will cause a runtime error.
import java.rmi.Remote; import java.rmi.RemoteException; import rmi.RemoteObserver; public interface RemoteController extends Remote{ public int getValue() throws RemoteException; public void setValue(int value) throws RemoteException; public void setChanged() throws RemoteException; public void notifyObservers() throws RemoteException; public void notifyObservers(Object arg) throws RemoteException; public void addObserver (RemoteObserver o) throws RemoteException; }
REMOTECONTROLLERIMPL
package rmi.controller; import controller.RemoteController; import controller.Controller; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import rmi.RemoteObserver; public class RemoteControllerImpl extends UnicastRemoteObject implements RemoteController{ private Controller controller; public RemoteControllerImpl() throws RemoteException { controller=new Controller(); } @Override public int getValue() throws RemoteException{ return controller.getValue(); } @Override public void setValue (int value) throws RemoteException{ controller.setValue(value); } @Override public void setChanged() throws RemoteException{ controller.setChanged(); } @Override public void notifyObservers () throws RemoteException{ controller.notifyObservers(); } @Override public void notifyObservers (Object arg) throws RemoteException{ controller.notifyObservers(arg); } @Override public void addObserver (RemoteObserver o) throws RemoteException{ controller.addObserver(o); } }
REMOTEFACTORY
import controller.RemoteController; import java.rmi.Remote; import java.rmi.RemoteException; public interface RemoteFactory extends Remote{ public RemoteController getController() throws RemoteException; }
REMOTEFACTORYIMPL
import controller.RemoteController; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import rmi.controller.RemoteControllerImpl; public class RemoteFactoryImpl extends UnicastRemoteObject implements RemoteFactory { private RemoteController c; public RemoteFactoryImpl() throws RemoteException { c=new RemoteControllerImpl(); } @Override public RemoteController getController() throws RemoteException{ return c; } }
Right. Now we have to make a server to bind this factory object to the Registry. Here I’m going to use just a main method to start the server.
package rmi.server; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.logging.Level; import java.util.logging.Logger; import rmi.factory.RemoteFactory; import rmi.factory.RemoteFactoryImpl; public class RemoteServer { public static void startServer(){ try { RemoteFactory rf=new RemoteFactoryImpl (); Registry createRegistry = LocateRegistry.createRe gistry(5050); createRegistry.rebind("server", rf); System.out.println("server started.... "); } catch (RemoteException ex) { Logger.getLogger (RemoteServer.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) { startServer(); } }
The Server is over.
Now let’s make the slider frame. Use NetBeans to make this easily. I just wanted to show how it is done.
package view; import java.awt.BorderLayout; import java.rmi.RemoteException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFrame; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import rmi.RemoteObservable; import rmi.RemoteObserver; import rmi.RemoteObserverImpl; import rmi.connector.RemoteConnector; import rmi.controller.RemoteController; public class Main extends JFrame implements RemoteObserver{ private RemoteController controller; private JSlider sldMain; public Main() { setSize(400, 100); setLayout(new BorderLayout()); sldMain=new JSlider(0,100); setDefaultCloseOperation(EXIT_ON_CLOSE); try { controller=(RemoteController) RemoteConnector.getRf ().getController(); controller.addObserver(new RemoteObserverImpl (this)); } catch (RemoteException ex) { Logger.getLogger(Slider.class.getName()).log (Level.SEVERE, null, ex); } this.add("North", sldMain); sldMain.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { try { if(controller! = null){ int value=sldMain.getValue(); controller.setValue(value); controller.setChanged(); controller.notifyObservers("changed"); } } catch (RemoteException ex) { Logger.getLo gger(Slider.class.getName()).log(Level.SEVERE, null, ex); } } }); } @Override public void update(RemoteObservable o, Object arg) throws RemoteException { try { int value = controller.getValue(); sldMain.setValue(value); } catch (RemoteException ex) { Logger.getLogger (Main.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) { new Main().setVisible (true); } }
Now it’s over. Just make sure you run the projects correctly. To work this properly you have to make a Common Project with all classes needed for both Server and the Client.
Start the server.
Start any number of clients.
Move the slider and see how others change the position.
All you must remember when using RMI is,
- Always throw RemoteException in interfaces and override them correctly.
- Always use the reference type of the interface to bind to the registry.
- Objects should be cast to the interface for objects that are looked up in the registry.
That’s all I think. This is just an example how to use RMI Observers. Can be a crap. But develop this for server-client architecture or for a chat system.