001 /** 002 * Copyright 2005-2011 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 */ 016 package org.kuali.rice.ken.services.impl; 017 018 import org.junit.Ignore; 019 import org.junit.Test; 020 import org.kuali.rice.core.framework.persistence.dao.GenericDao; 021 import org.kuali.rice.kcb.service.GlobalKCBServiceLocator; 022 import org.kuali.rice.kcb.service.MessageService; 023 import org.kuali.rice.ken.bo.NotificationBo; 024 import org.kuali.rice.ken.bo.NotificationMessageDelivery; 025 import org.kuali.rice.ken.service.NotificationMessageDeliveryResolverService; 026 import org.kuali.rice.ken.service.NotificationRecipientService; 027 import org.kuali.rice.ken.service.NotificationService; 028 import org.kuali.rice.ken.service.ProcessingResult; 029 import org.kuali.rice.ken.service.UserPreferenceService; 030 import org.kuali.rice.ken.service.impl.NotificationMessageDeliveryResolverServiceImpl; 031 import org.kuali.rice.ken.test.KENTestCase; 032 import org.kuali.rice.ken.util.NotificationConstants; 033 import org.kuali.rice.test.data.PerTestUnitTestData; 034 import org.kuali.rice.test.data.UnitTestData; 035 import org.kuali.rice.test.data.UnitTestSql; 036 import org.springframework.transaction.PlatformTransactionManager; 037 038 import java.util.Collection; 039 import java.util.HashMap; 040 import java.util.concurrent.ExecutorService; 041 import java.util.concurrent.Executors; 042 043 import static org.junit.Assert.*; 044 045 //import org.kuali.rice.core.jpa.criteria.Criteria; 046 047 /** 048 * Tests NotificationMessageDeliveryResolverServiceImpl 049 * @author Kuali Rice Team (rice.collab@kuali.org) 050 */ 051 // deadlocks are detected during clear database lifecycle (even when select for update is commented out...) 052 // Make sure KCB has some deliverers configured for the test users, so message deliveries get created and the messages aren't removed 053 @PerTestUnitTestData( 054 @UnitTestData( 055 order = { UnitTestData.Type.SQL_STATEMENTS }, 056 sqlStatements = { 057 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (1, 'testuser6', 'KEW', 'mock', 0)"), 058 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (2, 'testuser1', 'KEW', 'mock', 0)"), 059 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (3, 'testuser2', 'KEW', 'mock', 0)"), 060 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (4, 'quickstart', 'KEW', 'mock', 0)"), 061 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (5, 'testuser5', 'KEW', 'mock', 0)"), 062 @UnitTestSql("insert into KREN_RECIP_DELIV_T (RECIP_DELIV_ID, RECIP_ID, CHNL, NM, VER_NBR) values (6, 'testuser4', 'KEW', 'mock', 0)") 063 } 064 ) 065 ) 066 @Ignore 067 public class NotificationMessageDeliveryResolverServiceImplTest extends KENTestCase { 068 // NOTE: this value is HIGHLY dependent on the test data, make sure that it reflects the results 069 // expected from the test data 070 private static final int EXPECTED_SUCCESSES = 6; 071 072 /** 073 * Id of notification for which we will intentionally generate an exception during processing 074 */ 075 private static final long BAD_NOTIFICATION_ID = 3L; 076 077 private static class TestNotificationMessageDeliveryResolverService extends NotificationMessageDeliveryResolverServiceImpl { 078 public TestNotificationMessageDeliveryResolverService(NotificationService notificationService, NotificationRecipientService notificationRecipientService, 079 GenericDao businessObjectDao, PlatformTransactionManager txManager, ExecutorService executor, UserPreferenceService userPreferenceService) { 080 super(notificationService, notificationRecipientService, businessObjectDao, txManager, executor, userPreferenceService); 081 } 082 083 @Override 084 protected Collection<Object> processWorkItems(Collection<NotificationBo> notifications) { 085 for (NotificationBo notification: notifications) { 086 if (notification.getId().longValue() == BAD_NOTIFICATION_ID) { 087 throw new RuntimeException("Intentional heinous exception"); 088 } 089 } 090 return super.processWorkItems(notifications); 091 } 092 } 093 094 protected TestNotificationMessageDeliveryResolverService getResolverService() { 095 return new TestNotificationMessageDeliveryResolverService(services.getNotificationService(), services.getNotificationRecipientService(), services.getGenericDao(), transactionManager, 096 Executors.newFixedThreadPool(5), services.getUserPreferenceService()); 097 } 098 099 //this is the one need to tweek on Criteria 100 protected void assertProcessResults() { 101 // one error should have occurred and the delivery should have been marked unlocked again 102 Collection<NotificationMessageDelivery> lockedDeliveries = services.getNotificationMessegDeliveryDao().getLockedDeliveries(NotificationBo.class, services.getGenericDao()); 103 104 assertEquals(0, lockedDeliveries.size()); 105 106 // should be 1 unprocessed delivery (the one that had an error) 107 HashMap<String, String> queryCriteria = new HashMap<String, String>(); 108 queryCriteria.put(NotificationConstants.BO_PROPERTY_NAMES.PROCESSING_FLAG, NotificationConstants.PROCESSING_FLAGS.UNRESOLVED); 109 Collection<NotificationBo> unprocessedDeliveries = services.getGenericDao().findMatching(NotificationBo.class, queryCriteria); 110 assertEquals(1, unprocessedDeliveries.size()); 111 NotificationBo n = unprocessedDeliveries.iterator().next(); 112 // #3 is the bad one 113 assertEquals(BAD_NOTIFICATION_ID, n.getId().longValue()); 114 } 115 116 /** 117 * Test resolution of notifications 118 * This test resolves UNRESOLVED notification ids #3 and #4 in the test data. An artificial exception is generated for notification #3. 119 * For notification #4, the recipients are defined to be the Rice Team and testuser1. This results in 8 recipient resolutions, two of which 120 * are Email deliveries for jaf30 and ag266. 121 * If you change the test data this test should be updated to reflect the expected results. 122 */ 123 @Test 124 public void testResolveNotificationMessageDeliveries() throws Exception { 125 NotificationMessageDeliveryResolverService nSvc = getResolverService(); 126 127 ProcessingResult result = nSvc.resolveNotificationMessageDeliveries(); 128 129 Thread.sleep(20000); 130 131 assertEquals(EXPECTED_SUCCESSES, result.getSuccesses().size()); 132 133 MessageService ms = (MessageService) GlobalKCBServiceLocator.getInstance().getMessageService(); 134 assertEquals(result.getSuccesses().size(), ms.getAllMessages().size()); 135 136 assertProcessResults(); 137 } 138 139 140 /** 141 * Test concurrent resolution of notifications 142 */ 143 @Test 144 public void testResolverConcurrency() throws InterruptedException { 145 final NotificationMessageDeliveryResolverService nSvc = getResolverService(); 146 147 final ProcessingResult[] results = new ProcessingResult[2]; 148 Thread t1 = new Thread(new Runnable() { 149 public void run() { 150 try { 151 results[0] = nSvc.resolveNotificationMessageDeliveries(); 152 } catch (Exception e) { 153 System.err.println("Error resolving notification message deliveries"); 154 e.printStackTrace(); 155 } 156 } 157 }); 158 Thread t2 = new Thread(new Runnable() { 159 public void run() { 160 try { 161 results[1] = nSvc.resolveNotificationMessageDeliveries(); 162 } catch (Exception e) { 163 System.err.println("Error resolving notification message deliveries"); 164 e.printStackTrace(); 165 } 166 } 167 }); 168 169 t1.start(); 170 t2.start(); 171 172 t1.join(); 173 t2.join(); 174 175 // assert that ONE of the resolvers got all the items, and the other got NONE of the items 176 LOG.info("Results of thread #1: " + results[0]); 177 LOG.info("Results of thread #2: " + results[1]); 178 assertNotNull(results[0]); 179 assertNotNull(results[1]); 180 assertTrue((results[0].getSuccesses().size() == EXPECTED_SUCCESSES && results[0].getFailures().size() == 1 && results[1].getSuccesses().size() == 0 && results[1].getFailures().size() == 0) || 181 (results[1].getSuccesses().size() == EXPECTED_SUCCESSES && results[1].getFailures().size() == 1 && results[0].getSuccesses().size() == 0 && results[0].getFailures().size() == 0)); 182 183 assertProcessResults(); 184 } 185 }