View Javadoc
1   /**
2    * Copyright 2005-2016 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.krad.uif.util;
17  
18  import java.io.InputStream;
19  import java.net.URL;
20  import java.util.Arrays;
21  import java.util.Properties;
22  
23  import javax.xml.namespace.QName;
24  
25  import org.apache.log4j.Logger;
26  import org.junit.After;
27  import org.junit.AfterClass;
28  import org.junit.Assert;
29  import org.junit.Assume;
30  import org.junit.Before;
31  import org.junit.BeforeClass;
32  import org.kuali.rice.core.api.config.property.ConfigContext;
33  import org.kuali.rice.core.api.lifecycle.Lifecycle;
34  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
35  import org.kuali.rice.core.framework.config.property.SimpleConfig;
36  import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader;
37  import org.kuali.rice.krad.UserSession;
38  import org.kuali.rice.krad.uif.freemarker.FreeMarkerInlineRenderBootstrap;
39  import org.kuali.rice.krad.uif.view.ViewAuthorizer;
40  import org.kuali.rice.krad.util.GlobalVariables;
41  import org.springframework.beans.MutablePropertyValues;
42  import org.springframework.mock.web.MockServletContext;
43  import org.springframework.web.context.ConfigurableWebApplicationContext;
44  import org.springframework.web.context.WebApplicationContext;
45  import org.springframework.web.context.support.StaticWebApplicationContext;
46  import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
47  import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
48  
49  /**
50   * Utilities class for establishing a minimal environment for testing operations involving Uif
51   * components.
52   *
53   * @author Kuali Rice Team (rice.collab@kuali.org)
54   */
55  public final class UifUnitTestUtils {
56      private final static Logger LOG = Logger.getLogger(UifUnitTestUtils.class);
57  
58      private final static ThreadLocal<Properties> TL_CONFIG_PROPERTIES = new ThreadLocal<Properties>();
59  
60      private static ConfigurableWebApplicationContext webApplicationContext;
61  
62      /**
63       * Create a web application context suitable for FreeMarker unit testing.
64       */
65      private static void configureKradWebApplicationContext() {
66          MockServletContext sctx = new MockServletContext();
67          StaticWebApplicationContext ctx = new StaticWebApplicationContext();
68          ctx.setServletContext(sctx);
69  
70          MutablePropertyValues mpv = new MutablePropertyValues();
71          mpv.add("preferFileSystemAccess", false);
72          mpv.add("templateLoaderPath", "/krad-web");
73          Properties props = new Properties();
74          props.put("number_format", "computer");
75          props.put("template_update_delay", "2147483647");
76          mpv.add("freemarkerSettings", props);
77          ctx.registerSingleton("freemarkerConfig", FreeMarkerConfigurer.class, mpv);
78  
79          mpv = new MutablePropertyValues();
80          mpv.add("cache", true);
81          mpv.add("prefix", "");
82          mpv.add("suffix", ".ftl");
83          ctx.registerSingleton("viewResolver", FreeMarkerViewResolver.class, mpv);
84  
85          ctx.registerSingleton("freeMarkerInputBootstrap", FreeMarkerInlineRenderBootstrap.class);
86  
87          ctx.refresh();
88          ctx.start();
89          sctx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
90          webApplicationContext = ctx;
91      }
92  
93      /**
94       * Get the config properties for the current thread.
95       *
96       * @return The config properties for the current thread.
97       */
98      public static Properties getConfigProperties() {
99          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 }