001/** 002 * 003 * Copyright 2003-2007 Jive Software. 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; 019 020import java.util.concurrent.ArrayBlockingQueue; 021import java.util.concurrent.TimeUnit; 022 023import org.jivesoftware.smack.SmackException.NoResponseException; 024import org.jivesoftware.smack.XMPPException.XMPPErrorException; 025import org.jivesoftware.smack.filter.PacketFilter; 026import org.jivesoftware.smack.packet.Packet; 027import org.jivesoftware.smack.packet.XMPPError; 028 029/** 030 * Provides a mechanism to collect packets into a result queue that pass a 031 * specified filter. The collector lets you perform blocking and polling 032 * operations on the result queue. So, a PacketCollector is more suitable to 033 * use than a {@link PacketListener} when you need to wait for a specific 034 * result.<p> 035 * 036 * Each packet collector will queue up a configured number of packets for processing before 037 * older packets are automatically dropped. The default number is retrieved by 038 * {@link SmackConfiguration#getPacketCollectorSize()}. 039 * 040 * @see XMPPConnection#createPacketCollector(PacketFilter) 041 * @author Matt Tucker 042 */ 043public class PacketCollector { 044 045 private PacketFilter packetFilter; 046 private ArrayBlockingQueue<Packet> resultQueue; 047 private XMPPConnection connection; 048 private boolean cancelled = false; 049 050 /** 051 * Creates a new packet collector. If the packet filter is <tt>null</tt>, then 052 * all packets will match this collector. 053 * 054 * @param connection the connection the collector is tied to. 055 * @param packetFilter determines which packets will be returned by this collector. 056 */ 057 protected PacketCollector(XMPPConnection connection, PacketFilter packetFilter) { 058 this(connection, packetFilter, SmackConfiguration.getPacketCollectorSize()); 059 } 060 061 /** 062 * Creates a new packet collector. If the packet filter is <tt>null</tt>, then 063 * all packets will match this collector. 064 * 065 * @param connection the connection the collector is tied to. 066 * @param packetFilter determines which packets will be returned by this collector. 067 * @param maxSize the maximum number of packets that will be stored in the collector. 068 */ 069 protected PacketCollector(XMPPConnection connection, PacketFilter packetFilter, int maxSize) { 070 this.connection = connection; 071 this.packetFilter = packetFilter; 072 this.resultQueue = new ArrayBlockingQueue<Packet>(maxSize); 073 } 074 075 /** 076 * Explicitly cancels the packet collector so that no more results are 077 * queued up. Once a packet collector has been cancelled, it cannot be 078 * re-enabled. Instead, a new packet collector must be created. 079 */ 080 public void cancel() { 081 // If the packet collector has already been cancelled, do nothing. 082 if (!cancelled) { 083 cancelled = true; 084 connection.removePacketCollector(this); 085 } 086 } 087 088 /** 089 * Returns the packet filter associated with this packet collector. The packet 090 * filter is used to determine what packets are queued as results. 091 * 092 * @return the packet filter. 093 */ 094 public PacketFilter getPacketFilter() { 095 return packetFilter; 096 } 097 098 /** 099 * Polls to see if a packet is currently available and returns it, or 100 * immediately returns <tt>null</tt> if no packets are currently in the 101 * result queue. 102 * 103 * @return the next packet result, or <tt>null</tt> if there are no more 104 * results. 105 */ 106 public Packet pollResult() { 107 return resultQueue.poll(); 108 } 109 110 /** 111 * Returns the next available packet. The method call will block (not return) until a packet is 112 * available. 113 * 114 * @return the next available packet. 115 */ 116 public Packet nextResultBlockForever() { 117 try { 118 return resultQueue.take(); 119 } 120 catch (InterruptedException e) { 121 throw new RuntimeException(e); 122 } 123 } 124 125 /** 126 * Returns the next available packet. The method call will block until the connection's default 127 * timeout has elapsed. 128 * 129 * @return the next availabe packet. 130 */ 131 public Packet nextResult() { 132 return nextResult(connection.getPacketReplyTimeout()); 133 } 134 135 /** 136 * Returns the next available packet. The method call will block (not return) 137 * until a packet is available or the <tt>timeout</tt> has elapsed. If the 138 * timeout elapses without a result, <tt>null</tt> will be returned. 139 * 140 * @return the next available packet. 141 */ 142 public Packet nextResult(long timeout) { 143 try { 144 return resultQueue.poll(timeout, TimeUnit.MILLISECONDS); 145 } 146 catch (InterruptedException e) { 147 throw new RuntimeException(e); 148 } 149 } 150 151 /** 152 * Returns the next available packet. The method call will block until a packet is available or 153 * the connections reply timeout has elapsed. If the timeout elapses without a result, 154 * <tt>null</tt> will be returned. This method does also cancel the PacketCollector. 155 * 156 * @return the next available packet. 157 * @throws XMPPErrorException in case an error response. 158 * @throws NoResponseException if there was no response from the server. 159 */ 160 public Packet nextResultOrThrow() throws NoResponseException, XMPPErrorException { 161 return nextResultOrThrow(connection.getPacketReplyTimeout()); 162 } 163 164 /** 165 * Returns the next available packet. The method call will block until a packet is available or 166 * the <tt>timeout</tt> has elapsed. This method does also cancel the PacketCollector. 167 * 168 * @param timeout the amount of time to wait for the next packet (in milleseconds). 169 * @return the next available packet. 170 * @throws NoResponseException if there was no response from the server. 171 * @throws XMPPErrorException in case an error response. 172 */ 173 public Packet nextResultOrThrow(long timeout) throws NoResponseException, XMPPErrorException { 174 Packet result = nextResult(timeout); 175 cancel(); 176 if (result == null) { 177 throw new NoResponseException(); 178 } 179 180 XMPPError xmppError = result.getError(); 181 if (xmppError != null) { 182 throw new XMPPErrorException(xmppError); 183 } 184 185 return result; 186 } 187 188 /** 189 * Processes a packet to see if it meets the criteria for this packet collector. 190 * If so, the packet is added to the result queue. 191 * 192 * @param packet the packet to process. 193 */ 194 protected void processPacket(Packet packet) { 195 if (packet == null) { 196 return; 197 } 198 199 if (packetFilter == null || packetFilter.accept(packet)) { 200 while (!resultQueue.offer(packet)) { 201 // Since we know the queue is full, this poll should never actually block. 202 resultQueue.poll(); 203 } 204 } 205 } 206}