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.kew.preferences;
017
018 import static org.junit.Assert.assertFalse;
019 import static org.junit.Assert.assertNotNull;
020 import static org.junit.Assert.assertNull;
021 import static org.junit.Assert.assertTrue;
022
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.List;
026
027 import org.junit.Test;
028 import org.kuali.rice.kew.api.KewApiServiceLocator;
029 import org.kuali.rice.kew.api.preferences.Preferences;
030 import org.kuali.rice.kew.api.preferences.PreferencesService;
031 import org.kuali.rice.kew.service.KEWServiceLocator;
032 import org.kuali.rice.kew.test.KEWTestCase;
033 import org.kuali.rice.kew.useroptions.UserOptions;
034 import org.kuali.rice.kew.useroptions.UserOptionsService;
035 import org.kuali.rice.kim.api.identity.principal.Principal;
036 import org.springframework.transaction.TransactionStatus;
037 import org.springframework.transaction.support.TransactionCallback;
038 import org.springframework.transaction.support.TransactionTemplate;
039
040
041
042 public class PreferencesServiceTest extends KEWTestCase {
043
044 /**
045 * Test that the preferences are saved by default when going through the preferences service. This
046 * means that the preferences service will persist any user option that was not in the db when it went
047 * to fetch that preferences.
048 */
049 @Test public void testPreferencesDefaultSave() throws Exception {
050 //verify that user doesn't have any preferences in the db.
051
052 final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService();
053 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("rkirkend");
054 Collection userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
055 assertTrue("UserOptions should be empty", userOptions.isEmpty());
056
057 PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService();
058 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId());
059 assertTrue("Preferences should require a save.", preferences.isRequiresSave());
060
061 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
062 assertTrue("UserOptions should not empty", userOptions.isEmpty());
063
064 preferencesService.savePreferences(principal.getPrincipalId(), preferences);
065 userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
066 assertTrue("UserOptions should not be empty", !userOptions.isEmpty());
067
068 preferences = preferencesService.getPreferences(principal.getPrincipalId());
069 assertFalse("Preferences should NOT require a save.", preferences.isRequiresSave());
070
071 // now delete one of the options
072 final UserOptions refreshRateOption = userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId());
073 assertNotNull("REFRESH_RATE option should exist.", refreshRateOption);
074 TransactionTemplate template = new TransactionTemplate(KEWServiceLocator.getPlatformTransactionManager());
075 template.execute(new TransactionCallback() {
076 public Object doInTransaction(TransactionStatus status) {
077 userOptionsService.deleteUserOptions(refreshRateOption);
078 return null;
079 }
080 });
081 assertNull("REFRESH_RATE option should no longer exist.", userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId()));
082
083 preferences = preferencesService.getPreferences(principal.getPrincipalId());
084 assertTrue("Preferences should now require a save again.", preferences.isRequiresSave());
085
086 // save refresh rate again
087 template.execute(new TransactionCallback() {
088 public Object doInTransaction(TransactionStatus status) {
089 userOptionsService.save(refreshRateOption);
090 return null;
091 }
092 });
093 preferences = preferencesService.getPreferences(principal.getPrincipalId());
094 assertFalse("Preferences should no longer require a save.", preferences.isRequiresSave());
095 }
096
097
098 /**
099 * Tests default saving concurrently which can cause a race condition on startup
100 * that leads to constraint violations
101 */
102 @Test public void testPreferencesConcurrentDefaultSave() throws Throwable {
103 //verify that user doesn't have any preferences in the db.
104 final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService();
105 final Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("rkirkend");
106 Collection userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
107 assertTrue("UserOptions should be empty", userOptions.isEmpty());
108
109 final PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService();
110 Runnable getPrefRunnable = new Runnable() {
111 public void run() {
112 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId());
113 assertTrue("Preferences should require a save.", preferences.isRequiresSave());
114 Collection updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
115 assertTrue("UserOptions should be empty", updatedOptions.isEmpty());
116 }
117 };
118 final List<Throwable> errors = new ArrayList<Throwable>();
119 Thread.UncaughtExceptionHandler ueh = new Thread.UncaughtExceptionHandler() {
120 public void uncaughtException(Thread thread, Throwable error) {
121 errors.add(error);
122 }
123 };
124
125 // 3 threads should do
126 Thread t1 = new Thread(getPrefRunnable);
127 Thread t2 = new Thread(getPrefRunnable);
128 Thread t3 = new Thread(getPrefRunnable);
129 t1.setUncaughtExceptionHandler(ueh);
130 t2.setUncaughtExceptionHandler(ueh);
131 t3.setUncaughtExceptionHandler(ueh);
132 t1.start();
133 t2.start();
134 t3.start();
135 t1.join();
136 t2.join();
137 t3.join();
138
139 if (errors.size() > 0) {
140 throw errors.iterator().next();
141 }
142
143 Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId());
144 assertTrue("Preferences should require a save.", preferences.isRequiresSave());
145 Collection updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
146 assertTrue("UserOptions should be empty", updatedOptions.isEmpty());
147 preferencesService.savePreferences(principal.getPrincipalId(), preferences);
148 updatedOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
149 assertTrue("UserOptions should not be empty", !updatedOptions.isEmpty());
150 t1.stop();
151 }
152 }