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