rocks.xmpp.extensions.jingle.JingleSession Maven / Gradle / Ivy
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Christian Schudt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rocks.xmpp.extensions.jingle;
import rocks.xmpp.addr.Jid;
import rocks.xmpp.core.session.XmppSession;
import rocks.xmpp.core.stanza.model.IQ;
import rocks.xmpp.extensions.jingle.model.Jingle;
import rocks.xmpp.extensions.jingle.transports.model.TransportMethod;
import rocks.xmpp.util.concurrent.AsyncResult;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
/**
* @author Christian Schudt
*/
public final class JingleSession {
final Set> jingleListeners = new CopyOnWriteArraySet<>();
private final String sessionId;
private final XmppSession xmppSession;
private final Jid peer;
private final JingleManager jingleManager;
private final boolean createdLocally;
// See 7.2.2 content-add:
// Therefore it is the responsibility of the recipient to maintain a local copy of the current content definition(s).
private final List contents;
private State state = State.PENDING;
JingleSession(String sessionId, Jid peer, boolean createdLocally, XmppSession xmppSession, JingleManager jingleManager, Jingle.Content... contents) {
this(sessionId, peer, createdLocally, xmppSession, jingleManager, Arrays.asList(contents));
}
JingleSession(String sessionId, Jid peer, boolean createdLocally, XmppSession xmppSession, JingleManager jingleManager, List contents) {
this.sessionId = sessionId;
this.xmppSession = xmppSession;
this.peer = peer;
this.jingleManager = jingleManager;
this.createdLocally = createdLocally;
this.contents = new ArrayList<>(contents);
}
/**
* Gets the session id.
*
* @return The session id.
*/
public String getSessionId() {
return sessionId;
}
/**
* Initiates the session.
*
* @return The async result.
* @see 6.2 Initiation
* @see 7.2.10 session-initiate
*/
public AsyncResult initiate() {
if (!createdLocally) {
throw new UnsupportedOperationException("You are not the initiator.");
}
return xmppSession.query(IQ.set(peer, Jingle.initiator(xmppSession.getConnectedResource(), sessionId, Jingle.Action.SESSION_INITIATE, contents))).thenAccept(result ->
state = State.PENDING);
}
/**
* Accepts the session. You must at least provide one content element.
*
* @param contents The contents.
* @return The async result.
* @see 6.5 Acceptance
* @see 7.2.8 session-accept
*/
public AsyncResult accept(Jingle.Content... contents) {
if (state != State.PENDING) {
throw new IllegalStateException("The session is not in pending state.");
}
if (createdLocally) {
throw new UnsupportedOperationException("You are the initiator and cannot accept the session.");
}
// In the session-accept stanza, the element MUST contain one or more elements, each of which MUST contain one element and one element.
if (contents.length == 0) {
throw new IllegalArgumentException("No content element provided.");
}
for (Jingle.Content content : contents) {
if (content.getApplicationFormat() == null) {
throw new IllegalArgumentException("No application format provided in content.");
}
if (content.getTransportMethod() == null) {
throw new IllegalArgumentException("No transport method provided in content.");
}
}
return xmppSession.query(IQ.set(peer, Jingle.responder(xmppSession.getConnectedResource(), sessionId, Jingle.Action.SESSION_ACCEPT, Arrays.asList(contents)))).thenAccept(result -> {
// The session is now in the ACTIVE state.
state = State.ACTIVE;
});
}
/**
* Terminates the Jingle session.
*
* @param reason The reason for termination.
* @return The async result.
* @see 6.7 Termination
*/
public AsyncResult terminate(Jingle.Reason reason) {
// As soon as an entity sends a session-terminate action, it MUST consider the session to be in the ENDED state
// (even before receiving acknowledgement from the other party).
state = State.ENDED;
try {
return xmppSession.query(IQ.set(peer, new Jingle(sessionId, Jingle.Action.SESSION_TERMINATE, reason)));
} finally {
jingleManager.removeSession(sessionId);
}
}
/**
* @param contentName The content name.
* @param transportMethod The replaced transport method.
* @return The async result.
* @see 7.2.15 transport-replace
*/
public AsyncResult replaceTransport(String contentName, TransportMethod transportMethod) {
Jingle.Content content = new Jingle.Content(contentName, Jingle.Content.Creator.INITIATOR, null, transportMethod);
return xmppSession.query(IQ.set(peer, Jingle.initiator(xmppSession.getConnectedResource(), sessionId, Jingle.Action.TRANSPORT_REPLACE, Collections.singletonList(content))));
}
public AsyncResult acceptTransport(String contentName, TransportMethod transportMethod) {
Jingle.Content content = new Jingle.Content(contentName, Jingle.Content.Creator.INITIATOR, null, transportMethod);
return xmppSession.query(IQ.set(peer, Jingle.initiator(xmppSession.getConnectedResource(), sessionId, Jingle.Action.TRANSPORT_ACCEPT, Collections.singletonList(content))));
}
public AsyncResult rejectTransport(String contentName, TransportMethod transportMethod) {
Jingle.Content content = new Jingle.Content(contentName, Jingle.Content.Creator.INITIATOR, null, transportMethod);
return xmppSession.query(IQ.set(peer, Jingle.initiator(xmppSession.getConnectedResource(), sessionId, Jingle.Action.TRANSPORT_REJECT, Collections.singletonList(content))));
}
public List getContents() {
return Collections.unmodifiableList(contents);
}
/**
* Sends a session info.
*
* @param object The session info payload.
* @return The async result.
* @see 7.2.9 session-info
*/
public AsyncResult sendSessionInfo(Object object) {
return xmppSession.query(IQ.set(peer, new Jingle(sessionId, Jingle.Action.SESSION_INFO, object)));
}
/**
* Adds a Jingle listener, which allows to listen for Jingle events.
*
* @param jingleListener The listener.
* @see #removeJingleListener(Consumer)
*/
public final void addJingleListener(Consumer jingleListener) {
jingleListeners.add(jingleListener);
}
/**
* Removes a previously added Jingle listener.
*
* @param jingleListener The listener.
* @see #addJingleListener(Consumer)
*/
public final void removeJingleListener(Consumer jingleListener) {
jingleListeners.remove(jingleListener);
}
/**
* Represents the state of a Jingle session.
*
* @see 5.1 Overall Session Management
*/
public enum State {
/**
* The session has been initiated, but not yet accepted.
*/
PENDING,
/**
* The session has been accepted.
*/
ACTIVE,
/**
* The session has been ended.
*/
ENDED
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy