Migrate `MessagesDynamoDbRule` to `MessagesDynamoDbExtension`
This commit is contained in:
		
							parent
							
								
									6a5d475198
								
							
						
					
					
						commit
						83e0a19561
					
				|  | @ -25,6 +25,7 @@ import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; | |||
| import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; | ||||
| import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; | ||||
| import software.amazon.awssdk.services.dynamodb.model.KeyType; | ||||
| import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; | ||||
| 
 | ||||
| public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback { | ||||
|  | @ -45,6 +46,7 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
| 
 | ||||
|   private final List<AttributeDefinition> attributeDefinitions; | ||||
|   private final List<GlobalSecondaryIndex> globalSecondaryIndexes; | ||||
|   private final List<LocalSecondaryIndex> localSecondaryIndexes; | ||||
| 
 | ||||
|   private final long readCapacityUnits; | ||||
|   private final long writeCapacityUnits; | ||||
|  | @ -53,12 +55,16 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
|   private DynamoDbAsyncClient dynamoAsyncDB2; | ||||
|   private AmazonDynamoDB legacyDynamoClient; | ||||
| 
 | ||||
|   private DynamoDbExtension(String tableName, String hashKey, String rangeKey, List<AttributeDefinition> attributeDefinitions, List<GlobalSecondaryIndex> globalSecondaryIndexes, long readCapacityUnits, | ||||
|   private DynamoDbExtension(String tableName, String hashKey, String rangeKey, | ||||
|       List<AttributeDefinition> attributeDefinitions, List<GlobalSecondaryIndex> globalSecondaryIndexes, | ||||
|       final List<LocalSecondaryIndex> localSecondaryIndexes, | ||||
|       long readCapacityUnits, | ||||
|       long writeCapacityUnits) { | ||||
| 
 | ||||
|     this.tableName = tableName; | ||||
|     this.hashKeyName = hashKey; | ||||
|     this.rangeKeyName = rangeKey; | ||||
|     this.localSecondaryIndexes = localSecondaryIndexes; | ||||
| 
 | ||||
|     this.readCapacityUnits = readCapacityUnits; | ||||
|     this.writeCapacityUnits = writeCapacityUnits; | ||||
|  | @ -108,6 +114,7 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
|         .keySchema(keySchemaElements) | ||||
|         .attributeDefinitions(attributeDefinitions.isEmpty() ? null : attributeDefinitions) | ||||
|         .globalSecondaryIndexes(globalSecondaryIndexes.isEmpty() ? null : globalSecondaryIndexes) | ||||
|         .localSecondaryIndexes(localSecondaryIndexes.isEmpty() ? null : localSecondaryIndexes) | ||||
|         .provisionedThroughput(ProvisionedThroughput.builder() | ||||
|             .readCapacityUnits(readCapacityUnits) | ||||
|             .writeCapacityUnits(writeCapacityUnits) | ||||
|  | @ -150,7 +157,8 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
|         .build(); | ||||
|   } | ||||
| 
 | ||||
|   static class DynamoDbExtensionBuilder { | ||||
|   public static class DynamoDbExtensionBuilder { | ||||
| 
 | ||||
|     private String tableName = DEFAULT_TABLE_NAME; | ||||
| 
 | ||||
|     private String hashKey; | ||||
|  | @ -158,6 +166,7 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
| 
 | ||||
|     private List<AttributeDefinition> attributeDefinitions = new ArrayList<>(); | ||||
|     private List<GlobalSecondaryIndex> globalSecondaryIndexes = new ArrayList<>(); | ||||
|     private List<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<>(); | ||||
| 
 | ||||
|     private long readCapacityUnits = DEFAULT_PROVISIONED_THROUGHPUT.readCapacityUnits(); | ||||
|     private long writeCapacityUnits = DEFAULT_PROVISIONED_THROUGHPUT.writeCapacityUnits(); | ||||
|  | @ -166,22 +175,22 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     DynamoDbExtensionBuilder tableName(String databaseName) { | ||||
|     public DynamoDbExtensionBuilder tableName(String databaseName) { | ||||
|       this.tableName = databaseName; | ||||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     DynamoDbExtensionBuilder hashKey(String hashKey) { | ||||
|     public DynamoDbExtensionBuilder hashKey(String hashKey) { | ||||
|       this.hashKey = hashKey; | ||||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     DynamoDbExtensionBuilder rangeKey(String rangeKey) { | ||||
|     public DynamoDbExtensionBuilder rangeKey(String rangeKey) { | ||||
|       this.rangeKey = rangeKey; | ||||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     DynamoDbExtensionBuilder attributeDefinition(AttributeDefinition attributeDefinition) { | ||||
|     public DynamoDbExtensionBuilder attributeDefinition(AttributeDefinition attributeDefinition) { | ||||
|       attributeDefinitions.add(attributeDefinition); | ||||
|       return this; | ||||
|     } | ||||
|  | @ -191,9 +200,14 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback | |||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     DynamoDbExtension build() { | ||||
|     public DynamoDbExtensionBuilder localSecondaryIndex(LocalSecondaryIndex index) { | ||||
|       localSecondaryIndexes.add(index); | ||||
|       return this; | ||||
|     } | ||||
| 
 | ||||
|     public DynamoDbExtension build() { | ||||
|       return new DynamoDbExtension(tableName, hashKey, rangeKey, | ||||
|           attributeDefinitions, globalSecondaryIndexes, readCapacityUnits, writeCapacityUnits); | ||||
|           attributeDefinitions, globalSecondaryIndexes, localSecondaryIndexes, readCapacityUnits, writeCapacityUnits); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ | |||
| 
 | ||||
| package org.whispersystems.textsecuregcm.storage; | ||||
| 
 | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertTimeout; | ||||
| import static org.mockito.Mockito.mock; | ||||
| import static org.mockito.Mockito.when; | ||||
| 
 | ||||
|  | @ -24,24 +25,28 @@ import java.util.concurrent.Executors; | |||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
| import org.apache.commons.lang3.RandomStringUtils; | ||||
| import org.junit.After; | ||||
| import org.junit.Before; | ||||
| import org.junit.Rule; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.AfterEach; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.RegisterExtension; | ||||
| import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; | ||||
| import org.whispersystems.textsecuregcm.entities.MessageProtos; | ||||
| import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope.Type; | ||||
| import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; | ||||
| import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbRule; | ||||
| import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension; | ||||
| import org.whispersystems.textsecuregcm.util.AttributeValues; | ||||
| import software.amazon.awssdk.services.dynamodb.DynamoDbClient; | ||||
| import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ScanRequest; | ||||
| 
 | ||||
| public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | ||||
| class MessagePersisterIntegrationTest { | ||||
| 
 | ||||
|     @Rule | ||||
|     public MessagesDynamoDbRule messagesDynamoDbRule = new MessagesDynamoDbRule(); | ||||
|   @RegisterExtension | ||||
|   static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build(); | ||||
| 
 | ||||
|   @RegisterExtension | ||||
|   static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build(); | ||||
| 
 | ||||
|   private ExecutorService notificationExecutorService; | ||||
|   private MessagesCache messagesCache; | ||||
|  | @ -51,24 +56,25 @@ public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | |||
| 
 | ||||
|   private static final Duration PERSIST_DELAY = Duration.ofMinutes(10); | ||||
| 
 | ||||
|     @Before | ||||
|     @Override | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
| 
 | ||||
|         getRedisCluster().useCluster(connection -> { | ||||
|   @BeforeEach | ||||
|   void setUp() throws Exception { | ||||
|     REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection -> { | ||||
|       connection.sync().flushall(); | ||||
|       connection.sync().upstream().commands().configSet("notify-keyspace-events", "K$glz"); | ||||
|     }); | ||||
| 
 | ||||
|         final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(messagesDynamoDbRule.getDynamoDbClient(), MessagesDynamoDbRule.TABLE_NAME, Duration.ofDays(7)); | ||||
|     final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(), | ||||
|         MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(14)); | ||||
|     final AccountsManager accountsManager = mock(AccountsManager.class); | ||||
|     final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class); | ||||
| 
 | ||||
|     notificationExecutorService = Executors.newSingleThreadExecutor(); | ||||
|         messagesCache               = new MessagesCache(getRedisCluster(), getRedisCluster(), notificationExecutorService); | ||||
|         messagesManager             = new MessagesManager(messagesDynamoDb, messagesCache, mock(PushLatencyManager.class), mock(ReportMessageManager.class)); | ||||
|         messagePersister            = new MessagePersister(messagesCache, messagesManager, accountsManager, dynamicConfigurationManager, PERSIST_DELAY); | ||||
|     messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(), | ||||
|         REDIS_CLUSTER_EXTENSION.getRedisCluster(), notificationExecutorService); | ||||
|     messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, mock(PushLatencyManager.class), | ||||
|         mock(ReportMessageManager.class)); | ||||
|     messagePersister = new MessagePersister(messagesCache, messagesManager, accountsManager, | ||||
|         dynamicConfigurationManager, PERSIST_DELAY); | ||||
| 
 | ||||
|     account = mock(Account.class); | ||||
| 
 | ||||
|  | @ -82,21 +88,21 @@ public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | |||
|     messagesCache.start(); | ||||
|   } | ||||
| 
 | ||||
|     @After | ||||
|     @Override | ||||
|     public void tearDown() throws Exception { | ||||
|         super.tearDown(); | ||||
| 
 | ||||
|   @AfterEach | ||||
|   void tearDown() throws Exception { | ||||
|     notificationExecutorService.shutdown(); | ||||
|     notificationExecutorService.awaitTermination(15, TimeUnit.SECONDS); | ||||
|   } | ||||
| 
 | ||||
|     @Test(timeout = 15_000) | ||||
|     public void testScheduledPersistMessages() throws Exception { | ||||
|   @Test | ||||
|   void testScheduledPersistMessages() { | ||||
| 
 | ||||
|     final int messageCount = 377; | ||||
|     final List<MessageProtos.Envelope> expectedMessages = new ArrayList<>(messageCount); | ||||
|     final Instant now = Instant.now(); | ||||
| 
 | ||||
|     assertTimeout(Duration.ofSeconds(15), () -> { | ||||
| 
 | ||||
|       for (int i = 0; i < messageCount; i++) { | ||||
|         final UUID messageGuid = UUID.randomUUID(); | ||||
|         final long timestamp = now.minus(PERSIST_DELAY.multipliedBy(2)).toEpochMilli() + i; | ||||
|  | @ -107,7 +113,9 @@ public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | |||
|         expectedMessages.add(message); | ||||
|       } | ||||
| 
 | ||||
|         getRedisCluster().useCluster(connection -> connection.sync().set(MessagesCache.NEXT_SLOT_TO_PERSIST_KEY, String.valueOf(SlotHash.getSlot(MessagesCache.getMessageQueueKey(account.getUuid(), 1)) - 1))); | ||||
|       REDIS_CLUSTER_EXTENSION.getRedisCluster() | ||||
|           .useCluster(connection -> connection.sync().set(MessagesCache.NEXT_SLOT_TO_PERSIST_KEY, | ||||
|               String.valueOf(SlotHash.getSlot(MessagesCache.getMessageQueueKey(account.getUuid(), 1)) - 1))); | ||||
| 
 | ||||
|       final AtomicBoolean messagesPersisted = new AtomicBoolean(false); | ||||
| 
 | ||||
|  | @ -141,12 +149,12 @@ public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | |||
| 
 | ||||
|       final List<MessageProtos.Envelope> persistedMessages = new ArrayList<>(messageCount); | ||||
| 
 | ||||
|         DynamoDbClient dynamoDB = messagesDynamoDbRule.getDynamoDbClient(); | ||||
|       DynamoDbClient dynamoDB = dynamoDbExtension.getDynamoDbClient(); | ||||
|       for (Map<String, AttributeValue> item : dynamoDB | ||||
|           .scan(ScanRequest.builder().tableName(MessagesDynamoDbRule.TABLE_NAME).build()).items()) { | ||||
|           .scan(ScanRequest.builder().tableName(MessagesDynamoDbExtension.TABLE_NAME).build()).items()) { | ||||
|         persistedMessages.add(MessageProtos.Envelope.newBuilder() | ||||
|             .setServerGuid(AttributeValues.getUUID(item, "U", null).toString()) | ||||
|             .setType(MessageProtos.Envelope.Type.valueOf(AttributeValues.getInt(item, "T", -1))) | ||||
|             .setType(Type.forNumber(AttributeValues.getInt(item, "T", -1))) | ||||
|             .setTimestamp(AttributeValues.getLong(item, "TS", -1)) | ||||
|             .setServerTimestamp(extractServerTimestamp(AttributeValues.getByteArray(item, "S", null))) | ||||
|             .setContent(ByteString.copyFrom(AttributeValues.getByteArray(item, "C", null))) | ||||
|  | @ -154,13 +162,7 @@ public class MessagePersisterIntegrationTest extends AbstractRedisClusterTest { | |||
|       } | ||||
| 
 | ||||
|       assertEquals(expectedMessages, persistedMessages); | ||||
|     } | ||||
| 
 | ||||
|     private static UUID convertBinaryToUuid(byte[] bytes) { | ||||
|         ByteBuffer bb = ByteBuffer.wrap(bytes); | ||||
|         long msb = bb.getLong(); | ||||
|         long lsb = bb.getLong(); | ||||
|         return new UUID(msb, lsb); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   private static long extractServerTimestamp(byte[] bytes) { | ||||
|  |  | |||
|  | @ -13,15 +13,18 @@ import java.util.List; | |||
| import java.util.Random; | ||||
| import java.util.UUID; | ||||
| import java.util.function.Consumer; | ||||
| import org.junit.Before; | ||||
| import org.junit.ClassRule; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.RegisterExtension; | ||||
| import org.whispersystems.textsecuregcm.entities.MessageProtos; | ||||
| import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamoDbExtension; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbRule; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension; | ||||
| 
 | ||||
| class MessagesDynamoDbTest { | ||||
| 
 | ||||
| 
 | ||||
| public class MessagesDynamoDbTest { | ||||
|   private static final Random random = new Random(); | ||||
|   private static final MessageProtos.Envelope MESSAGE1; | ||||
|   private static final MessageProtos.Envelope MESSAGE2; | ||||
|  | @ -61,27 +64,31 @@ public class MessagesDynamoDbTest { | |||
| 
 | ||||
|   private MessagesDynamoDb messagesDynamoDb; | ||||
| 
 | ||||
|   @ClassRule | ||||
|   public static MessagesDynamoDbRule dynamoDbRule = new MessagesDynamoDbRule(); | ||||
| 
 | ||||
|   @Before | ||||
|   public void setup() { | ||||
|     messagesDynamoDb = new MessagesDynamoDb(dynamoDbRule.getDynamoDbClient(), MessagesDynamoDbRule.TABLE_NAME, Duration.ofDays(7)); | ||||
|   @RegisterExtension | ||||
|   static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build(); | ||||
| 
 | ||||
|   @BeforeEach | ||||
|   void setup() { | ||||
|     messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(), MessagesDynamoDbExtension.TABLE_NAME, | ||||
|         Duration.ofDays(14)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testServerStart() { | ||||
|   void testServerStart() { | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testSimpleFetchAfterInsert() { | ||||
|   void testSimpleFetchAfterInsert() { | ||||
|     final UUID destinationUuid = UUID.randomUUID(); | ||||
|     final int destinationDeviceId = random.nextInt(255) + 1; | ||||
|     messagesDynamoDb.store(List.of(MESSAGE1, MESSAGE2, MESSAGE3), destinationUuid, destinationDeviceId); | ||||
| 
 | ||||
|     final List<OutgoingMessageEntity> messagesStored = messagesDynamoDb.load(destinationUuid, destinationDeviceId, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE); | ||||
|     final List<OutgoingMessageEntity> messagesStored = messagesDynamoDb.load(destinationUuid, destinationDeviceId, | ||||
|         MessagesDynamoDb.RESULT_SET_CHUNK_SIZE); | ||||
|     assertThat(messagesStored).isNotNull().hasSize(3); | ||||
|     final MessageProtos.Envelope firstMessage = MESSAGE1.getServerGuid().compareTo(MESSAGE3.getServerGuid()) < 0 ? MESSAGE1 : MESSAGE3; | ||||
|     final MessageProtos.Envelope firstMessage = | ||||
|         MESSAGE1.getServerGuid().compareTo(MESSAGE3.getServerGuid()) < 0 ? MESSAGE1 : MESSAGE3; | ||||
|     final MessageProtos.Envelope secondMessage = firstMessage == MESSAGE1 ? MESSAGE3 : MESSAGE1; | ||||
|     assertThat(messagesStored).element(0).satisfies(verify(firstMessage)); | ||||
|     assertThat(messagesStored).element(1).satisfies(verify(secondMessage)); | ||||
|  | @ -89,61 +96,76 @@ public class MessagesDynamoDbTest { | |||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testDeleteForDestination() { | ||||
|   void testDeleteForDestination() { | ||||
|     final UUID destinationUuid = UUID.randomUUID(); | ||||
|     final UUID secondDestinationUuid = UUID.randomUUID(); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE1), destinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE2), secondDestinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE3), destinationUuid, 2); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
| 
 | ||||
|     messagesDynamoDb.deleteAllMessagesForAccount(destinationUuid); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().isEmpty(); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().isEmpty(); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testDeleteForDestinationDevice() { | ||||
|   void testDeleteForDestinationDevice() { | ||||
|     final UUID destinationUuid = UUID.randomUUID(); | ||||
|     final UUID secondDestinationUuid = UUID.randomUUID(); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE1), destinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE2), secondDestinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE3), destinationUuid, 2); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
| 
 | ||||
|     messagesDynamoDb.deleteAllMessagesForDevice(destinationUuid, 2); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().isEmpty(); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|   } | ||||
| 
 | ||||
|   @Test | ||||
|   public void testDeleteMessageByDestinationAndGuid() { | ||||
|   void testDeleteMessageByDestinationAndGuid() { | ||||
|     final UUID destinationUuid = UUID.randomUUID(); | ||||
|     final UUID secondDestinationUuid = UUID.randomUUID(); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE1), destinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE2), secondDestinationUuid, 1); | ||||
|     messagesDynamoDb.store(List.of(MESSAGE3), destinationUuid, 2); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .hasSize(1).element(0).satisfies(verify(MESSAGE2)); | ||||
| 
 | ||||
|     messagesDynamoDb.deleteMessageByDestinationAndGuid(secondDestinationUuid, | ||||
|         UUID.fromString(MESSAGE2.getServerGuid())); | ||||
| 
 | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1).element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().isEmpty(); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE1)); | ||||
|     assertThat(messagesDynamoDb.load(destinationUuid, 2, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull().hasSize(1) | ||||
|         .element(0).satisfies(verify(MESSAGE3)); | ||||
|     assertThat(messagesDynamoDb.load(secondDestinationUuid, 1, MessagesDynamoDb.RESULT_SET_CHUNK_SIZE)).isNotNull() | ||||
|         .isEmpty(); | ||||
|   } | ||||
| 
 | ||||
|   private static void verify(OutgoingMessageEntity retrieved, MessageProtos.Envelope inserted) { | ||||
|  | @ -164,6 +186,7 @@ public class MessagesDynamoDbTest { | |||
|   } | ||||
| 
 | ||||
|   private static final class VerifyMessage implements Consumer<OutgoingMessageEntity> { | ||||
| 
 | ||||
|     private final MessageProtos.Envelope expected; | ||||
| 
 | ||||
|     public VerifyMessage(MessageProtos.Envelope expected) { | ||||
|  |  | |||
|  | @ -5,42 +5,36 @@ | |||
| 
 | ||||
| package org.whispersystems.textsecuregcm.tests.util; | ||||
| 
 | ||||
| import org.whispersystems.textsecuregcm.storage.DynamoDbExtension; | ||||
| import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; | ||||
| import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; | ||||
| import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; | ||||
| import software.amazon.awssdk.services.dynamodb.model.KeyType; | ||||
| import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; | ||||
| import software.amazon.awssdk.services.dynamodb.model.Projection; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ProjectionType; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; | ||||
| import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; | ||||
| 
 | ||||
| public class MessagesDynamoDbRule extends LocalDynamoDbRule { | ||||
| public class MessagesDynamoDbExtension { | ||||
| 
 | ||||
|   public static final String TABLE_NAME = "Signal_Messages_UnitTest"; | ||||
| 
 | ||||
|   @Override | ||||
|   protected void before() throws Throwable { | ||||
|     super.before(); | ||||
|     getDynamoDbClient().createTable(CreateTableRequest.builder() | ||||
|   public static DynamoDbExtension build() { | ||||
|     return DynamoDbExtension.builder() | ||||
|         .tableName(TABLE_NAME) | ||||
|         .keySchema(KeySchemaElement.builder().attributeName("H").keyType(KeyType.HASH).build(), | ||||
|             KeySchemaElement.builder().attributeName("S").keyType(KeyType.RANGE).build()) | ||||
|         .attributeDefinitions( | ||||
|             AttributeDefinition.builder().attributeName("H").attributeType(ScalarAttributeType.B).build(), | ||||
|             AttributeDefinition.builder().attributeName("S").attributeType(ScalarAttributeType.B).build(), | ||||
|         .hashKey("H") | ||||
|         .rangeKey("S") | ||||
|         .attributeDefinition( | ||||
|             AttributeDefinition.builder().attributeName("H").attributeType(ScalarAttributeType.B).build()) | ||||
|         .attributeDefinition( | ||||
|             AttributeDefinition.builder().attributeName("S").attributeType(ScalarAttributeType.B).build()) | ||||
|         .attributeDefinition( | ||||
|             AttributeDefinition.builder().attributeName("U").attributeType(ScalarAttributeType.B).build()) | ||||
|         .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(20L).writeCapacityUnits(20L).build()) | ||||
|         .localSecondaryIndexes(LocalSecondaryIndex.builder().indexName("Message_UUID_Index") | ||||
|         .localSecondaryIndex(LocalSecondaryIndex.builder().indexName("Message_UUID_Index") | ||||
|             .keySchema(KeySchemaElement.builder().attributeName("H").keyType(KeyType.HASH).build(), | ||||
|                 KeySchemaElement.builder().attributeName("U").keyType(KeyType.RANGE).build()) | ||||
|             .projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build()) | ||||
|             .build()) | ||||
|         .build()); | ||||
|         .build(); | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|   protected void after() { | ||||
|     super.after(); | ||||
|   } | ||||
| } | ||||
|  | @ -5,9 +5,10 @@ | |||
| 
 | ||||
| package org.whispersystems.textsecuregcm.websocket; | ||||
| 
 | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.junit.Assert.fail; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertTimeout; | ||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
| import static org.junit.jupiter.api.Assertions.fail; | ||||
| import static org.mockito.ArgumentMatchers.any; | ||||
| import static org.mockito.ArgumentMatchers.anyList; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
|  | @ -34,10 +35,10 @@ import java.util.concurrent.TimeUnit; | |||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
| import java.util.stream.Collectors; | ||||
| import org.apache.commons.lang3.RandomStringUtils; | ||||
| import org.junit.After; | ||||
| import org.junit.Before; | ||||
| import org.junit.Rule; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.AfterEach; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.RegisterExtension; | ||||
| import org.mockito.ArgumentCaptor; | ||||
| import org.mockito.stubbing.Answer; | ||||
| import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; | ||||
|  | @ -45,22 +46,26 @@ import org.whispersystems.textsecuregcm.entities.MessageProtos; | |||
| import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope; | ||||
| import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; | ||||
| import org.whispersystems.textsecuregcm.push.ReceiptSender; | ||||
| import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest; | ||||
| import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; | ||||
| import org.whispersystems.textsecuregcm.storage.Account; | ||||
| import org.whispersystems.textsecuregcm.storage.Device; | ||||
| import org.whispersystems.textsecuregcm.storage.DynamoDbExtension; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesCache; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb; | ||||
| import org.whispersystems.textsecuregcm.storage.MessagesManager; | ||||
| import org.whispersystems.textsecuregcm.storage.ReportMessageManager; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbRule; | ||||
| import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension; | ||||
| import org.whispersystems.textsecuregcm.util.Pair; | ||||
| import org.whispersystems.websocket.WebSocketClient; | ||||
| import org.whispersystems.websocket.messages.WebSocketResponseMessage; | ||||
| 
 | ||||
| public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest { | ||||
| class WebSocketConnectionIntegrationTest { | ||||
| 
 | ||||
|     @Rule | ||||
|     public MessagesDynamoDbRule messagesDynamoDbRule = new MessagesDynamoDbRule(); | ||||
|   @RegisterExtension | ||||
|   static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build(); | ||||
| 
 | ||||
|   @RegisterExtension | ||||
|   static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build(); | ||||
| 
 | ||||
|   private ExecutorService executorService; | ||||
|   private MessagesDynamoDb messagesDynamoDb; | ||||
|  | @ -74,14 +79,14 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
| 
 | ||||
|   private long serialTimestamp = System.currentTimeMillis(); | ||||
| 
 | ||||
|     @Before | ||||
|     @Override | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|   @BeforeEach | ||||
|   void setUp() throws Exception { | ||||
| 
 | ||||
|     executorService = Executors.newSingleThreadExecutor(); | ||||
|         messagesCache = new MessagesCache(getRedisCluster(), getRedisCluster(), executorService); | ||||
|         messagesDynamoDb = new MessagesDynamoDb(messagesDynamoDbRule.getDynamoDbClient(), MessagesDynamoDbRule.TABLE_NAME, Duration.ofDays(7)); | ||||
|     messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(), | ||||
|         REDIS_CLUSTER_EXTENSION.getRedisCluster(), executorService); | ||||
|     messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(), MessagesDynamoDbExtension.TABLE_NAME, | ||||
|         Duration.ofDays(7)); | ||||
|     reportMessageManager = mock(ReportMessageManager.class); | ||||
|     account = mock(Account.class); | ||||
|     device = mock(Device.class); | ||||
|  | @ -101,25 +106,24 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|         retrySchedulingExecutor); | ||||
|   } | ||||
| 
 | ||||
|     @After | ||||
|     @Override | ||||
|     public void tearDown() throws Exception { | ||||
|   @AfterEach | ||||
|   void tearDown() throws Exception { | ||||
|     executorService.shutdown(); | ||||
|     executorService.awaitTermination(2, TimeUnit.SECONDS); | ||||
| 
 | ||||
|     retrySchedulingExecutor.shutdown(); | ||||
|     retrySchedulingExecutor.awaitTermination(2, TimeUnit.SECONDS); | ||||
| 
 | ||||
|         super.tearDown(); | ||||
|   } | ||||
| 
 | ||||
|     @Test(timeout = 15_000) | ||||
|     public void testProcessStoredMessages() throws InterruptedException { | ||||
|   @Test | ||||
|   void testProcessStoredMessages() { | ||||
|     final int persistedMessageCount = 207; | ||||
|     final int cachedMessageCount = 173; | ||||
| 
 | ||||
|     final List<MessageProtos.Envelope> expectedMessages = new ArrayList<>(persistedMessageCount + cachedMessageCount); | ||||
| 
 | ||||
|     assertTimeout(Duration.ofSeconds(15), () -> { | ||||
| 
 | ||||
|       { | ||||
|         final List<MessageProtos.Envelope> persistedMessages = new ArrayList<>(persistedMessageCount); | ||||
| 
 | ||||
|  | @ -145,9 +149,11 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|       final AtomicBoolean queueCleared = new AtomicBoolean(false); | ||||
| 
 | ||||
|       when(successResponse.getStatus()).thenReturn(200); | ||||
|         when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), any())).thenReturn(CompletableFuture.completedFuture(successResponse)); | ||||
|       when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), any())).thenReturn( | ||||
|           CompletableFuture.completedFuture(successResponse)); | ||||
| 
 | ||||
|         when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/queue/empty"), anyList(), any())).thenAnswer((Answer<CompletableFuture<WebSocketResponseMessage>>)invocation -> { | ||||
|       when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/queue/empty"), anyList(), any())).thenAnswer( | ||||
|           (Answer<CompletableFuture<WebSocketResponseMessage>>) invocation -> { | ||||
|             synchronized (queueCleared) { | ||||
|               queueCleared.set(true); | ||||
|               queueCleared.notifyAll(); | ||||
|  | @ -164,10 +170,11 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|         } | ||||
|       } | ||||
| 
 | ||||
|         @SuppressWarnings("unchecked") | ||||
|         final ArgumentCaptor<Optional<byte[]>> messageBodyCaptor = ArgumentCaptor.forClass(Optional.class); | ||||
|       @SuppressWarnings("unchecked") final ArgumentCaptor<Optional<byte[]>> messageBodyCaptor = ArgumentCaptor.forClass( | ||||
|           Optional.class); | ||||
| 
 | ||||
|         verify(webSocketClient, times(persistedMessageCount + cachedMessageCount)).sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), messageBodyCaptor.capture()); | ||||
|       verify(webSocketClient, times(persistedMessageCount + cachedMessageCount)).sendRequest(eq("PUT"), | ||||
|           eq("/api/v1/message"), anyList(), messageBodyCaptor.capture()); | ||||
|       verify(webSocketClient).sendRequest(eq("PUT"), eq("/api/v1/queue/empty"), anyList(), eq(Optional.empty())); | ||||
| 
 | ||||
|       final List<MessageProtos.Envelope> sentMessages = new ArrayList<>(); | ||||
|  | @ -183,15 +190,18 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|       } | ||||
| 
 | ||||
|       assertEquals(expectedMessages, sentMessages); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|     @Test(timeout = 15_000) | ||||
|     public void testProcessStoredMessagesClientClosed() { | ||||
|   @Test | ||||
|   void testProcessStoredMessagesClientClosed() { | ||||
|     final int persistedMessageCount = 207; | ||||
|     final int cachedMessageCount = 173; | ||||
| 
 | ||||
|     final List<MessageProtos.Envelope> expectedMessages = new ArrayList<>(persistedMessageCount + cachedMessageCount); | ||||
| 
 | ||||
|     assertTimeout(Duration.ofSeconds(15), () -> { | ||||
| 
 | ||||
|       { | ||||
|         final List<MessageProtos.Envelope> persistedMessages = new ArrayList<>(persistedMessageCount); | ||||
| 
 | ||||
|  | @ -212,15 +222,18 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|         expectedMessages.add(envelope); | ||||
|       } | ||||
| 
 | ||||
|         when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), any())).thenReturn(CompletableFuture.failedFuture(new IOException("Connection closed"))); | ||||
|       when(webSocketClient.sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), any())).thenReturn( | ||||
|           CompletableFuture.failedFuture(new IOException("Connection closed"))); | ||||
| 
 | ||||
|       webSocketConnection.processStoredMessages(); | ||||
| 
 | ||||
|       //noinspection unchecked | ||||
|       ArgumentCaptor<Optional<byte[]>> messageBodyCaptor = ArgumentCaptor.forClass(Optional.class); | ||||
| 
 | ||||
|         verify(webSocketClient, atMost(persistedMessageCount + cachedMessageCount)).sendRequest(eq("PUT"), eq("/api/v1/message"), anyList(), messageBodyCaptor.capture()); | ||||
|         verify(webSocketClient, never()).sendRequest(eq("PUT"), eq("/api/v1/queue/empty"), anyList(), eq(Optional.empty())); | ||||
|       verify(webSocketClient, atMost(persistedMessageCount + cachedMessageCount)).sendRequest(eq("PUT"), | ||||
|           eq("/api/v1/message"), anyList(), messageBodyCaptor.capture()); | ||||
|       verify(webSocketClient, never()).sendRequest(eq("PUT"), eq("/api/v1/queue/empty"), anyList(), | ||||
|           eq(Optional.empty())); | ||||
| 
 | ||||
|       final List<MessageProtos.Envelope> sentMessages = messageBodyCaptor.getAllValues().stream() | ||||
|           .map(Optional::get) | ||||
|  | @ -234,6 +247,7 @@ public class WebSocketConnectionIntegrationTest extends AbstractRedisClusterTest | |||
|           .collect(Collectors.toList()); | ||||
| 
 | ||||
|       assertTrue(expectedMessages.containsAll(sentMessages)); | ||||
|     }); | ||||
|     } | ||||
| 
 | ||||
|     private MessageProtos.Envelope generateRandomMessage(final UUID messageGuid) { | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ public class WebSocketResourceProviderFactory<T extends Principal> extends WebSo | |||
|         } | ||||
|       } | ||||
| 
 | ||||
|       return new WebSocketResourceProvider<T>(getRemoteAddress(request), | ||||
|       return new WebSocketResourceProvider<>(getRemoteAddress(request), | ||||
|           this.jerseyApplicationHandler, | ||||
|           this.environment.getRequestLog(), | ||||
|           authenticated, | ||||
|  | @ -76,7 +76,8 @@ public class WebSocketResourceProviderFactory<T extends Principal> extends WebSo | |||
|       logger.warn("Authentication failure", e); | ||||
|       try { | ||||
|         response.sendError(500, "Failure"); | ||||
|       } catch (IOException ex) {} | ||||
|       } catch (IOException ignored) { | ||||
|       } | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Chris Eager
						Chris Eager