|
![]() |
A few years ago, Aspect-oriented Programming first defined non-functional properties (security, persistence, transaction...) to produce separation of concerns (for information and more, see AspectJ website). Distribution is considered as a non-functional property, especially in ProActive where distributed mechanisms are transparent. Unfortunately, exceptions related to distribution are often raised from non-functional levels (i.e. network, Java RMI or JVM) and propagated to functional one (application code). But users do not have to deal with exceptions not related to the application. Such exceptions, raised from non-functional properties, are called non-functional exceptions and should not be propagated to the application. The handlers mechanism of ProActive is the result of intensive work presentes in a EHOOS'03 (ECOOP Workshop) article here.
Non-functional exceptions signal abnormal behaviour of non-functional properties (ie. distribution). They are gathered into a hierarchy based on inheritance. This classification is used by the handling mechanism (look forward).
Figure 1 : Hierarchy of Non-Functional Exceptions for Distribution
Distributed exceptions can be found in the following packages :
Handlers are exception managers dedicated to non-functional exceptions. Every handler implements the following Handler interface.
public Interface Handler implements java.io.Serializable { // Is the exception managed by the current handler ? public boolean isHandling(NonFunctionalException e); // Provide a treatment for handled exception(s) public void handle(NonFunctionalException e); }
Then, handlers are gathered into static and dynamic structures providing different levels of handling. The following levels are ordered from lower to higher priority :
The default level avoid intensive use of dynamic levels. It must handle every single non functional exception to provide soundness for distributed applications. This level is also useful to create different strategies according to the version of the distributed middleware (Peer to Peer, Client/Server or Desktop/Mobile applications). Higher levels (VM, body, proxy, future or temporarily code) use dynamic handlers (created at runtime) to improve fault tolerance strategies defined at default level. A standard strategy, common to every distributed application, is defined in default level while more specific strategies are achieved in dynamic levels. Dynamic handlers, modified or exchanged according to the context of environment, offers obviously much more possibilities than the static ones.
The API consists of three static functions, accessible from any point of the distributed application (from functional or non functional code) :
public static void setExceptionHandler(HandlerClass, ExceptionClass, level, HandlerizableObject); public static Handler unsetExceptionHandler(ExceptionClass, level, HandlerizableObject); public static Handler searchExceptionHandler(NonFunctionalException, HandlerizableObject);
setExceptionHandler binds a class of handler with a class of exception in a specific level.
unsetExceptionHandler removes the class of handler associated to the given class of exception.
searchExceptionHandler searches and return the right handler for a non functional exception.
Default handlers are statically created during ProActive initialization. They should not be removed nor overloaded because they are essentiel for the safety of the application built over the library. As described before, dynamic handlers from higher level should be used to improve the overall fault tolerance behaviour. Default level gives a basic and safe exception handling behaviour while VM and higher level specializate it. However, when different versions of ProActive are planed, default level can be specialized (ex : Peer to Peer application, Client/Server or Desktop/Mobile require different basic handling behaviour). This exception to the rule avoid large configuration during runtime.
The following code show the initialization of the mechanism.
// Levels of exception handling static public HashMap defaultLevel = null; static public HashMap VMLevel = null; static public HashMap codeLevel = null; // Static initialization of ProActive static { // Creation of the default level defaultLevel = new HashMap(); // We set default handlers to use at default level // VM and higher level are still set to null as long as they remain empty from any handler setExceptionHandler(HandlerNonFunctionalException.class, NonFunctionalException.class, Handler.ID_Default, null); setExceptionHandler(HandlerCommunicationException.class, CommunicationException.class, Handler.ID_Default, null); // Other initializations follow... }
The next lines associate handlers with active objects. This mobile object is then protected during any migration.
// Creation of an active object RO remote = (RO) org.objectweb.proactive.ProActive.newActive(RO.getClass().getName(), null, remoteNode); // Get the remote body associated to the object (body is a meta-object, look forward MOP documentation) UniversalBody ro_body = ((BodyProxy) ((org.objectweb.proactive.core.mop.StubObject) remote).getProxy()).getBody(); // If the configuration is made by the remote object itself, reference to the body must be obtained with UniversalBody ro_body = ProActive.getBodyOnThis(); // Set exception handler to the body of the remote object // Remote object can now migrate safely ProActive.setExceptionHandler(HandlerMigrationException.class, MigrationException.class, Handler.ID_Body, ro_body); remote.moveTo(destinationNode);
The last thing to know is how to search the right handler for a given exception. Keep in mind that the research starts from higher priority levels.
try { // Reified method call as founded in the core of ProActive sendRequest(methodCall, null); catch (ProActiveException e) { // Create a non functional exception encapsulating the network exception Context context = new ContextOfExecution(); NonFunctionalException nfe = new CommunicationException(e, context); // Research of the right handler for the given exception // Give reference to any active object structure to use active object levels Handler handler = searchExceptionHandler(nfe, null); handler.handle(nfe); }
here are the most important points to customize handlers mechanism :
We made several tests showing that the mechanism remains light for the application. Overall performance are not affected by the configuration of hundred handlers or less. And that number of handlers is huge ! Moreover, configuration is a punctual operation. Mobility is not really affected until the migration of an object with hundred handlers. Both results show that every handlerizable object supports a few dozen of handlers with no other problem than a limitation to hundred handlers.
![]() Figure 2 : Performances of mechanism configuration |
![]() Figure 3 : Performances of mechanism during migration |
Coming soon...