Author: foodmike
Date: Mon Mar 19 03:09:21 2012
New Revision: 1302264

URL: http://svn.apache.org/viewvc?rev=1302264&view=rev
Log:
Fix to address VYSPER-18. Modifications to XML parsing/handling in order to 
recognize and act on stream close events. This fix adds a closed flag to the 
XMLElementListener that allows the XMPPDecoder to determine when a stream is 
closed.

Additionally added a check to the XMLTokenizer that allows it to ignore 
whitespace when it is in the START state. This prevents is from buffering 
unnecessary whitespace characters. 

Modified:
    mina/vysper/trunk/   (props changed)
    
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMLElementListener.java
    
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPContentHandler.java
    
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPDecoder.java
    
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/sax/impl/XMLTokenizer.java
    
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/impl/XMPPContentHandlerTestCase.java
    
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/perf/PerfRunner.java
    
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/mina/XmppIoHandlerAdapter.java
    
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/parser/XMLParserUtil.java
    
mina/vysper/trunk/server/extensions/websockets/src/main/java/org/apache/vysper/xmpp/extension/websockets/WebSocketBackedSessionContext.java

Propchange: mina/vysper/trunk/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Mon Mar 19 03:09:21 2012
@@ -2,3 +2,6 @@ target
 .settings
 .classpath
 .project
+lib
+bogus_mina_tls.cert
+spring-config.xml

Modified: 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMLElementListener.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMLElementListener.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMLElementListener.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMLElementListener.java
 Mon Mar 19 03:09:21 2012
@@ -23,4 +23,6 @@ import org.apache.vysper.xml.fragment.XM
 
 public interface XMLElementListener {
     void element(XMLElement element);
+    void close();
+    boolean isClosed();
 }
\ No newline at end of file

Modified: 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPContentHandler.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPContentHandler.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPContentHandler.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPContentHandler.java
 Mon Mar 19 03:09:21 2012
@@ -83,8 +83,7 @@ public class XMPPContentHandler implemen
             // complete stanza, emit
             emit();
         } else if (depth == 0) {
-            // end stream:stream element
-            // TODO handle
+            // End of stream. Handled in endDocument().
         } else {
             builder.endInnerElement();
         }
@@ -137,9 +136,10 @@ public class XMPPContentHandler implemen
     }
 
     /**
-     * {@inheritDoc}
+     * End of the XMPP stream. Tell the XML listener we're done.
      */
-    public void endDocument() throws SAXException { /* ignore */
+    public void endDocument() throws SAXException { 
+        this.listener.close();
     }
 
     /**

Modified: 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPDecoder.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPDecoder.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPDecoder.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/decoder/XMPPDecoder.java
 Mon Mar 19 03:09:21 2012
@@ -34,7 +34,7 @@ import org.apache.vysper.xml.sax.impl.De
  * @author The Apache MINA Project ([email protected])
  */
 public class XMPPDecoder extends CumulativeProtocolDecoder {
-
+    
     private static final String XML_DECL = "<?xml";
 
     private static final String STREAM_STREAM = "<stream:stream";
@@ -53,6 +53,7 @@ public class XMPPDecoder extends Cumulat
 
     public static class MinaStanzaListener implements XMLElementListener {
         private ProtocolDecoderOutput protocolDecoder;
+        private boolean closed = false;
 
         public MinaStanzaListener(ProtocolDecoderOutput protocolDecoder) {
             this.protocolDecoder = protocolDecoder;
@@ -65,6 +66,14 @@ public class XMPPDecoder extends Cumulat
 
             protocolDecoder.write(element);
         }
+        
+        public void close() {
+            this.closed = true;
+        }
+        
+        public boolean isClosed() {
+            return this.closed;
+        }
     }
 
     /**
@@ -90,11 +99,17 @@ public class XMPPDecoder extends Cumulat
         }
 
         XMPPContentHandler contentHandler = (XMPPContentHandler) 
reader.getContentHandler();
-        contentHandler.setListener(new MinaStanzaListener(out));
+        XMLElementListener listener = new MinaStanzaListener(out);
+        contentHandler.setListener(listener);
 
         reader.parse(in, CharsetUtil.UTF8_DECODER);
-
-        // we have parsed what we got, invoke again when more data is available
-        return false;
+        
+        if (listener.isClosed()) {
+            session.close(true);
+            return true;
+        } else {
+            // we have parsed what we got, invoke again when more data is 
available
+            return false;
+        }
     }
 }
\ No newline at end of file

Modified: 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/sax/impl/XMLTokenizer.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/sax/impl/XMLTokenizer.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/sax/impl/XMLTokenizer.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/main/java/org/apache/vysper/xml/sax/impl/XMLTokenizer.java
 Mon Mar 19 03:09:21 2012
@@ -55,7 +55,6 @@ public class XMLTokenizer {
     /**
      * @param byteBuffer
      * @param charsetDecoder
-     * @return the new particle or NULL, if the buffer was exhausted before 
the particle was completed
      * @throws Exception
      */
     public void parse(IoBuffer byteBuffer, CharsetDecoder decoder) throws 
SAXException {
@@ -66,6 +65,8 @@ public class XMLTokenizer {
                 if (c == '<') {
                     emit(c, byteBuffer);
                     state = State.IN_TAG;
+                } else if (Character.isWhitespace(c)) {
+                    // ignore
                 } else {
                     state = State.IN_TEXT;
                     buffer.put((byte) c);

Modified: 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/impl/XMPPContentHandlerTestCase.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/impl/XMPPContentHandlerTestCase.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/impl/XMPPContentHandlerTestCase.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/impl/XMPPContentHandlerTestCase.java
 Mon Mar 19 03:09:21 2012
@@ -39,10 +39,14 @@ public class XMPPContentHandlerTestCase 
 
     private static class TestListener implements XMLElementListener {
         public List<XMLElement> elements = new ArrayList<XMLElement>();
+        private boolean closed = false;
 
         public void element(XMLElement element) {
             elements.add(element);
         }
+        
+        public void close() { closed = true; }
+        public boolean isClosed() { return closed; }
     }
 
     public void test() throws Exception {
@@ -63,6 +67,7 @@ public class XMPPContentHandlerTestCase 
         assertEquals("stanza", actual.next().getName());
         assertEquals("message", actual.next().getName());
         assertEquals("iq", actual.next().getName());
+        assertTrue(listener.isClosed());
     }
 
     private void parse(NonBlockingXMLReader reader, String xml) throws 
Exception {

Modified: 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/perf/PerfRunner.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/perf/PerfRunner.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/perf/PerfRunner.java
 (original)
+++ 
mina/vysper/trunk/nbxml/src/test/java/org/apache/vysper/xml/sax/perf/PerfRunner.java
 Mon Mar 19 03:09:21 2012
@@ -39,7 +39,9 @@ public class PerfRunner {
         public void element(XMLElement element) {
             counter++;
         }
-
+        
+        public void close() {}
+        public boolean isClosed() { return false; }
     }
 
     private static final String SINGLE_LEVEL_XML = "<child att='foo' 
att2='bar'></child>";

Modified: 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/mina/XmppIoHandlerAdapter.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/mina/XmppIoHandlerAdapter.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/mina/XmppIoHandlerAdapter.java
 (original)
+++ 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/mina/XmppIoHandlerAdapter.java
 Mon Mar 19 03:09:21 2012
@@ -31,6 +31,7 @@ import org.apache.vysper.xmpp.protocol.S
 import org.apache.vysper.xmpp.protocol.StreamErrorCondition;
 import org.apache.vysper.xmpp.server.ServerRuntimeContext;
 import org.apache.vysper.xmpp.server.SessionContext;
+import org.apache.vysper.xmpp.server.SessionContext.SessionTerminationCause;
 import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
 import org.apache.vysper.xmpp.stanza.Stanza;
 import org.slf4j.Logger;
@@ -47,6 +48,8 @@ public class XmppIoHandlerAdapter implem
 
     public static final String ATTRIBUTE_VYSPER_SESSIONSTATEHOLDER = 
"vysperSessionStateHolder";
 
+    public static final String ATTRIBUTE_VYSPER_TERMINATE_REASON = 
"vysperTerminateReason";
+    
     final Logger logger = LoggerFactory.getLogger(XmppIoHandlerAdapter.class);
 
     private ServerRuntimeContext serverRuntimeContext;
@@ -106,6 +109,7 @@ public class XmppIoHandlerAdapter implem
         SessionContext sessionContext = new 
MinaBackedSessionContext(serverRuntimeContext, stateHolder, ioSession);
         ioSession.setAttribute(ATTRIBUTE_VYSPER_SESSION, sessionContext);
         ioSession.setAttribute(ATTRIBUTE_VYSPER_SESSIONSTATEHOLDER, 
stateHolder);
+        ioSession.setAttribute(ATTRIBUTE_VYSPER_TERMINATE_REASON, 
SessionTerminationCause.CLIENT_BYEBYE);
     }
 
     public void sessionOpened(IoSession ioSession) throws Exception {
@@ -114,10 +118,11 @@ public class XmppIoHandlerAdapter implem
 
     public void sessionClosed(IoSession ioSession) throws Exception {
         SessionContext sessionContext = extractSession(ioSession);
+        SessionTerminationCause cause = (SessionTerminationCause) 
ioSession.getAttribute(ATTRIBUTE_VYSPER_TERMINATE_REASON);
         String sessionId = "UNKNOWN";
         if (sessionContext != null) {
             sessionId = sessionContext.getSessionId();
-            
sessionContext.endSession(SessionContext.SessionTerminationCause.CONNECTION_ABORT);
+            sessionContext.endSession(cause);
         }
         logger.info("session {} has been closed", sessionId);
     }
@@ -129,12 +134,17 @@ public class XmppIoHandlerAdapter implem
 
     public void exceptionCaught(IoSession ioSession, Throwable throwable) 
throws Exception {
         SessionContext sessionContext = extractSession(ioSession);
-
+        
+        // Assume that the connection was aborted for now. Might be different 
depending on 
+        // cause of exception determined below.
+        ioSession.setAttribute(ATTRIBUTE_VYSPER_TERMINATE_REASON, 
SessionTerminationCause.CONNECTION_ABORT);
+        
         Stanza errorStanza;
         if(throwable.getCause() != null && throwable.getCause() instanceof 
SAXParseException) {
             logger.info("Client sent not well-formed XML, closing session", 
throwable);
             errorStanza = 
ServerErrorResponses.getStreamError(StreamErrorCondition.XML_NOT_WELL_FORMED,
                     sessionContext.getXMLLang(), "Stanza not well-formed", 
null);
+            ioSession.setAttribute(ATTRIBUTE_VYSPER_TERMINATE_REASON, 
SessionTerminationCause.STREAM_ERROR);
         } else if(throwable instanceof WriteToClosedSessionException) {
             // ignore
             return;
@@ -145,8 +155,8 @@ public class XmppIoHandlerAdapter implem
             logger.warn("error caught on transportation layer", throwable);
             errorStanza = 
ServerErrorResponses.getStreamError(StreamErrorCondition.UNDEFINED_CONDITION,
                     sessionContext.getXMLLang(), "Unknown error", null);
+            ioSession.setAttribute(ATTRIBUTE_VYSPER_TERMINATE_REASON, 
SessionTerminationCause.STREAM_ERROR);
         }
         sessionContext.getResponseWriter().write(errorStanza);
-        
sessionContext.endSession(SessionContext.SessionTerminationCause.STREAM_ERROR);
     }
 }

Modified: 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/parser/XMLParserUtil.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/parser/XMLParserUtil.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/parser/XMLParserUtil.java
 (original)
+++ 
mina/vysper/trunk/server/core/src/main/java/org/apache/vysper/xmpp/parser/XMLParserUtil.java
 Mon Mar 19 03:09:21 2012
@@ -41,7 +41,7 @@ public class XMLParserUtil {
 
     /**
      * Parses a complete XML document. If the XML string contains errors, a
-     * {@link SAXException} will be trown. If the XML string is not a complete
+     * {@link SAXException} will be thrown. If the XML string is not a complete
      * XML document, null will be returned
      * @param xml A string with a complete XML document
      * @return
@@ -60,6 +60,8 @@ public class XMLParserUtil {
             public void element(XMLElement element) {
                 documents.add(element);
             }
+            public void close() {}
+            public boolean isClosed() { return false; }
         });
 
         IoBuffer buffer = IoBuffer.wrap(xml.getBytes("UTF-8"));

Modified: 
mina/vysper/trunk/server/extensions/websockets/src/main/java/org/apache/vysper/xmpp/extension/websockets/WebSocketBackedSessionContext.java
URL: 
http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/websockets/src/main/java/org/apache/vysper/xmpp/extension/websockets/WebSocketBackedSessionContext.java?rev=1302264&r1=1302263&r2=1302264&view=diff
==============================================================================
--- 
mina/vysper/trunk/server/extensions/websockets/src/main/java/org/apache/vysper/xmpp/extension/websockets/WebSocketBackedSessionContext.java
 (original)
+++ 
mina/vysper/trunk/server/extensions/websockets/src/main/java/org/apache/vysper/xmpp/extension/websockets/WebSocketBackedSessionContext.java
 Mon Mar 19 03:09:21 2012
@@ -60,6 +60,7 @@ public class WebSocketBackedSessionConte
 
     private Connection outbound;
     private NonBlockingXMLReader xmlReader = new DefaultNonBlockingXMLReader();
+    private boolean closed = false;
 
     public WebSocketBackedSessionContext(ServerRuntimeContext 
serverRuntimeContext) {
         super(serverRuntimeContext, new SessionStateHolder());
@@ -174,7 +175,13 @@ public class WebSocketBackedSessionConte
      */
     public void close() {
         // TODO how to handle?
+       closed = true;
+    }
+    
+    /**
+     * Implementation for XMLElementListener.
+     */
+    public boolean isClosed() {
+       return closed;
     }
-
-
 }


Reply via email to