我正在尝试编写一个简单的服务器,它接收消息并将它们分发给已注册的侦听器。我找到了ListenerList注释,这似乎是完美的消除样板代码。它像一个魅力,所有的方法都按预期生成,听众被称为。唯一的问题是,在某些调用后,fireXXX()
方法停止工作并抛出NoSuchMethodError
。
堆栈跟踪的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"
}
}
printAllMethods()的输出。当fireXXX()工作时和不工作时都是相同的。
class connector.Connector functions:
equals([class java.lang.Object]boolean); getClass([]class java.lang.Class); hashCode([]int); notify([]void); notifyAll([]void); toString([]class java.lang.String); wait([]void); addMessageListener([interface connector.MessageListener]void); connectionDown([]void); connectionUp([]void); fireMessageReceived([class [B]void); getListenerList([]interface java.util.List); getMessageListeners([]class [Lconnector.MessageListener;); getMetaClass([]interface groovy.lang.MetaClass); getProperty([class java.lang.String]class java.lang.Object); getConnThread([]class java.lang.Thread); getTcpconn([]class connector.TCPConnector); invokeMethod([class java.lang.String, class java.lang.Object]class java.lang.Object); printAllMethods([class java.lang.Object]void); removeMessageListener([interface connector.MessageListener]void); setListenerList([interface java.util.List]void); setMetaClass([interface groovy.lang.MetaClass]void); setProperty([class java.lang.String, class java.lang.Object]void); setConnThread([class java.lang.Thread]void); setTcpconn([class connector.TCPConnector]void); start([]class java.lang.Object);
异常
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)
答案 0 :(得分:0)
我已将示例代码浓缩为较小的版本。
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')
运行此代码会重现错误
java.lang.NoSuchMethodError: MessageProducer.fireMessageReceived([B)V
您可以在GroovyConsole(脚本菜单 - &gt; Inspect 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)
}
}
}
我的猜测是,当数组类型用作fireXXX方法的参数时,Groovy方法选择机制中可能存在错误。它非常适用于非数组类型。我建议前往http://groovy-lang.org/mailing-lists.html并在报告Groovy问题跟踪器中的错误之前直接询问开发人员。
使用Groovy 2.4.7进行测试。