001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2017 Florian Schmaus. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack.debugger; 019 020import java.io.IOException; 021import java.io.Reader; 022import java.io.Writer; 023 024import org.jivesoftware.smack.XMPPConnection; 025import org.jivesoftware.smack.packet.TopLevelStreamElement; 026import org.jivesoftware.smack.util.ObservableReader; 027import org.jivesoftware.smack.util.ObservableWriter; 028 029import org.jxmpp.jid.EntityFullJid; 030import org.jxmpp.xml.splitter.XmlPrettyPrinter; 031import org.jxmpp.xml.splitter.XmppXmlSplitter; 032 033/** 034 * Interface that allows for implementing classes to debug XML traffic. That is a GUI window that 035 * displays XML traffic.<p> 036 * 037 * Every implementation of this interface <b>must</b> have a public constructor with the following 038 * arguments: XMPPConnection, Writer, Reader. 039 * 040 * @author Gaston Dombiak 041 */ 042public abstract class SmackDebugger { 043 044 protected final XMPPConnection connection; 045 046 private XmppXmlSplitter outgoingStreamSplitterForPrettyPrinting; 047 private XmppXmlSplitter incomingStreamSplitterForPrettyPrinting; 048 049 protected SmackDebugger(XMPPConnection connection) { 050 this.connection = connection; 051 } 052 053 /** 054 * Called when a user has logged in to the server. The user could be an anonymous user, this 055 * means that the user would be of the form host/resource instead of the form 056 * user@host/resource. 057 * 058 * @param user the user@host/resource that has just logged in 059 */ 060 // TODO: Should be replaced with a connection listener authenticated(). 061 public abstract void userHasLogged(EntityFullJid user); 062 063 /** 064 * Note that the sequence of characters may be pretty printed. 065 * 066 * @param outgoingCharSequence the outgoing character sequence. 067 */ 068 public abstract void outgoingStreamSink(CharSequence outgoingCharSequence); 069 070 public void onOutgoingElementCompleted() { 071 } 072 073 public abstract void incomingStreamSink(CharSequence incomingCharSequence); 074 075 public void onIncomingElementCompleted() { 076 } 077 078 /** 079 * Returns a new special Reader that wraps the new connection Reader. The connection 080 * has been secured so the connection is using a new reader and writer. The debugger 081 * needs to wrap the new reader and writer to keep being notified of the connection 082 * traffic. 083 * 084 * @param reader connection reader. 085 * @return a new special Reader that wraps the new connection Reader. 086 */ 087 public final Reader newConnectionReader(Reader reader) { 088 XmlPrettyPrinter xmlPrettyPrinter = XmlPrettyPrinter.builder() 089 .setPrettyWriter(sb -> incomingStreamSink(sb)) 090 .build(); 091 incomingStreamSplitterForPrettyPrinting = new XmppXmlSplitter(xmlPrettyPrinter); 092 093 ObservableReader observableReader = new ObservableReader(reader); 094 observableReader.addReaderListener(readString -> { 095 try { 096 incomingStreamSplitterForPrettyPrinting.append(readString); 097 } 098 catch (IOException e) { 099 throw new AssertionError(e); 100 } 101 }); 102 return observableReader; 103 } 104 105 /** 106 * Returns a new special Writer that wraps the new connection Writer. The connection 107 * has been secured so the connection is using a new reader and writer. The debugger 108 * needs to wrap the new reader and writer to keep being notified of the connection 109 * traffic. 110 * 111 * @param writer connection writer. 112 * @return a new special Writer that wraps the new connection Writer. 113 */ 114 public final Writer newConnectionWriter(Writer writer) { 115 XmlPrettyPrinter xmlPrettyPrinter = XmlPrettyPrinter.builder() 116 .setPrettyWriter(sb -> outgoingStreamSink(sb)) 117 .build(); 118 outgoingStreamSplitterForPrettyPrinting = new XmppXmlSplitter(xmlPrettyPrinter); 119 120 ObservableWriter observableWriter = new ObservableWriter(writer); 121 observableWriter.addWriterListener(writtenString -> { 122 try { 123 outgoingStreamSplitterForPrettyPrinting.append(writtenString); 124 } 125 catch (IOException e) { 126 throw new AssertionError(e); 127 } 128 }); 129 return observableWriter; 130 } 131 132 /** 133 * Used by the connection to notify about an incoming top level stream element. 134 * <p> 135 * This method is invoked right after the incoming stream was parsed. 136 * </p> 137 * 138 * @param streamElement the incoming top level stream element. 139 */ 140 public abstract void onIncomingStreamElement(TopLevelStreamElement streamElement); 141 142 /** 143 * Used by the connection to notify about a outgoing top level stream element. 144 * <p> 145 * This method is invoked right before the element is serialized to XML and put into the outgoing stream. 146 * </p> 147 * 148 * @param streamElement the outgoing top level stream element. 149 */ 150 public abstract void onOutgoingStreamElement(TopLevelStreamElement streamElement); 151 152}