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    
151        }
152    }