1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-13 23:24:50 +02:00
Smack/smack-experimental/src/main/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationManager.java
Florian Schmaus 3d4e7938a7 Make ExtensionElement marker interface wrt. QNAME field
ExtensionElement is now a marker interface that requires all
implementation non-abstract classes to carry a static final QNAME
field (of type QName). This is verified by a new unit test.

Also FullyQualifiedElement is renamed to simply XmlElement. XmlElement
is used over ExtensionElement when implementing classes do not
statically know the qualified name of the XML elements they
represent. In general, XmlElement should be used sparingly, and every
XML element should be modeled by its own Java class (implementing
ExtensionElement).
2021-04-18 21:07:19 +02:00

172 lines
7.3 KiB
Java

/**
*
* Copyright 2020 Paul Schaub
*
* 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.fallback_indication;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jivesoftware.smack.AsyncButOrdered;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.fallback_indication.element.FallbackIndicationElement;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
/**
* Smacks API for XEP-0428: Fallback Indication.
* In some scenarios it might make sense to mark the body of a message as fallback for legacy clients.
* Examples are encryption mechanisms where the sender might include a hint for legacy clients stating that the
* body (eg. "This message is encrypted") should be ignored.
*
* @see <a href="https://xmpp.org/extensions/xep-0428.html">XEP-0428: Fallback Indication</a>
*/
public final class FallbackIndicationManager extends Manager {
private static final Map<XMPPConnection, FallbackIndicationManager> INSTANCES = new WeakHashMap<>();
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
@Override
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
private final Set<FallbackIndicationListener> listeners = new CopyOnWriteArraySet<>();
private final AsyncButOrdered<BareJid> asyncButOrdered = new AsyncButOrdered<>();
private final StanzaFilter fallbackIndicationElementFilter = new AndFilter(StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT, FallbackIndicationElement.NAMESPACE));
private void fallbackIndicationElementListener(Stanza packet) {
Message message = (Message) packet;
FallbackIndicationElement indicator = FallbackIndicationElement.fromMessage(message);
String body = message.getBody();
asyncButOrdered.performAsyncButOrdered(message.getFrom().asBareJid(), () -> {
for (FallbackIndicationListener l : listeners) {
l.onFallbackIndicationReceived(message, indicator, body);
}
});
}
private FallbackIndicationManager(XMPPConnection connection) {
super(connection);
connection.addAsyncStanzaListener(this::fallbackIndicationElementListener, fallbackIndicationElementFilter);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(FallbackIndicationElement.NAMESPACE);
}
public static synchronized FallbackIndicationManager getInstanceFor(XMPPConnection connection) {
FallbackIndicationManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new FallbackIndicationManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
/**
* Determine, whether or not a user supports Fallback Indications.
*
* @param jid BareJid of the user.
* @return feature support
*
* @throws XMPPException.XMPPErrorException if a protocol level error happens
* @throws SmackException.NotConnectedException if the connection is not connected
* @throws InterruptedException if the thread is being interrupted
* @throws SmackException.NoResponseException if the server doesn't send a response in time
*/
public boolean userSupportsFallbackIndications(EntityBareJid jid)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException {
return ServiceDiscoveryManager.getInstanceFor(connection())
.supportsFeature(jid, FallbackIndicationElement.NAMESPACE);
}
/**
* Determine, whether or not the server supports Fallback Indications.
*
* @return server side feature support
*
* @throws XMPPException.XMPPErrorException if a protocol level error happens
* @throws SmackException.NotConnectedException if the connection is not connected
* @throws InterruptedException if the thread is being interrupted
* @throws SmackException.NoResponseException if the server doesn't send a response in time
*/
public boolean serverSupportsFallbackIndications()
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException {
return ServiceDiscoveryManager.getInstanceFor(connection())
.serverSupportsFeature(FallbackIndicationElement.NAMESPACE);
}
/**
* Set the body of the message to the provided fallback message and add a {@link FallbackIndicationElement}.
*
* @param messageBuilder message builder
* @param fallbackMessageBody fallback message body
* @return builder with set body and added fallback element
*/
public static MessageBuilder addFallbackIndicationWithBody(MessageBuilder messageBuilder, String fallbackMessageBody) {
return addFallbackIndication(messageBuilder).setBody(fallbackMessageBody);
}
/**
* Add a {@link FallbackIndicationElement} to the provided message builder.
*
* @param messageBuilder message builder
* @return message builder with added fallback element
*/
public static MessageBuilder addFallbackIndication(MessageBuilder messageBuilder) {
return messageBuilder.addExtension(new FallbackIndicationElement());
}
/**
* Register a {@link FallbackIndicationListener} that gets notified whenever a message that contains a
* {@link FallbackIndicationElement} is received.
*
* @param listener listener to be registered.
*/
public synchronized void addFallbackIndicationListener(FallbackIndicationListener listener) {
listeners.add(listener);
}
/**
* Unregister a {@link FallbackIndicationListener}.
*
* @param listener listener to be unregistered.
*/
public synchronized void removeFallbackIndicationListener(FallbackIndicationListener listener) {
listeners.remove(listener);
}
}