java RMI-service中的常见事务逻辑?

我们有几个遗留的Java服务与RMI-api,由旧的JRMP方法实现,需要’rmic’预编译.

作为将一切迁移到最新JDK的一部分,
我还试图将RMI内容重写为更新的方法,其中实现类从UnicastRemoteObject扩展,从而摆脱了rmic预编译步骤.

按照一个简单的例子,就像这里:
    https://www.mkyong.com/java/java-rmi-hello-world-example/
但我无法通过提交/回滚事务逻辑找到这样的示例.

在当前的遗留代码中,所有事务逻辑都在JRMP容器代码中的单个公共方法invokeObject()中处理,该方法将拦截一个地方的所有RMI api调用,
如果RMI调用成功则将提交,或者如果抛出异常则返回.

在没有JRMP容器存在的新方法中,我无法弄清楚如何做到这一点.
显然,我不想将提交/回滚逻辑编码到每个api方法中(它们有很多种),
但仍然保持统一的逻辑在一个地方.

任何建议,提示,参考等,如何在一个点上拦截所有RMI调用来实现事务逻辑?

解决方法:

首先,我同意@ df778899他提供的解决方案是一个强大的解决方案.虽然如果您不想使用spring框架并且想要进一步挖掘它,我会给您一个替代选择.

拦截器提供强大而灵活的设计,包括调用监视,日志记录和消息路由.从某种意义上说,您正在寻找的关键值是一种RMI级别的AOP(面向方面​​编程)支持

一般来说:

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

尽管如此

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.

下面的解决方案通过一个示例实现来演示,该实现支持使用RMI透明传递事务上下文数据,例如事务ID(xid).该解决方案包含以下三个重要组件:

  > RMI远程接口命名功能封装和拦截器插件
  >服务上下文传播机制和服务器端接口支持
  >服务上下文数据结构和事务上下文传播支持

java RMI-service中的常见事务逻辑?

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

命名功能封装

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

java RMI-service中的常见事务逻辑?

RMI调用拦截器

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

java RMI-service中的常见事务逻辑?

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 ... 
   }
}

要完成ServiceManager类,需要实现一个简单的接口代理缓存:

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
         }
      }
   }
}

您可以在以下指南here中找到更多详细信息.如果您希望从头开始实现上述所有这些,与Spring-AOP Framework强大的解决方案相比,这一点非常重要.此外,我认为您会发现Spring非常有趣用于实现RmiClientInterceptor的框架纯源代码.再看看here.

上一篇:java – JUnit和RMI应用程序的最佳实践,RMI Registry


下一篇:Java RMI 服务远程方法调用漏洞加固