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     * &lt;jingle&gt;
216     *      &lt;content&gt;
217     *          &lt;description&gt;
218     *          &lt;transport&gt;
219     *      &lt;content&gt;
220     *          &lt;description&gt;
221     *          &lt;transport&gt;
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 arbitraty 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}