Java Groovy@ListenerList生成的fireXXX()方法停止工作
我正在尝试编写一个简单的服务器,它接收消息并将它们分发给注册的侦听器。我找到了注释,它似乎完美地消除了样板代码。它就像一个符咒,所有的方法都按预期生成,侦听器被调用。唯一的问题是在一些调用之后,Java Groovy@ListenerList生成的fireXXX()方法停止工作,java,groovy,Java,Groovy,我正在尝试编写一个简单的服务器,它接收消息并将它们分发给注册的侦听器。我找到了注释,它似乎完美地消除了样板代码。它就像一个符咒,所有的方法都按预期生成,侦听器被调用。唯一的问题是在一些调用之后,fireXXX()方法停止工作,并抛出一个NoSuchMethodError stacktrace的GeneratedMethodAccessor部分让我怀疑JVM优化了某些东西,这是问题的原因,但据我所知,这就是问题的原因。我是Groovy的新手,所以我也不太熟悉引擎盖下发生的事情 有人能告诉我问题出在
fireXXX()
方法停止工作,并抛出一个NoSuchMethodError
stacktrace的GeneratedMethodAccessor
部分让我怀疑JVM优化了某些东西,这是问题的原因,但据我所知,这就是问题的原因。我是Groovy的新手,所以我也不太熟悉引擎盖下发生的事情
有人能告诉我问题出在哪里吗
侦听器接口
package connector
interface MessageListener {
void messageReceived(byte[] mess)
}
TCPConnector类
package connector
import groovy.beans.ListenerList
import groovy.util.logging.Slf4j
import java.nio.ByteBuffer
import java.nio.channels.AsynchronousCloseException
import java.nio.channels.ServerSocketChannel
import java.nio.channels.SocketChannel
import java.util.concurrent.*
@Slf4j
class TCPConnector {
@ListenerList(name="StatusListener")
List<TCPConnectorStatusListener> listeners
String host
Integer port
BlockingQueue<byte[]> inQueue = new ArrayBlockingQueue<>(10)
BlockingQueue<byte[]> outQueue = new ArrayBlockingQueue<>(10)
ExecutorService senderExecutor
Future sender
ExecutorService receiverExecutor
Future receiver
ServerSocketChannel ssc
TCPConnector(String host, Integer port) {
this.host = host
this.port = port
}
def start() {
ssc = ServerSocketChannel.open()
def address = new InetSocketAddress(host, port)
ssc.bind(address)
log.info("Accepting on $host:$port - $address")
try {
SocketChannel sc = ssc.accept()
log.info("Accepted connection from $sc")
startThreads(sc)
fireConnectionUp()
} catch (AsynchronousCloseException ace) {
log.info("ace - shutdown ${ace.getMessage()}")
}
}
private void startThreads(sc) {
def senderThread = {
log.info "Poller start"
try {
while (sc.isConnected()) {
def taken = outQueue.take()
log.info "poller ${taken}"
def wr = ByteBuffer.allocate(taken.size())
wr.put(taken)
wr.flip()
sc.write(wr)
}
} catch (InterruptedException ie) {
log.info("interrupt $ie")
throw ie
} catch (Exception ex) {
log.error(ex.getMessage())
stop()
throw ex
}
} as Runnable
senderExecutor = Executors.newSingleThreadExecutor({ Runnable r -> new Thread(r, "Sender-$sc") } as ThreadFactory)
sender = senderExecutor.submit(senderThread)
def receiverThread = {
try {
ByteBuffer receiver = ByteBuffer.allocate(16384)
int rec
while ((rec = sc.read(receiver)) != -1) {
log.info "received $rec $sc"
receiver.flip()
byte[] readed = new byte[receiver.remaining()]
receiver.get(readed)
inQueue.offer(readed)
receiver.clear()
}
log.info("minusone")
stop()
} catch (Exception ex) {
log.error(ex.getMessage())
throw ex
} finally {
try {
log.info("finally stop")
stop()
} catch (Exception ex) {
log.error("ex while stop ${ex.getMessage()}")
throw ex
}
}
}
receiverExecutor = Executors.newSingleThreadExecutor(
{ Runnable r -> new Thread(r, "Receiver-$sc") } as ThreadFactory)
receiver = receiverExecutor.submit(receiverThread)
}
def stop() {
if (sender != null) {
sender.cancel(true)
receiver.cancel(true)
senderExecutor.shutdown()
receiverExecutor.shutdown()
fireConnectionDown()
}
ssc.close()
log.info("stopped")
}
static void main(String[] args) {
new TCPConnector("192.168.0.1", 1234)
}
}
package connector
import groovy.beans.ListenerList
import groovy.util.logging.Slf4j
@Slf4j
class Connector implements TCPConnectorStatusListener {
@ListenerList(name="MessageListener")
List<MessageListener> listenerList
TCPConnector tcpconn
Thread ConnThread
Connector(String host, Integer port) {
tcpconn = new TCPConnector(host, port)
tcpconn.addStatusListener((TCPConnectorStatusListener) this)
}
def start() {
tcpconn.start()
}
@Override
void connectionUp() {
log.info("Connection up")
def recvMessage = {
try {
while (!Thread.currentThread().isInterrupted()) {
def mess = tcpconn.inQueue.take()
log.info(" $mess")
try {
printAllMethods(this)
this.fireMessageReceived(mess)
} catch (NoSuchMethodError nsme) {
log.error("WHY????", nsme)
nsme.printStackTrace()
}
}
} catch (Exception ex) {
log.error(ex.getMessage())
throw ex
}
}
ConnThread = new Thread(recvMessage, "Conn-${tcpconn.host}:${tcpconn.port}")
ConnThread.setUncaughtExceptionHandler({t, e ->
log.error(e.getMessage())
e.printStackTrace()
})
ConnThread.start()
}
@Override
void connectionDown() {
log.info("Connection down")
ConnThread.interrupt()
}
static void printAllMethods( obj ){
if( !obj ){
println( "Object is null\r\n" );
return;
}
if( !obj.metaClass && obj.getClass() ){
printAllMethods( obj.getClass() );
return;
}
def str = "class ${obj.getClass().name} functions:\r\n";
obj.metaClass.methods.name.unique().each{
str += it+"("
obj.metaClass.methods.find {m -> it == m.name}.each {mm -> str += mm.parameterTypes}
str += "); "
}
println "${str}\r\n"
}
}
例外情况
12:25:12.544 [Conn-192.168.0.1:5555] ERROR connector.Connector - WHY????
java.lang.NoSuchMethodError: connector.Connector.fireMessageReceived([B)V
at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1218)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at connector.Connector$_connectionUp_closure1.doCall(Connector.groovy:34)
at connector.Connector$_connectionUp_closure1.doCall(Connector.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:408)
at groovy.lang.Closure.run(Closure.java:495)
at java.lang.Thread.run(Thread.java:745)
java.lang.NoSuchMethodError: connector.Connector.fireMessageReceived([B)V
at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1218)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at connector.Connector$_connectionUp_closure1.doCall(Connector.groovy:34)
at connector.Connector$_connectionUp_closure1.doCall(Connector.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:408)
at groovy.lang.Closure.run(Closure.java:495)
at java.lang.Thread.run(Thread.java:745)
我已将示例代码压缩为一个较小的版本
import groovy.beans.ListenerList
interface MessageListener {
void messageReceived(byte[] msg)
}
class MessageProducer {
@ListenerList
List<MessageListener> listeners
void produce(String msg) {
fireMessageReceived(msg.getBytes())
}
}
producer = new MessageProducer()
producer.addMessageListener({ println it } as MessageListener)
producer.produce('Groovy')
您可以在GroovyConsole中运行生成的代码(脚本菜单->检查AST)时检查生成的代码,从而使MessageProducer
类具有以下方法
public void fireMessageReceived([B msg) {
if ( listeners != null) {
java.util.ArrayList<E extends java.lang.Object> __list = new java.util.ArrayList<MessageListener>(listeners)
for (java.lang.Object listener : __list ) {
listener.messageReceived(msg)
}
}
}
public void fireMessageReceived([B msg){
if(侦听器!=null){
java.util.ArrayList\uuu list=新建java.util.ArrayList(侦听器)
for(java.lang.Object侦听器:\列表){
listener.messageReceived(msg)
}
}
}
我的猜测是,当数组类型用作fireXXX方法的参数时,Groovy方法选择机制中可能存在一个bug。它与非数组类型的情况非常好。我建议在Groovy的问题跟踪器中报告bug之前,直接去问开发人员
使用Groovy 2.4.7进行测试。谢谢!groovyConsole的好提示。将转到邮件列表并询问了解者。已在中解决
java.lang.NoSuchMethodError: MessageProducer.fireMessageReceived([B)V
public void fireMessageReceived([B msg) {
if ( listeners != null) {
java.util.ArrayList<E extends java.lang.Object> __list = new java.util.ArrayList<MessageListener>(listeners)
for (java.lang.Object listener : __list ) {
listener.messageReceived(msg)
}
}
}