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 }