001 /** 002 * Copyright 2005-2014 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.krad.uif.util; 017 018 import java.io.InputStream; 019 import java.net.URL; 020 import java.util.Arrays; 021 import java.util.Properties; 022 023 import javax.xml.namespace.QName; 024 025 import org.apache.log4j.Logger; 026 import org.junit.After; 027 import org.junit.AfterClass; 028 import org.junit.Assert; 029 import org.junit.Assume; 030 import org.junit.Before; 031 import org.junit.BeforeClass; 032 import org.kuali.rice.core.api.config.property.ConfigContext; 033 import org.kuali.rice.core.api.lifecycle.Lifecycle; 034 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 035 import org.kuali.rice.core.framework.config.property.SimpleConfig; 036 import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader; 037 import org.kuali.rice.krad.UserSession; 038 import org.kuali.rice.krad.uif.freemarker.FreeMarkerInlineRenderBootstrap; 039 import org.kuali.rice.krad.uif.view.ViewAuthorizer; 040 import org.kuali.rice.krad.util.GlobalVariables; 041 import org.springframework.beans.MutablePropertyValues; 042 import org.springframework.mock.web.MockServletContext; 043 import org.springframework.web.context.ConfigurableWebApplicationContext; 044 import org.springframework.web.context.WebApplicationContext; 045 import org.springframework.web.context.support.StaticWebApplicationContext; 046 import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; 047 import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; 048 049 /** 050 * Utilities class for establishing a minimal environment for testing operations involving Uif 051 * components. 052 * 053 * @author Kuali Rice Team (rice.collab@kuali.org) 054 */ 055 public final class UifUnitTestUtils { 056 private final static Logger LOG = Logger.getLogger(UifUnitTestUtils.class); 057 058 private final static ThreadLocal<Properties> TL_CONFIG_PROPERTIES = new ThreadLocal<Properties>(); 059 060 private static ConfigurableWebApplicationContext webApplicationContext; 061 062 /** 063 * Create a web application context suitable for FreeMarker unit testing. 064 */ 065 private static void configureKradWebApplicationContext() { 066 MockServletContext sctx = new MockServletContext(); 067 StaticWebApplicationContext ctx = new StaticWebApplicationContext(); 068 ctx.setServletContext(sctx); 069 070 MutablePropertyValues mpv = new MutablePropertyValues(); 071 mpv.add("preferFileSystemAccess", false); 072 mpv.add("templateLoaderPath", "/krad-web"); 073 Properties props = new Properties(); 074 props.put("number_format", "computer"); 075 props.put("template_update_delay", "2147483647"); 076 mpv.add("freemarkerSettings", props); 077 ctx.registerSingleton("freemarkerConfig", FreeMarkerConfigurer.class, mpv); 078 079 mpv = new MutablePropertyValues(); 080 mpv.add("cache", true); 081 mpv.add("prefix", ""); 082 mpv.add("suffix", ".ftl"); 083 ctx.registerSingleton("viewResolver", FreeMarkerViewResolver.class, mpv); 084 085 ctx.registerSingleton("freeMarkerInputBootstrap", FreeMarkerInlineRenderBootstrap.class); 086 087 ctx.refresh(); 088 ctx.start(); 089 sctx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx); 090 webApplicationContext = ctx; 091 } 092 093 /** 094 * Get the config properties for the current thread. 095 * 096 * @return The config properties for the current thread. 097 */ 098 public static Properties getConfigProperties() { 099 return TL_CONFIG_PROPERTIES.get(); 100 } 101 102 /** 103 * Get the web application context. 104 */ 105 public static WebApplicationContext getWebApplicationContext() { 106 return webApplicationContext; 107 } 108 109 /** 110 * Establish a Rice configuration providing enough mock services via 111 * {@link GlobalResourceLoader} to support the use of KRAD UIF components in unit tests. 112 * 113 * @param applicationId The application ID for the fake environment. 114 * @throws Exception 115 */ 116 public static void establishMockConfig(String applicationId) throws Exception { 117 try { 118 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 119 120 SimpleConfig config = new SimpleConfig(); 121 Properties configProperties = new Properties(); 122 123 URL defaultsUrl = loader.getResource("KRAD-UifDefaults.properties"); 124 URL rootUrl = new URL(defaultsUrl.toExternalForm().substring(0, 125 defaultsUrl.toExternalForm().lastIndexOf('/'))); 126 configProperties.setProperty("root.url", rootUrl.toExternalForm()); 127 128 InputStream defaultPropertyResource = defaultsUrl.openStream(); 129 Assert.assertNotNull("KRAD-UifDefaults.properties", defaultPropertyResource); 130 configProperties.load(defaultPropertyResource); 131 132 InputStream appPropertyResource = loader.getResourceAsStream(applicationId + ".properties"); 133 Assert.assertNotNull(applicationId + ".properties", appPropertyResource); 134 configProperties.load(appPropertyResource); 135 136 for (String propName : configProperties.stringPropertyNames()) { 137 String propValue = (String) configProperties.getProperty(propName); 138 StringBuilder propBuilder = new StringBuilder(propValue); 139 int exprStart = 0, exprEnd = 0; 140 while (exprStart != -1) { 141 exprStart = propBuilder.indexOf("${", exprEnd); 142 if (exprStart == -1) { 143 continue; 144 } 145 146 exprEnd = propBuilder.indexOf("}", exprStart); 147 if (exprEnd - exprStart < 3) { 148 continue; 149 } 150 151 String expr = propBuilder.substring(exprStart + 2, exprEnd); 152 String exprValue = configProperties.getProperty(expr); 153 if (exprValue != null) { 154 propBuilder.delete(exprStart, exprEnd + 1); 155 propBuilder.insert(exprStart, exprValue); 156 configProperties.setProperty(propName, propBuilder.toString()); 157 exprEnd = exprStart + exprValue.length(); 158 } 159 } 160 } 161 162 String resourceBundles = configProperties.getProperty("test.resource.bundles"); 163 if (resourceBundles != null) { 164 for (String resourceBundle : resourceBundles.split(",")) { 165 InputStream propertyResource = loader.getResourceAsStream(resourceBundle); 166 Assert.assertNotNull(resourceBundle, resourceBundle); 167 configProperties.load(propertyResource); 168 LOG.info("Added resource bundle " + resourceBundle); 169 } 170 } 171 172 config.putProperties(configProperties); 173 config.putProperty("application.id", applicationId); 174 175 ConfigContext.init(config); 176 177 MockServletContext servletContext = new MockServletContext(); 178 GlobalResourceLoader.addResourceLoader(new SpringResourceLoader(new QName("KRAD-UifDefaults"), Arrays 179 .asList( 180 "KRAD-UifDefaults-test-context.xml"), servletContext)); 181 GlobalResourceLoader.addResourceLoader(new SpringResourceLoader(new QName(applicationId), Arrays.asList( 182 applicationId + "-test-context.xml"), servletContext)); 183 184 TL_CONFIG_PROPERTIES.set(ConfigContext.getCurrentContextConfig().getProperties()); 185 try { 186 GlobalResourceLoader.start(); 187 Lifecycle viewService = GlobalResourceLoader.getService("viewService"); 188 189 if (viewService != null) { 190 viewService.start(); 191 } 192 193 } finally { 194 TL_CONFIG_PROPERTIES.remove(); 195 } 196 197 configureKradWebApplicationContext(); 198 } catch (Throwable t) { 199 LOG.error("Skipping tests, resource setup failed", t); 200 Assume.assumeNoException("Skipping tests, resource setup failed", t); 201 } 202 } 203 204 /** 205 * Shut down the mock configuration. When {@link #establishMockConfig(String)} is used with 206 * {@link BeforeClass}, then this method should be used with {@link AfterClass} to tear down 207 * resources. 208 */ 209 public static void tearDownMockConfig() throws Exception { 210 GlobalResourceLoader.stop(); 211 } 212 213 /** 214 * Establish a user session with the given principal name. 215 * 216 * <p> 217 * This method will use KIM API calls to look up a person with the provided principal name. Use 218 * {@link #establishMockConfig(String)} to set up a mock KIM environment if needed. 219 * </p> 220 * 221 * @param principalName The principal name of the user to establish a session with. 222 */ 223 public static void establishMockUserSession(String principalName) { 224 UserSession session = new UserSession(principalName); 225 GlobalVariables.setUserSession(session); 226 } 227 228 /** 229 * Shut down the mock user session. When {@link #establishMockUserSession(String)} is used with 230 * {@link Before}, then this method should be used with {@link After} to tear down resources. 231 */ 232 public static void tearDownMockUserSession() { 233 GlobalVariables.setUserSession(null); 234 GlobalVariables.clear(); 235 } 236 237 /** 238 * Get a view authorizer allowing most operations. 239 * 240 * @return A view authorizer allowing most operations. 241 */ 242 public static ViewAuthorizer getAllowMostViewAuthorizer() { 243 return new MockViewAuthorizer(); 244 } 245 246 }