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 }