001 /* 002 * Copyright 2005-2007 The Kuali Foundation 003 * 004 * 005 * Licensed under the Educational Community License, Version 2.0 (the "License"); you may not use this file except in 006 * compliance with the License. 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 distributed under the License is distributed on an "AS 011 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 012 * language governing permissions and limitations under the License. 013 */ 014 package org.kuali.rice.kew.test; 015 016 import java.io.InputStream; 017 import java.util.ArrayList; 018 import java.util.List; 019 020 import org.kuali.rice.core.config.Config; 021 import org.kuali.rice.core.config.ConfigContext; 022 import org.kuali.rice.core.lifecycle.BaseLifecycle; 023 import org.kuali.rice.core.lifecycle.Lifecycle; 024 import org.kuali.rice.core.web.jetty.JettyServer; 025 import org.kuali.rice.kew.batch.KEWXmlDataLoader; 026 import org.kuali.rice.kew.exception.WorkflowRuntimeException; 027 import org.kuali.rice.kew.service.KEWServiceLocator; 028 import org.kuali.rice.kew.util.KEWConstants; 029 import org.kuali.rice.kim.service.KIMServiceLocator; 030 import org.kuali.rice.kns.util.GlobalVariables; 031 import org.kuali.rice.kns.util.MessageMap; 032 import org.kuali.rice.test.ClearDatabaseLifecycle; 033 import org.kuali.rice.test.RiceInternalSuiteDataTestCase; 034 import org.kuali.rice.test.SQLDataLoader; 035 import org.springframework.transaction.support.TransactionTemplate; 036 037 /** 038 * Useful superclass for all KEW test cases. Handles setup of test utilities and 039 * a test environment. Configures the Spring test environment providing a 040 * template method for custom context files in test mode. Also provides a 041 * template method for running custom transactional setUp. Tear down handles 042 * automatic tear down of objects created inside the test environment. 043 */ 044 public abstract class KEWTestCase extends RiceInternalSuiteDataTestCase { 045 046 /** 047 * This is the "bootstrap", aka Rice client, Spring beans file that the KEW 048 * test harness will load 049 */ 050 private static final String DEFAULT_KEW_BOOTSTRAP_SPRING_FILE = "classpath:org/kuali/rice/kew/config/TestKEWSpringBeans.xml"; 051 052 @Override 053 protected String getModuleName() { 054 return TestUtils.getModuleName(); 055 } 056 057 /** 058 * Default implementation does nothing. Subclasses should override this 059 * method if they want to perform setup work inside of a database 060 * transaction. 061 */ 062 protected void setUpAfterDataLoad() throws Exception { 063 // override 064 } 065 066 protected void loadTestData() throws Exception { 067 // override this to load your own test data 068 } 069 070 protected TransactionTemplate getTransactionTemplate() { 071 return TestUtilities.getTransactionTemplate(); 072 } 073 074 /** 075 * Override the RiceTestCase setUpInternal in order to set a system property 076 * beforehand. 077 * 078 * @see org.kuali.rice.test.RiceTestCase#setUpInternal() 079 */ 080 @Override 081 protected void setUpInternal() throws Exception { 082 System.setProperty(KEWConstants.BOOTSTRAP_SPRING_FILE, 083 getKEWBootstrapSpringFile()); 084 super.setUpInternal(); 085 } 086 087 /** 088 * Initiates loading of per-test data 089 */ 090 @Override 091 protected void loadPerTestData() throws Exception { 092 final long t1 = System.currentTimeMillis(); 093 094 loadDefaultTestData(); 095 096 final long t2 = System.currentTimeMillis(); 097 report("Time to load default test data: " + (t2 - t1)); 098 099 loadTestData(); 100 101 final long t3 = System.currentTimeMillis(); 102 report("Time to load test-specific test data: " + (t3 - t2)); 103 104 setUpAfterDataLoad(); 105 106 final long t4 = System.currentTimeMillis(); 107 report("Time to run test-specific setup: " + (t4 - t3)); 108 } 109 110 /** 111 * Returns the "bootstrap", aka Rice client, Spring beans file that the KEW 112 * test harness will load. KEW test cases can override this to provide an 113 * alternative bootstrap spring file. Currently only one file is supported, 114 * so one must override this file and then import the core file. 115 * 116 * @return the "bootstrap", aka Rice client, Spring beans file that the KEW 117 * test harness will load 118 */ 119 protected String getKEWBootstrapSpringFile() { 120 return DEFAULT_KEW_BOOTSTRAP_SPRING_FILE; 121 } 122 123 /** 124 * Override the standard per-test lifecycles to prepend 125 * ClearDatabaseLifecycle and ClearCacheLifecycle 126 * 127 * @see org.kuali.rice.test.RiceTestCase#getPerTestLifecycles() 128 */ 129 @Override 130 protected List<Lifecycle> getPerTestLifecycles() { 131 List<Lifecycle> lifecycles = new ArrayList<Lifecycle>(); 132 lifecycles.add(new ClearDatabaseLifecycle(getPerTestTablesToClear(), 133 getPerTestTablesNotToClear())); 134 lifecycles.add(new ClearCacheLifecycle()); 135 lifecycles.addAll(super.getPerTestLifecycles()); 136 return lifecycles; 137 } 138 139 /** 140 * Override the suite lifecycles to avoid the ClearDatabaseLifecycle (which 141 * we do on a per-test basis, as above) and to add on a JettyServer 142 * lifecycle 143 * 144 * @see org.kuali.rice.test.RiceTestCase#getSuiteLifecycles() 145 */ 146 @Override 147 protected List<Lifecycle> getSuiteLifecycles() { 148 149 List<Lifecycle> lifeCycles = super.getSuiteLifecycles(); 150 lifeCycles.add( buildJettyServer(getJettyServerPort(), getJettyServerContextName(), getJettyServerRelativeWebappRoot())); 151 lifeCycles.add(new InitializeGRL()); 152 lifeCycles.add(new BaseLifecycle() { 153 public void start() throws Exception { 154 KEWXmlDataLoader.loadXmlClassLoaderResource(getClass(), "DefaultSuiteTestData.xml"); 155 super.start(); 156 } 157 }); 158 return lifeCycles; 159 } 160 161 @Override 162 protected void loadSuiteTestData() throws Exception { 163 super.loadSuiteTestData(); 164 new SQLDataLoader( 165 "classpath:org/kuali/rice/kew/test/DefaultSuiteTestData.sql", ";") 166 .runSql(); 167 } 168 169 protected JettyServer buildJettyServer(int port, String contextName, String relativeWebappRoot) { 170 JettyServer server = new JettyServer(port, contextName, relativeWebappRoot); 171 server.setFailOnContextFailure(true); 172 server.setTestMode(true); 173 return server; 174 } 175 176 /* 177 * Checks to see if a Jetty server is runningneeds to be randomly generated and then made available to subsequent 178 * tests 179 */ 180 protected int getJettyServerPort() { 181 Config cfgCtx = null; 182 try { 183 cfgCtx = getTestHarnessConfig(); 184 } catch (Exception e) { 185 log.error("Caught exception attempting to load test harness prior to aggregating suite lifecycles."); 186 } 187 return Integer.parseInt(cfgCtx.getProperty(KEWConstants.HTTP_SERVICE_PORT)); 188 } 189 190 protected String getJettyServerContextName() { 191 Config cfgCtx = null; 192 try { 193 cfgCtx = getTestHarnessConfig(); 194 } catch (Exception e) { 195 log.error("Caught exception attempting to load test harness prior to aggregating suite lifecycles."); 196 } 197 return cfgCtx.getProperty(KEWConstants.KEW_SERVER_CONTEXT); 198 } 199 200 protected String getJettyServerRelativeWebappRoot() { 201 return "/../web/src/main/webapp/kew"; 202 } 203 204 /** 205 * Adds any ResourceLoaders that have been registered for WebAppClassLoaders 206 * to the GlobalResourceLoader 207 */ 208 private class InitializeGRL extends BaseLifecycle { 209 @Override 210 public void start() throws Exception { 211 org.kuali.rice.test.TestUtilities.addWebappsToContext(); 212 super.start(); 213 } 214 215 } 216 217 /** 218 * Flushes the KEW cache(s) 219 */ 220 public class ClearCacheLifecycle extends BaseLifecycle { 221 @Override 222 public void stop() throws Exception { 223 KEWServiceLocator.getCacheAdministrator().flushAll(); 224 KIMServiceLocator.getIdentityManagementService().flushAllCaches(); 225 KIMServiceLocator.getRoleManagementService().flushRoleCaches(); 226 super.stop(); 227 } 228 229 } 230 231 /** 232 * Returns the List of tables that should be cleared on every test run. 233 */ 234 protected List<String> getPerTestTablesToClear() { 235 List<String> tablesToClear = new ArrayList<String>(); 236 tablesToClear.add("KREW_.*"); 237 tablesToClear.add("KRSB_.*"); 238 tablesToClear.add("KREN_.*"); 239 return tablesToClear; 240 } 241 242 protected List<String> getPerTestTablesNotToClear() { 243 return new ArrayList<String>(); 244 } 245 246 /** 247 * By default this loads the "default" data set from the DefaultTestData.sql 248 * and DefaultTestData.xml files. Subclasses can override this to change 249 * this behaviour 250 */ 251 protected void loadDefaultTestData() throws Exception { 252 // at this point this is constants. loading these through xml import is 253 // problematic because of cache notification 254 // issues in certain low level constants. 255 new SQLDataLoader( 256 "classpath:org/kuali/rice/kew/test/DefaultPerTestData.sql", ";") 257 .runSql(); 258 259 KEWXmlDataLoader.loadXmlClassLoaderResource(KEWTestCase.class, 260 "DefaultPerTestData.xml"); 261 GlobalVariables.setMessageMap(new MessageMap()); 262 } 263 264 protected void loadXmlFile(String fileName) { 265 try { 266 KEWXmlDataLoader.loadXmlClassLoaderResource(getClass(), fileName); 267 } catch (Exception e) { 268 throw new WorkflowRuntimeException(e); 269 } 270 } 271 272 protected void loadXmlFile(Class clazz, String fileName) { 273 try { 274 KEWXmlDataLoader.loadXmlClassLoaderResource(clazz, fileName); 275 } catch (Exception e) { 276 throw new WorkflowRuntimeException(e); 277 } 278 } 279 280 protected void loadXmlFileFromFileSystem(String fileName) { 281 try { 282 KEWXmlDataLoader.loadXmlFile(fileName); 283 } catch (Exception e) { 284 throw new WorkflowRuntimeException(e); 285 } 286 } 287 288 protected void loadXmlStream(InputStream xmlStream) { 289 try { 290 KEWXmlDataLoader.loadXmlStream(xmlStream); 291 } catch (Exception e) { 292 throw new WorkflowRuntimeException(e); 293 } 294 } 295 296 protected String getPrincipalIdForName(String principalName) { 297 return KEWServiceLocator.getIdentityHelperService() 298 .getIdForPrincipalName(principalName); 299 } 300 301 protected String getPrincipalNameForId(String principalId) { 302 return KEWServiceLocator.getIdentityHelperService().getPrincipal( 303 principalId).getPrincipalName(); 304 } 305 306 protected String getGroupIdForName(String namespace, String groupName) { 307 return KEWServiceLocator.getIdentityHelperService().getIdForGroupName( 308 namespace, groupName); 309 } 310 }