1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-11-29 09:42:06 +01:00

Add MucEnterConfiguration API

Allows to configure the presence used when joining a MUC.

Part of SMACK-648: Improve the MUC API.
This commit is contained in:
Florian Schmaus 2015-07-06 09:55:25 +02:00
parent bfdcfba092
commit 57cc3aa3f8
4 changed files with 369 additions and 48 deletions

View file

@ -39,7 +39,9 @@ import org.jivesoftware.smackx.muc.packet.MUCInitialPresence;
* Note: Setting maxchars to 0 indicates that the user requests to receive no history. * Note: Setting maxchars to 0 indicates that the user requests to receive no history.
* *
* @author Gaston Dombiak * @author Gaston Dombiak
* @deprecated use {@link org.jivesoftware.smackx.muc.MucEnterConfiguration} instead.
*/ */
@Deprecated
public class DiscussionHistory { public class DiscussionHistory {
private int maxChars = -1; private int maxChars = -1;
@ -150,19 +152,6 @@ public class DiscussionHistory {
return null; return null;
} }
MUCInitialPresence.History mucHistory = new MUCInitialPresence.History(); return new MUCInitialPresence.History(maxChars, maxStanzas, seconds, since);
if (maxChars > -1) {
mucHistory.setMaxChars(maxChars);
}
if (maxStanzas > -1) {
mucHistory.setMaxStanzas(maxStanzas);
}
if (seconds > -1) {
mucHistory.setSeconds(seconds);
}
if (since != null) {
mucHistory.setSince(since);
}
return mucHistory;
} }
} }

View file

@ -0,0 +1,208 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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.muc;
import java.util.Date;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smackx.muc.packet.MUCInitialPresence;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
/**
* The configuration used to enter a MUC room. This configuration is usually used when joining an
* existing room. When creating a new room, only the Nickname setting is relevant.
*
* @author Florian Schmaus
* @since 4.2
*/
public final class MucEnterConfiguration {
private final Resourcepart nickname;
private final String password;
private final int maxChars;
private final int maxStanzas;
private final int seconds;
private final Date since;
private final long timeout;
private final Presence joinPresence;
MucEnterConfiguration(Builder builder) {
nickname = builder.nickname;
password = builder.password;
maxChars = builder.maxChars;
maxStanzas = builder.maxStanzas;
seconds = builder.seconds;
since = builder.since;
timeout = builder.timeout;
if (builder.joinPresence == null) {
joinPresence = new Presence(Presence.Type.available);
}
else {
joinPresence = builder.joinPresence.clone();
}
// Indicate the the client supports MUC
joinPresence.addExtension(new MUCInitialPresence(password, maxChars, maxStanzas, seconds,
since));
}
Presence getJoinPresence(MultiUserChat multiUserChat) {
final EntityFullJid jid = JidCreate.fullFrom(multiUserChat.getRoom(), nickname);
joinPresence.setTo(jid);
return joinPresence;
}
long getTimeout() {
return timeout;
}
public static final class Builder {
private final Resourcepart nickname;
private String password;
private int maxChars = -1;
private int maxStanzas = -1;
private int seconds = -1;
private Date since;
private long timeout;
private Presence joinPresence;
Builder(Resourcepart nickname, long timeout) {
this.nickname = Objects.requireNonNull(nickname, "Nickname must not be null");
timeoutAfter(timeout);
}
/**
* Set the presence used to join the MUC room.
* <p>
* The 'to' value of the given presence will be overridden.
* <p>
*
* @param presence
* @return a reference to this builder.
*/
public Builder withPresence(Presence presence) {
if (presence.getType() == Presence.Type.available) {
throw new IllegalArgumentException();
}
joinPresence = presence;
return this;
}
/**
* Use the given password to join the MUC room.
*
* @param password the password used to join.
* @return a reference to this builder.
*/
public Builder withPassword(String password) {
this.password = password;
return this;
}
/**
* Set the timeout used when joining the MUC room.
*
* @param timeout the timeout to use when joining.
* @return a reference to this builder.
*/
public Builder timeoutAfter(long timeout) {
if (timeout <= 0) {
throw new IllegalArgumentException("timeout must be positive");
}
this.timeout = timeout;
return this;
}
/**
* Request that that MUC is going to sent us no history when joining.
*
* @return
* @return a reference to this builder.
*/
public Builder requestNoHistory() {
maxChars = 0;
maxStanzas = -1;
seconds = -1;
since = null;
return this;
}
/**
* Sets the total number of characters to receive in the history.
*
* @param maxChars the total number of characters to receive in the history.
* @return a reference to this builder.
*/
public Builder requestMaxCharsHistory(int maxChars) {
this.maxChars = maxChars;
return this;
}
/**
* Sets the total number of messages to receive in the history.
*
* @param maxStanzas the total number of messages to receive in the history.
* @return a reference to this builder.
*/
public Builder requestMaxStanzasHistory(int maxStanzas) {
this.maxStanzas = maxStanzas;
return this;
}
/**
* Sets the number of seconds to use to filter the messages received during that time.
* In other words, only the messages received in the last "X" seconds will be included in
* the history.
*
* @param seconds the number of seconds to use to filter the messages received during
* that time.
* @return a reference to this builder.
*/
public Builder requestHistorySince(int seconds) {
this.seconds = seconds;
return this;
}
/**
* Sets the since date to use to filter the messages received during that time.
* In other words, only the messages received since the datetime specified will be
* included in the history.
*
* @param since the since date to use to filter the messages received during that time.
* @return a reference to this builder.
*/
public Builder requestHistorySince(Date since) {
this.since = since;
return this;
}
/**
* Build a new {@link MucEnterConfiguration} with the current builder.
*
* @return a new {@code MucEnterConfiguration}.
*/
public MucEnterConfiguration build() {
return new MucEnterConfiguration(this);
}
}
}

View file

@ -284,10 +284,7 @@ public class MultiUserChat {
/** /**
* Enter a room, as described in XEP-45 7.2. * Enter a room, as described in XEP-45 7.2.
* *
* @param nickname * @param conf the configuration used to enter the room.
* @param password
* @param history
* @param timeout
* @return the returned presence by the service after the client send the initial presence in order to enter the room. * @return the returned presence by the service after the client send the initial presence in order to enter the room.
* @throws NotConnectedException * @throws NotConnectedException
* @throws NoResponseException * @throws NoResponseException
@ -296,8 +293,7 @@ public class MultiUserChat {
* @throws NotAMucServiceException * @throws NotAMucServiceException
* @see <a href="http://xmpp.org/extensions/xep-0045.html#enter">XEP-45 7.2 Entering a Room</a> * @see <a href="http://xmpp.org/extensions/xep-0045.html#enter">XEP-45 7.2 Entering a Room</a>
*/ */
private Presence enter(Resourcepart nickname, String password, DiscussionHistory history, private Presence enter(MucEnterConfiguration conf) throws NotConnectedException, NoResponseException,
long timeout) throws NotConnectedException, NoResponseException,
XMPPErrorException, InterruptedException, NotAMucServiceException { XMPPErrorException, InterruptedException, NotAMucServiceException {
StringUtils.requireNotNullOrEmpty(nickname, "Nickname must not be null or blank."); StringUtils.requireNotNullOrEmpty(nickname, "Nickname must not be null or blank.");
final DomainBareJid mucService = room.asDomainBareJid(); final DomainBareJid mucService = room.asDomainBareJid();
@ -310,19 +306,7 @@ public class MultiUserChat {
} }
// We enter a room by sending a presence packet where the "to" // We enter a room by sending a presence packet where the "to"
// field is in the form "roomName@service/nickname" // field is in the form "roomName@service/nickname"
Presence joinPresence = new Presence(Presence.Type.available); Presence joinPresence = conf.getJoinPresence(this);
final EntityFullJid jid = JidCreate.fullFrom(room, nickname);
joinPresence.setTo(jid);
// Indicate the the client supports MUC
MUCInitialPresence mucInitialPresence = new MUCInitialPresence();
if (password != null) {
mucInitialPresence.setPassword(password);
}
if (history != null) {
mucInitialPresence.setHistory(history.getMUCHistory());
}
joinPresence.addExtension(mucInitialPresence);
// Setup the messageListeners and presenceListeners *before* the join presence is send. // Setup the messageListeners and presenceListeners *before* the join presence is send.
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter); connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
@ -338,11 +322,11 @@ public class MultiUserChat {
// Wait for a presence packet back from the server. // Wait for a presence packet back from the server.
// Use a bare JID filter, since the room may rewrite the nickname. // Use a bare JID filter, since the room may rewrite the nickname.
StanzaFilter responseFilter = new AndFilter(FromMatchesFilter.createBare(jid), new StanzaTypeFilter( StanzaFilter responseFilter = new AndFilter(FromMatchesFilter.createBare(getRoom()), new StanzaTypeFilter(
Presence.class), MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF); Presence.class), MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF);
Presence presence; Presence presence;
try { try {
presence = connection.createPacketCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(timeout); presence = connection.createPacketCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(conf.getTimeout());
} }
catch (InterruptedException | NoResponseException | XMPPErrorException e) { catch (InterruptedException | NoResponseException | XMPPErrorException e) {
// Ensure that all callbacks are removed if there is an exception // Ensure that all callbacks are removed if there is an exception
@ -360,6 +344,17 @@ public class MultiUserChat {
return presence; return presence;
} }
/**
* Get a new MUC enter configuration builder.
*
* @param nickname the nickname used when entering the MUC room.
* @return a new MUC enter configuration builder.
* @since 4.2
*/
public MucEnterConfiguration.Builder getEnterConfigurationBuilder(Resourcepart nickname) {
return new MucEnterConfiguration.Builder(nickname, connection.getPacketReplyTimeout());
}
/** /**
* Creates the room according to some default configuration, assign the requesting user as the * Creates the room according to some default configuration, assign the requesting user as the
* room owner, and add the owner to the room but not allow anyone else to enter the room * room owner, and add the owner to the room but not allow anyone else to enter the room
@ -419,7 +414,8 @@ public class MultiUserChat {
*/ */
public synchronized MucCreateConfigFormHandle createOrJoin(Resourcepart nickname) throws NoResponseException, XMPPErrorException, public synchronized MucCreateConfigFormHandle createOrJoin(Resourcepart nickname) throws NoResponseException, XMPPErrorException,
InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException { InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException {
return createOrJoin(nickname, null, null, connection.getPacketReplyTimeout()); MucEnterConfiguration mucEnterConfiguration = getEnterConfigurationBuilder(nickname).build();
return createOrJoin(mucEnterConfiguration);
} }
/** /**
@ -440,14 +436,40 @@ public class MultiUserChat {
* @throws MucAlreadyJoinedException if the MUC is already joined * @throws MucAlreadyJoinedException if the MUC is already joined
* @throws NotConnectedException * @throws NotConnectedException
* @throws NotAMucServiceException * @throws NotAMucServiceException
* @deprecated use {@link #createOrJoin(MucEnterConfiguration)} instead.
*/ */
public synchronized MucCreateConfigFormHandle createOrJoin(Resourcepart nickname, String password, DiscussionHistory history, long timeout) @Deprecated
public MucCreateConfigFormHandle createOrJoin(Resourcepart nickname, String password, DiscussionHistory history, long timeout)
throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException {
MucEnterConfiguration.Builder builder = getEnterConfigurationBuilder(nickname).withPassword(
password).timeoutAfter(timeout);
return createOrJoin(builder.build());
}
/**
* Like {@link #create(Resourcepart)}, but will return true if the room creation was acknowledged by
* the service (with an 201 status code). It's up to the caller to decide, based on the return
* value, if he needs to continue sending the room configuration. If false is returned, the room
* already existed and the user is able to join right away, without sending a form.
*
* @param mucEnterConfiguration the configuration used to enter the MUC.
* @return A {@link MucCreateConfigFormHandle} if the room was created, or {code null} if the room was joined.
* @throws XMPPErrorException if the room couldn't be created for some reason (e.g. 405 error if
* the user is not allowed to create the room)
* @throws NoResponseException if there was no response from the server.
* @throws InterruptedException
* @throws MucAlreadyJoinedException if the MUC is already joined
* @throws NotConnectedException
* @throws NotAMucServiceException
*/
public synchronized MucCreateConfigFormHandle createOrJoin(MucEnterConfiguration mucEnterConfiguration)
throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException { throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException {
if (joined) { if (joined) {
throw new MucAlreadyJoinedException(); throw new MucAlreadyJoinedException();
} }
Presence presence = enter(nickname, password, history, timeout); Presence presence = enter(mucEnterConfiguration);
// Look for confirmation of room creation from the server // Look for confirmation of room creation from the server
MUCUser mucUser = MUCUser.from(presence); MUCUser mucUser = MUCUser.from(presence);
@ -458,7 +480,6 @@ public class MultiUserChat {
return null; return null;
} }
/** /**
* A handle used to configure a newly created room. As long as the room is not configured it will be locked, which * A handle used to configure a newly created room. As long as the room is not configured it will be locked, which
* means that no one is able to join. The room will become unlocked as soon it got configured. In order to create an * means that no one is able to join. The room will become unlocked as soon it got configured. In order to create an
@ -520,8 +541,10 @@ public class MultiUserChat {
if (isJoined()) { if (isJoined()) {
return null; return null;
} }
MucEnterConfiguration mucEnterConfiguration = getEnterConfigurationBuilder(nickname).withPassword(
password).build();
try { try {
return createOrJoin(nickname, password, null, connection.getPacketReplyTimeout()); return createOrJoin(mucEnterConfiguration);
} }
catch (MucAlreadyJoinedException e) { catch (MucAlreadyJoinedException e) {
return null; return null;
@ -548,8 +571,10 @@ public class MultiUserChat {
* @throws InterruptedException * @throws InterruptedException
* @throws NotAMucServiceException * @throws NotAMucServiceException
*/ */
public void join(Resourcepart nickname) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotAMucServiceException { public void join(Resourcepart nickname) throws NoResponseException, XMPPErrorException,
join(nickname, null, null, connection.getPacketReplyTimeout()); NotConnectedException, InterruptedException, NotAMucServiceException {
MucEnterConfiguration.Builder builder = getEnterConfigurationBuilder(nickname);
join(builder.build());
} }
/** /**
@ -576,7 +601,9 @@ public class MultiUserChat {
* @throws NotAMucServiceException * @throws NotAMucServiceException
*/ */
public void join(Resourcepart nickname, String password) throws XMPPErrorException, InterruptedException, NoResponseException, NotConnectedException, NotAMucServiceException { public void join(Resourcepart nickname, String password) throws XMPPErrorException, InterruptedException, NoResponseException, NotConnectedException, NotAMucServiceException {
join(nickname, password, null, connection.getPacketReplyTimeout()); MucEnterConfiguration.Builder builder = getEnterConfigurationBuilder(nickname).withPassword(
password);
join(builder.build());
} }
/** /**
@ -607,19 +634,55 @@ public class MultiUserChat {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
* @throws NotAMucServiceException * @throws NotAMucServiceException
* @deprecated use {@link #join(MucEnterConfiguration) instead.
*/ */
public synchronized void join( @Deprecated
public void join(
Resourcepart nickname, Resourcepart nickname,
String password, String password,
DiscussionHistory history, DiscussionHistory history,
long timeout) long timeout)
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException {
MucEnterConfiguration.Builder builder = getEnterConfigurationBuilder(nickname).withPassword(
password).timeoutAfter(timeout);
join(builder.build());
}
/**
* Joins the chat room using the specified nickname and password. If already joined
* using another nickname, this method will first leave the room and then
* re-join using the new nickname.<p>
*
* To control the amount of history to receive while joining a room you will need to provide
* a configured DiscussionHistory object.<p>
*
* A password is required when joining password protected rooms. If the room does
* not require a password there is no need to provide one.<p>
*
* If the room does not already exist when the user seeks to enter it, the server will
* decide to create a new room or not.
*
* @param mucEnterConfiguration the configuration used to enter the MUC.
* @throws XMPPErrorException if an error occurs joining the room. In particular, a
* 401 error can occur if no password was provided and one is required; or a
* 403 error can occur if the user is banned; or a
* 404 error can occur if the room does not exist or is locked; or a
* 407 error can occur if user is not on the member list; or a
* 409 error can occur if someone is already in the group chat with the same nickname.
* @throws NoResponseException if there was no response from the server.
* @throws NotConnectedException
* @throws InterruptedException
* @throws NotAMucServiceException
*/
public synchronized void join(MucEnterConfiguration mucEnterConfiguration)
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException { throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException {
// If we've already joined the room, leave it before joining under a new // If we've already joined the room, leave it before joining under a new
// nickname. // nickname.
if (joined) { if (joined) {
leave(); leave();
} }
enter(nickname, password, history, timeout); enter(mucEnterConfiguration);
} }
/** /**

View file

@ -42,9 +42,36 @@ public class MUCInitialPresence implements ExtensionElement {
public static final String ELEMENT = "x"; public static final String ELEMENT = "x";
public static final String NAMESPACE = "http://jabber.org/protocol/muc"; public static final String NAMESPACE = "http://jabber.org/protocol/muc";
// TODO make those fields final once deprecated setter methods have been removed.
private String password; private String password;
private History history; private History history;
/**
* Deprecated constructor.
* @deprecated use {@link #MUCInitialPresence(String, int, int, int, Date)} instead.
*/
@Deprecated
public MUCInitialPresence() {
}
/**
* Construct a new MUC initial presence extension.
*
* @param password the optional password used to enter the room.
* @param maxChars the maximal count of characters of history to request.
* @param maxStanzas the maximal count of stanzas of history to request.
* @param seconds the last seconds since when to request history.
* @param since the date since when to request history.
*/
public MUCInitialPresence(String password, int maxChars, int maxStanzas, int seconds, Date since) {
this.password = password;
if (maxChars > -1 || maxStanzas > -1 || seconds > -1 || since != null) {
this.history = new History(maxChars, maxStanzas, seconds, since);
} else {
this.history = null;
}
}
public String getElementName() { public String getElementName() {
return ELEMENT; return ELEMENT;
} }
@ -89,7 +116,9 @@ public class MUCInitialPresence implements ExtensionElement {
* *
* @param history that manages the amount of discussion history provided on * @param history that manages the amount of discussion history provided on
* entering a room. * entering a room.
* @deprecated use {@link #MUCInitialPresence(String, int, int, int, Date)} instead.
*/ */
@Deprecated
public void setHistory(History history) { public void setHistory(History history) {
this.history = history; this.history = history;
} }
@ -98,7 +127,9 @@ public class MUCInitialPresence implements ExtensionElement {
* Sets the password to use when the room requires a password. * Sets the password to use when the room requires a password.
* *
* @param password the password to use when the room requires a password. * @param password the password to use when the room requires a password.
* @deprecated use {@link #MUCInitialPresence(String, int, int, int, Date)} instead.
*/ */
@Deprecated
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
@ -135,11 +166,33 @@ public class MUCInitialPresence implements ExtensionElement {
public static final String ELEMENT = "history"; public static final String ELEMENT = "history";
private int maxChars = -1; // TODO make those fields final once the deprecated setter methods have been removed.
private int maxStanzas = -1; private int maxChars;
private int seconds = -1; private int maxStanzas;
private int seconds;
private Date since; private Date since;
/**
* Deprecated constructor.
* @deprecated use {@link #MUCInitialPresence.History(int, int, int, Date)} instead.
*/
@Deprecated
public History() {
this.maxChars = -1;
this.maxStanzas = -1;
this.seconds = -1;
}
public History(int maxChars, int maxStanzas, int seconds, Date since) {
if (maxChars < 0 && maxStanzas < 0 && seconds < 0 && since == null) {
throw new IllegalArgumentException();
}
this.maxChars = maxChars;
this.maxStanzas = maxStanzas;
this.seconds = seconds;
this.since = since;
}
/** /**
* Returns the total number of characters to receive in the history. * Returns the total number of characters to receive in the history.
* *
@ -184,7 +237,9 @@ public class MUCInitialPresence implements ExtensionElement {
* Sets the total number of characters to receive in the history. * Sets the total number of characters to receive in the history.
* *
* @param maxChars the total number of characters to receive in the history. * @param maxChars the total number of characters to receive in the history.
* @deprecated use {@link #MUCInitialPresence.History(int, int, int, Date)} instead.
*/ */
@Deprecated
public void setMaxChars(int maxChars) { public void setMaxChars(int maxChars) {
this.maxChars = maxChars; this.maxChars = maxChars;
} }
@ -193,7 +248,9 @@ public class MUCInitialPresence implements ExtensionElement {
* Sets the total number of messages to receive in the history. * Sets the total number of messages to receive in the history.
* *
* @param maxStanzas the total number of messages to receive in the history. * @param maxStanzas the total number of messages to receive in the history.
* @deprecated use {@link #MUCInitialPresence.History(int, int, int, Date)} instead.
*/ */
@Deprecated
public void setMaxStanzas(int maxStanzas) { public void setMaxStanzas(int maxStanzas) {
this.maxStanzas = maxStanzas; this.maxStanzas = maxStanzas;
} }
@ -205,7 +262,9 @@ public class MUCInitialPresence implements ExtensionElement {
* *
* @param seconds the number of seconds to use to filter the messages received during * @param seconds the number of seconds to use to filter the messages received during
* that time. * that time.
* @deprecated use {@link #MUCInitialPresence.History(int, int, int, Date)} instead.
*/ */
@Deprecated
public void setSeconds(int seconds) { public void setSeconds(int seconds) {
this.seconds = seconds; this.seconds = seconds;
} }
@ -216,7 +275,9 @@ public class MUCInitialPresence implements ExtensionElement {
* included in the history. * included in the history.
* *
* @param since the since date to use to filter the messages received during that time. * @param since the since date to use to filter the messages received during that time.
* @deprecated use {@link #MUCInitialPresence.History(int, int, int, Date)} instead.
*/ */
@Deprecated
public void setSince(Date since) { public void setSince(Date since) {
this.since = since; this.since = since;
} }