View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.preferences;
17  
18  import static org.junit.Assert.assertFalse;
19  import static org.junit.Assert.assertNotNull;
20  import static org.junit.Assert.assertNull;
21  import static org.junit.Assert.assertTrue;
22  
23  import java.io.StringReader;
24  import java.io.StringWriter;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import junit.framework.Assert;
32  import org.junit.Test;
33  import org.kuali.rice.kew.api.KewApiServiceLocator;
34  import org.kuali.rice.kew.api.preferences.Preferences;
35  import org.kuali.rice.kew.api.preferences.PreferencesService;
36  import org.kuali.rice.kew.service.KEWServiceLocator;
37  import org.kuali.rice.kew.test.KEWTestCase;
38  import org.kuali.rice.kew.useroptions.UserOptions;
39  import org.kuali.rice.kew.useroptions.UserOptionsService;
40  import org.kuali.rice.kim.api.identity.principal.Principal;
41  import org.springframework.transaction.TransactionStatus;
42  import org.springframework.transaction.support.TransactionCallback;
43  import org.springframework.transaction.support.TransactionTemplate;
44  
45  import javax.xml.bind.JAXBContext;
46  import javax.xml.bind.Marshaller;
47  import javax.xml.bind.Unmarshaller;
48  
49  public class PreferencesServiceTest extends KEWTestCase {
50  
51      /**
52       * Test that the preferences are saved by default when going through the preferences service.  This
53       * means that the preferences service will persist any user option that was not in the db when it went
54       * to fetch that preferences.
55       */
56  	@Test public void testPreferencesDefaultSave() throws Exception {
57         //verify that user doesn't have any preferences in the db.
58  
59         final UserOptionsService userOptionsService = KEWServiceLocator.getUserOptionsService();
60         Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipalByPrincipalName("rkirkend");
61         Collection userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
62         assertTrue("UserOptions should be empty", userOptions.isEmpty());
63  
64         PreferencesService preferencesService = KewApiServiceLocator.getPreferencesService();
65         Preferences preferences = preferencesService.getPreferences(principal.getPrincipalId());
66         assertTrue("Preferences should require a save.", preferences.isRequiresSave());
67  
68         userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
69         assertTrue("UserOptions should not empty", userOptions.isEmpty());
70  
71         preferencesService.savePreferences(principal.getPrincipalId(), preferences);
72         userOptions = userOptionsService.findByWorkflowUser(principal.getPrincipalId());
73         assertTrue("UserOptions should not be empty", !userOptions.isEmpty());
74  
75         preferences = preferencesService.getPreferences(principal.getPrincipalId());
76         assertFalse("Preferences should NOT require a save.", preferences.isRequiresSave());
77  
78         // now delete one of the options
79         final UserOptions refreshRateOption = userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId());
80         assertNotNull("REFRESH_RATE option should exist.", refreshRateOption);
81         TransactionTemplate template = new TransactionTemplate(KEWServiceLocator.getPlatformTransactionManager());
82         template.execute(new TransactionCallback() {
83             public Object doInTransaction(TransactionStatus status) {
84                 userOptionsService.deleteUserOptions(refreshRateOption);
85                 return null;
86             }
87         });
88         assertNull("REFRESH_RATE option should no longer exist.", userOptionsService.findByOptionId("REFRESH_RATE", principal.getPrincipalId()));
89  
90         preferences = preferencesService.getPreferences(principal.getPrincipalId());
91         assertTrue("Preferences should now require a save again.", preferences.isRequiresSave());
92  
93         // save refresh rate again
94         template.execute(new TransactionCallback() {
95             public Object doInTransaction(TransactionStatus status) {
96                 userOptionsService.save(refreshRateOption);
97                 return null;
98             }
99         });
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 }