2017-03-07 15 views
0

我想寫一個簡單的服務器接收消息並將它們分發給註冊的監聽器。我發現ListenerList註釋,這似乎是完美的消除樣板代碼。它像魅力一樣工作,所有的方法都是按預期生成的,聽衆被稱爲。唯一的問題是,在一些調用後,fireXXX()方法停止工作,並引發NoSuchMethodErrorGroovy @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) 
+0

已解決https://issues.apache.org/jira/browse/GROOVY-8110 – frigo

回答

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中可以檢查所生成的代碼(腳本菜單 - >檢查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) 
     } 
    } 
} 

我的猜測是,當數組類型被用作fire的參數時,Groovy方法選擇機制中可能存在一個bug XXX方法。它非常適合非數組類型。我建議前往http://groovy-lang.org/mailing-lists.html,並在報告Groovy問題跟蹤器中的錯誤之前直接詢問開發人員。

使用Groovy 2.4.7進行測試。

+0

謝謝!與groovyConsole好的提示。將前往郵件列表並詢問有關人員。 – frigo