![]() |
|
|
|
|
1
5th October 11:44
External User
Posts: 1
|
The open source project (LGPL license) Mandala (http://mandala.sf.net) provides
a new model to deal with both concurrent and distributed programming. Moreover, invocation paradigm to asynchronous (and potentially remote) method invocation. Whereas threads are a good entities for concurrent programming in imperative languages (such as C), I believe they are not good ones (and especially Java threads) for concurrent programming in object oriented languages. For example, in Java, Thread requires a run() method to be defined which represents the thread behavior. Invoking this method produce the same result -- from a Java newbie point a view -- as invoking start(). Is is hard to understand that a Java Thread instance is not the *real* thread but only a (bad?) representation of it. Many problems with Java Threads have already been mentioned in the literature (stop(), suspend()/resume(), scheduling and priorities which are OS dependent (no more green threads since the JDK 1.3)). Last but not least, implementing concurrency using threads leads to a direct dependency between the asynchronism functionality and the actual implementation of it. For example, consider the following server code: ServerSocket ss = new ServerSocket(); while(true) { Socket socket = serverSocket.accept(); new Thread() { public void run() { serve(socket); } }.start(); } The problem is that the asynchronism is tied to the implementation, here, one thread per call. For performance reasons, it might be better to use a threads pool... Another example, suppose you want asynchronous input/output (of course, you may use the new Java IO package -- available since the JDK 1.4 -- for this purpose , but this example serves my speech): The "natural" synchronous code is: InputStream is = ...; OutputStream os = ...; byte[] b = ...; int n = ...; while((n = is.read(b)) != -1) { os.write(b, 0, n); } which leads when using threads *naively* to something like: while((n = is.read(b)) != -1) { new Thread() { public void run() { os.write(b, 0, n); } }.start(); } which is erroneous as you may have guessed: the byte buffer 'b' is shared across multiple threads. Whereas a 'synchronized' section solves the problem it leads to really poor performance and breaks the concurrency searched for: writers write while readers read. Asynchronism in Mandala is based on its RAMI (Reflective Asynchronous Method Invocation) package which provides asynchronous method invocations using the asynchronous reference concept. Asynchronous references are the standard Java synchronous references extension to the asynchronous world: an asynchronous reference on an object allows any public method of this object to be invoked asynchronously. Asynchronous proxy are "sugar syntactic" mirror classes of standard classes which provides transparency in asynchronous (and potentially remote, see below) method invocation. An asynchronous proxy uses an asynchronous reference for the actual asynchronous method invocation mechanism implementation. Using asynchronous references in the server example above leads to something like: ServerSocket ss = new ServerSocket(); // Creates an asynchronous proxy on a 'Service' instance // which contains the serve() "business" method jaya.Service service = new jaya.Service(); while(true) { Socket socket = serverSocket.accept(); service.rami_serve(socket); // asynchronous call } The advantages are the following: 1) the asynchronous functionality is customizable (tied to the asynchronous reference) and does not appear in the code; the creator of the asynchronous proxy on the 'Service' instance has the responsibility of the asynchronous semantic attached to its object. Two semantics are defined: - concurrent semantic: asynchronous method invocations may run concurrently on the same object. This semantic has currently two implementations: * a thread per calls implementation, * a threads pool implementation; - single-threaded semantic: asynchronous method invocations are never run concurrently on the same object. This semantic allows non-thread-safe objects to be used asynchronously anyhow. Two implementations is provided: * a FIFO implementation where methods are run in the order they are invoked, * a random implementation where methods are run in a random order. In the example above, the default semantic is used because no semantic are defined. If a threads pool implementation is required, the instantiation must be: AsynchronousSemanticFactory asf = new ThreadPooledASFactory(); jaya.Service = new jaya.Service(asf); 2) Code simplification: no more thread in the code, everything is method invocation (either synchronous or asynchronous). I believe programmers must write their code using only method invocation which extends naturally to remote method invocation as proved by the RMI success. Similarly, concurrent programming may use asynchronous method invocations instead of dealing directly with threads. Using asynchronous references in the asynchronous input/output example above leads to: InputStream is = ...; OutputStream os = ...; // A FIFO semantic is required! AsynchronousSemanticFactory asf = new FifoASfactory(); // Gets an asynchronous proxy on the 'os' instance jaya.java.io.OuputStream ramios = (jaya.java.io.OuputStream) Framework.getSemiTransparentAsynchronousProxy(os, asf); int n = ...; FutureClient future; while(true) { byte[] b = new byte[n]; n = is.read(b); if (n == -1) break; future = ramios.rami_write(b, 0, n); } doSomething(); // During the output... // Using a FIFO semantic guarantees that everything is written // in order. Hence, when the last asynchronous method invocation // has terminated, we are sure everything has been done. future.waitUntilDone(); And, as you see, the problem is solved by using a FIFO asynchronous semantic and by the creation of a new byte buffer for each read()/write(). The use of asynchronous method invocation is quite natural and prevent developers to deal with threads related problems such as scheduling, priorities and canceling (stopping(), interrupting()). For example, canceling an asynchronous operation is not performed same by a thread per call implementation (where you just call interrupt() on the target thread) than by a threads pool implementation call scheme (interrupt() is erroneous!). In Mandala, canceling an asynchronous method invocation is just a matter of invoking the method 'cancel()' on the 'Future' object returned. The actual implementation does all the magic for you. All this asynchronous implementation dependent issues are handled by the asynchronous reference implementation used. Naturally, Mandala extends asynchronous method invocation to asynchronous remote method invocation using its JACOb (Java Active Container of Objects) package which relies on RAMI and on the active container concept. Any objects can become remote and used asynchronously. No more interface to implements (like in RMI or in Corba), no class to extend: only business code in your objects! Hence, your remote objects do not depend on any particular technology. JACOb uses the "active container" concept through the 'ActiveMap' implementation. The concept is very simple. An active container is just a map which provides a call() method to invoke method on its stored objects. Consider the example below: // Use JNDI to get a remote active map reference ActiveMap activeMap = null; String URL = "rmi://host/RMIActiveMap"; // may be LDAP // JNDI part try{ javax.naming.InitialContext context = new javax.naming.InitialContext(); activeMap = (RemoteActiveMap) context.lookup(URL); }catch(NamingException ne) { ... } // A library returns an object I want to use remotely java.math.BigInteger bi = library.foo(); // Get a global unique key Object key = StoredObjectReference.getGlobalUniqueKey(); // Insert 'bi' in the remote active map activeMap.put(key, bi); // Get a stored object reference on it StoredObjectReference sor = StoredObjectReference.getInstance(activeMap, key); // Get an asynchronous proxy on this stored object jaya.java.math.BigInteger i = jaya.java.math.BigInteger.getInstance(sor); // 'i' is now a proxy on the stored object 'bi' in 'activeMap' assert sor == (StoredObjectReference) i.getAsychronousReference(); assert activeMap.containsKey(sor.getKey()); // Our 'BigInteger' is remote whereas it has not be designed for it! assert activeMap instanceof RemoteActiveMap; // Invoke a method remotely and asynchronously FutureClient future = i.isProbablePrime(CERTAINTY); doSomethingElse(); try{ boolean isPrime = future.waitForResult(); }catch(TransportException te) { // Handle if necessary } As you see, 'ActiveMap' extends the 'java.util.Map' interface - nothing to learn! 'RemoteActiveMap' is just an 'ActiveMap' remotely accessible. Then, we can insert an object into the active map (here a 'java.math.BigDecimal' instance returned by a library) using the 'put()' method. And then, the stored object is automatically remotely accessible through the active container it resides in! Asynchronous remote method invocation on stored objects are done using asynchronous proxies of the RAMI package. Note that if you are the creator of the stored object, things are simpler: // Create a stored object transparently // (A new key is automatically created) jaya.java.math.BigInteger i = new jaya.java.math.BigInteger("1234567890", new SORFactory(activeMap)); // Invoke a method remotely and asynchronously FutureClient future = i.isProbablePrime(CERTAINTY); doSomethingElse(); try{ boolean isPrime = future.waitForResult(); }catch(TransportException te) { // Handle if necessary } Features of the Mandala RAMI packages are: - Asynchronous references: extension of standard Java synchronous reference (provides the '==' semantic); - Asynchronous method invocations using future objects and/or callbacks (event-driven programming) mechanism; - Separation of asynchronous semantics and their implementations (concurrent versus single-threaded semantic) Features of the Mandala JACOb packages are: - Based on a formal model: the active container concept; - Simple API: ActiveMap extends Map; - Multi-protocol: currently RMI, TCP, UDP are provided; - Asynchronous remote method invocations using StoredObjectReference which is an asynchronous reference implementation; - Remote exception handling based on global listeners: this allows "only business code in remote method invocation" paradigm or the traditional per-method remote exception handling (RMI-like) to be implemented; Features of the whole Mandala projects: - Designed by interfaces: anyone may provide its own implementation of asynchronous semantic, asynchronous references, ActiveMap, protocol, etc. - Extensive use of design pattern -- maybe too extensive! - Designed with unitary test (JUnit) - Build with the standard Apache-ant build tool - Well documented: Javadoc and PDF (LaTEX generated) User's guide Lacks: - A good web site: I'm not a web designer! Christina -- the web designer shortly enrolled -- will deliver a more complete and nicer web site soon. - A lot of things have to be done (see the TODO list in the root directory). As I'm alone in this project I would like some help (object oriented designer, developers, tester, ...). Furthermore, I'm very interested in software engineering and I would like experiment the distributed extreme programming agile method. Anyone interested? Join! Any remark, comment or suggestion would be greatly appreciated! Thanks. eipi. The Mandala project: http://mandala.sf.net -- No one equals eipi : -1 = e^(i.Pi) |
|
|
|