001/** 002 * Copyright 2005-2013 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.kew.preferences; 017 018import static org.junit.Assert.assertFalse; 019import static org.junit.Assert.assertNotNull; 020import static org.junit.Assert.assertNull; 021import static org.junit.Assert.assertTrue; 022 023import java.io.StringReader; 024import java.io.StringWriter; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import junit.framework.Assert; 032import org.junit.Test; 033import org.kuali.rice.kew.api.KewApiServiceLocator; 034import org.kuali.rice.kew.api.preferences.Preferences; 035import org.kuali.rice.kew.api.preferences.PreferencesService; 036import org.kuali.rice.kew.service.KEWServiceLocator; 037import org.kuali.rice.kew.test.KEWTestCase; 038import org.kuali.rice.kew.useroptions.UserOptions; 039import org.kuali.rice.kew.useroptions.UserOptionsService; 040import org.kuali.rice.kim.api.identity.principal.Principal; 041import org.springframework.transaction.TransactionStatus; 042import org.springframework.transaction.support.TransactionCallback; 043import org.springframework.transaction.support.TransactionTemplate; 044 045import javax.xml.bind.JAXBContext; 046import javax.xml.bind.Marshaller; 047import javax.xml.bind.Unmarshaller; 048 049public class PreferencesServiceTest extends KEWTestCase { 050 051 /** 052 * Test that the preferences are saved by default when going through the preferences service. This 053 * means that the preferences service will persist any user option that was not in the db when it went 054 * to fetch that preferences. 055 */ 056 @Test public void testPreferencesDefaultSave() throws Exception { 057 //verify that user doesn't have any preferences in the db. 058 059 final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService(); 060 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("rkirkend"); 061 Collection userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 062 assertTrue("UserOptions should be empty", userOptions.isEmpty()); 063 064 PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService(); 065 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId()); 066 assertTrue("Preferences should require a save.", preferences.isRequiresSave()); 067 068 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 069 assertTrue("UserOptions should not empty", userOptions.isEmpty()); 070 071 preferencesService.savePreferences(principal.getPrincipalId(), preferences); 072 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 073 assertTrue("UserOptions should not be empty", !userOptions.isEmpty()); 074 075 preferences = preferencesService.getPreferences(principal.getPrincipalId()); 076 assertFalse("Preferences should NOT require a save.", preferences.isRequiresSave()); 077 078 // now delete one of the options 079 final UserOptions refreshRateOption = userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId()); 080 assertNotNull("REFRESH_RATE option should exist.", refreshRateOption); 081 TransactionTemplate template = new TransactionTemplate(KEWServiceLocator.getPlatformTransactionManager()); 082 template.execute(new TransactionCallback() { 083 public Object doInTransaction(TransactionStatus status) { 084 userOptionsService.deleteUserOptions(refreshRateOption); 085 return null; 086 } 087 }); 088 assertNull("REFRESH_RATE option should no longer exist.", userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId())); 089 090 preferences = preferencesService.getPreferences(principal.getPrincipalId()); 091 assertTrue("Preferences should now require a save again.", preferences.isRequiresSave()); 092 093 // save refresh rate again 094 template.execute(new TransactionCallback() { 095 public Object doInTransaction(TransactionStatus status) { 096 userOptionsService.save(refreshRateOption); 097 return null; 098 } 099 }); 100 preferences = preferencesService.getPreferences(principal.getPrincipalId()); 101 assertFalse("Preferences should no longer require a save.", preferences.isRequiresSave()); 102 } 103 104 @Test 105 public void testPreferencesMarshallingWithInvalidJson() { 106 final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService(); 107 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("ewestfal"); 108 Collection<UserOptions> userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 109 assertTrue("UserOptions should be empty", userOptions.isEmpty()); 110 111 PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService(); 112 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId()); 113 assertTrue("Preferences should require a save.", preferences.isRequiresSave()); 114 preferencesService.savePreferences(principal.getPrincipalId(), preferences); 115 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 116 117 UserOptions docSearchOrder = new UserOptions(); 118 docSearchOrder.setOptionId("DocSearch.LastSearch.Order"); 119 docSearchOrder.setWorkflowId(principal.getPrincipalId()); 120 docSearchOrder.setOptionVal("DocSearch.LastSearch.Holding0"); 121 122 UserOptions badJsonOption = new UserOptions(); 123 badJsonOption.setOptionId("DocSearch.LastSearch.Holding0"); 124 badJsonOption.setWorkflowId(principal.getPrincipalId()); 125 126 //this be invalid 127 badJsonOption.setOptionVal("{isAdvancedSearch\":\"NO\",\"dateCreatedFrom\":1339168393063,\"documentStatuses\":[],\"documentStatusCategories\":[],\"documentAttributeValues\":{},\"additionalDocumentTypeNames\":[]}"); 128 129 userOptionsService.save(docSearchOrder); 130 userOptionsService.save(badJsonOption); 131 132 133 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 134 assertTrue("UserOptions should not empty", !userOptions.isEmpty()); 135 136 //UserOptions previousDocSearch0 = userOptionsService.findByOptionId("DocSearch.LastSearch.Holding0", principal.getPrincipalId()); 137 preferences = preferencesService.getPreferences(principal.getPrincipalId()); 138 Preferences.Builder pBuilder = Preferences.Builder.create(preferences); 139 Map<String, String> docTypeNotification = new HashMap<String, String>(); 140 141 docTypeNotification.put("hello", "world"); 142 docTypeNotification.put("does_this_thing_have_a", "bookstore example"); 143 pBuilder.setDocumentTypeNotificationPreferences(docTypeNotification); 144 145 String marshaledXml = null; 146 try { 147 JAXBContext jaxbContext = JAXBContext.newInstance(Preferences.class); 148 Marshaller marshaller = jaxbContext.createMarshaller(); 149 150 StringWriter stringWriter = new StringWriter(); 151 152 marshaller.marshal(pBuilder.build(), stringWriter); 153 154 marshaledXml = stringWriter.toString(); 155 156 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 157 Preferences actual = (Preferences)unmarshaller.unmarshal(new StringReader(marshaledXml)); 158 //Object expected = unmarshaller.unmarshal(new StringReader(XML)) 159 Assert.assertEquals(pBuilder.build(), actual); 160 161 } catch (Throwable e) { 162 e.printStackTrace(); 163 } 164 165 166 } 167 168 169 /** 170 * Tests default saving concurrently which can cause a race condition on startup 171 * that leads to constraint violations 172 */ 173 @Test public void testPreferencesConcurrentDefaultSave() throws Throwable { 174 //verify that user doesn't have any preferences in the db. 175 final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService(); 176 final Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("rkirkend"); 177 Collection userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 178 assertTrue("UserOptions should be empty", userOptions.isEmpty()); 179 180 final PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService(); 181 Runnable getPrefRunnable = new Runnable() { 182 public void run() { 183 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId()); 184 assertTrue("Preferences should require a save.", preferences.isRequiresSave()); 185 Collection updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 186 assertTrue("UserOptions should be empty", updatedOptions.isEmpty()); 187 } 188 }; 189 final List<Throwable> errors = new ArrayList<Throwable>(); 190 Thread.UncaughtExceptionHandler ueh = new Thread.UncaughtExceptionHandler() { 191 public void uncaughtException(Thread thread, Throwable error) { 192 errors.add(error); 193 } 194 }; 195 196 // 3 threads should do 197 Thread t1 = new Thread(getPrefRunnable); 198 Thread t2 = new Thread(getPrefRunnable); 199 Thread t3 = new Thread(getPrefRunnable); 200 t1.setUncaughtExceptionHandler(ueh); 201 t2.setUncaughtExceptionHandler(ueh); 202 t3.setUncaughtExceptionHandler(ueh); 203 t1.start(); 204 t2.start(); 205 t3.start(); 206 t1.join(); 207 t2.join(); 208 t3.join(); 209 210 if (errors.size() > 0) { 211 throw errors.iterator().next(); 212 } 213 214 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId()); 215 assertTrue("Preferences should require a save.", preferences.isRequiresSave()); 216 Collection updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 217 assertTrue("UserOptions should be empty", updatedOptions.isEmpty()); 218 preferencesService.savePreferences(principal.getPrincipalId(), preferences); 219 updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId()); 220 assertTrue("UserOptions should not be empty", !updatedOptions.isEmpty()); 221 t1.stop(); 222 } 223}