我想寫一個簡單的服務器接收消息並將它們分發給註冊的監聽器。我發現ListenerList註釋,這似乎是完美的消除樣板代碼。它像魅力一樣工作,所有的方法都是按預期生成的,聽衆被稱爲。唯一的問題是,在一些調用後,fireXXX()
方法停止工作,並引發NoSuchMethodError
。Groovy @ListenerList生成的fireXXX()方法停止工作
GeneratedMethodAccessor
stacktrace的一部分讓我懷疑某些事情被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)
已解決https://issues.apache.org/jira/browse/GROOVY-8110 – frigo