1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-13 15:14:54 +02:00
Smack/smack-core/src/main/java/org/jivesoftware/smack/NonzaCallback.java
Florian Schmaus eeb6c52f7e Move SASL logic into AbstractXMPPConnection
Besides the way the transport handles the stream after SASL
<success/>, the SASL logic is independend from the underlying
transport (BOSH, TCP, …). Hence move it up into
AbstractXMPPConnection.

This also has the benefit that we can make some more methods private
or package-private.

Also introduce XmlStringBuilder.optTextChild(), which causes some
associated changes.
2019-09-25 13:49:21 +02:00

199 lines
6.9 KiB
Java

/**
*
* Copyright 2018-2019 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.smack;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.util.XmppElementUtil;
public class NonzaCallback {
protected final AbstractXMPPConnection connection;
protected final Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners;
private NonzaCallback(Builder builder) {
this.connection = builder.connection;
this.filterAndListeners = builder.filterAndListeners;
install();
}
void onNonzaReceived(Nonza nonza) throws IOException {
QName key = nonza.getQName();
ClassAndConsumer<? extends Nonza> classAndConsumer = filterAndListeners.get(key);
classAndConsumer.accept(nonza);
}
public void cancel() {
for (Map.Entry<QName, ClassAndConsumer<? extends Nonza>> entry : filterAndListeners.entrySet()) {
QName filterKey = entry.getKey();
synchronized (connection.nonzaCallbacksMap) {
connection.nonzaCallbacksMap.removeOne(filterKey, this);
}
}
}
protected void install() {
if (filterAndListeners.isEmpty()) {
return;
}
for (QName key : filterAndListeners.keySet()) {
synchronized (connection.nonzaCallbacksMap) {
connection.nonzaCallbacksMap.put(key, this);
}
}
}
private static final class NonzaResponseCallback<SN extends Nonza, FN extends Nonza> extends NonzaCallback {
private SN successNonza;
private FN failedNonza;
private NonzaResponseCallback(Class<SN> successNonzaClass, Class<FN> failedNonzaClass,
Builder builder) {
super(builder);
final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
final NonzaListener<SN> successListener = new NonzaListener<SN>() {
@Override
public void accept(SN successNonza) {
NonzaResponseCallback.this.successNonza = successNonza;
notifyResponse();
}
};
final ClassAndConsumer<SN> successClassAndConsumer = new ClassAndConsumer<>(successNonzaClass,
successListener);
final NonzaListener<FN> failedListener = new NonzaListener<FN>() {
@Override
public void accept(FN failedNonza) {
NonzaResponseCallback.this.failedNonza = failedNonza;
notifyResponse();
}
};
final ClassAndConsumer<FN> failedClassAndConsumer = new ClassAndConsumer<>(failedNonzaClass,
failedListener);
filterAndListeners.put(successNonzaKey, successClassAndConsumer);
filterAndListeners.put(failedNonzaKey, failedClassAndConsumer);
install();
}
private void notifyResponse() {
synchronized (this) {
notifyAll();
}
}
private boolean hasReceivedSuccessOrFailedNonza() {
return successNonza != null || failedNonza != null;
}
private SN waitForResponse() throws NoResponseException, InterruptedException, FailedNonzaException {
final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
synchronized (this) {
while (!hasReceivedSuccessOrFailedNonza()) {
final long now = System.currentTimeMillis();
if (now >= deadline) break;
wait(deadline - now);
}
}
if (!hasReceivedSuccessOrFailedNonza()) {
throw NoResponseException.newWith(connection, "Nonza Listener");
}
if (failedNonza != null) {
throw new XMPPException.FailedNonzaException(failedNonza);
}
assert successNonza != null;
return successNonza;
}
}
public static final class Builder {
private final AbstractXMPPConnection connection;
private Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners = new HashMap<>();
Builder(AbstractXMPPConnection connection) {
this.connection = connection;
}
public <N extends Nonza> Builder listenFor(Class<N> nonza, NonzaListener<N> nonzaListener) {
QName key = XmppElementUtil.getQNameFor(nonza);
ClassAndConsumer<N> classAndConsumer = new ClassAndConsumer<>(nonza, nonzaListener);
filterAndListeners.put(key, classAndConsumer);
return this;
}
public NonzaCallback install() {
return new NonzaCallback(this);
}
}
public interface NonzaListener<N extends Nonza> {
void accept(N nonza) throws IOException;
}
private static final class ClassAndConsumer<N extends Nonza> {
private final Class<N> clazz;
private final NonzaListener<N> consumer;
private ClassAndConsumer(Class<N> clazz, NonzaListener<N> consumer) {
this.clazz = clazz;
this.consumer = consumer;
}
private void accept(Object object) throws IOException {
N nonza = clazz.cast(object);
consumer.accept(nonza);
}
}
static <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(NonzaCallback.Builder builder, Nonza nonza, Class<SN> successNonzaClass,
Class<FN> failedNonzaClass)
throws NoResponseException, NotConnectedException, InterruptedException, FailedNonzaException {
NonzaResponseCallback<SN, FN> nonzaCallback = new NonzaResponseCallback<>(successNonzaClass,
failedNonzaClass, builder);
SN successNonza;
try {
nonzaCallback.connection.sendNonza(nonza);
successNonza = nonzaCallback.waitForResponse();
}
finally {
nonzaCallback.cancel();
}
return successNonza;
}
}