/** * JBoss, Home of Professional Open Source * Copyright Red Hat, Inc., and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.aerogear.unifiedpush.message.jms; import org.jboss.aerogear.unifiedpush.api.AndroidVariant; import org.jboss.aerogear.unifiedpush.api.PushMessageInformation; import org.jboss.aerogear.unifiedpush.api.Variant; import org.jboss.aerogear.unifiedpush.api.VariantType; import org.jboss.aerogear.unifiedpush.message.AbstractJMSTest; import org.jboss.aerogear.unifiedpush.message.UnifiedPushMessage; import org.jboss.aerogear.unifiedpush.message.configuration.SenderConfiguration; import org.jboss.aerogear.unifiedpush.message.configuration.SenderConfigurationProvider; import org.jboss.aerogear.unifiedpush.message.holder.MessageHolderWithTokens; import org.jboss.aerogear.unifiedpush.message.holder.MessageHolderWithVariants; import org.jboss.aerogear.unifiedpush.message.sender.SenderType; import org.jboss.aerogear.unifiedpush.message.sender.SenderTypeLiteral; import org.jboss.aerogear.unifiedpush.message.token.TokenLoader; import org.jboss.aerogear.unifiedpush.message.token.TokenLoaderUtils; import org.jboss.aerogear.unifiedpush.message.util.JmsClient; import org.jboss.aerogear.unifiedpush.service.ClientInstallationService; import org.jboss.aerogear.unifiedpush.test.archive.UnifiedPushArchive; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.jms.Queue; import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; //import org.jboss.aerogear.unifiedpush.service.ClientInstallationService; @RunWith(Arquillian.class) public class TestTokenLoaderTransactionFailForGCM extends AbstractJMSTest { private final Logger logger = LoggerFactory.getLogger(TestTokenLoaderTransactionFailForGCM.class); private static final int CONCURRENT_WORKERS = 15; // specified by maxSession MDB config in jboss-ejb.xml public static final int BATCH_SIZE = 1000; // maximum number of tokens in one batch for Android/GCM public static final int NUMBER_OF_BATCHES_TO_SEND = 120; // 120 public static final int TOKENS_TO_SEND = BATCH_SIZE * NUMBER_OF_BATCHES_TO_SEND; private static final long TIME_TO_DELIVER_BATCH = 1000L; // in practice this depends on network delay/bandwidth between UPS instance and GCM servers private static final long MIN_TIME_TO_DELIVER_ALL_BATCHES = (NUMBER_OF_BATCHES_TO_SEND / CONCURRENT_WORKERS) * TIME_TO_DELIVER_BATCH; private static final long MAX_TIME_TO_DELIVER_ALL_BATCHES = MIN_TIME_TO_DELIVER_ALL_BATCHES + 10000L; // 10 sec tolerance private static final long MAX_TIME_TO_DELIVER_ALL_BATCHES_ONCE_THEY_ARE_ALL_LODED = 12000L; // the bigger the queue is (max-size-bytes), the bigger is the "buffer" and so it takes longer to deliver all batches remaining in queue @Inject @DispatchToQueue private Event<MessageHolderWithVariants> startLoadingTokensForVariant; @Resource(mappedName = "java:/queue/AllBatchesLoadedQueue") private Queue allBatchesLoaded; @Resource(mappedName = "java:/queue/TestTokenLoaderTransactionFailForGCM") private Queue allTokens; @Inject private JmsClient jmsClient; public static final String messageId = UUID.randomUUID().toString(); private static final CountDownLatch waitToDeliverAllBatches = new CountDownLatch(NUMBER_OF_BATCHES_TO_SEND); private static final AtomicInteger currentConcurrency = new AtomicInteger(0); private static final AtomicInteger maxConcurrency = new AtomicInteger(0); private static final Set<Integer> deliveredSerials = Collections.newSetFromMap(new ConcurrentHashMap()); @Deployment public static WebArchive archive() { return UnifiedPushArchive.forTestClass(TestTokenLoaderTransactionFailForGCM.class) .withMessaging() .withMessageDrivenBeans() .addClasses(TokenLoaderUtils.class, TokenLoader.class, ClientInstallationService.class, SenderTypeLiteral.class, SenderType.class) .addClasses(SenderConfiguration.class, SenderConfigurationProvider.class) .withMockito() .addClass(MocksForTokenLoaderTransactionFailForGCM.class) .addAsWebInfResource("test-jms.xml") .as(WebArchive.class); } @Test(timeout = 20000) @Ignore("Travis CI timing issue") public void testAndroidTransactedRedelivery() throws InterruptedException { for (int i = 0; i < NUMBER_OF_BATCHES_TO_SEND; i++) { jmsClient.send("x").withProperty("id", messageId).to(allTokens); } // given PushMessageInformation pmi = new PushMessageInformation(); pmi.setId(messageId); UnifiedPushMessage pushMessage = new UnifiedPushMessage(); Variant variant = new AndroidVariant(); variant.setVariantID(messageId); variant.setType(VariantType.ANDROID); // when long start = System.currentTimeMillis(); startLoadingTokensForVariant.fire(new MessageHolderWithVariants(pmi, pushMessage, VariantType.ANDROID, Arrays.asList(variant))); // then Assert.assertNotNull("all batches should be loaded within time limits", jmsClient.receive().withTimeout(15000L).withSelector("variantID = '%s'", messageId + ":" + messageId).from(allBatchesLoaded)); long allBatchesWereLoaded = System.currentTimeMillis(); waitToDeliverAllBatches.await(); long finishedDeliveringOfAllBatchesOnceAllAreLoaded = System.currentTimeMillis() - allBatchesWereLoaded; long took = System.currentTimeMillis() - start; for (int i = 0; i < NUMBER_OF_BATCHES_TO_SEND; i++) { assertTrue(String.format("serialId=%s should be delivered", i), deliveredSerials.contains(i + 1)); } assertTrue(String.format("multiple workers are used to send notifications (were used %s out of %s configured)", maxConcurrency.get(), CONCURRENT_WORKERS), maxConcurrency.get() > 1); if (took < MIN_TIME_TO_DELIVER_ALL_BATCHES) { fail(String.format("it should take at least %s ms to load and deliver all batches, but it took %s", MIN_TIME_TO_DELIVER_ALL_BATCHES, took)); }if (took > MAX_TIME_TO_DELIVER_ALL_BATCHES) { fail(String.format("it should take at most %s ms to load and deliver all batches, but it took %s", MAX_TIME_TO_DELIVER_ALL_BATCHES, took)); } if (finishedDeliveringOfAllBatchesOnceAllAreLoaded < TIME_TO_DELIVER_BATCH) { fail(String.format("it should take at least %s ms to deliver all batches once all tokens were loaded, but it took %s", TIME_TO_DELIVER_BATCH, finishedDeliveringOfAllBatchesOnceAllAreLoaded)); } if (finishedDeliveringOfAllBatchesOnceAllAreLoaded > MAX_TIME_TO_DELIVER_ALL_BATCHES_ONCE_THEY_ARE_ALL_LODED) { fail(String.format("it should take at most %s ms to deliver all batches once all tokens were loaded, but it took %s", MAX_TIME_TO_DELIVER_ALL_BATCHES_ONCE_THEY_ARE_ALL_LODED, finishedDeliveringOfAllBatchesOnceAllAreLoaded)); } } public void observeMessage(@Observes @Dequeue MessageHolderWithTokens msg) throws InterruptedException { if (msg.getPushMessageInformation().getId().equals(messageId)) { int concurrency = currentConcurrency.incrementAndGet(); maxConcurrency.set(Math.max(concurrency, maxConcurrency.get())); logger.debug("started processing: " + msg.getSerialId()); Thread.sleep(TIME_TO_DELIVER_BATCH); deliveredSerials.add(msg.getSerialId()); logger.debug("finished processing: " + msg.getSerialId()); currentConcurrency.decrementAndGet(); waitToDeliverAllBatches.countDown(); } } }