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