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.core.web.util;
17  
18  import static com.google.common.base.Preconditions.checkNotNull;
19  import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors;
20  
21  import java.util.Collection;
22  import java.util.Enumeration;
23  import java.util.Properties;
24  
25  import javax.servlet.ServletContext;
26  import javax.servlet.ServletContextEvent;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.springframework.beans.factory.ListableBeanFactory;
32  import org.springframework.context.ConfigurableApplicationContext;
33  import org.springframework.core.env.ConfigurableEnvironment;
34  import org.springframework.core.env.MutablePropertySources;
35  import org.springframework.core.env.PropertySource;
36  import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
37  
38  import com.google.common.base.Optional;
39  import com.google.common.base.Preconditions;
40  
41  /**
42   * Utility class for dealing with {@link PropertySource}s.
43   *
44   * @author Kuali Rice Team (rice.collab@kuali.org)
45   */
46  public class PropertySources {
47  
48      private static final Logger logger = LoggerFactory.getLogger(PropertySources.class);
49  
50      /**
51       * Check system properties and servlet context init params for an annotated Spring configuration
52       * class located under {@code key}. If present, create a new
53       * {@AnnotationConfigWebApplicationContext} and register
54       * the annotated configuration class. Refresh the context and then examine it for a
55       * {@code PropertySource<?>} bean. There must be exactly one {@code PropertySource<?>} bean
56       * present in the application context.
57       */
58      public static Optional<PropertySource<?>> getPropertySource(ServletContextEvent sce, String key) {
59          Optional<String> annotatedClass = getProperty(sce, key);
60          if (annotatedClass.isPresent()) {
61              AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
62              context.setServletContext(sce.getServletContext());
63              PropertySource<?> propertySource = getPropertySource(annotatedClass.get(), context);
64              return Optional.<PropertySource<?>> of(propertySource);
65          } else {
66              return Optional.absent();
67          }
68      }
69  
70      /**
71       * Register {@code propertySource} as the first thing Spring will check when looking up property
72       * values.
73       */
74      public static void addFirst(ConfigurableApplicationContext context, PropertySource<?> propertySource) {
75          ConfigurableEnvironment env = context.getEnvironment();
76          MutablePropertySources propertySources = env.getPropertySources();
77          propertySources.addFirst(propertySource);
78      }
79  
80      protected static PropertySource<?> getPropertySource(String className, AnnotationConfigWebApplicationContext context) {
81          try {
82              logger.info("Loading [{}] to setup a Spring property source", className);
83              Class<?> annotatedClass = Class.forName(className);
84              context.register(annotatedClass);
85              context.refresh();
86              PropertySource<?> propertySource = getPropertySource(context, annotatedClass);
87              context.close();
88              logger.info("Spring property source was successfully setup");
89              return propertySource;
90          } catch (Exception e) {
91              throw new IllegalStateException("Unexpected error configuring Spring property source", e);
92          }
93      }
94  
95      protected static PropertySource<?> getPropertySource(ListableBeanFactory factory, Class<?> annotatedClass) {
96          @SuppressWarnings("rawtypes")
97          Collection<PropertySource> collection = beansOfTypeIncludingAncestors(factory, PropertySource.class).values();
98          checkSizeEqualsOne(collection, annotatedClass);
99          return collection.iterator().next();
100     }
101 
102     protected static void checkSizeEqualsOne(Collection<?> collection, Class<?> annotatedClass) {
103         Object[] args = {annotatedClass.getCanonicalName(), collection.size()};
104         String errorMessage = "[%s] contained %s property source beans.  There must always be exactly 1 property source bean";
105         Preconditions.checkState(collection.size() == 1, errorMessage, args);
106     }
107 
108     /**
109      * Examine both system properties and servlet context init parameters for the presence of a
110      * value under {@code key}.
111      */
112     public static Optional<String> getProperty(ServletContextEvent sce, String key) {
113         checkNotNull(key, "'key' cannot be null");
114 
115         // If there is a system property value, use it
116         String sys = System.getProperty(key);
117         if (!StringUtils.isBlank(sys)) {
118             logger.info("Found [{}] defined in system properties: [{}]", key, sys);
119             return Optional.of(sys);
120         }
121 
122         // If there is a servlet context value, use it. Unless it's blank or an unresolved placeholder
123         String web = sce.getServletContext().getInitParameter(key);
124         if (StringUtils.isBlank(web) || isPlaceHolder(web)) {
125             return Optional.absent();
126         } else {
127             logger.info("Found [{}] defined in servlet context: [{}]", key, web);
128             return Optional.of(web);
129         }
130     }
131 
132     /**
133      * Convert {@code ServletContext} init parameters into a {@code Properties} object.
134      */
135     public static Properties convert(ServletContext context) {
136         Properties properties = new Properties();
137         @SuppressWarnings("unchecked")
138         Enumeration<String> paramNames = context.getInitParameterNames();
139         while (paramNames.hasMoreElements()) {
140             String paramName = paramNames.nextElement();
141             properties.put(paramName, context.getInitParameter(paramName));
142         }
143         return properties;
144     }
145 
146     protected static boolean isPlaceHolder(String value) {
147         checkNotNull(value, "'value' cannot be null");
148         return value.startsWith("${") && value.endsWith("}");
149     }
150 
151 }