1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-12-08 22:21:08 +01:00

Updated Jingle implementation. SMACK-240. Thanks to Jeff Williams.

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@10419 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Gaston Dombiak 2008-05-21 04:35:42 +00:00 committed by gato
parent 1cbfdcc7db
commit 646271abac
12 changed files with 1199 additions and 0 deletions

View file

@ -0,0 +1,49 @@
package org.jivesoftware.smackx.jingle;
/**
* The "action" in the jingle packet, as an enum.
*
* Changed to reflect XEP-166 rev: 20JUN07
*
* @author Jeff Williams
*/
public enum JingleActionEnum {
UNKNOWN("unknown"),
CONTENT_ACCEPT("content-accept"),
CONTENT_ADD("content-add"),
CONTENT_MODIFY("content-modify"),
CONTENT_REMOVE("content-remove"),
SESSION_ACCEPT("session-accept"),
SESSION_INFO("session-info"),
SESSION_INITIATE("session-initiate"),
SESSION_TERMINATE("session-terminate"),
TRANSPORT_INFO("transport-info");
private String actionCode;
private JingleActionEnum(String inActionCode) {
actionCode = inActionCode;
}
/**
* Returns the String value for an Action.
*/
public String toString() {
return actionCode;
}
/**
* Returns the Action enum for a String action value.
*/
public static JingleActionEnum getAction(String inActionCode) {
for (JingleActionEnum jingleAction : JingleActionEnum.values()) {
if (jingleAction.actionCode.equals(inActionCode)) {
return jingleAction;
}
}
return null;
}
}

View file

@ -0,0 +1,52 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.packet.JingleError;
/**
* A Jingle exception.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public 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;
}
}

View file

@ -0,0 +1,10 @@
package org.jivesoftware.smackx.jingle;
/**
* @author Jeff Williams
*/
public enum JingleNegotiatorState {
PENDING,
FAILED,
SUCCEEDED
}

View file

@ -0,0 +1,49 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.packet.Jingle;
/**
* Implement the Jingle Session state using the State Behavioral pattern.
* (From the book Design Patterns, AKA GoF.)
* These classes also employ the Flyweight and Singleton patterns as recommended for the State pattern by GoF.
*
* There seems to be three ways to go with the State pattern in Java: interface, abstract class and enums.
* Most of the accepted models use abstract classes. It wasn't clear to me that any of the three models was
* superior, so I went with the most common example.
*
* @author Jeff Williams
*/
public abstract class JingleSessionState {
/**
* Called when entering the state.
*/
public static JingleSessionState getInstance() {
// Since we can never instantiate this class there is nothing to return (ever).
return null;
}
/**
* Called when entering the state.
*/
public abstract void enter();
/**
* Called when exiting the state.
*/
public abstract void exit();
/**
* Process an incoming Jingle Packet.
* When you look at the GoF State pattern this method roughly corresponds to example on p310: ProcessOctect().
*/
public abstract IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action);
/**
* For debugging just emit the short name of the class.
*/
public String toString() {
return this.getClass().getSimpleName();
}
}

View file

@ -0,0 +1,92 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleError;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateActive extends JingleSessionState {
private static JingleSessionStateActive INSTANCE = null;
protected JingleSessionStateActive() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateActive();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
switch (action) {
case CONTENT_ACCEPT:
break;
case CONTENT_ADD:
break;
case CONTENT_MODIFY:
break;
case CONTENT_REMOVE:
break;
case SESSION_INFO:
break;
case SESSION_TERMINATE:
receiveSessionTerminateAction(session, jingle);
break;
case TRANSPORT_INFO:
break;
default:
// Anything other action is an error.
response = session.createJingleError(jingle, JingleError.OUT_OF_ORDER);
break;
}
return response;
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (XMPPException e) {
e.printStackTrace();
}
return response;
}
}

View file

@ -0,0 +1,51 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleError;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateEnded extends JingleSessionState {
private static JingleSessionStateEnded INSTANCE = null;
protected JingleSessionStateEnded() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateEnded();
}
return INSTANCE;
}
public void enter() {
System.out.println("Session Ended");
System.out.println("-------------------------------------------------------------------");
}
public void exit() {
// TODO Auto-generated method stub
}
/**
* Pretty much nothing is valid for receiving once we've ended the session.
*/
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
return response;
}
}

View file

@ -0,0 +1,117 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.packet.Jingle;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStatePending extends JingleSessionState {
private static JingleSessionStatePending INSTANCE = null;
protected JingleSessionStatePending() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStatePending();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
switch (action) {
case CONTENT_ACCEPT:
response = receiveContentAcceptAction(jingle);
break;
case CONTENT_MODIFY:
break;
case CONTENT_REMOVE:
break;
case SESSION_ACCEPT:
response = receiveSessionAcceptAction(session, jingle);
break;
case SESSION_INFO:
break;
case SESSION_TERMINATE:
response = receiveSessionTerminateAction(session, jingle);
break;
case TRANSPORT_INFO:
break;
default:
// Anything other action is an error.
//response = createJingleError(inJingle, JingleError.OUT_OF_ORDER);
break;
}
return response;
}
/**
* Receive and process the <session-accept> action.
*/
private IQ receiveContentAcceptAction(Jingle inJingle) {
// According to XEP-167 the only thing we can do is ack.
//setSessionState(JingleSessionStateEnum.ACTIVE);
//return createAck(inJingle);
// This is now handled by the media negotiator for the matching <content> segment.
return null;
}
/**
* Receive and process the <session-accept> action.
*/
private IQ receiveSessionAcceptAction(JingleSession session, Jingle inJingle) {
// According to XEP-166 the only thing we can do is ack.
session.setSessionState(JingleSessionStateActive.getInstance());
return session.createAck(inJingle);
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (XMPPException e) {
e.printStackTrace();
}
return response;
}
}

View file

@ -0,0 +1,195 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.MediaNegotiator;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingle.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingle.nat.TransportResolver;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleContent;
import org.jivesoftware.smackx.packet.JingleDescription;
import org.jivesoftware.smackx.packet.JingleError;
import org.jivesoftware.smackx.packet.JingleTransport;
/**
* @author Jeff Williams
* @see JingleSessionState
*/
public class JingleSessionStateUnknown extends JingleSessionState {
private static JingleSessionStateUnknown INSTANCE = null;
protected JingleSessionStateUnknown() {
// Prevent instantiation of the class.
}
/**
* A thread-safe means of getting the one instance of this class.
* @return The singleton instance of this class.
*/
public synchronized static JingleSessionState getInstance() {
if (INSTANCE == null) {
INSTANCE = new JingleSessionStateUnknown();
}
return INSTANCE;
}
public void enter() {
// TODO Auto-generated method stub
}
public void exit() {
// TODO Auto-generated method stub
}
public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) {
IQ response = null;
switch (action) {
case SESSION_INITIATE:
response = receiveSessionInitiateAction(session, jingle);
break;
case SESSION_TERMINATE:
response = receiveSessionTerminateAction(session, jingle);
break;
default:
// Anything other than session-initiate is an error.
response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA);
break;
}
return response;
}
/**
* In the UNKNOWN state we received a <session-initiate> action.
* This method processes that action.
*/
private IQ receiveSessionInitiateAction(JingleSession session, Jingle inJingle) {
IQ response = null;
boolean shouldAck = true;
// According to XEP-166 when we get a session-initiate we need to check for:
// 1. Initiator unknown
// 2. Receiver redirection
// 3. Does not support Jingle
// 4. Does not support any <description> formats
// 5. Does not support any <transport> formats
// If all of the above are OK then we send an IQ type = result to ACK the session-initiate.
// 1. Initiator unknown
// TODO
// 2. Receiver redirection
// TODO
// 3. Does not support Jingle
// Handled by Smack's lower layer.
// 4. Does not support any <description> formats
// TODO
// 5. Does not support any <transport> formats
// TODO
if (!shouldAck) {
response = session.createJingleError(inJingle, JingleError.NEGOTIATION_ERROR);
} else {
// Create the Ack
response = session.createAck(inJingle);
session.setSessionState(JingleSessionStatePending.getInstance());
// Now set up all of the initial content negotiators for the session.
for (JingleContent jingleContent : inJingle.getContentsList()) {
// First create the content negotiator for this <content> section.
ContentNegotiator contentNeg = new ContentNegotiator(session, jingleContent.getCreator(), jingleContent
.getName());
// Get the media negotiator that goes with the <description> of this content.
JingleDescription jingleDescription = jingleContent.getDescription();
// Loop through each media manager looking for the ones that matches the incoming
// session-initiate <content> choices.
// (Set the first media manager as the default, so that in case things don't match we can still negotiate.)
JingleMediaManager chosenMediaManager = session.getMediaManagers().get(0);
for (JingleMediaManager mediaManager : session.getMediaManagers()) {
boolean matches = true;
for (PayloadType mediaPayloadType : mediaManager.getPayloads()) {
for (PayloadType descPayloadType2 : jingleDescription.getPayloadTypesList()) {
if (mediaPayloadType.getId() != descPayloadType2.getId()) {
matches = false;
}
}
if (matches) {
chosenMediaManager = mediaManager;
}
}
}
// Create the media negotiator for this content description.
contentNeg.setMediaNegotiator(new MediaNegotiator(session, chosenMediaManager, jingleDescription
.getPayloadTypesList(), contentNeg));
// For each transport type in this content, try to find the corresponding transport manager.
// Then create a transport negotiator for that transport.
for (JingleTransport jingleTransport : jingleContent.getJingleTransportsList()) {
for (JingleMediaManager mediaManager : session.getMediaManagers()) {
JingleTransportManager transportManager = mediaManager.getTransportManager();
TransportResolver resolver = null;
try {
resolver = transportManager.getResolver(session);
} catch (XMPPException e) {
e.printStackTrace();
}
if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
contentNeg.setTransportNegotiator(new TransportNegotiator.RawUdp(session, resolver, contentNeg));
}
if (resolver.getType().equals(TransportResolver.Type.ice)) {
contentNeg.setTransportNegotiator(new TransportNegotiator.Ice(session, resolver, contentNeg));
}
}
}
// Add the content negotiator to the session.
session.addContentNegotiator(contentNeg);
}
// Now setup to track the media negotiators, so that we know when (if) to send a session-accept.
session.setupListeners();
}
return response;
}
/**
* Receive and process the <session-terminate> action.
*/
private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) {
// According to XEP-166 the only thing we can do is ack.
IQ response = session.createAck(jingle);
try {
session.terminate("Closed remotely");
} catch (XMPPException e) {
e.printStackTrace();
}
return response;
}
}