001/** 002 * 003 * Copyright 2003-2005 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 */ 017package org.jivesoftware.smackx.jingle; 018 019import java.util.ArrayList; 020import java.util.List; 021import java.util.logging.Logger; 022 023import org.jivesoftware.smack.SmackException; 024import org.jivesoftware.smack.XMPPConnection; 025import org.jivesoftware.smack.XMPPException; 026import org.jivesoftware.smack.packet.IQ; 027import org.jivesoftware.smackx.jingle.listeners.JingleListener; 028 029/** 030 * Basic Jingle negotiator. 031 * <p/> 032 * </p> 033 * <p/> 034 * JingleNegotiator implements some basic behavior for every Jingle negotiation. 035 * It implements a "state" pattern: each stage should process Jingle packets and 036 * act depending on the current state in the negotiation... 037 * <p/> 038 * </p> 039 * 040 * @author Alvaro Saurin 041 * @author Jeff Williams 042 */ 043public abstract class JingleNegotiator { 044 045 private static final Logger LOGGER = Logger.getLogger(JingleNegotiator.class.getName()); 046 047 //private XMPPConnection connection; // The connection associated 048 049 protected JingleSession session; 050 051 private final List<JingleListener> listeners = new ArrayList<JingleListener>(); 052 053 private String expectedAckId; 054 055 private JingleNegotiatorState state; 056 057 private boolean isStarted; 058 059 /** 060 * Default constructor. 061 */ 062 public JingleNegotiator() { 063 this(null); 064 } 065 066 /** 067 * Default constructor with a Connection 068 * 069 * @param session the jingle session 070 */ 071 public JingleNegotiator(JingleSession session) { 072 this.session = session; 073 state = JingleNegotiatorState.PENDING; 074 } 075 076 public JingleNegotiatorState getNegotiatorState() { 077 return state; 078 } 079 080 public void setNegotiatorState(JingleNegotiatorState stateIs) { 081 082 JingleNegotiatorState stateWas = state; 083 084 LOGGER.fine("Negotiator state change: " + stateWas + "->" + stateIs + "(" + this.getClass().getSimpleName() + ")"); 085 086 switch (stateIs) { 087 case PENDING: 088 break; 089 090 case FAILED: 091 break; 092 093 case SUCCEEDED: 094 break; 095 096 default: 097 break; 098 } 099 100 this.state = stateIs; 101 } 102 103 public XMPPConnection getConnection() { 104 if (session != null) { 105 return session.getConnection(); 106 } else { 107 return null; 108 } 109 } 110 111 /** 112 * Get the XMPP connection associated with this negotiation. 113 * 114 * @return the connection 115 */ 116 public JingleSession getSession() { 117 return session; 118 } 119 120 /** 121 * Set the XMPP connection associated. 122 * 123 * @param session the jingle session 124 */ 125 public void setSession(JingleSession session) { 126 this.session = session; 127 } 128 129 // Acks management 130 131 /** 132 * Add expected ID 133 * 134 * @param id 135 */ 136 public void addExpectedId(String id) { 137 expectedAckId = id; 138 } 139 140 /** 141 * Check if the passed ID is the expected ID 142 * 143 * @param id 144 * @return true if is expected id 145 */ 146 public boolean isExpectedId(String id) { 147 if (id != null) { 148 return id.equals(expectedAckId); 149 } else { 150 return false; 151 } 152 } 153 154 /** 155 * Remove and expected ID 156 * 157 * @param id 158 */ 159 public void removeExpectedId(String id) { 160 addExpectedId((String) null); 161 } 162 163 // Listeners 164 165 /** 166 * Add a Jingle session listener to listen to incoming session requests. 167 * 168 * @param li The listener 169 * @see org.jivesoftware.smackx.jingle.listeners.JingleListener 170 */ 171 public void addListener(JingleListener li) { 172 synchronized (listeners) { 173 listeners.add(li); 174 } 175 } 176 177 /** 178 * Removes a Jingle session listener. 179 * 180 * @param li The jingle session listener to be removed 181 * @see org.jivesoftware.smackx.jingle.listeners.JingleListener 182 */ 183 public void removeListener(JingleListener li) { 184 synchronized (listeners) { 185 listeners.remove(li); 186 } 187 } 188 189 /** 190 * Get a copy of the listeners 191 * 192 * @return a copy of the listeners 193 */ 194 protected List<JingleListener> getListenersList() { 195 ArrayList<JingleListener> result; 196 197 synchronized (listeners) { 198 result = new ArrayList<JingleListener>(listeners); 199 } 200 201 return result; 202 } 203 204 /** 205 * Dispatch an incoming packet. 206 * 207 * The negotiators form a tree relationship that roughly matches the Jingle packet format: 208 * 209 * JingleSession 210 * Content Negotiator 211 * Media Negotiator 212 * Transport Negotiator 213 * Content Negotiator 214 * Media Negotiator 215 * Transport Negotiator 216 * 217 * <jingle> 218 * <content> 219 * <description> 220 * <transport> 221 * <content> 222 * <description> 223 * <transport> 224 * 225 * This way, each segment of a Jingle packet has a corresponding negotiator that know how to deal with that 226 * part of the Jingle packet. It also allows us to support Jingle packets of arbitraty complexity. 227 * 228 * Each parent calls dispatchIncomingPacket for each of its children. The children then pass back a List<> of 229 * results that will get sent when we reach the top level negotiator (JingleSession). 230 * 231 * @param iq the packet received 232 * @param id the ID of the response that will be sent 233 * @return the new packet to send (either a Jingle or an IQ error). 234 * @throws XMPPException 235 */ 236 public abstract List<IQ> dispatchIncomingPacket(IQ iq, String id) throws XMPPException, SmackException; 237 238 239 public void start() { 240 isStarted = true; 241 doStart(); 242 } 243 244 public boolean isStarted() { 245 return isStarted; 246 } 247 248 /** 249 * Each of the negotiators has their individual behavior when they start. 250 */ 251 protected abstract void doStart(); 252 253 /** 254 * Close the negotiation. 255 */ 256 public void close() { 257 258 } 259}