View Javadoc
1   /**
2    * Copyright 2005-2015 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.xml.ingest;
17  
18  import java.io.IOException;
19  import java.util.Properties;
20  import java.util.SortedSet;
21  
22  import javax.servlet.ServletContext;
23  
24  import org.kuali.common.util.PropertyUtils;
25  import org.kuali.common.util.Str;
26  import org.kuali.common.util.log.LoggerUtils;
27  import org.kuali.common.util.property.ImmutableProperties;
28  import org.kuali.rice.core.api.config.property.Config;
29  import org.kuali.rice.core.api.config.property.ConfigContext;
30  import org.kuali.rice.core.impl.config.property.ConfigLogger;
31  import org.kuali.rice.core.impl.config.property.JAXBConfigImpl;
32  import org.kuali.rice.core.web.util.PropertySources;
33  import org.slf4j.Logger;
34  
35  import com.google.common.base.Preconditions;
36  import com.google.common.collect.Sets;
37  
38  /**
39   * Utility class to handle {@link PropertySources} and Rice config files.
40   * 
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class RiceConfigUtils {
44  
45  	private static final Logger logger = LoggerUtils.make();
46  
47      private RiceConfigUtils() {}
48  
49      /**
50       * Gets the {@link Config} from both the current configuration and the ones in {@code loaded}, at {@code location},
51       * and in the {@code servletContext}.
52       *
53       * @param loaded the loaded properties
54       * @param location the location of additional properties
55       * @param servletContext the servlet context in which to add more properties
56       *
57       * @return the final configuration
58       */
59  	public static Config getRootConfig(Properties loaded, String location, ServletContext servletContext) {
60  		// Get the Rice config object the listener created
61  		Config config = ConfigContext.getCurrentContextConfig();
62  		Preconditions.checkNotNull(config, "'config' cannot be null");
63  		Properties listenerProperties = getProperties(config);
64  
65  		// Parse config from the location indicated, using listener properties in the process of doing so
66  		JAXBConfigImpl parsed = parseConfig(location, listenerProperties);
67  
68  		// Add and override loaded properties with parsed properties
69  		addAndOverride(loaded, parsed.getRawProperties());
70  
71  		// Priority is servlet -> env -> system
72  		// Override anything we've loaded with servlet, env, and system properties
73  		Properties servlet = PropertySources.convert(servletContext);
74  		Properties global = PropertyUtils.getGlobalProperties(servlet);
75  		addAndOverride(loaded, global);
76  		logger.info("Using {} distinct properties", Integer.valueOf(loaded.size()));
77  
78  		// Use JAXBConfigImpl in order to perform Rice's custom placeholder resolution logic now that everything is loaded
79  		return new JAXBConfigImpl(loaded);
80  
81  	}
82  
83  	/**
84  	 * Parse the configuration stored at {@code location}.
85       *
86       * @param location the location to get properties from
87       *
88       * @return the new configuration
89       */
90  	public static JAXBConfigImpl parseConfig(String location) {
91  		return parseConfig(location, ImmutableProperties.of());
92  	}
93  
94  	/**
95  	 * Parse the configuration stored at {@code location}, adding any additional properties from {@code properties}.
96       *
97       * @param location the location to get properties from
98       * @param properties any additional properties to add
99       *
100      * @return the new configuration
101 	 */
102 	public static JAXBConfigImpl parseConfig(String location, Properties properties) {
103 		try {
104 			JAXBConfigImpl config = new JAXBConfigImpl(location, properties);
105 			config.parseConfig();
106 			return config;
107 		} catch (IOException e) {
108 			throw new IllegalStateException("Unexpected error parsing config", e);
109 		}
110 	}
111 
112 	/**
113 	 * Parse the configuration stored at {@code location} and initialize.
114      *
115      * @param location the location to get properties from
116      *
117      * @return the new configuration
118      */
119 	public static JAXBConfigImpl parseAndInit(String location) {
120 		JAXBConfigImpl config = parseConfig(location);
121 		ConfigContext.init(config);
122 		return config;
123 	}
124 
125     /**
126      * Returns the {@link Properties} from the given {@code config}.
127      *
128      * @param config the {@link Config} to get the {@link Properties} from
129      *
130      * @return the {@link Properties}
131      */
132 	public static Properties getProperties(Config config) {
133 		if (config instanceof JAXBConfigImpl) {
134 			JAXBConfigImpl jci = (JAXBConfigImpl) config;
135 			return jci.getRawProperties();
136 		} else {
137 			logger.warn("Unable to access raw Rice config properties.");
138 			return config.getProperties();
139 		}
140 	}
141 
142     /**
143      * Put all of the given {@code properties} into the given {@code config}.
144      *
145      * @param config the {@link Config} to add the {@link Properties} to
146      * @param properties the {@link Properties} to add
147      */
148     public static void putProperties(Config config, Properties properties) {
149         SortedSet<String> keys = Sets.newTreeSet(properties.stringPropertyNames());
150         for (String key : keys) {
151             config.putProperty(key, properties.getProperty(key));
152         }
153     }
154 
155     private static void add(Properties oldProperties, Properties newProperties) {
156         SortedSet<String> newKeys = Sets.newTreeSet(Sets.difference(newProperties.stringPropertyNames(), oldProperties.stringPropertyNames()));
157 
158         if (newKeys.isEmpty()) {
159             return;
160         }
161 
162         logger.info("Adding {} properties", Integer.valueOf(newKeys.size()));
163 
164         for (String newKey : newKeys) {
165             String value = newProperties.getProperty(newKey);
166             logger.debug("Adding - [{}]=[{}]", newKey, toLogMsg(newKey, value));
167             oldProperties.setProperty(newKey, value);
168         }
169     }
170 
171 	private static void override(Properties oldProperties, Properties newProperties) {
172 		SortedSet<String> commonKeys = Sets.newTreeSet(Sets.intersection(newProperties.stringPropertyNames(), oldProperties.stringPropertyNames()));
173 
174 		if (commonKeys.isEmpty()) {
175 			return;
176 		}
177 
178 		logger.debug("{} keys in common", Integer.valueOf(commonKeys.size()));
179 
180         for (String commonKey : commonKeys) {
181 			String oldValue = oldProperties.getProperty(commonKey);
182 			String newValue = newProperties.getProperty(commonKey);
183 
184 			if (!newValue.equals(oldValue)) {
185 				Object[] args = { commonKey, toLogMsg(commonKey, oldValue), toLogMsg(commonKey, newValue) };
186 				logger.info("Overriding - [{}]=[{}]->[{}]", args);
187                 oldProperties.setProperty(commonKey, newValue);
188 			}
189 		}
190 	}
191 
192     private static void addAndOverride(Properties oldProperties, Properties newProperties) {
193         add(oldProperties, newProperties);
194         override(oldProperties, newProperties);
195     }
196 
197 	private static String toLogMsg(String key, String value) {
198 		return Str.flatten(ConfigLogger.getDisplaySafeValue(key, value));
199 	}
200 
201 }