We want to implement a business class with a login(user,
passwd)
method which returns true
if the user can log
into the system and false
otherwise.
business.Login
: In this case, using RMI involves many steps:
package business; public interface Login extends java.rmi.Remote { boolean login(String user, String passwd) throws java.remi.RemoteException; }
package business; public class LoginImpl extends java.rmi.server.UnicastRemoteObject implements Login { private java.util.Random random = new java.util.Random(); public boolean login(String user, String passwd) throws java.remi.RemoteException { // Bad logic ?! Thread.sleep(random.nextInt(10000)); return random.nextBoolean(); } }
As you see, their is no logic in this business method, but this
implementation will simulate an access to a database for our example.
Note that if the class does not extend
java.rmi.server.UnicastRemoteObject
the code is still
valid for the compiler since types are correct but will not have the
behavior we want.
try{ business.Login loginService = new business.Login(); // bind(loginService) do the hardwork of binding... bind(loginService); }catch(RemoteException e) { log.error(e, "Can't instantiate the Login service"); System.exit(); }
First, the client must perform a lookup on the registry service the loginService have been binded to:
// lookup() do the hardwork of looking up... business.Login loginService = lookup(bindName); // Use the service: try{ boolean ok = loginService.login(userName, passwd); if (!ok) { displayErrorMessage(...); return; } // continue... }catch(RemoteException re) { // handle the remote exception (hard job !) }Notice how the code is populated with non business code (RMI code).
package business; public class Login { private java.util.Random random = new java.util.Random(); public boolean login(String user, String passwd) { // Bad logic ?! Thread.sleep(random.nextInt(10000)); return random.nextBoolean(); } }
As you see, no more interface to implement, no class to extend, no
exception to throw. Just a standard business class. Mandala allows
the developer to concentrate on its business logic only.
The next step is to insert an instance of our Login
class into a remote ActiveMap
and to bind our service to
a registry in order for clients to lookup our service. We may
instantiate our class locally and then insert it into the active map
(using put()
directly or
SORFactory.getInstance()
indirectly), but in order for
this to be possible, our class needs to be serializable
which is not the case. Note that this is not efficient if you have to
use many stored objects. The other solution is to directly
instantiate our class remotely. This can be done directly using the
Instanciator
class or indirectly using the
SORFactory.newInstance()
method. In fact, a simpler
approach is available, you just invoke a jayac
generated
constructor, and it will do the job for you !
// gets a proxy on a remote active map ActiveMap activeMap = null; try{ activeMap = (mandala.jacob.remote.RemoteActiveMap) new javax.naming.InitialContext().lookup(bindName); }catch(NamingException ne) { logger.error(ne, "Can't find " + bindName); return; } // Instanciate a Login object remotely into the active map jaya.business.Login proxy = new jaya.business.Login(new SORFactory(activeMap)); // bind(proxy) do the hardwork of binding... bind(broxy);
Notice that since Login
just contains business code, it
is not concerned with remote access. So, it must be inserted into a
remote server to become remotely accessible.
Note also that this operation can be performed directly from the
client so there is no need to bind anything if you are the only
client of a given server!
Client uses the service as any other RAMI object:
// lookup() do the hardwork of looking up... jaya.business.Login proxy = lookup(bindName); // Use the service: boolean ok = proxy.login(userName, passwd); if (!ok) { displayErrorMessage(...); return; } // continue...
Notice how the code is no more polluted with non business code. Note
also that you can perform easily an asynchronous remote method
invocation in order to compute something during the
login()
execution:
// lookup() do the hardwork of looking up... jaya.business.Login proxy = lookup(bindName); // Use the service: FutureClient future = proxy.rami_login(userName, passwd); doSomethingElse(); (during the remote execution of login()) boolean ok = false; try{ ok = ((Boolean) future.waitForResult()).booleanValue(); }catch(Throwable t) { logger.warning(t, "Exception during a remote asychnonous call"); ok = false; } if (!ok) { displayErrorMessage(...); return; } // continue...
Suppose you already have a business.Login
class, that you
must use, but you don't have the source code. How would you render this
class remotely accessible
In the RMI world, several steps are necessary using the adapter pattern.
Using Mandala, since objects do not have to be designed specifically to be accessed remotely (and asynchronously), you use exactly the same steps as in the from scratch case. The only difference is that you didn't produce the business code. But as far as the client (the one who use a given class) is concerned, it doesn't matter.