2007-01-04 18:25:30 +01:00
|
|
|
package org.jivesoftware.smackx.jingle;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
|
|
|
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
|
|
|
|
import org.jivesoftware.smackx.packet.Jingle;
|
|
|
|
import org.jivesoftware.smackx.packet.JingleError;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Basic Jingle negotiator.
|
|
|
|
* <p/>
|
|
|
|
* </p>
|
|
|
|
* <p/>
|
|
|
|
* JingleNegotiator implements some basic behavior for every Jingle negotiation.
|
|
|
|
* It implements a "state" pattern: each stage should process Jingle packets and
|
|
|
|
* act depending on the current state in the negotiation...
|
|
|
|
* <p/>
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* @author Alvaro Saurin
|
|
|
|
*/
|
|
|
|
public abstract class JingleNegotiator {
|
|
|
|
|
|
|
|
private State state; // Current negotiation state
|
|
|
|
|
|
|
|
private XMPPConnection connection; // The connection associated
|
|
|
|
|
|
|
|
private final ArrayList listeners = new ArrayList();
|
|
|
|
|
|
|
|
private String expectedAckId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*/
|
|
|
|
public JingleNegotiator() {
|
|
|
|
this(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor with a XMPPConnection
|
|
|
|
*
|
|
|
|
* @param connection the connection associated
|
|
|
|
*/
|
|
|
|
public JingleNegotiator(XMPPConnection connection) {
|
|
|
|
this.connection = connection;
|
|
|
|
state = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the XMPP connection associated with this negotiation.
|
|
|
|
*
|
|
|
|
* @return the connection
|
|
|
|
*/
|
|
|
|
public XMPPConnection getConnection() {
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the XMPP connection associated.
|
|
|
|
*
|
|
|
|
* @param connection the connection to set
|
|
|
|
*/
|
|
|
|
public void setConnection(XMPPConnection connection) {
|
|
|
|
this.connection = connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inform if current state is null
|
|
|
|
*
|
|
|
|
* @return true if current state is null
|
|
|
|
*/
|
|
|
|
public boolean invalidState() {
|
|
|
|
return state == null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the current state
|
|
|
|
*
|
|
|
|
* @return the state
|
|
|
|
*/
|
|
|
|
public State getState() {
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the current state class
|
|
|
|
*
|
|
|
|
* @return the state
|
|
|
|
*/
|
|
|
|
public Class getStateClass() {
|
|
|
|
if (state != null) {
|
|
|
|
return state.getClass();
|
2007-02-14 21:50:37 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
return Object.class;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the new state.
|
|
|
|
*
|
|
|
|
* @param newState the state to set
|
|
|
|
* @throws XMPPException
|
|
|
|
*/
|
|
|
|
protected void setState(State newState) {
|
|
|
|
boolean transition = newState != state;
|
|
|
|
|
|
|
|
if (transition && state != null) {
|
|
|
|
state.eventExit();
|
|
|
|
}
|
|
|
|
|
|
|
|
state = newState;
|
|
|
|
|
|
|
|
if (transition && state != null) {
|
|
|
|
state.eventEnter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Acks management
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add expected ID
|
2007-02-14 21:50:37 +01:00
|
|
|
*
|
2007-01-04 18:25:30 +01:00
|
|
|
* @param id
|
|
|
|
*/
|
|
|
|
public void addExpectedId(String id) {
|
|
|
|
expectedAckId = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the passed ID is the expected ID
|
2007-02-14 21:50:37 +01:00
|
|
|
*
|
2007-01-04 18:25:30 +01:00
|
|
|
* @param id
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public boolean isExpectedId(String id) {
|
|
|
|
if (id != null) {
|
|
|
|
return id.equals(expectedAckId);
|
2007-02-14 21:50:37 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove and expected ID
|
2007-02-14 21:50:37 +01:00
|
|
|
*
|
2007-01-04 18:25:30 +01:00
|
|
|
* @param id
|
|
|
|
*/
|
|
|
|
public void removeExpectedId(String id) {
|
|
|
|
addExpectedId((String) null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Listeners
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Jingle session listener to listen to incoming session requests.
|
|
|
|
*
|
|
|
|
* @param li The listener
|
|
|
|
* @see org.jivesoftware.smackx.jingle.listeners.JingleListener
|
|
|
|
*/
|
|
|
|
public void addListener(JingleListener li) {
|
|
|
|
synchronized (listeners) {
|
|
|
|
listeners.add(li);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a Jingle session listener.
|
|
|
|
*
|
|
|
|
* @param li The jingle session listener to be removed
|
|
|
|
* @see org.jivesoftware.smackx.jingle.listeners.JingleListener
|
|
|
|
*/
|
|
|
|
public void removeListener(JingleListener li) {
|
|
|
|
synchronized (listeners) {
|
|
|
|
listeners.remove(li);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a copy of the listeners
|
|
|
|
*
|
|
|
|
* @return a copy of the listeners
|
|
|
|
*/
|
|
|
|
protected ArrayList getListenersList() {
|
|
|
|
ArrayList result;
|
|
|
|
|
|
|
|
synchronized (listeners) {
|
|
|
|
result = new ArrayList(listeners);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatch an incomming packet. This method is responsible for recognizing
|
|
|
|
* the packet type and, depending on the current state, deliverying the
|
|
|
|
* packet to the right event handler and wait for a response.
|
|
|
|
*
|
|
|
|
* @param iq the packet received
|
|
|
|
* @param id the ID of the response that will be sent
|
|
|
|
* @return the new packet to send (either a Jingle or an IQ error).
|
|
|
|
* @throws XMPPException
|
|
|
|
*/
|
|
|
|
public abstract IQ dispatchIncomingPacket(IQ iq, String id)
|
|
|
|
throws XMPPException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close the negotiation.
|
|
|
|
*/
|
|
|
|
public void close() {
|
|
|
|
setState(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A Jingle exception.
|
|
|
|
*
|
|
|
|
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
|
|
|
|
*/
|
|
|
|
public static class JingleException extends XMPPException {
|
|
|
|
|
|
|
|
private final JingleError error;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*/
|
|
|
|
public JingleException() {
|
|
|
|
super();
|
|
|
|
error = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor with an error message.
|
|
|
|
*
|
|
|
|
* @param msg The message.
|
|
|
|
*/
|
|
|
|
public JingleException(String msg) {
|
|
|
|
super(msg);
|
|
|
|
error = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor with an error response.
|
|
|
|
*
|
|
|
|
* @param error The error message.
|
|
|
|
*/
|
|
|
|
public JingleException(JingleError error) {
|
|
|
|
super();
|
|
|
|
this.error = error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the error message.
|
|
|
|
*
|
|
|
|
* @return the error
|
|
|
|
*/
|
|
|
|
public JingleError getError() {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Negotiation state and events.
|
|
|
|
* <p/>
|
|
|
|
* </p>
|
|
|
|
* <p/>
|
|
|
|
* Describes the negotiation stage.
|
|
|
|
*/
|
|
|
|
public static class State {
|
|
|
|
|
|
|
|
private JingleNegotiator neg; // The negotiator
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor, with a reference to the negotiator.
|
|
|
|
*
|
|
|
|
* @param neg The negotiator instance.
|
|
|
|
*/
|
|
|
|
public State(JingleNegotiator neg) {
|
|
|
|
this.neg = neg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the negotiator
|
|
|
|
*
|
|
|
|
* @return the negotiator.
|
|
|
|
*/
|
|
|
|
public JingleNegotiator getNegotiator() {
|
|
|
|
return neg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the negotiator.
|
|
|
|
*
|
|
|
|
* @param neg the neg to set
|
|
|
|
*/
|
|
|
|
public void setNegotiator(JingleNegotiator neg) {
|
|
|
|
this.neg = neg;
|
|
|
|
}
|
|
|
|
|
|
|
|
// State transition events
|
|
|
|
|
|
|
|
public Jingle eventAck(IQ iq) throws XMPPException {
|
|
|
|
// We have received an Ack
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void eventError(IQ iq) throws XMPPException {
|
|
|
|
throw new JingleException(iq.getError().getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventInvite() throws XMPPException {
|
|
|
|
throw new IllegalStateException(
|
|
|
|
"Negotiation can not be started in this state.");
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventInitiate(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventAccept(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventRedirect(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventModify(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventDecline(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventInfo(Jingle jin) throws XMPPException {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Jingle eventTerminate(Jingle jin) throws XMPPException {
|
|
|
|
if (neg != null) {
|
|
|
|
neg.close();
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void eventEnter() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public void eventExit() {
|
|
|
|
if (neg != null) {
|
|
|
|
neg.removeExpectedId(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|