1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $RCSfile$ 3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Revision$ 4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Date$ 5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2003-2007 Jive Software. 7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License. 10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at 11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * http://www.apache.org/licenses/LICENSE-2.0 13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software 15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS, 16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and 18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License. 19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smack; 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Packet; 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException; 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.Writer; 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ArrayBlockingQueue; 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.BlockingQueue; 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Writes packets to a XMPP server. Packets are sent using a dedicated thread. Packet 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * interceptors can be registered to dynamically modify packets before they're actually 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * sent. Packet listeners can be registered to listen for all outgoing packets. 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see Connection#addPacketInterceptor 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see Connection#addPacketSendingListener 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Matt Tucker 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenclass PacketWriter { 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Thread writerThread; 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Thread keepAliveThread; 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Writer writer; 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private XMPPConnection connection; 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private final BlockingQueue<Packet> queue; 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen volatile boolean done; 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Creates a new packet writer with the specified connection. 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param connection the connection. 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected PacketWriter(XMPPConnection connection) { 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.queue = new ArrayBlockingQueue<Packet>(500, true); 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.connection = connection; 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen init(); 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Initializes the writer in order to be used. It is called at the first connection and also 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * is invoked if the connection is disconnected by an error. 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected void init() { 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.writer = connection.writer; 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen done = false; 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writerThread = new Thread() { 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void run() { 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writePackets(this); 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }; 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")"); 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writerThread.setDaemon(true); 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sends the specified packet to the server. 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param packet the packet to send. 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void sendPacket(Packet packet) { 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!done) { 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Invoke interceptors for the new packet that is about to be sent. Interceptors 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // may modify the content of the packet. 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.firePacketInterceptors(packet); 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen queue.put(packet); 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (InterruptedException ie) { 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ie.printStackTrace(); 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen synchronized (queue) { 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen queue.notifyAll(); 97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Process packet writer listeners. Note that we're using the sending 100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // thread so it's expected that listeners are fast. 101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.firePacketSendingListeners(packet); 102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Starts the packet writer thread and opens a connection to the server. The 107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * packet writer will continue writing packets until {@link #shutdown} or an 108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * error occurs. 109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void startup() { 111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writerThread.start(); 112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen void setWriter(Writer writer) { 115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.writer = writer; 116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Shuts down the packet writer. Once this method has been called, no further 120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * packets will be written to the server. 121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void shutdown() { 123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen done = true; 124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen synchronized (queue) { 125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen queue.notifyAll(); 126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Interrupt the keep alive thread if one was created 128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (keepAliveThread != null) 129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen keepAliveThread.interrupt(); 130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Cleans up all resources used by the packet writer. 134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen void cleanup() { 136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.interceptors.clear(); 137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendListeners.clear(); 138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the next available packet from the queue for writing. 142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the next packet for writing. 144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Packet nextPacket() { 146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Packet packet = null; 147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Wait until there's a packet or we're done. 148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (!done && (packet = queue.poll()) == null) { 149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen synchronized (queue) { 151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen queue.wait(); 152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (InterruptedException ie) { 155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Do nothing 156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return packet; 159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private void writePackets(Thread thisThread) { 162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Open the stream. 164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen openStream(); 165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Write out packets from the queue. 166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (!done && (writerThread == thisThread)) { 167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Packet packet = nextPacket(); 168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (packet != null) { 169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.write(packet.toXML()); 170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (queue.isEmpty()) { 171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.flush(); 172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Flush out the rest of the queue. If the queue is extremely large, it's possible 176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // we won't have time to entirely flush it before the socket is forced closed 177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // by the shutdown process. 178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (!queue.isEmpty()) { 180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Packet packet = queue.remove(); 181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.write(packet.toXML()); 182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.flush(); 184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (Exception e) { 186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen e.printStackTrace(); 187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Delete the queue contents (hopefully nothing is left). 190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen queue.clear(); 191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Close the stream. 193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.write("</stream:stream>"); 195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.flush(); 196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (Exception e) { 198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Do nothing 199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen finally { 201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.close(); 203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (Exception e) { 205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Do nothing 206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (IOException ioe) { 210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // The exception can be ignored if the the connection is 'done' 211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // or if the it was caused because the socket got closed 212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!(done || connection.isSocketClosed())) { 213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen done = true; 214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // packetReader could be set to null by an concurrent disconnect() call. 215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Therefore Prevent NPE exceptions by checking packetReader. 216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (connection.packetReader != null) { 217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.notifyConnectionError(ioe); 218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sends to the server a new stream element. This operation may be requested several times 225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * so we need to encapsulate the logic in one place. This message will be sent while doing 226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * TLS, SASL and resource binding. 227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException If an error occurs while sending the stanza to the server. 229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen void openStream() throws IOException { 231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen StringBuilder stream = new StringBuilder(); 232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen stream.append("<stream:stream"); 233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen stream.append(" to=\"").append(connection.getServiceName()).append("\""); 234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen stream.append(" xmlns=\"jabber:client\""); 235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); 236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen stream.append(" version=\"1.0\">"); 237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.write(stream.toString()); 238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen writer.flush(); 239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 241