001/** 002 * Copyright 2005-2018 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 */ 016package org.kuali.rice.xml.ingest; 017 018import java.io.IOException; 019import java.util.Properties; 020import java.util.SortedSet; 021 022import javax.servlet.ServletContext; 023 024import org.kuali.common.util.PropertyUtils; 025import org.kuali.common.util.Str; 026import org.kuali.common.util.log.LoggerUtils; 027import org.kuali.common.util.property.ImmutableProperties; 028import org.kuali.rice.core.api.config.property.Config; 029import org.kuali.rice.core.api.config.property.ConfigContext; 030import org.kuali.rice.core.impl.config.property.ConfigLogger; 031import org.kuali.rice.core.impl.config.property.JAXBConfigImpl; 032import org.kuali.rice.core.web.util.PropertySources; 033import org.slf4j.Logger; 034 035import com.google.common.base.Preconditions; 036import com.google.common.collect.Sets; 037 038/** 039 * Utility class to handle {@link PropertySources} and Rice config files. 040 * 041 * @author Kuali Rice Team (rice.collab@kuali.org) 042 */ 043public class RiceConfigUtils { 044 045 private static final Logger logger = LoggerUtils.make(); 046 047 private RiceConfigUtils() {} 048 049 /** 050 * Gets the {@link Config} from both the current configuration and the ones in {@code loaded}, at {@code location}, 051 * and in the {@code servletContext}. 052 * 053 * @param loaded the loaded properties 054 * @param location the location of additional properties 055 * @param servletContext the servlet context in which to add more properties 056 * 057 * @return the final configuration 058 */ 059 public static Config getRootConfig(Properties loaded, String location, ServletContext servletContext) { 060 // Get the Rice config object the listener created 061 Config config = ConfigContext.getCurrentContextConfig(); 062 Preconditions.checkNotNull(config, "'config' cannot be null"); 063 Properties listenerProperties = getProperties(config); 064 065 // Parse config from the location indicated, using listener properties in the process of doing so 066 JAXBConfigImpl parsed = parseConfig(location, listenerProperties); 067 068 // Add and override loaded properties with parsed properties 069 addAndOverride(loaded, parsed.getRawProperties()); 070 071 // Priority is servlet -> env -> system 072 // Override anything we've loaded with servlet, env, and system properties 073 Properties servlet = PropertySources.convert(servletContext); 074 Properties global = PropertyUtils.getGlobalProperties(servlet); 075 addAndOverride(loaded, global); 076 logger.info("Using {} distinct properties", Integer.valueOf(loaded.size())); 077 078 // Use JAXBConfigImpl in order to perform Rice's custom placeholder resolution logic now that everything is loaded 079 return new JAXBConfigImpl(loaded); 080 081 } 082 083 /** 084 * Parse the configuration stored at {@code location}. 085 * 086 * @param location the location to get properties from 087 * 088 * @return the new configuration 089 */ 090 public static JAXBConfigImpl parseConfig(String location) { 091 return parseConfig(location, ImmutableProperties.of()); 092 } 093 094 /** 095 * Parse the configuration stored at {@code location}, adding any additional properties from {@code properties}. 096 * 097 * @param location the location to get properties from 098 * @param properties any additional properties to add 099 * 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}