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.io.StringReader;
024    import java.io.StringWriter;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.HashMap;
028    import java.util.List;
029    import java.util.Map;
030    
031    import junit.framework.Assert;
032    import org.junit.Test;
033    import org.kuali.rice.kew.api.KewApiServiceLocator;
034    import org.kuali.rice.kew.api.preferences.Preferences;
035    import org.kuali.rice.kew.api.preferences.PreferencesService;
036    import org.kuali.rice.kew.service.KEWServiceLocator;
037    import org.kuali.rice.kew.test.KEWTestCase;
038    import org.kuali.rice.kew.useroptions.UserOptions;
039    import org.kuali.rice.kew.useroptions.UserOptionsService;
040    import org.kuali.rice.kim.api.identity.principal.Principal;
041    import org.springframework.transaction.TransactionStatus;
042    import org.springframework.transaction.support.TransactionCallback;
043    import org.springframework.transaction.support.TransactionTemplate;
044    
045    import javax.xml.bind.JAXBContext;
046    import javax.xml.bind.Marshaller;
047    import javax.xml.bind.Unmarshaller;
048    
049    public 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    }