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 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 beanMap=(Map)getBeansOfTypeMethod.invoke(remoteApplicationContext,remoteAppContextClass.getClassLoader().loadClass(interfaceClass)); if(null==beanMap || beanMap.isEmpty()) return false; Set 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(); } }