mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-25 21:42:07 +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:
parent
1cbfdcc7db
commit
646271abac
12 changed files with 1199 additions and 0 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.jivesoftware.smackx.jingle;
|
||||
|
||||
/**
|
||||
* @author Jeff Williams
|
||||
*/
|
||||
public enum JingleNegotiatorState {
|
||||
PENDING,
|
||||
FAILED,
|
||||
SUCCEEDED
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* $RCSfile: JingleContent.java,v $
|
||||
* $Revision: 1.2 $
|
||||
* $Date: 2007/07/02 22:45:36 $
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
/**
|
||||
* Jingle content.
|
||||
*
|
||||
* @author Jeff Williams
|
||||
*/
|
||||
public class JingleContent implements PacketExtension {
|
||||
|
||||
public static final String NODENAME = "content";
|
||||
public static final String CREATOR = "creator";
|
||||
public static final String NAME = "name";
|
||||
|
||||
private String creator;
|
||||
private String name;
|
||||
|
||||
private JingleDescription description;
|
||||
private final List<JingleTransport> transports = new ArrayList<JingleTransport>();
|
||||
|
||||
/**
|
||||
* Creates a content description..
|
||||
*/
|
||||
public JingleContent(String creator, String name) {
|
||||
super();
|
||||
this.creator = creator;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element name of the element.
|
||||
*
|
||||
* @return the XML element name of the element.
|
||||
*/
|
||||
public String getElementName() {
|
||||
return NODENAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace.
|
||||
*
|
||||
* @return The namespace
|
||||
*/
|
||||
public String getNamespace() {
|
||||
// There is no namespace for <content>
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description for this Jingle content.
|
||||
*
|
||||
* @param description
|
||||
* The description
|
||||
*/
|
||||
public void setDescription(JingleDescription description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description for this Jingle content.
|
||||
*
|
||||
* @return The description.
|
||||
*/
|
||||
public JingleDescription getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JingleTransport type to the packet.
|
||||
*
|
||||
* @param transport
|
||||
* the JignleTransport to add.
|
||||
*/
|
||||
public void addJingleTransport(final JingleTransport transport) {
|
||||
synchronized (transports) {
|
||||
transports.add(transport);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of transports to add to the packet.
|
||||
*
|
||||
* @param transports
|
||||
* the transports to add.
|
||||
*/
|
||||
public void addTransports(final List<JingleTransport> transports) {
|
||||
synchronized (transports) {
|
||||
for (JingleTransport transport : transports) {
|
||||
addJingleTransport(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the JingleTransports in the packet.
|
||||
*
|
||||
* @return an Iterator for the JingleTransports in the packet.
|
||||
*/
|
||||
public Iterator<JingleTransport> getJingleTransports() {
|
||||
return Collections.unmodifiableList(getJingleTransportsList()).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list for the JingleTransports in the packet.
|
||||
*
|
||||
* @return a list for the JingleTransports in the packet.
|
||||
*/
|
||||
public List<JingleTransport> getJingleTransportsList() {
|
||||
synchronized (transports) {
|
||||
return new ArrayList<JingleTransport>(transports);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the JingleTransports in the Jingle packet.
|
||||
*
|
||||
* @return the number of the JingleTransports in the Jingle packet.
|
||||
*/
|
||||
public int getJingleTransportsCount() {
|
||||
synchronized (transports) {
|
||||
return transports.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Jingle description to XML.
|
||||
*
|
||||
* @return a string with the XML representation
|
||||
*/
|
||||
public String toXML() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
synchronized (transports) {
|
||||
|
||||
buf.append("<").append(getElementName());
|
||||
|
||||
buf.append(" creator='" + creator + "' name='" + name + "'>");
|
||||
|
||||
// Add the description.
|
||||
if (description != null) {
|
||||
buf.append(description.toXML());
|
||||
}
|
||||
|
||||
// Add all of the transports.
|
||||
for (JingleTransport transport : transports) {
|
||||
buf.append(transport.toXML());
|
||||
}
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/**
|
||||
* $RCSfile: JingleDescription.java,v $
|
||||
* $Revision: 1.1 $
|
||||
* $Date: 2007/07/02 17:41:08 $
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smackx.jingle.media.PayloadType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Jingle content description.
|
||||
*
|
||||
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
|
||||
*/
|
||||
public abstract class JingleDescription implements PacketExtension {
|
||||
|
||||
// static
|
||||
|
||||
public static final String NODENAME = "description";
|
||||
|
||||
// non-static
|
||||
|
||||
private final List<PayloadType> payloads = new ArrayList<PayloadType>();
|
||||
|
||||
/**
|
||||
* Creates a content description..
|
||||
*/
|
||||
public JingleDescription() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element name of the element.
|
||||
*
|
||||
* @return the XML element name of the element.
|
||||
*/
|
||||
public String getElementName() {
|
||||
return NODENAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace.
|
||||
*
|
||||
* @return The namespace
|
||||
*/
|
||||
public abstract String getNamespace();
|
||||
|
||||
/**
|
||||
* Adds a audio payload type to the packet.
|
||||
*
|
||||
* @param pt the audio payload type to add.
|
||||
*/
|
||||
public void addPayloadType(final PayloadType pt) {
|
||||
synchronized (payloads) {
|
||||
if (pt == null) {
|
||||
System.err.println("Null payload type");
|
||||
} else {
|
||||
payloads.add(pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of payloads to the packet.
|
||||
*
|
||||
* @param pts the payloads to add.
|
||||
*/
|
||||
public void addAudioPayloadTypes(final List<PayloadType> pts) {
|
||||
synchronized (payloads) {
|
||||
Iterator ptIter = pts.iterator();
|
||||
while (ptIter.hasNext()) {
|
||||
PayloadType.Audio pt = (PayloadType.Audio) ptIter.next();
|
||||
addPayloadType(new PayloadType.Audio(pt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the audio payloads in the packet.
|
||||
*
|
||||
* @return an Iterator for the audio payloads in the packet.
|
||||
*/
|
||||
public Iterator<PayloadType> getPayloadTypes() {
|
||||
return Collections.unmodifiableList(getPayloadTypesList()).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list for the audio payloads in the packet.
|
||||
*
|
||||
* @return a list for the audio payloads in the packet.
|
||||
*/
|
||||
public List<PayloadType> getPayloadTypesList() {
|
||||
synchronized (payloads) {
|
||||
return new ArrayList<PayloadType>(payloads);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Payload types contained in the description.
|
||||
*
|
||||
* @return a list of PayloadType.Audio
|
||||
*/
|
||||
public List<PayloadType> getAudioPayloadTypesList() {
|
||||
ArrayList<PayloadType> result = new ArrayList<PayloadType>();
|
||||
Iterator<PayloadType> jinglePtsIter = getPayloadTypes();
|
||||
|
||||
while (jinglePtsIter.hasNext()) {
|
||||
PayloadType jpt = (PayloadType) jinglePtsIter.next();
|
||||
if (jpt instanceof PayloadType.Audio) {
|
||||
PayloadType.Audio jpta = (PayloadType.Audio) jpt;
|
||||
result.add(jpta);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the audio payloads in the Jingle packet.
|
||||
*
|
||||
* @return the number of audio payloads in the Jingle packet.
|
||||
*/
|
||||
public int getPayloadTypesCount() {
|
||||
synchronized (payloads) {
|
||||
return payloads.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Jingle description to XML.
|
||||
*
|
||||
* @return a string with the XML representation
|
||||
*/
|
||||
public String toXML() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
synchronized (payloads) {
|
||||
if (payloads.size() > 0) {
|
||||
buf.append("<").append(getElementName());
|
||||
buf.append(" xmlns=\"").append(getNamespace()).append("\" >");
|
||||
|
||||
for (PayloadType payloadType : payloads) {
|
||||
if (payloadType != null) {
|
||||
buf.append(payloadType.toXML());
|
||||
}
|
||||
}
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Jingle audio description
|
||||
*/
|
||||
public static class Audio extends JingleDescription {
|
||||
|
||||
public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0167.html#ns";
|
||||
|
||||
public Audio() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility constructor, with a PayloadType
|
||||
*/
|
||||
public Audio(final PayloadType pt) {
|
||||
super();
|
||||
addPayloadType(pt);
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* $RCSfile: JingleContentProvider.java,v $
|
||||
* $Revision: 1.2 $
|
||||
* $Date: 2007/07/04 00:52:33 $
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.jivesoftware.smackx.packet.JingleContent;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Jingle <content> provider
|
||||
*
|
||||
* @author Jeff Williams
|
||||
*/
|
||||
public class JingleContentProvider implements PacketExtensionProvider {
|
||||
|
||||
/**
|
||||
* Creates a new provider. ProviderManager requires that every
|
||||
* PacketExtensionProvider has a public, no-argument constructor
|
||||
*/
|
||||
public JingleContentProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JingleContent extension.
|
||||
*/
|
||||
public PacketExtension parseExtension(final XmlPullParser parser) throws Exception {
|
||||
PacketExtension result = null;
|
||||
|
||||
String elementName = parser.getName();
|
||||
String creator = parser.getAttributeValue("", JingleContent.CREATOR);
|
||||
String name = parser.getAttributeValue("", JingleContent.NAME);
|
||||
|
||||
// Try to get an Audio content info
|
||||
result = new JingleContent(creator, name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* $RCSfile: JingleDescriptionProvider.java,v $
|
||||
* $Revision: 1.1 $
|
||||
* $Date: 2007/07/02 17:41:11 $
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.jivesoftware.smackx.jingle.media.PayloadType;
|
||||
import org.jivesoftware.smackx.packet.JingleDescription;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Parser for a Jingle description
|
||||
*
|
||||
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
|
||||
*/
|
||||
public abstract class JingleDescriptionProvider implements PacketExtensionProvider {
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public JingleDescriptionProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a iq/jingle/description/payload-type element.
|
||||
*
|
||||
* @param parser
|
||||
* the input to parse
|
||||
* @return a payload type element
|
||||
* @throws Exception
|
||||
*/
|
||||
protected PayloadType parsePayload(final XmlPullParser parser) throws Exception {
|
||||
int ptId = 0;
|
||||
String ptName;
|
||||
int ptChannels = 0;
|
||||
|
||||
try {
|
||||
ptId = Integer.parseInt(parser.getAttributeValue("", "id"));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
ptName = parser.getAttributeValue("", "name");
|
||||
|
||||
try {
|
||||
ptChannels = Integer.parseInt(parser.getAttributeValue("", "channels"));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return new PayloadType(ptId, ptName, ptChannels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a iq/jingle/description element.
|
||||
*
|
||||
* @param parser
|
||||
* the input to parse
|
||||
* @return a description element
|
||||
* @throws Exception
|
||||
*/
|
||||
public PacketExtension parseExtension(final XmlPullParser parser) throws Exception {
|
||||
boolean done = false;
|
||||
JingleDescription desc = getInstance();
|
||||
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
String name = parser.getName();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (name.equals(PayloadType.NODENAME)) {
|
||||
desc.addPayloadType(parsePayload(parser));
|
||||
} else {
|
||||
throw new Exception("Unknow element \"" + name + "\" in content.");
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (name.equals(JingleDescription.NODENAME)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of this class. Subclasses must overwrite this
|
||||
* method.
|
||||
*/
|
||||
protected abstract JingleDescription getInstance();
|
||||
|
||||
/**
|
||||
* Jingle audio
|
||||
*/
|
||||
public static class Audio extends JingleDescriptionProvider {
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public Audio() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an audio payload type.
|
||||
*/
|
||||
public PayloadType parsePayload(final XmlPullParser parser) throws Exception {
|
||||
PayloadType pte = super.parsePayload(parser);
|
||||
PayloadType.Audio pt = new PayloadType.Audio(pte);
|
||||
int ptClockRate = 0;
|
||||
|
||||
try {
|
||||
ptClockRate = Integer.parseInt(parser.getAttributeValue("", "clockrate"));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
pt.setClockRate(ptClockRate);
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new instance of this object.
|
||||
*/
|
||||
protected JingleDescription getInstance() {
|
||||
return new JingleDescription.Audio();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue