1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-11-22 06:12:05 +01:00

Bump Error Prone version to 2.3.4 and fix new bug patterns

This commit is contained in:
Florian Schmaus 2020-05-24 21:07:56 +02:00
parent 1c0bdfae40
commit d65f2c932e
19 changed files with 499 additions and 585 deletions

View file

@ -283,7 +283,7 @@ tasks.withType(Javadoc) {
testFixturesApi "org.mockito:mockito-core:3.3.3" testFixturesApi "org.mockito:mockito-core:3.3.3"
testImplementation 'com.jamesmurty.utils:java-xmlbuilder:1.2' testImplementation 'com.jamesmurty.utils:java-xmlbuilder:1.2'
errorprone 'com.google.errorprone:error_prone_core:2.3.3' errorprone 'com.google.errorprone:error_prone_core:2.3.4'
errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')
} }

View file

@ -373,17 +373,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected final AsyncButOrdered<StanzaListener> inOrderListeners = new AsyncButOrdered<>(); protected final AsyncButOrdered<StanzaListener> inOrderListeners = new AsyncButOrdered<>();
/**
* An executor which uses {@link #asyncGoLimited(Runnable)} to limit the number of asynchronously processed runnables
* per connection.
*/
private final Executor limitedExcutor = new Executor() {
@Override
public void execute(Runnable runnable) {
asyncGoLimited(runnable);
}
};
/** /**
* The used host to establish the connection to * The used host to establish the connection to
*/ */
@ -1524,7 +1513,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
executorService = ASYNC_BUT_ORDERED.asExecutorFor(this); executorService = ASYNC_BUT_ORDERED.asExecutorFor(this);
break; break;
case async: case async:
executorService = limitedExcutor; executorService = this::asyncGoLimited;
break; break;
} }
final IQRequestHandler finalIqRequestHandler = iqRequestHandler; final IQRequestHandler finalIqRequestHandler = iqRequestHandler;

View file

@ -38,7 +38,6 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
@ -143,14 +142,6 @@ public class TLSUtils {
return builder; return builder;
} }
private static final HostnameVerifier DOES_NOT_VERIFY_VERIFIER = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// This verifier doesn't verify the hostname, it always returns true.
return true;
}
};
/** /**
* Disable the hostname verification of TLS certificates. * Disable the hostname verification of TLS certificates.
* <p> * <p>
@ -164,7 +155,9 @@ public class TLSUtils {
* @return the given builder. * @return the given builder.
*/ */
public static <B extends ConnectionConfiguration.Builder<B, ?>> B disableHostnameVerificationForTlsCertificates(B builder) { public static <B extends ConnectionConfiguration.Builder<B, ?>> B disableHostnameVerificationForTlsCertificates(B builder) {
builder.setHostnameVerifier(DOES_NOT_VERIFY_VERIFIER); builder.setHostnameVerifier((hostname, session) -> {
return true;
});
return builder; return builder;
} }

View file

@ -25,6 +25,7 @@ import org.junit.Test;
public class DnsUtilTest { public class DnsUtilTest {
@SuppressWarnings("UnnecessaryAnonymousClass")
private static final SmackDaneProvider DNS_UTIL_TEST_DANE_PROVIDER = new SmackDaneProvider() { private static final SmackDaneProvider DNS_UTIL_TEST_DANE_PROVIDER = new SmackDaneProvider() {
@Override @Override
public SmackDaneVerifier newInstance() { public SmackDaneVerifier newInstance() {

View file

@ -25,7 +25,6 @@ import org.jivesoftware.smack.AsyncButOrdered;
import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
@ -68,23 +67,20 @@ public final class FallbackIndicationManager extends Manager {
private final StanzaFilter fallbackIndicationElementFilter = new AndFilter(StanzaTypeFilter.MESSAGE, private final StanzaFilter fallbackIndicationElementFilter = new AndFilter(StanzaTypeFilter.MESSAGE,
new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT, FallbackIndicationElement.NAMESPACE)); new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT, FallbackIndicationElement.NAMESPACE));
private final StanzaListener fallbackIndicationElementListener = new StanzaListener() { private void fallbackIndicationElementListener(Stanza packet) {
@Override Message message = (Message) packet;
public void processStanza(Stanza packet) { FallbackIndicationElement indicator = FallbackIndicationElement.fromMessage(message);
Message message = (Message) packet; String body = message.getBody();
FallbackIndicationElement indicator = FallbackIndicationElement.fromMessage(message); asyncButOrdered.performAsyncButOrdered(message.getFrom().asBareJid(), () -> {
String body = message.getBody(); for (FallbackIndicationListener l : listeners) {
asyncButOrdered.performAsyncButOrdered(message.getFrom().asBareJid(), () -> { l.onFallbackIndicationReceived(message, indicator, body);
for (FallbackIndicationListener l : listeners) { }
l.onFallbackIndicationReceived(message, indicator, body); });
} }
});
}
};
private FallbackIndicationManager(XMPPConnection connection) { private FallbackIndicationManager(XMPPConnection connection) {
super(connection); super(connection);
connection.addAsyncStanzaListener(fallbackIndicationElementListener, fallbackIndicationElementFilter); connection.addAsyncStanzaListener(this::fallbackIndicationElementListener, fallbackIndicationElementFilter);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(FallbackIndicationElement.NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection).addFeature(FallbackIndicationElement.NAMESPACE);
} }

View file

@ -307,7 +307,9 @@ public final class HttpFileUploadManager extends Manager {
public URL uploadFile(InputStream inputStream, String fileName, long fileSize, UploadProgressListener listener) throws XMPPErrorException, InterruptedException, SmackException, IOException { public URL uploadFile(InputStream inputStream, String fileName, long fileSize, UploadProgressListener listener) throws XMPPErrorException, InterruptedException, SmackException, IOException {
Objects.requireNonNull(inputStream, "Input Stream cannot be null"); Objects.requireNonNull(inputStream, "Input Stream cannot be null");
Objects.requireNonNull(fileName, "Filename Stream cannot be null"); Objects.requireNonNull(fileName, "Filename Stream cannot be null");
Objects.requireNonNull(fileSize, "Filesize Stream cannot be null"); if (fileSize < 0) {
throw new IllegalArgumentException("File size cannot be negative");
}
final Slot slot = requestSlot(fileName, fileSize, "application/octet-stream"); final Slot slot = requestSlot(fileName, fileSize, "application/octet-stream");
upload(inputStream, fileSize, slot, listener); upload(inputStream, fileSize, slot, listener);
return slot.getGetUrl(); return slot.getGetUrl();

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2018 Paul Schaub * Copyright 2018 Paul Schaub, 2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,10 +29,6 @@ import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter; import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.Predicate;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.sid.element.OriginIdElement; import org.jivesoftware.smackx.sid.element.OriginIdElement;
@ -62,14 +58,8 @@ public final class StableUniqueStanzaIdManager extends Manager {
// Filter that filters for messages with an origin id // Filter that filters for messages with an origin id
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE); private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
private static final Consumer<MessageBuilder> ADD_ORIGIN_ID_INTERCEPTOR = mb -> OriginIdElement.addOriginId(mb);
// We need a filter for outgoing messages that do not carry an origin-id already. // We need a filter for outgoing messages that do not carry an origin-id already.
private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER)); private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER));
private static final Predicate<Message> ADD_ORIGIN_ID_PREDICATE = m -> {
return ADD_ORIGIN_ID_FILTER.accept(m);
};
static { static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
@ -113,7 +103,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
* Start appending origin-id elements to outgoing stanzas and add the feature to disco. * Start appending origin-id elements to outgoing stanzas and add the feature to disco.
*/ */
public synchronized void enable() { public synchronized void enable() {
connection().addMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, ADD_ORIGIN_ID_PREDICATE); connection().addMessageInterceptor(OriginIdElement::addTo, ADD_ORIGIN_ID_FILTER::accept);
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
} }
@ -122,7 +112,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
*/ */
public synchronized void disable() { public synchronized void disable() {
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
connection().removeMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR); connection().removeMessageInterceptor(OriginIdElement::addTo);
} }
/** /**

View file

@ -39,7 +39,7 @@ public class OriginIdElement extends StableAndUniqueIdElement {
* *
* @param message message. * @param message message.
* @return the added origin-id element. * @return the added origin-id element.
* @deprecated use {@link #addOriginId(MessageBuilder)} instead. * @deprecated use {@link #addTo(MessageBuilder)} instead.
*/ */
@Deprecated @Deprecated
// TODO: Remove in Smack 4.5. // TODO: Remove in Smack 4.5.
@ -57,7 +57,7 @@ public class OriginIdElement extends StableAndUniqueIdElement {
* @param messageBuilder the message builder to add an origin ID to. * @param messageBuilder the message builder to add an origin ID to.
* @return the added origin-id element. * @return the added origin-id element.
*/ */
public static OriginIdElement addOriginId(MessageBuilder messageBuilder) { public static OriginIdElement addTo(MessageBuilder messageBuilder) {
OriginIdElement originId = new OriginIdElement(); OriginIdElement originId = new OriginIdElement();
messageBuilder.addExtension(originId); messageBuilder.addExtension(originId);
// TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID. // TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID.

View file

@ -78,7 +78,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertFalse(OriginIdElement.hasOriginId(message)); assertFalse(OriginIdElement.hasOriginId(message));
assertFalse(StanzaIdElement.hasStanzaId(message)); assertFalse(StanzaIdElement.hasStanzaId(message));
OriginIdElement.addOriginId(messageBuilder); OriginIdElement.addTo(messageBuilder);
message = messageBuilder.build(); message = messageBuilder.build();
assertTrue(OriginIdElement.hasOriginId(message)); assertTrue(OriginIdElement.hasOriginId(message));

View file

@ -54,7 +54,6 @@ import org.jivesoftware.smack.packet.PresenceBuilder;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.AbstractPresenceEventListener; import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smack.util.stringencoder.Base64;
@ -312,11 +311,11 @@ public final class EntityCapsManager extends Manager {
// Intercept presence packages and add caps data when intended. // Intercept presence packages and add caps data when intended.
// XEP-0115 specifies that a client SHOULD include entity capabilities // XEP-0115 specifies that a client SHOULD include entity capabilities
// with every presence notification it sends. // with every presence notification it sends.
private final Consumer<PresenceBuilder> presenceInterceptor = presenceBuilder -> { private void addCapsExtension(PresenceBuilder presenceBuilder) {
CapsVersionAndHash capsVersionAndHash = getCapsVersionAndHash(); CapsVersionAndHash capsVersionAndHash = getCapsVersionAndHash();
CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash); CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash);
presenceBuilder.overrideExtension(caps); presenceBuilder.overrideExtension(caps);
}; }
private EntityCapsManager(XMPPConnection connection) { private EntityCapsManager(XMPPConnection connection) {
super(connection); super(connection);
@ -402,7 +401,7 @@ public final class EntityCapsManager extends Manager {
} }
public synchronized void enableEntityCaps() { public synchronized void enableEntityCaps() {
connection().addPresenceInterceptor(presenceInterceptor, p -> { connection().addPresenceInterceptor(this::addCapsExtension, p -> {
return PresenceTypeFilter.AVAILABLE.accept(p); return PresenceTypeFilter.AVAILABLE.accept(p);
}); });
@ -415,7 +414,7 @@ public final class EntityCapsManager extends Manager {
entityCapsEnabled = false; entityCapsEnabled = false;
sdm.removeFeature(NAMESPACE); sdm.removeFeature(NAMESPACE);
connection().removePresenceInterceptor(presenceInterceptor); connection().removePresenceInterceptor(this::addCapsExtension);
} }
public boolean entityCapsEnabled() { public boolean entityCapsEnabled() {

View file

@ -517,7 +517,7 @@ public final class AdHocCommandManager extends Manager {
private boolean sessionSweeperScheduled; private boolean sessionSweeperScheduled;
private final Runnable sessionSweeper = () -> { private void sessionSweeper() {
final long currentTime = System.currentTimeMillis(); final long currentTime = System.currentTimeMillis();
synchronized (this) { synchronized (this) {
for (Iterator<Entry<String, LocalCommand>> it = executingCommands.entrySet().iterator(); it.hasNext();) { for (Iterator<Entry<String, LocalCommand>> it = executingCommands.entrySet().iterator(); it.hasNext();) {
@ -553,7 +553,7 @@ public final class AdHocCommandManager extends Manager {
} }
sessionSweeperScheduled = true; sessionSweeperScheduled = true;
schedule(sessionSweeper, 10, TimeUnit.SECONDS); schedule(this::sessionSweeper, 10, TimeUnit.SECONDS);
} }
/** /**

View file

@ -390,7 +390,7 @@ public final class PingManager extends Manager {
int nextPingIn = pingInterval - delta; int nextPingIn = pingInterval - delta;
LOGGER.fine("Scheduling ServerPingTask in " + nextPingIn + " seconds (pingInterval=" LOGGER.fine("Scheduling ServerPingTask in " + nextPingIn + " seconds (pingInterval="
+ pingInterval + ", delta=" + delta + ")"); + pingInterval + ", delta=" + delta + ")");
nextAutomaticPing = schedule(pingServerRunnable, nextPingIn, TimeUnit.SECONDS); nextAutomaticPing = schedule(this::pingServerIfNecessary, nextPingIn, TimeUnit.SECONDS);
} }
} }
@ -467,13 +467,4 @@ public final class PingManager extends Manager {
} }
}); });
} }
private final Runnable pingServerRunnable = new Runnable() {
@Override
public void run() {
LOGGER.fine("ServerPingTask run()");
pingServerIfNecessary();
}
};
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2014 Georg Lukas, 2015-2019 Florian Schmaus * Copyright 2013-2014 Georg Lukas, 2015-2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,11 +38,9 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaBuilder; import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
@ -273,8 +271,6 @@ public final class DeliveryReceiptManager extends Manager {
); );
// @formatter:on // @formatter:on
private static final Consumer<MessageBuilder> AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER = mb -> DeliveryReceiptRequest.addTo(mb);
/** /**
* Enables automatic requests of delivery receipts for outgoing messages of * Enables automatic requests of delivery receipts for outgoing messages of
* {@link org.jivesoftware.smack.packet.Message.Type#normal}, {@link org.jivesoftware.smack.packet.Message.Type#chat} or {@link org.jivesoftware.smack.packet.Message.Type#headline}, and * {@link org.jivesoftware.smack.packet.Message.Type#normal}, {@link org.jivesoftware.smack.packet.Message.Type#chat} or {@link org.jivesoftware.smack.packet.Message.Type#headline}, and
@ -284,7 +280,7 @@ public final class DeliveryReceiptManager extends Manager {
* @see #dontAutoAddDeliveryReceiptRequests() * @see #dontAutoAddDeliveryReceiptRequests()
*/ */
public void autoAddDeliveryReceiptRequests() { public void autoAddDeliveryReceiptRequests() {
connection().addMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER, m -> { connection().addMessageInterceptor(DeliveryReceiptRequest::addTo, m -> {
return MESSAGES_TO_REQUEST_RECEIPTS_FOR.accept(m); return MESSAGES_TO_REQUEST_RECEIPTS_FOR.accept(m);
}); });
} }
@ -296,7 +292,7 @@ public final class DeliveryReceiptManager extends Manager {
* @see #autoAddDeliveryReceiptRequests() * @see #autoAddDeliveryReceiptRequests()
*/ */
public void dontAutoAddDeliveryReceiptRequests() { public void dontAutoAddDeliveryReceiptRequests() {
connection().removeMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER); connection().removeMessageInterceptor(DeliveryReceiptRequest::addTo);
} }
/** /**

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2013-2015 the original author or authors * Copyright 2013-2015 the original author or authors, 2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package org.jivesoftware.smack.roster.rosterstore; package org.jivesoftware.smack.roster.rosterstore;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
@ -56,15 +55,10 @@ public final class DirectoryRosterStore implements RosterStore {
private static final String STORE_ID = "DEFAULT_ROSTER_STORE"; private static final String STORE_ID = "DEFAULT_ROSTER_STORE";
private static final Logger LOGGER = Logger.getLogger(DirectoryRosterStore.class.getName()); private static final Logger LOGGER = Logger.getLogger(DirectoryRosterStore.class.getName());
private static final FileFilter rosterDirFilter = new FileFilter() { private static boolean rosterDirFilter(File file) {
String name = file.getName();
@Override return name.startsWith(ENTRY_PREFIX);
public boolean accept(File file) { }
String name = file.getName();
return name.startsWith(ENTRY_PREFIX);
}
};
/** /**
* @param baseDir TODO javadoc me please * @param baseDir TODO javadoc me please
@ -122,7 +116,7 @@ public final class DirectoryRosterStore implements RosterStore {
public List<Item> getEntries() { public List<Item> getEntries() {
List<Item> entries = new ArrayList<>(); List<Item> entries = new ArrayList<>();
for (File file : fileDir.listFiles(rosterDirFilter)) { for (File file : fileDir.listFiles(DirectoryRosterStore::rosterDirFilter)) {
Item entry = readEntry(file); Item entry = readEntry(file);
if (entry == null) { if (entry == null) {
// Roster directory store corrupt. Abort and signal this by returning null. // Roster directory store corrupt. Abort and signal this by returning null.
@ -168,7 +162,7 @@ public final class DirectoryRosterStore implements RosterStore {
@Override @Override
public boolean resetEntries(Collection<Item> items, String version) { public boolean resetEntries(Collection<Item> items, String version) {
for (File file : fileDir.listFiles(rosterDirFilter)) { for (File file : fileDir.listFiles(DirectoryRosterStore::rosterDirFilter)) {
file.delete(); file.delete();
} }
for (Item item : items) { for (Item item : items) {

View file

@ -21,7 +21,6 @@ import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.chatstates.ChatState; import org.jivesoftware.smackx.chatstates.ChatState;
import org.jivesoftware.smackx.chatstates.ChatStateListener;
import org.jivesoftware.smackx.chatstates.ChatStateManager; import org.jivesoftware.smackx.chatstates.ChatStateManager;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
@ -34,25 +33,19 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
// Listener for composing chat state // Listener for composing chat state
private final SimpleResultSyncPoint composingSyncPoint = new SimpleResultSyncPoint(); private final SimpleResultSyncPoint composingSyncPoint = new SimpleResultSyncPoint();
private final ChatStateListener composingListener = new ChatStateListener() { private void composingListener(Chat chat, ChatState state, Message message) {
@Override if (state.equals(ChatState.composing)) {
public void stateChanged(Chat chat, ChatState state, Message message) { composingSyncPoint.signal();
if (state.equals(ChatState.composing)) {
composingSyncPoint.signal();
}
} }
}; }
// Listener for active chat state // Listener for active chat state
private final SimpleResultSyncPoint activeSyncPoint = new SimpleResultSyncPoint(); private final SimpleResultSyncPoint activeSyncPoint = new SimpleResultSyncPoint();
private final ChatStateListener activeListener = new ChatStateListener() { private void activeListener(Chat chat, ChatState state, Message message) {
@Override if (state.equals(ChatState.active)) {
public void stateChanged(Chat chat, ChatState state, Message message) { activeSyncPoint.signal();
if (state.equals(ChatState.active)) {
activeSyncPoint.signal();
}
} }
}; }
public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) { public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) {
@ -65,8 +58,8 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); ChatStateManager manTwo = ChatStateManager.getInstance(conTwo);
// Add chatState listeners. // Add chatState listeners.
manTwo.addChatStateListener(composingListener); manTwo.addChatStateListener(this::composingListener);
manTwo.addChatStateListener(activeListener); manTwo.addChatStateListener(this::activeListener);
Chat chatOne = ChatManager.getInstanceFor(conOne) Chat chatOne = ChatManager.getInstanceFor(conOne)
.chatWith(conTwo.getUser().asEntityBareJid()); .chatWith(conTwo.getUser().asEntityBareJid());
@ -86,7 +79,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
@AfterClass @AfterClass
public void cleanup() { public void cleanup() {
ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); ChatStateManager manTwo = ChatStateManager.getInstance(conTwo);
manTwo.removeChatStateListener(composingListener); manTwo.removeChatStateListener(this::composingListener);
manTwo.removeChatStateListener(activeListener); manTwo.removeChatStateListener(this::activeListener);
} }
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2017 Paul Schaub * Copyright 2017 Paul Schaub, 2020 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,17 +37,14 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.carbons.CarbonCopyReceivedListener;
import org.jivesoftware.smackx.carbons.CarbonManager; import org.jivesoftware.smackx.carbons.CarbonManager;
import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
@ -74,7 +71,6 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
import org.jivesoftware.smackx.omemo.trust.OmemoTrustCallback; import org.jivesoftware.smackx.omemo.trust.OmemoTrustCallback;
import org.jivesoftware.smackx.omemo.trust.TrustState; import org.jivesoftware.smackx.omemo.trust.TrustState;
import org.jivesoftware.smackx.omemo.util.MessageOrOmemoMessage; import org.jivesoftware.smackx.omemo.util.MessageOrOmemoMessage;
import org.jivesoftware.smackx.pep.PepListener;
import org.jivesoftware.smackx.pep.PepManager; import org.jivesoftware.smackx.pep.PepManager;
import org.jivesoftware.smackx.pubsub.EventElement; import org.jivesoftware.smackx.pubsub.EventElement;
import org.jivesoftware.smackx.pubsub.ItemsExtension; import org.jivesoftware.smackx.pubsub.ItemsExtension;
@ -897,23 +893,23 @@ public final class OmemoManager extends Manager {
CarbonManager carbonManager = CarbonManager.getInstanceFor(connection()); CarbonManager carbonManager = CarbonManager.getInstanceFor(connection());
// Remove listeners to avoid them getting added twice // Remove listeners to avoid them getting added twice
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener);
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); carbonManager.removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener);
pepManager.removePepListener(deviceListUpdateListener); pepManager.removePepListener(this::deviceListUpdateListener);
// Add listeners // Add listeners
pepManager.addPepListener(deviceListUpdateListener); pepManager.addPepListener(this::deviceListUpdateListener);
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter); connection().addAsyncStanzaListener(this::internalOmemoMessageStanzaListener, OmemoManager::isOmemoMessage);
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); carbonManager.addCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener);
} }
/** /**
* Remove active stanza listeners needed for OMEMO. * Remove active stanza listeners needed for OMEMO.
*/ */
public void stopStanzaAndPEPListeners() { public void stopStanzaAndPEPListeners() {
PepManager.getInstanceFor(connection()).removePepListener(deviceListUpdateListener); PepManager.getInstanceFor(connection()).removePepListener(this::deviceListUpdateListener);
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener);
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener);
} }
/** /**
@ -961,127 +957,113 @@ public final class OmemoManager extends Manager {
/** /**
* StanzaListener that listens for incoming Stanzas which contain OMEMO elements. * StanzaListener that listens for incoming Stanzas which contain OMEMO elements.
*/ */
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() { private void internalOmemoMessageStanzaListener(final Stanza packet) {
Async.go(new Runnable() {
@Override
public void run() {
try {
getOmemoService().onOmemoMessageStanzaReceived(packet,
new LoggedInOmemoManager(OmemoManager.this));
} catch (SmackException.NotLoggedInException | IOException e) {
LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e);
}
}
});
}
@Override /**
public void processStanza(final Stanza packet) { * CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements.
Async.go(new Runnable() { */
@Override private void internalOmemoCarbonCopyListener(final CarbonExtension.Direction direction,
public void run() { final Message carbonCopy,
final Message wrappingMessage) {
Async.go(new Runnable() {
@Override
public void run() {
if (isOmemoMessage(carbonCopy)) {
try { try {
getOmemoService().onOmemoMessageStanzaReceived(packet, getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage,
new LoggedInOmemoManager(OmemoManager.this)); new LoggedInOmemoManager(OmemoManager.this));
} catch (SmackException.NotLoggedInException | IOException e) { } catch (SmackException.NotLoggedInException | IOException e) {
LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e);
} }
} }
}); }
} });
}; }
/**
* CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements.
*/
private final CarbonCopyReceivedListener internalOmemoCarbonCopyListener = new CarbonCopyReceivedListener() {
@Override
public void onCarbonCopyReceived(final CarbonExtension.Direction direction,
final Message carbonCopy,
final Message wrappingMessage) {
Async.go(new Runnable() {
@Override
public void run() {
if (omemoMessageStanzaFilter.accept(carbonCopy)) {
try {
getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage,
new LoggedInOmemoManager(OmemoManager.this));
} catch (SmackException.NotLoggedInException | IOException e) {
LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e);
}
}
}
});
}
};
/** /**
* PEPListener that listens for OMEMO deviceList updates. * PEPListener that listens for OMEMO deviceList updates.
*/ */
private final PepListener deviceListUpdateListener = new PepListener() { private void deviceListUpdateListener(EntityBareJid from, EventElement event, Message message) {
@Override // Unknown sender, no more work to do.
public void eventReceived(EntityBareJid from, EventElement event, Message message) { if (from == null) {
// TODO: This DOES happen for some reason. Figure out when...
return;
}
// Unknown sender, no more work to do. for (ExtensionElement items : event.getExtensions()) {
if (from == null) { if (!(items instanceof ItemsExtension)) {
// TODO: This DOES happen for some reason. Figure out when... continue;
return;
} }
for (ExtensionElement items : event.getExtensions()) { for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) {
if (!(items instanceof ItemsExtension)) { if (!(item instanceof PayloadItem<?>)) {
continue; continue;
} }
for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) { PayloadItem<?> payloadItem = (PayloadItem<?>) item;
if (!(item instanceof PayloadItem<?>)) {
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) {
continue;
}
// Device List <list>
OmemoCachedDeviceList deviceList;
OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload();
try {
getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from,
receivedDeviceList);
if (!from.asBareJid().equals(getOwnJid())) {
continue; continue;
} }
PayloadItem<?> payloadItem = (PayloadItem<?>) item; deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice());
} catch (IOException e) {
LOGGER.log(Level.SEVERE,
"IOException while processing OMEMO PEP device updates. Message: " + message,
e);
continue;
}
final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList);
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) { if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) {
continue; LOGGER.log(Level.FINE, "Republish deviceList due to changes:" +
} " Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) +
" Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray()));
// Device List <list> Async.go(new Runnable() {
OmemoCachedDeviceList deviceList; @Override
OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload(); public void run() {
try { try {
getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, OmemoService.publishDeviceList(connection(), newDeviceList);
receivedDeviceList); } catch (InterruptedException | XMPPException.XMPPErrorException |
SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) {
if (!from.asBareJid().equals(getOwnJid())) { LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e);
continue;
}
deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice());
} catch (IOException e) {
LOGGER.log(Level.SEVERE,
"IOException while processing OMEMO PEP device updates. Message: " + message,
e);
continue;
}
final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList);
if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) {
LOGGER.log(Level.FINE, "Republish deviceList due to changes:" +
" Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) +
" Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray()));
Async.go(new Runnable() {
@Override
public void run() {
try {
OmemoService.publishDeviceList(connection(), newDeviceList);
} catch (InterruptedException | XMPPException.XMPPErrorException |
SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) {
LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e);
}
} }
}); }
} });
} }
} }
} }
}; }
/** /**
* StanzaFilter that filters messages containing a OMEMO element. * StanzaFilter that filters messages containing a OMEMO element.
*/ */
private final StanzaFilter omemoMessageStanzaFilter = new StanzaFilter() { private static boolean isOmemoMessage(Stanza stanza) {
@Override return stanza instanceof Message && OmemoManager.stanzaContainsOmemoElement(stanza);
public boolean accept(Stanza stanza) { }
return stanza instanceof Message && OmemoManager.stanzaContainsOmemoElement(stanza);
}
};
/** /**
* Guard class which ensures that the wrapped OmemoManager knows its BareJid. * Guard class which ensures that the wrapped OmemoManager knows its BareJid.

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. * Copyright 2017-2020 Florian Schmaus, 2018 Paul Schaub.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,7 +39,6 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat2.Chat; import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smack.util.stringencoder.Base64;
@ -180,7 +179,7 @@ public final class OpenPgpManager extends Manager {
*/ */
private OpenPgpManager(XMPPConnection connection) { private OpenPgpManager(XMPPConnection connection) {
super(connection); super(connection);
ChatManager.getInstanceFor(connection).addIncomingListener(incomingOpenPgpMessageListener); ChatManager.getInstanceFor(connection).addIncomingListener(this::incomingChatMessageListener);
pepManager = PepManager.getInstanceFor(connection); pepManager = PepManager.getInstanceFor(connection);
} }
@ -279,7 +278,7 @@ public final class OpenPgpManager extends Manager {
publishPublicKey(pepManager, pubkeyElement, primaryFingerprint); publishPublicKey(pepManager, pubkeyElement, primaryFingerprint);
// Subscribe to public key changes // Subscribe to public key changes
PepManager.getInstanceFor(connection()).addPepListener(metadataListener); PepManager.getInstanceFor(connection()).addPepListener(this::metadataListener);
ServiceDiscoveryManager.getInstanceFor(connection()) ServiceDiscoveryManager.getInstanceFor(connection())
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY); .addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
} }
@ -381,7 +380,7 @@ public final class OpenPgpManager extends Manager {
* Remove the metadata listener. This method is mainly used in tests. * Remove the metadata listener. This method is mainly used in tests.
*/ */
public void stopMetadataListener() { public void stopMetadataListener() {
PepManager.getInstanceFor(connection()).removePepListener(metadataListener); PepManager.getInstanceFor(connection()).removePepListener(this::metadataListener);
} }
/** /**
@ -502,25 +501,22 @@ public final class OpenPgpManager extends Manager {
* *
* @see <a href="https://xmpp.org/extensions/xep-0373.html#pubsub-notifications">XEP-0373 §4.4</a> * @see <a href="https://xmpp.org/extensions/xep-0373.html#pubsub-notifications">XEP-0373 §4.4</a>
*/ */
private final PepListener metadataListener = new PepListener() { private void metadataListener(final EntityBareJid from, final EventElement event, final Message message) {
@Override if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) {
public void eventReceived(final EntityBareJid from, final EventElement event, final Message message) { final BareJid contact = from.asBareJid();
if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) { LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + contact);
final BareJid contact = from.asBareJid(); Async.go(new Runnable() {
LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + contact); @Override
Async.go(new Runnable() { public void run() {
@Override ItemsExtension items = (ItemsExtension) event.getExtensions().get(0);
public void run() { PayloadItem<?> payload = (PayloadItem<?>) items.getItems().get(0);
ItemsExtension items = (ItemsExtension) event.getExtensions().get(0); PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload();
PayloadItem<?> payload = (PayloadItem<?>) items.getItems().get(0);
PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload();
processPublicKeysListElement(from, listElement); processPublicKeysListElement(from, listElement);
} }
}, "ProcessOXMetadata"); }, "ProcessOXMetadata");
}
} }
}; }
private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) { private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) {
OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible()); OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible());
@ -548,62 +544,60 @@ public final class OpenPgpManager extends Manager {
return provider.decryptAndOrVerify(element, getOpenPgpSelf(), sender); return provider.decryptAndOrVerify(element, getOpenPgpSelf(), sender);
} }
private final IncomingChatMessageListener incomingOpenPgpMessageListener = private void incomingChatMessageListener(final EntityBareJid from, final Message message, Chat chat) {
new IncomingChatMessageListener() { Async.go(new Runnable() {
@Override @Override
public void newIncomingMessage(final EntityBareJid from, final Message message, Chat chat) { public void run() {
Async.go(new Runnable() { OpenPgpElement element = message.getExtension(OpenPgpElement.class);
@Override if (element == null) {
public void run() { // Message does not contain an OpenPgpElement -> discard
OpenPgpElement element = message.getExtension(OpenPgpElement.class); return;
if (element == null) {
// Message does not contain an OpenPgpElement -> discard
return;
}
OpenPgpContact contact = getOpenPgpContact(from);
OpenPgpMessage decrypted = null;
OpenPgpContentElement contentElement = null;
try {
decrypted = decryptOpenPgpElement(element, contact);
contentElement = decrypted.getOpenPgpContentElement();
} catch (PGPException e) {
LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message", e);
} catch (XmlPullParserException | IOException e) {
LOGGER.log(Level.WARNING, "Invalid XML content of incoming OpenPGP encrypted message", e);
} catch (SmackException.NotLoggedInException e) {
LOGGER.log(Level.WARNING, "Cannot determine our JID, since we are not logged in.", e);
}
if (contentElement instanceof SigncryptElement) {
for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) {
l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement, decrypted.getMetadata());
}
return;
}
if (contentElement instanceof SignElement) {
for (SignElementReceivedListener l : signElementReceivedListeners) {
l.signElementReceived(contact, message, (SignElement) contentElement, decrypted.getMetadata());
}
return;
}
if (contentElement instanceof CryptElement) {
for (CryptElementReceivedListener l : cryptElementReceivedListeners) {
l.cryptElementReceived(contact, message, (CryptElement) contentElement, decrypted.getMetadata());
}
return;
}
else {
throw new AssertionError("Invalid element received: " + contentElement.getClass().getName());
}
}
});
} }
};
OpenPgpContact contact = getOpenPgpContact(from);
OpenPgpMessage decrypted = null;
OpenPgpContentElement contentElement = null;
try {
decrypted = decryptOpenPgpElement(element, contact);
contentElement = decrypted.getOpenPgpContentElement();
} catch (PGPException e) {
LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message", e);
} catch (XmlPullParserException | IOException e) {
LOGGER.log(Level.WARNING, "Invalid XML content of incoming OpenPGP encrypted message", e);
} catch (SmackException.NotLoggedInException e) {
LOGGER.log(Level.WARNING, "Cannot determine our JID, since we are not logged in.", e);
}
if (contentElement instanceof SigncryptElement) {
for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) {
l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement,
decrypted.getMetadata());
}
return;
}
if (contentElement instanceof SignElement) {
for (SignElementReceivedListener l : signElementReceivedListeners) {
l.signElementReceived(contact, message, (SignElement) contentElement, decrypted.getMetadata());
}
return;
}
if (contentElement instanceof CryptElement) {
for (CryptElementReceivedListener l : cryptElementReceivedListeners) {
l.cryptElementReceived(contact, message, (CryptElement) contentElement,
decrypted.getMetadata());
}
return;
}
else {
throw new AssertionError("Invalid element received: " + contentElement.getClass().getName());
}
}
});
}
/** /**
* Create a {@link PubkeyElement} which contains the OpenPGP public key of {@code owner} which belongs to * Create a {@link PubkeyElement} which contains the OpenPGP public key of {@code owner} which belongs to

View file

@ -44,7 +44,6 @@ import org.jivesoftware.smackx.ox.crypto.OpenPgpElementAndMetadata;
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement;
import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.element.SigncryptElement;
import org.jivesoftware.smackx.ox.listener.SigncryptElementReceivedListener;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
@ -127,7 +126,7 @@ public final class OXInstantMessagingManager extends Manager {
private OXInstantMessagingManager(final XMPPConnection connection) { private OXInstantMessagingManager(final XMPPConnection connection) {
super(connection); super(connection);
openPgpManager = OpenPgpManager.getInstanceFor(connection); openPgpManager = OpenPgpManager.getInstanceFor(connection);
openPgpManager.registerSigncryptReceivedListener(signcryptElementReceivedListener); openPgpManager.registerSigncryptReceivedListener(this::signcryptElementReceivedListener);
announceSupportForOxInstantMessaging(); announceSupportForOxInstantMessaging();
} }
@ -358,12 +357,9 @@ public final class OXInstantMessagingManager extends Manager {
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging."); message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
} }
private final SigncryptElementReceivedListener signcryptElementReceivedListener = new SigncryptElementReceivedListener() { private void signcryptElementReceivedListener(OpenPgpContact contact, Message originalMessage, SigncryptElement signcryptElement, OpenPgpMetadata metadata) {
@Override for (OxMessageListener listener : oxMessageListeners) {
public void signcryptElementReceived(OpenPgpContact contact, Message originalMessage, SigncryptElement signcryptElement, OpenPgpMetadata metadata) { listener.newIncomingOxMessage(contact, originalMessage, signcryptElement, metadata);
for (OxMessageListener listener : oxMessageListeners) {
listener.newIncomingOxMessage(contact, originalMessage, signcryptElement, metadata);
}
} }
}; }
} }

View file

@ -21,6 +21,7 @@ import java.net.InetSocketAddress;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.security.KeyManagementException; import java.security.KeyManagementException;
@ -61,7 +62,6 @@ import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException;
import org.jivesoftware.smack.SmackException.SmackWrappedException; import org.jivesoftware.smack.SmackException.SmackWrappedException;
import org.jivesoftware.smack.SmackFuture; import org.jivesoftware.smack.SmackFuture;
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
import org.jivesoftware.smack.SmackReactor.ChannelSelectedCallback;
import org.jivesoftware.smack.SmackReactor.SelectionKeyAttachment; import org.jivesoftware.smack.SmackReactor.SelectionKeyAttachment;
import org.jivesoftware.smack.XMPPException.FailedNonzaException; import org.jivesoftware.smack.XMPPException.FailedNonzaException;
import org.jivesoftware.smack.XmppInputOutputFilter; import org.jivesoftware.smack.XmppInputOutputFilter;
@ -280,294 +280,292 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
} }
}; };
private final ChannelSelectedCallback channelSelectedCallback = private void onChannelSelected(SelectableChannel selectedChannel, SelectionKey selectedSelectionKey) {
(selectedChannel, selectedSelectionKey) -> { assert selectionKey == null || selectionKey == selectedSelectionKey;
assert selectionKey == null || selectionKey == selectedSelectionKey; SocketChannel selectedSocketChannel = (SocketChannel) selectedChannel;
SocketChannel selectedSocketChannel = (SocketChannel) selectedChannel; // We are *always* interested in OP_READ.
// We are *always* interested in OP_READ. int newInterestedOps = SelectionKey.OP_READ;
int newInterestedOps = SelectionKey.OP_READ; boolean newPendingOutputFilterData = false;
boolean newPendingOutputFilterData = false;
if (!channelSelectedCallbackLock.tryLock()) { if (!channelSelectedCallbackLock.tryLock()) {
rejectedChannelSelectedCallbacks.incrementAndGet(); rejectedChannelSelectedCallbacks.incrementAndGet();
return;
}
handledChannelSelectedCallbacks++;
long callbackBytesRead = 0;
long callbackBytesWritten = 0;
try {
boolean destinationAddressChanged = false;
boolean isLastPartOfElement = false;
TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null;
StringBuilder outgoingStreamForDebugger = null;
writeLoop: while (true) {
final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty();
if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) {
if (filteredOutgoingBuffer != null) {
networkOutgoingBuffers.add(filteredOutgoingBuffer);
networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining();
filteredOutgoingBuffer = null;
if (moreDataAvailable && networkOutgoingBuffersBytes < 8096) {
continue;
}
}
ByteBuffer[] output = networkOutgoingBuffers.toArray(new ByteBuffer[networkOutgoingBuffers.size()]);
long bytesWritten;
try {
bytesWritten = selectedSocketChannel.write(output);
} catch (IOException e) {
// We have seen here so far
// - IOException "Broken pipe"
handleReadWriteIoException(e);
break;
}
if (bytesWritten == 0) {
newInterestedOps |= SelectionKey.OP_WRITE;
break;
}
callbackBytesWritten += bytesWritten;
networkOutgoingBuffersBytes -= bytesWritten;
List<? extends Buffer> prunedBuffers = pruneBufferList(networkOutgoingBuffers);
for (Buffer prunedBuffer : prunedBuffers) {
List<TopLevelStreamElement> sendElements = bufferToElementMap.remove(prunedBuffer);
if (sendElements == null) {
continue;
}
for (TopLevelStreamElement elementJustSend : sendElements) {
connectionInternal.fireFirstLevelElementSendListeners(elementJustSend);
}
}
// Prevent one callback from dominating the reactor thread. Break out of the write-loop if we have
// written a certain amount.
if (callbackBytesWritten > CALLBACK_MAX_BYTES_WRITEN) {
newInterestedOps |= SelectionKey.OP_WRITE;
callbackPreemtBecauseBytesWritten++;
break;
}
} else if (outgoingBuffer != null || pendingOutputFilterData) {
pendingOutputFilterData = false;
if (outgoingBuffer != null) {
totalBytesWrittenBeforeFilter += outgoingBuffer.remaining();
if (isLastPartOfElement) {
assert currentlyOutgonigTopLevelStreamElement != null;
currentlyOutgoingElements.add(currentlyOutgonigTopLevelStreamElement);
}
}
ByteBuffer outputFilterInputData = outgoingBuffer;
// We can now null the outgoingBuffer since the filter step will take care of it from now on.
outgoingBuffer = null;
for (ListIterator<XmppInputOutputFilter> it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) {
XmppInputOutputFilter inputOutputFilter = it.next();
XmppInputOutputFilter.OutputResult outputResult;
try {
outputResult = inputOutputFilter.output(outputFilterInputData, isLastPartOfElement,
destinationAddressChanged, moreDataAvailable);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
break writeLoop;
}
newPendingOutputFilterData |= outputResult.pendingFilterData;
outputFilterInputData = outputResult.filteredOutputData;
if (outputFilterInputData != null) {
outputFilterInputData.flip();
}
}
// It is ok if outpuFilterInputData is 'null' here, this is expected behavior.
if (outputFilterInputData != null && outputFilterInputData.hasRemaining()) {
filteredOutgoingBuffer = outputFilterInputData;
} else {
filteredOutgoingBuffer = null;
}
// If the filters did eventually not produce any output data but if there is
// pending output data then we have a pending write request after read.
if (filteredOutgoingBuffer == null && newPendingOutputFilterData) {
pendingWriteInterestAfterRead = true;
}
if (filteredOutgoingBuffer != null && isLastPartOfElement) {
bufferToElementMap.put(filteredOutgoingBuffer, new ArrayList<>(currentlyOutgoingElements));
currentlyOutgoingElements.clear();
}
// Reset that the destination address has changed.
if (destinationAddressChanged) {
destinationAddressChanged = false;
}
} else if (outgoingCharSequenceIterator != null) {
CharSequence nextCharSequence = outgoingCharSequenceIterator.next();
outgoingBuffer = UTF8.encode(nextCharSequence);
if (!outgoingCharSequenceIterator.hasNext()) {
outgoingCharSequenceIterator = null;
isLastPartOfElement = true;
} else {
isLastPartOfElement = false;
}
final SmackDebugger debugger = connectionInternal.smackDebugger;
if (debugger != null) {
if (outgoingStreamForDebugger == null) {
outgoingStreamForDebugger = new StringBuilder();
}
outgoingStreamForDebugger.append(nextCharSequence);
if (isLastPartOfElement) {
try {
outputDebugSplitter.append(outgoingStreamForDebugger);
} catch (IOException e) {
throw new AssertionError(e);
}
debugger.onOutgoingElementCompleted();
outgoingStreamForDebugger = null;
}
}
} else if (!connectionInternal.outgoingElementsQueue.isEmpty()) {
currentlyOutgonigTopLevelStreamElement = connectionInternal.outgoingElementsQueue.poll();
if (currentlyOutgonigTopLevelStreamElement instanceof Stanza) {
Stanza currentlyOutgoingStanza = (Stanza) currentlyOutgonigTopLevelStreamElement;
Jid currentDestinationAddress = currentlyOutgoingStanza.getTo();
destinationAddressChanged = !JidUtil.equals(lastDestinationAddress, currentDestinationAddress);
lastDestinationAddress = currentDestinationAddress;
}
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
if (nextCharSequence instanceof XmlStringBuilder) {
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
XmlEnvironment outgoingStreamXmlEnvironment = connectionInternal.getOutgoingStreamXmlEnvironment();
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
} else {
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
}
assert outgoingCharSequenceIterator != null;
} else {
// There is nothing more to write.
break;
}
}
pendingOutputFilterData = newPendingOutputFilterData;
if (!pendingWriteInterestAfterRead && pendingOutputFilterData) {
newInterestedOps |= SelectionKey.OP_WRITE;
}
readLoop: while (true) {
// Prevent one callback from dominating the reactor thread. Break out of the read-loop if we have
// read a certain amount.
if (callbackBytesRead > CALLBACK_MAX_BYTES_READ) {
callbackPreemtBecauseBytesRead++;
break;
}
int bytesRead;
incomingBuffer.clear();
try {
bytesRead = selectedSocketChannel.read(incomingBuffer);
} catch (IOException e) {
handleReadWriteIoException(e);
return; return;
} }
handledChannelSelectedCallbacks++; if (bytesRead < 0) {
LOGGER.finer("NIO read() returned " + bytesRead
long callbackBytesRead = 0; + " for " + this + ". This probably means that the TCP connection was terminated.");
long callbackBytesWritten = 0; // According to the socket channel javadoc section about "asynchronous reads" a socket channel's
// read() may return -1 if the input side of a socket is shut down.
try { // Note that we do not call notifyConnectionError() here because the connection may be
boolean destinationAddressChanged = false; // cleanly shutdown which would also cause read() to return '-1. I assume that this socket
boolean isLastPartOfElement = false; // will be selected again, on which read() would throw an IOException, which will be catched
TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null; // and invoke notifyConnectionError() (see a few lines above).
StringBuilder outgoingStreamForDebugger = null; /*
IOException exception = new IOException("NIO read() returned " + bytesRead);
writeLoop: while (true) { notifyConnectionError(exception);
final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty(); */
return;
if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) {
if (filteredOutgoingBuffer != null) {
networkOutgoingBuffers.add(filteredOutgoingBuffer);
networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining();
filteredOutgoingBuffer = null;
if (moreDataAvailable && networkOutgoingBuffersBytes < 8096) {
continue;
}
}
ByteBuffer[] output = networkOutgoingBuffers.toArray(new ByteBuffer[networkOutgoingBuffers.size()]);
long bytesWritten;
try {
bytesWritten = selectedSocketChannel.write(output);
} catch (IOException e) {
// We have seen here so far
// - IOException "Broken pipe"
handleReadWriteIoException(e);
break;
}
if (bytesWritten == 0) {
newInterestedOps |= SelectionKey.OP_WRITE;
break;
}
callbackBytesWritten += bytesWritten;
networkOutgoingBuffersBytes -= bytesWritten;
List<? extends Buffer> prunedBuffers = pruneBufferList(networkOutgoingBuffers);
for (Buffer prunedBuffer : prunedBuffers) {
List<TopLevelStreamElement> sendElements = bufferToElementMap.remove(prunedBuffer);
if (sendElements == null) {
continue;
}
for (TopLevelStreamElement elementJustSend : sendElements) {
connectionInternal.fireFirstLevelElementSendListeners(elementJustSend);
}
}
// Prevent one callback from dominating the reactor thread. Break out of the write-loop if we have
// written a certain amount.
if (callbackBytesWritten > CALLBACK_MAX_BYTES_WRITEN) {
newInterestedOps |= SelectionKey.OP_WRITE;
callbackPreemtBecauseBytesWritten++;
break;
}
} else if (outgoingBuffer != null || pendingOutputFilterData) {
pendingOutputFilterData = false;
if (outgoingBuffer != null) {
totalBytesWrittenBeforeFilter += outgoingBuffer.remaining();
if (isLastPartOfElement) {
assert currentlyOutgonigTopLevelStreamElement != null;
currentlyOutgoingElements.add(currentlyOutgonigTopLevelStreamElement);
}
}
ByteBuffer outputFilterInputData = outgoingBuffer;
// We can now null the outgoingBuffer since the filter step will take care of it from now on.
outgoingBuffer = null;
for (ListIterator<XmppInputOutputFilter> it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) {
XmppInputOutputFilter inputOutputFilter = it.next();
XmppInputOutputFilter.OutputResult outputResult;
try {
outputResult = inputOutputFilter.output(outputFilterInputData, isLastPartOfElement,
destinationAddressChanged, moreDataAvailable);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
break writeLoop;
}
newPendingOutputFilterData |= outputResult.pendingFilterData;
outputFilterInputData = outputResult.filteredOutputData;
if (outputFilterInputData != null) {
outputFilterInputData.flip();
}
}
// It is ok if outpuFilterInputData is 'null' here, this is expected behavior.
if (outputFilterInputData != null && outputFilterInputData.hasRemaining()) {
filteredOutgoingBuffer = outputFilterInputData;
} else {
filteredOutgoingBuffer = null;
}
// If the filters did eventually not produce any output data but if there is
// pending output data then we have a pending write request after read.
if (filteredOutgoingBuffer == null && newPendingOutputFilterData) {
pendingWriteInterestAfterRead = true;
}
if (filteredOutgoingBuffer != null && isLastPartOfElement) {
bufferToElementMap.put(filteredOutgoingBuffer, new ArrayList<>(currentlyOutgoingElements));
currentlyOutgoingElements.clear();
}
// Reset that the destination address has changed.
if (destinationAddressChanged) {
destinationAddressChanged = false;
}
} else if (outgoingCharSequenceIterator != null) {
CharSequence nextCharSequence = outgoingCharSequenceIterator.next();
outgoingBuffer = UTF8.encode(nextCharSequence);
if (!outgoingCharSequenceIterator.hasNext()) {
outgoingCharSequenceIterator = null;
isLastPartOfElement = true;
} else {
isLastPartOfElement = false;
}
final SmackDebugger debugger = connectionInternal.smackDebugger;
if (debugger != null) {
if (outgoingStreamForDebugger == null) {
outgoingStreamForDebugger = new StringBuilder();
}
outgoingStreamForDebugger.append(nextCharSequence);
if (isLastPartOfElement) {
try {
outputDebugSplitter.append(outgoingStreamForDebugger);
} catch (IOException e) {
throw new AssertionError(e);
}
debugger.onOutgoingElementCompleted();
outgoingStreamForDebugger = null;
}
}
} else if (!connectionInternal.outgoingElementsQueue.isEmpty()) {
currentlyOutgonigTopLevelStreamElement = connectionInternal.outgoingElementsQueue.poll();
if (currentlyOutgonigTopLevelStreamElement instanceof Stanza) {
Stanza currentlyOutgoingStanza = (Stanza) currentlyOutgonigTopLevelStreamElement;
Jid currentDestinationAddress = currentlyOutgoingStanza.getTo();
destinationAddressChanged = !JidUtil.equals(lastDestinationAddress, currentDestinationAddress);
lastDestinationAddress = currentDestinationAddress;
}
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
if (nextCharSequence instanceof XmlStringBuilder) {
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
XmlEnvironment outgoingStreamXmlEnvironment = connectionInternal.getOutgoingStreamXmlEnvironment();
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
} else {
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
}
assert outgoingCharSequenceIterator != null;
} else {
// There is nothing more to write.
break;
}
}
pendingOutputFilterData = newPendingOutputFilterData;
if (!pendingWriteInterestAfterRead && pendingOutputFilterData) {
newInterestedOps |= SelectionKey.OP_WRITE;
}
readLoop: while (true) {
// Prevent one callback from dominating the reactor thread. Break out of the read-loop if we have
// read a certain amount.
if (callbackBytesRead > CALLBACK_MAX_BYTES_READ) {
callbackPreemtBecauseBytesRead++;
break;
}
int bytesRead;
incomingBuffer.clear();
try {
bytesRead = selectedSocketChannel.read(incomingBuffer);
} catch (IOException e) {
handleReadWriteIoException(e);
return;
}
if (bytesRead < 0) {
LOGGER.finer("NIO read() returned " + bytesRead
+ " for " + this + ". This probably means that the TCP connection was terminated.");
// According to the socket channel javadoc section about "asynchronous reads" a socket channel's
// read() may return -1 if the input side of a socket is shut down.
// Note that we do not call notifyConnectionError() here because the connection may be
// cleanly shutdown which would also cause read() to return '-1. I assume that this socket
// will be selected again, on which read() would throw an IOException, which will be catched
// and invoke notifyConnectionError() (see a few lines above).
/*
IOException exception = new IOException("NIO read() returned " + bytesRead);
notifyConnectionError(exception);
*/
return;
}
if (!pendingInputFilterData) {
if (bytesRead == 0) {
// Nothing more to read.
break;
}
} else {
pendingInputFilterData = false;
}
// We have successfully read something. It is now possible that a filter is now also able to write
// additional data (for example SSLEngine).
if (pendingWriteInterestAfterRead) {
pendingWriteInterestAfterRead = false;
newInterestedOps |= SelectionKey.OP_WRITE;
}
callbackBytesRead += bytesRead;
ByteBuffer filteredIncomingBuffer = incomingBuffer;
for (ListIterator<XmppInputOutputFilter> it = connectionInternal.getXmppInputOutputFilterEndIterator(); it.hasPrevious();) {
filteredIncomingBuffer.flip();
ByteBuffer newFilteredIncomingBuffer;
try {
newFilteredIncomingBuffer = it.previous().input(filteredIncomingBuffer);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
return;
}
if (newFilteredIncomingBuffer == null) {
break readLoop;
}
filteredIncomingBuffer = newFilteredIncomingBuffer;
}
final int bytesReadAfterFilter = filteredIncomingBuffer.flip().remaining();
totalBytesReadAfterFilter += bytesReadAfterFilter;
try {
splitter.write(filteredIncomingBuffer);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
return;
}
}
} finally {
totalBytesWritten += callbackBytesWritten;
totalBytesRead += callbackBytesRead;
channelSelectedCallbackLock.unlock();
} }
// Indicate that there is no reactor thread racing towards handling this selection key. if (!pendingInputFilterData) {
final SelectionKeyAttachment selectionKeyAttachment = this.selectionKeyAttachment; if (bytesRead == 0) {
if (selectionKeyAttachment != null) { // Nothing more to read.
selectionKeyAttachment.resetReactorThreadRacing(); break;
}
} else {
pendingInputFilterData = false;
} }
// Check the queue again to prevent lost wakeups caused by elements inserted before we // We have successfully read something. It is now possible that a filter is now also able to write
// called resetReactorThreadRacing() a few lines above. // additional data (for example SSLEngine).
if (!connectionInternal.outgoingElementsQueue.isEmpty()) { if (pendingWriteInterestAfterRead) {
setWriteInterestAfterChannelSelectedCallback.incrementAndGet(); pendingWriteInterestAfterRead = false;
newInterestedOps |= SelectionKey.OP_WRITE; newInterestedOps |= SelectionKey.OP_WRITE;
} }
connectionInternal.setInterestOps(selectionKey, newInterestedOps); callbackBytesRead += bytesRead;
};
ByteBuffer filteredIncomingBuffer = incomingBuffer;
for (ListIterator<XmppInputOutputFilter> it = connectionInternal.getXmppInputOutputFilterEndIterator(); it.hasPrevious();) {
filteredIncomingBuffer.flip();
ByteBuffer newFilteredIncomingBuffer;
try {
newFilteredIncomingBuffer = it.previous().input(filteredIncomingBuffer);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
return;
}
if (newFilteredIncomingBuffer == null) {
break readLoop;
}
filteredIncomingBuffer = newFilteredIncomingBuffer;
}
final int bytesReadAfterFilter = filteredIncomingBuffer.flip().remaining();
totalBytesReadAfterFilter += bytesReadAfterFilter;
try {
splitter.write(filteredIncomingBuffer);
} catch (IOException e) {
connectionInternal.notifyConnectionError(e);
return;
}
}
} finally {
totalBytesWritten += callbackBytesWritten;
totalBytesRead += callbackBytesRead;
channelSelectedCallbackLock.unlock();
}
// Indicate that there is no reactor thread racing towards handling this selection key.
final SelectionKeyAttachment selectionKeyAttachment = this.selectionKeyAttachment;
if (selectionKeyAttachment != null) {
selectionKeyAttachment.resetReactorThreadRacing();
}
// Check the queue again to prevent lost wakeups caused by elements inserted before we
// called resetReactorThreadRacing() a few lines above.
if (!connectionInternal.outgoingElementsQueue.isEmpty()) {
setWriteInterestAfterChannelSelectedCallback.incrementAndGet();
newInterestedOps |= SelectionKey.OP_WRITE;
}
connectionInternal.setInterestOps(selectionKey, newInterestedOps);
}
private void handleReadWriteIoException(IOException e) { private void handleReadWriteIoException(IOException e) {
if (e instanceof ClosedChannelException && !tcpNioTransport.isConnected()) { if (e instanceof ClosedChannelException && !tcpNioTransport.isConnected()) {
@ -677,7 +675,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
} }
@Override @Override
public Stats getStats() { public XmppTcpTransportModule.Stats getStats() {
return XmppTcpTransportModule.this.getStats(); return XmppTcpTransportModule.this.getStats();
} }
@ -774,7 +772,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
remoteAddress = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress(); remoteAddress = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
selectionKey = connectionInternal.registerWithSelector(socketChannel, SelectionKey.OP_READ, selectionKey = connectionInternal.registerWithSelector(socketChannel, SelectionKey.OP_READ,
channelSelectedCallback); XmppTcpTransportModule.this::onChannelSelected);
selectionKeyAttachment = (SelectionKeyAttachment) selectionKey.attachment(); selectionKeyAttachment = (SelectionKeyAttachment) selectionKey.attachment();
connectionInternal.setTransport(tcpNioTransport); connectionInternal.setTransport(tcpNioTransport);
@ -1316,7 +1314,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
pendingOutputFilterData = true; pendingOutputFilterData = true;
} }
channelSelectedCallback.onChannelSelected(channel, key); onChannelSelected(channel, key);
} finally { } finally {
channelSelectedCallbackLock.unlock(); channelSelectedCallbackLock.unlock();
} }
@ -1347,7 +1345,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
return CollectionUtil.removeUntil(buffers, b -> b.hasRemaining()); return CollectionUtil.removeUntil(buffers, b -> b.hasRemaining());
} }
public Stats getStats() { public XmppTcpTransportModule.Stats getStats() {
return new Stats(this); return new Stats(this);
} }