Spring - 通过 RMI 传递监听器
Spring - Passing Listener via RMI
基础
我有几个项目,它们实现了一个缓存服务——每个项目都有自己的缓存服务,而且每个项目都几乎与其他项目相同。我已经将现有的缓存服务压缩并缩短为一个项目,其中包含两种不同的缓存服务——一种用于基本对象,一种用于复杂对象(它扩展了基本服务)。我已经创建了 2 个项目 - 一个部署为 .war-file 和 API-project for this.
整个应用程序在 JBoss AS 7.1 "Thunder" 上运行,最新的 JDK 7.
问题
一个 class 特别需要在旧的缓存条目被删除时得到通知。为此,我使用 CleanupListeners 实现了一个通知程序。
CleanupListener.java
public interface CleanupListener extends Remote {
public abstract boolean supports(Class<?> clazz) throws RemoteException;
public abstract void notify(Object removedObject) throws RemoteException;
}
缓存服务中的必要实现
public void registerCleanupListener(CleanupListener listener) {
listeners.add(listener);
}
private void fireCleanupEvent(Object removedObject) {
for (CleanupListener cleanupListener : listeners) {
try {
if (cleanupListener.supports(removedObject.getClass())) {
cleanupListener.notify(removedObject);
}
} catch (RemoteException re) {
LOGGER.error("Remote Listener not available", re);
}
}
}
监听器通过 private final HashSet<CleanupListener>
管理
需要通知的项目使用了这个接口的实现:
@Component
public class CleanupEventListener extends UnicastRemoteObject implements CleanupListener {
protected CleanupEventListener() throws RemoteException {
super();
}
@Resource
private ClassRequiresNotification notifyMe;
public boolean supports(Class<?> clazz) {
return SupportedObject.class.isAssignableFrom(clazz);
}
public void notify(Object removedObject) {
notifyMe.someMethod((SupportedObject) removedObject);
}
}
和一个注册 bean:
@Component
public class CleanupEventListenerRegistrator {
@Resource
private CleanupEventListener cleanupEventListener;
@Resource
private BasicCacheService cacheService;
@PostConstruct
public void init() {
cacheService.registerCleanupListener(cleanupEventListener);
}
}
远程服务通过SpringBean导出:
@Bean
public RmiServiceExporter basicCacheServiceExporter(BasicCacheService basicCacheService) {
RmiServiceExporter cacheService = new RmiServiceExporter();
cacheService.setService(basicCacheService);
cacheService.setServiceName(BASIC_CACHE_SERVICE_NAME);
cacheService.setServiceInterface(some.package.BasicCacheService.class);
return cacheService;
}
并通过 XML-Config:
注册
<bean id="basicCacheService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="lookupStubOnStartup" value="false"/>
<property name="serviceUrl" value="basicCacheService" />
<property name="serviceInterface" value="some.package.BasicCacheService" />
<property name="refreshStubOnConnectFailure" value="true"/>
</bean>
异常
所以,每当我尝试调用预期的方法时,我都会得到两个异常之一:
要么我得到一个 NoSuchObjectException
(罕见),要么更常见的是 ClassNotFoundException
包裹在 UnmarshalException
中
我做了什么
我已经查阅了 JavaDocs,大量搜索并在这里筛选了几个问题,但无济于事。我发现一些信息表明标准 属性 rmi.codebase
设置为 true
,这可能需要重写一些代码,但是我们已经在项目的另一个地方使用 RMI,它在那里工作得很好。添加 SecurityManager 会破坏我们应用程序的其余部分(由于外部先决条件)并调用 NoSuchObjectException
。
到目前为止,Whosebug 中的答案已经产生 "this is a problem with the codebase"、"you need to install a securityManager"、"the classes have to have the same name and be in the same package",当然还有 "it's the codebase"。我提到这个问题两次,因为它确实经常出现,但没有提到如何处理代码库问题。
附加信息
我不确定这是否重要,但这里有更多信息:
cacheservice-project在java代码中配置,使用该服务的项目在XML.
中配置
Listener-Interface 没有通过 Annotations 或 XML-Beans 导出,因为几个帖子(包括至少一个这里)暗示这将导出 class,但只在服务器端继续监听,这不是我想要的。
我还尝试使 CleanupListener
成为一个抽象 class,它本身扩展了 UnicastRemoteObject
,但这也没有成功——事实上,它比我现在拥有的更糟糕。
我正在 eclipse 中编写和测试应用程序。
组装和发布到服务器是通过命令行中的 gradle 完成的。
问题
有谁知道这里的问题是什么,以及如何实际解决它?当我所做的一切要么 运行 成为异常,要么是 "yeah that's a codebase error",这就变得非常令人恼火,因为两者都无济于事。
如有任何帮助,我们将不胜感激。
实际答案
好的,这既令人尴尬又令人宽慰。首先...
好消息
如上所述的侦听器接口有效。它在服务器端注册,并在调用时向客户端发出通知,然后客户端可能会采取相应的行动。所以,如果您一直在寻找整个 "passing a listener" 问题的答案,那么您就有了。
坏消息
然而,我对某些事实一无所知。除其他事项外,术语 codebase 实际涵盖的内容。在我的特殊情况下,问题是缓存服务本身不依赖于其他项目,它应该保留(自定义)类。所以从技术上讲,代码库 实际上 是 问题 - 由于 build.gradle
.
中缺少依赖项
我希望我不会在这个问题上浪费任何人的时间,如果是这样,请接受我的道歉,希望这个问题有时能对其他人有所帮助。
基础
我有几个项目,它们实现了一个缓存服务——每个项目都有自己的缓存服务,而且每个项目都几乎与其他项目相同。我已经将现有的缓存服务压缩并缩短为一个项目,其中包含两种不同的缓存服务——一种用于基本对象,一种用于复杂对象(它扩展了基本服务)。我已经创建了 2 个项目 - 一个部署为 .war-file 和 API-project for this.
整个应用程序在 JBoss AS 7.1 "Thunder" 上运行,最新的 JDK 7.
问题
一个 class 特别需要在旧的缓存条目被删除时得到通知。为此,我使用 CleanupListeners 实现了一个通知程序。
CleanupListener.java
public interface CleanupListener extends Remote {
public abstract boolean supports(Class<?> clazz) throws RemoteException;
public abstract void notify(Object removedObject) throws RemoteException;
}
缓存服务中的必要实现
public void registerCleanupListener(CleanupListener listener) {
listeners.add(listener);
}
private void fireCleanupEvent(Object removedObject) {
for (CleanupListener cleanupListener : listeners) {
try {
if (cleanupListener.supports(removedObject.getClass())) {
cleanupListener.notify(removedObject);
}
} catch (RemoteException re) {
LOGGER.error("Remote Listener not available", re);
}
}
}
监听器通过 private final HashSet<CleanupListener>
管理
需要通知的项目使用了这个接口的实现:
@Component
public class CleanupEventListener extends UnicastRemoteObject implements CleanupListener {
protected CleanupEventListener() throws RemoteException {
super();
}
@Resource
private ClassRequiresNotification notifyMe;
public boolean supports(Class<?> clazz) {
return SupportedObject.class.isAssignableFrom(clazz);
}
public void notify(Object removedObject) {
notifyMe.someMethod((SupportedObject) removedObject);
}
}
和一个注册 bean:
@Component
public class CleanupEventListenerRegistrator {
@Resource
private CleanupEventListener cleanupEventListener;
@Resource
private BasicCacheService cacheService;
@PostConstruct
public void init() {
cacheService.registerCleanupListener(cleanupEventListener);
}
}
远程服务通过SpringBean导出:
@Bean
public RmiServiceExporter basicCacheServiceExporter(BasicCacheService basicCacheService) {
RmiServiceExporter cacheService = new RmiServiceExporter();
cacheService.setService(basicCacheService);
cacheService.setServiceName(BASIC_CACHE_SERVICE_NAME);
cacheService.setServiceInterface(some.package.BasicCacheService.class);
return cacheService;
}
并通过 XML-Config:
注册<bean id="basicCacheService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="lookupStubOnStartup" value="false"/>
<property name="serviceUrl" value="basicCacheService" />
<property name="serviceInterface" value="some.package.BasicCacheService" />
<property name="refreshStubOnConnectFailure" value="true"/>
</bean>
异常
所以,每当我尝试调用预期的方法时,我都会得到两个异常之一:
要么我得到一个 NoSuchObjectException
(罕见),要么更常见的是 ClassNotFoundException
包裹在 UnmarshalException
我做了什么
我已经查阅了 JavaDocs,大量搜索并在这里筛选了几个问题,但无济于事。我发现一些信息表明标准 属性 rmi.codebase
设置为 true
,这可能需要重写一些代码,但是我们已经在项目的另一个地方使用 RMI,它在那里工作得很好。添加 SecurityManager 会破坏我们应用程序的其余部分(由于外部先决条件)并调用 NoSuchObjectException
。
到目前为止,Whosebug 中的答案已经产生 "this is a problem with the codebase"、"you need to install a securityManager"、"the classes have to have the same name and be in the same package",当然还有 "it's the codebase"。我提到这个问题两次,因为它确实经常出现,但没有提到如何处理代码库问题。
附加信息
我不确定这是否重要,但这里有更多信息:
cacheservice-project在java代码中配置,使用该服务的项目在XML.
中配置
Listener-Interface 没有通过 Annotations 或 XML-Beans 导出,因为几个帖子(包括至少一个这里)暗示这将导出 class,但只在服务器端继续监听,这不是我想要的。
我还尝试使 CleanupListener
成为一个抽象 class,它本身扩展了 UnicastRemoteObject
,但这也没有成功——事实上,它比我现在拥有的更糟糕。
我正在 eclipse 中编写和测试应用程序。
组装和发布到服务器是通过命令行中的 gradle 完成的。
问题
有谁知道这里的问题是什么,以及如何实际解决它?当我所做的一切要么 运行 成为异常,要么是 "yeah that's a codebase error",这就变得非常令人恼火,因为两者都无济于事。
如有任何帮助,我们将不胜感激。
实际答案
好的,这既令人尴尬又令人宽慰。首先...
好消息
如上所述的侦听器接口有效。它在服务器端注册,并在调用时向客户端发出通知,然后客户端可能会采取相应的行动。所以,如果您一直在寻找整个 "passing a listener" 问题的答案,那么您就有了。
坏消息
然而,我对某些事实一无所知。除其他事项外,术语 codebase 实际涵盖的内容。在我的特殊情况下,问题是缓存服务本身不依赖于其他项目,它应该保留(自定义)类。所以从技术上讲,代码库 实际上 是 问题 - 由于 build.gradle
.
我希望我不会在这个问题上浪费任何人的时间,如果是这样,请接受我的道歉,希望这个问题有时能对其他人有所帮助。