1
zj
2025-06-23 dc9bd22833255bc602dd42c7f603ecb50842ab35
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package kernel.rmi;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.rmi.Naming;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
 
import javax.servlet.ServletContext;
 
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.WebApplicationContext;
 
import kernel.web.ApplicationUtil;
import kernel.web.RmiProxyFactoryBean;
 
/**
 * @author JORGE
 * @description RMI远程调用处理器;
 * SPRING应用上下文中的每一个代理BEAN实例对应一个处理器实例
 */
@SuppressWarnings("unchecked")
public class RmiInvocationHandler implements InvocationHandler{
    /**
     * 当前代理的远程接口
     */
    private RmiFacade rmiFacade;
    
    /**
     * 远端Bean名称
     */
    private String remoteBeanName;
    
    /**
     * 远端接口字段类型
     */
    private String interfaceClass;
    
    /**
     * 当前类全名
     */
    private String currentClassName;
    
    /**
     * 字段注解配置的远程上下文路径
     */
    private String remoteContextPath;
    
    /**
     * 当前SERVLET上下文路径
     */
    private String currentContextPath;
    
    /**
     * 常量提示(未找到配置的SERVLET上下文路径)
     */
    private static final String NOT_FOUND_CONTEXT="RMI-Client: Not Found Configed Context Path: %s";
    
    /**
     * 常量提示(未实现远程接口)
     */
    private static final String NOT_IMPLEMENTED_INTERFACE="RMI-Client: Not Implemented Remote Interface: %s";
    
    /**
     * 常量提示(未找到当前的SERVLET上下文路径)
     */
    private static final String CONTEXT_PATH_NOT_EXISTS="RMI-Client: Not Found Current Context Path,Not Web Application Or Root Web Application?";
    
    public RmiInvocationHandler(String interfaceClass) throws Exception{
        this(null,interfaceClass,null);
    }
    
    public RmiInvocationHandler(String proxyBeanName,String interfaceClass) throws Exception{
        this(proxyBeanName,interfaceClass,null);
    }
    
    public RmiInvocationHandler(String remoteBeanName,String interfaceClass,String remoteContextPath) throws Exception{
        this.remoteBeanName=remoteBeanName;
        this.interfaceClass=interfaceClass;
        this.remoteContextPath=remoteContextPath;
        this.currentClassName=this.getClass().getName();
        
        String contextPath=ApplicationUtil.getContextPath();
        if(null==contextPath) {
            throw new RuntimeException(CONTEXT_PATH_NOT_EXISTS);
        }else {
            this.currentContextPath=contextPath;
        }
    }
    
    @Override
    public Object invoke(Object proxy, Method localMethod, Object[] localMethodArgs) throws Throwable {
        if(ReflectionUtils.isObjectMethod(localMethod)) {
            return localMethod.invoke(this, localMethodArgs);
        }
        
        if(null!=rmiFacade) {
            RmiEntity rmiEntity=rmiFacade.invoke(interfaceClass, remoteBeanName, localMethod.getName(), new RmiEntity(localMethod,localMethodArgs));
            return rmiEntity.getResult(localMethod);
        }
        
        boolean remoteContextPathEmpty=remoteContextPath.isEmpty();
        
        if(!remoteContextPathEmpty && !RmiProxyFactoryBean.servletContextMaps.containsKey(remoteContextPath)) {
            throw new RuntimeException(String.format(Locale.ENGLISH,NOT_FOUND_CONTEXT,remoteContextPath));
        }
        
        if(remoteContextPathEmpty) {
            ServletContext remoteServletContext=null;
            for(Map.Entry<String, ServletContext> scEntry:RmiProxyFactoryBean.servletContextMaps.entrySet()) {
                String remotePath=scEntry.getKey();
                if(currentContextPath.equals(remotePath)) continue;
                if(null==(remoteServletContext=scEntry.getValue())) continue;
                if(hasRemoteBean(remoteServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE))) {
                    this.remoteContextPath=remotePath;
                    break;
                }
            }
        }
        
        if(remoteContextPath.isEmpty()) {
            throw new RuntimeException(String.format(Locale.ENGLISH,NOT_IMPLEMENTED_INTERFACE,interfaceClass));
        }
        
        String requestURL=String.format(Locale.ENGLISH,RmiProxyFactoryBean.baseURL,RmiProxyFactoryBean.rmiPortMap.get(remoteContextPath));
        RmiEntity rmiEntity=(this.rmiFacade=(RmiFacade)Naming.lookup(requestURL)).invoke(interfaceClass, remoteBeanName, localMethod.getName(), new RmiEntity(localMethod,localMethodArgs));
        
        return rmiEntity.getResult(localMethod);
    }
    
    /**
     * 判断参数上下文中是否存在当前RMI接口的远程BEAN实现
     * @param remoteApplicationContext 远程应用上下文
     * @return 是否存在远程BEAN实现
     * @throws Exception
     */
    private final boolean hasRemoteBean(Object remoteApplicationContext) throws Exception{
        if(null==remoteApplicationContext) return false;
        
        Class<?> remoteAppContextClass=remoteApplicationContext.getClass();
        Method getBeansOfTypeMethod=ReflectionUtils.findMethod(remoteAppContextClass,"getBeansOfType",Class.class);
        Map<String,Object> beanMap=(Map<String,Object>)getBeansOfTypeMethod.invoke(remoteApplicationContext,remoteAppContextClass.getClassLoader().loadClass(interfaceClass));
        
        if(null==beanMap || beanMap.isEmpty()) return false;
        
        Set<Object> targetBeanSet=beanMap.values().stream()
                .filter(targetBean->{
                    if(null==targetBean) return false;
                    String beanInfo=targetBean.toString();
                    int index=beanInfo.indexOf("@");
                    if(-1==index) return false;
                    return !currentClassName.equals(beanInfo.substring(0,index));
                    })
                .collect(Collectors.toSet());
        
        return !targetBeanSet.isEmpty();
    }
}