Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common transaction-logic in java RMI-service?

We have several legacy java-services with RMI-api, implemented by the old JRMP approach requiring 'rmic' pre-compilation.

As part of migrating everything to latest JDK, I am also trying to rewrite the RMI stuff to the more current approach, where the implementation-classes extend from UnicastRemoteObject, thus getting rid of the rmic pre-compilation step.

Following a simple example, like here: https://www.mkyong.com/java/java-rmi-hello-world-example/ but I have not been able to find such example with commit/rollback transaction-logic.

In the current legacy-code, all transaction-logic is handled in a single, common method invokeObject() in the JRMP container-code that will intercept all RMI api-calls in one place, and will simply commit if the RMI-call is successful, or rollback if an Exception was thrown.

I haven't been able to figure out how to do this in the new approach with no JRMP container present. Obviously, I do not want to code commit/rollback-logic into every single api-method (there are many dozens of them), but still keep that uniform logic in one place.

Any advice, hints, references, etc, how to intercept all RMI-calls in a single point to implement the transaction-logic?

like image 414
Rop Avatar asked Aug 14 '18 08:08

Rop


People also ask

What is Java RMI service?

Java Remote Method Invocation (Java RMI) enables the programmer to create distributed Java technology-based to Java technology-based applications, in which the methods of remote Java objects can be invoked from other Java virtual machines, possibly on different hosts.

Does RMI use TCP?

RMI is built atop TCP/IP.

What is RMI in Java with example?

The RMI (Remote Method Invocation) is an API that provides a mechanism to create distributed application in java. The RMI allows an object to invoke methods on an object running in another JVM. The RMI provides remote communication between the applications using two objects stub and skeleton.


1 Answers

To begin with, I agree with @df778899 the solution he provides is a robust solution. Although,I will give you an alternative choice if you don't want to work with spring framework and you want to dig it further.

Interceptors provide a powerful and flexible design include invocation monitoring, logging, and message routing. In some sense, the key value you are looking is a kind of RMI-level AOP (aspect-oriented programming) support

Generally speaking:

it is not fair to ask RMI to directly support such capabilities as it is only a basic remote method invocation primitive, while CORBA ORB is at a layer close to what the J2EE EJB (Enterprise JavaBean) container offers. In the CORBA specification, service context is directly supported at the IIOP level (GIOP, or General Inter-Orb Protocol) and integrated with the ORB runtime. However, for RMI/IIOP, it is not easy for applications to utilize the underlying IIOP service-context support, even when the protocol layer does have such support. At the same time, such support is not available when RMI/JRMP (Java Remote Method Protocol) is used. As a result, for RMI-based distributed applications that do not use, or do not have to use, an ORB or EJB container environment, the lack of such capabilities limits the available design choices, especially when existing applications must be extended to support new infrastructure-level functions. Modifying existing RMI interfaces often proves undesirable due to the dependencies between components and the huge impact to client-side applications. The observation of this RMI limitation leads to the generic solution that I describe in this article

Despite all the above

The solution is based on Java reflection techniques and some common methods for implementing interceptors. More importantly, it defines an architecture that can be easily integrated into any RMI-based distributed application design.

The solution below is demonstrated through an example implementation that supports the transparent passing of transaction-context data, such as a transaction ID (xid), with RMI. The solution contains the following three important components:

  • RMI remote interface naming-function encapsulation and interceptor plug-in
  • Service-context propagation mechanism and server-side interface support
  • Service-context data structure and transaction-context propagation support

enter image description here

The implementation assumes that RMI/IIOP is used. However, it by no means implies that this solution is only for RMI/IIOP. In fact, either RMI/JRMP or RMI/IIOP can be used as the underlying RMI environments, or even a hybrid of the two environments if the naming service supports both

Naming-function encapsulation

First we encapsulate the naming function that provides the RMI remote interface lookup, allowing interceptors to be transparently plugged in. Such an encapsulation is always desirable and can always be found in most RMI-based applications. The underlying naming resolution mechanism is not a concern here; it can be anything that supports JNDI (Java Naming and Directory Interface). In this example, to make the code more illustrative, we assume all server-side remote RMI interfaces inherit from a mark remote interface ServiceInterface, which itself inherits from the Java RMI Remote interface. Figure shows the class diagram, which is followed by code snippets that I will describe further

enter image description here

RMI invocation interceptor

To enable the invocation interceptor, the original RMI stub reference acquired from the RMI naming service must be wrapped by a local proxy. To provide a generic implementation, such a proxy is realized using a Java dynamic proxy API. In the runtime, a proxy instance is created; it implements the same ServiceInterface RMI interface as the wrapped stub reference. Any invocation will be delegated to the stub eventually after first being processed by the interceptor. A simple implementation of an RMI interceptor factory follows the class diagram shown in Figure

enter image description here

package rmicontext.interceptor;

public interface ServiceInterfaceInterceptorFactoryInterface {
   ServiceInterface newInterceptor(ServiceInterface serviceStub, Class serviceInterfaceClass) throws Exception;
}

package rmicontext.interceptor;

public class ServiceInterfaceInterceptorFactory
   implements ServiceInterfaceInterceptorFactoryInterface {

   public ServiceInterface newInterceptor(ServiceInterface serviceStub, Class serviceInterfaceClass)
      throws Exception {

      ServiceInterface interceptor = (ServiceInterface)
         Proxy.newProxyInstance(serviceInterfaceClass.getClassLoader(),
            new Class[]{serviceInterfaceClass},
            new ServiceContextPropagationInterceptor(serviceStub));   // ClassCastException

      return interceptor;
   }
}

package rmicontext.interceptor;

public class ServiceContextPropagationInterceptor
   implements InvocationHandler {

   /**
    * The delegation stub reference of the original service interface.
    */
   private ServiceInterface serviceStub;

   /**
    * The delegation stub reference of the service interceptor remote interface.
    */
   private ServiceInterceptorRemoteInterface interceptorRemote;

   /**
    * Constructor.
    *
    * @param serviceStub The delegation target RMI reference
    * @throws ClassCastException as a specified uncaught exception
    */
   public ServiceContextPropagationInterceptor(ServiceInterface serviceStub)
      throws ClassCastException {

      this.serviceStub = serviceStub;

      interceptorRemote = (ServiceInterceptorRemoteInterface)
         PortableRemoteObject.narrow(serviceStub, ServiceInterceptorRemoteInterface.class);
   }

   public Object invoke(Object proxy, Method m, Object[] args)
      throws Throwable {
      // Skip it for now ... 
   }
}

To complete the ServiceManager class, a simple interface proxy cache is implemented:

package rmicontext.service;

public class ServiceManager
   implements ServiceManagerInterface {

   /**
    * The interceptor stub reference cache.
    * <br><br>
    * The key is the specific serviceInterface sub-class and the value is the interceptor stub reference.
    */
   private transient HashMap serviceInterfaceInterceptorMap = new HashMap();

   /**
    * Gets a reference to a service interface.
    *
    * @param serviceInterfaceClassName The full class name of the requested interface
    * @return selected service interface
    */
   public ServiceInterface getServiceInterface(String serviceInterfaceClassName) {

      // The actual naming lookup is skipped here.
      ServiceInterface serviceInterface = ...;

      synchronized (serviceInterfaceInterceptorMap) {

         if (serviceInterfaceInterceptorMap.containsKey(serviceInterfaceClassName)) {
            WeakReference ref = (WeakReference) serviceInterfaceInterceptorMap.get(serviceInterfaceClassName);
            if (ref.get() != null) {
               return (ServiceInterface) ref.get();
            }
         }
         try {
            Class serviceInterfaceClass = Class.forName(serviceInterfaceClassName);

            ServiceInterface serviceStub =
               (ServiceInterface) PortableRemoteObject.narrow(serviceInterface, serviceInterfaceClass);

            ServiceInterfaceInterceptorFactoryInterface factory = ServiceInterfaceInterceptorFactory.getInstance();
            ServiceInterface serviceInterceptor =
               factory.newInterceptor(serviceStub, serviceInterfaceClass);

            WeakReference ref = new WeakReference(serviceInterceptor);
            serviceInterfaceInterceptorMap.put(serviceInterfaceClassName, ref);

            return serviceInterceptor;
         } catch (Exception ex) {
            return serviceInterface;   // no interceptor
         }
      }
   }
}

You can find more details in the following guideline here. It is important to understand all this above if you desire to make it from scratch, in contrast with Spring-AOP Framework robust solution.In addition,I think you will find very interesting the Spring Framework plain source code for implementing an RmiClientInterceptor. Take another look here also.

like image 154
Panagiotis Drakatos Avatar answered Oct 17 '22 02:10

Panagiotis Drakatos