001/** 002 * Copyright 2005-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.ken.services.impl; 017 018import org.junit.Test; 019import org.kuali.rice.core.api.criteria.QueryByCriteria; 020import org.kuali.rice.ken.bo.NotificationBo; 021import org.kuali.rice.ken.bo.NotificationMessageDelivery; 022import org.kuali.rice.ken.service.NotificationMessageDeliveryResolverService; 023import org.kuali.rice.ken.service.NotificationRecipientService; 024import org.kuali.rice.ken.service.NotificationService; 025import org.kuali.rice.ken.service.ProcessingResult; 026import org.kuali.rice.ken.service.impl.NotificationMessageDeliveryResolverServiceImpl; 027import org.kuali.rice.ken.test.KENTestCase; 028import org.kuali.rice.ken.util.NotificationConstants; 029import org.kuali.rice.krad.data.DataObjectService; 030import org.kuali.rice.krad.service.KRADServiceLocator; 031import org.kuali.rice.test.BaselineTestCase; 032import org.kuali.rice.test.data.PerTestUnitTestData; 033import org.kuali.rice.test.data.UnitTestData; 034import org.kuali.rice.test.data.UnitTestSql; 035import org.springframework.transaction.PlatformTransactionManager; 036 037import java.util.Collection; 038import java.util.concurrent.ExecutorService; 039import java.util.concurrent.Executors; 040 041import static org.junit.Assert.assertEquals; 042import static org.junit.Assert.assertNotNull; 043import static org.junit.Assert.assertTrue; 044import static org.kuali.rice.core.api.criteria.PredicateFactory.equal; 045 046/** 047 * Tests NotificationMessageDeliveryResolverServiceImpl 048 * @author Kuali Rice Team (rice.collab@kuali.org) 049 */ 050// Make sure KCB has some deliverers configured for the test users, so message deliveries get created and the messages aren't removed 051@PerTestUnitTestData( 052 @UnitTestData( 053 order = { UnitTestData.Type.SQL_STATEMENTS }, 054 sqlStatements = { 055 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (1, 'testuser6', 'KEW', 'mock', 0)"), 056 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (2, 'testuser1', 'KEW', 'mock', 0)"), 057 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (3, 'testuser2', 'KEW', 'mock', 0)"), 058 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (4, 'quickstart', 'KEW', 'mock', 0)"), 059 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (5, 'testuser5', 'KEW', 'mock', 0)"), 060 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (6, 'testuser4', 'KEW', 'mock', 0)") 061 } 062 ) 063) 064@BaselineTestCase.BaselineMode(BaselineTestCase.Mode.CLEAR_DB) 065public class NotificationMessageDeliveryResolverServiceImplTest extends KENTestCase { 066 // NOTE: this value is HIGHLY dependent on the test data, make sure that it reflects the results 067 // expected from the test data 068 private static final int EXPECTED_SUCCESSES = 6; 069 070 /** 071 * Id of notification for which we will intentionally generate an exception during processing 072 */ 073 private static final long BAD_NOTIFICATION_ID = 3L; 074 075 private static class TestNotificationMessageDeliveryResolverService extends NotificationMessageDeliveryResolverServiceImpl { 076 public TestNotificationMessageDeliveryResolverService(NotificationService notificationService, NotificationRecipientService notificationRecipientService, 077 DataObjectService dataObjectService, PlatformTransactionManager txManager, ExecutorService executor) { 078 super(notificationService, notificationRecipientService, dataObjectService, txManager, executor); 079 } 080 081 @Override 082 protected Collection<Object> processWorkItems(Collection<NotificationBo> notifications) { 083 for (NotificationBo notification: notifications) { 084 if (notification.getId().longValue() == BAD_NOTIFICATION_ID) { 085 throw new RuntimeException("Intentional heinous exception"); 086 } 087 } 088 return super.processWorkItems(notifications); 089 } 090 } 091 092 protected TestNotificationMessageDeliveryResolverService getResolverService() { 093 return new TestNotificationMessageDeliveryResolverService(services.getNotificationService(), services.getNotificationRecipientService(), 094 KRADServiceLocator.getDataObjectService(), transactionManager, 095 Executors.newFixedThreadPool(5)); 096 } 097 098 //this is the one need to tweek on Criteria 099 protected void assertProcessResults() { 100 // one error should have occurred and the delivery should have been marked unlocked again 101 Collection<NotificationMessageDelivery> lockedDeliveries = services.getNotificationMessegDeliveryDao().getLockedDeliveries(NotificationBo.class, KRADServiceLocator.getDataObjectService()); 102 assertEquals(0, lockedDeliveries.size()); 103 104 // should be 1 unprocessed delivery (the one that had an error) 105 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create(); 106 criteria.setPredicates(equal(NotificationConstants.BO_PROPERTY_NAMES.PROCESSING_FLAG, NotificationConstants.PROCESSING_FLAGS.UNRESOLVED)); 107 Collection<NotificationBo> unprocessedDeliveries = KRADServiceLocator.getDataObjectService().findMatching(NotificationBo.class, criteria.build()).getResults(); 108 109 assertEquals(1, unprocessedDeliveries.size()); 110 NotificationBo n = unprocessedDeliveries.iterator().next(); 111 // #3 is the bad one 112 assertEquals(BAD_NOTIFICATION_ID, n.getId().longValue()); 113 } 114 115 /** 116 * Test resolution of notifications 117 * This test resolves UNRESOLVED notification ids #3 and #4 in the test data. An artificial exception is generated for notification #3. 118 * For notification #4, the recipients are defined to be the Rice Team and testuser1. This results in 8 recipient resolutions, two of which 119 * are Email deliveries for jaf30 and ag266. 120 * If you change the test data this test should be updated to reflect the expected results. 121 */ 122 @Test 123 public void testResolveNotificationMessageDeliveries() throws Exception { 124 NotificationMessageDeliveryResolverService nSvc = getResolverService(); 125 126 ProcessingResult result = nSvc.resolveNotificationMessageDeliveries(); 127 128 assertEquals(EXPECTED_SUCCESSES, result.getSuccesses().size()); 129 130 assertProcessResults(); 131 } 132 133 134 /** 135 * Test concurrent resolution of notifications 136 */ 137 @Test 138 public void testResolverConcurrency() throws InterruptedException { 139 final NotificationMessageDeliveryResolverService nSvc = getResolverService(); 140 141 final ProcessingResult[] results = new ProcessingResult[2]; 142 Thread t1 = new Thread(new Runnable() { 143 public void run() { 144 try { 145 results[0] = nSvc.resolveNotificationMessageDeliveries(); 146 } catch (Exception e) { 147 System.err.println("Error resolving notification message deliveries"); 148 e.printStackTrace(); 149 } 150 } 151 }); 152 Thread t2 = new Thread(new Runnable() { 153 public void run() { 154 try { 155 results[1] = nSvc.resolveNotificationMessageDeliveries(); 156 } catch (Exception e) { 157 System.err.println("Error resolving notification message deliveries"); 158 e.printStackTrace(); 159 } 160 } 161 }); 162 163 t1.start(); 164 t2.start(); 165 166 t1.join(); 167 t2.join(); 168 169 // assert that ONE of the resolvers got all the items, and the other got NONE of the items 170 LOG.info("Results of thread #1: " + results[0]); 171 LOG.info("Results of thread #2: " + results[1]); 172 assertNotNull(results[0]); 173 assertNotNull(results[1]); 174 assertTrue((results[0].getSuccesses().size() == EXPECTED_SUCCESSES && results[0].getFailures().size() == 1 && results[1].getSuccesses().size() == 0 && results[1].getFailures().size() == 0) || 175 (results[1].getSuccesses().size() == EXPECTED_SUCCESSES && results[1].getFailures().size() == 1 && results[0].getSuccesses().size() == 0 && results[0].getFailures().size() == 0)); 176 177 assertProcessResults(); 178 } 179}