/*
* Copyright (C) 2014 Indeed Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.indeed.imhotep.web.config;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.indeed.util.varexport.Export;
import com.indeed.util.varexport.VarExporter;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* @author vladimir
*/
/**
* Adds Spring property sources for the following locations in the following order:
* WEB-INF/config/iql-base.properties
* WEB-INF/config/iql-${environment}.properties where environment is one of constants from ProfileIds for the active profile.
* ${catalina.base)/conf/iql.properties
* Path pointed to by propertyPlaceholderResourceLocation (or configFile) Tomcat context parameter
*
* This class can be extended to customize the properties file name and paths.
*/
@SuppressWarnings("UnusedDeclaration")
public class PropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger log = Logger.getLogger(PropertiesInitializer.class);
private static final String contextPropertiesParameterName = "propertyPlaceholderResourceLocation";
private static final String contextPropertiesParameterName2 = "configFile";
private static final String indeedEnvironmentJVMProperty = "indeed.staging.level";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
final ConfigurableEnvironment springEnv = applicationContext.getEnvironment();
activateSpringProfiles(springEnv);
final MutablePropertySources propSources = springEnv.getPropertySources();
for(String location : getPropertyLocations(applicationContext)) {
tryAddPropertySource(applicationContext, propSources, location);
}
addPropertySources(applicationContext, propSources);
}
private static void activateSpringProfiles(ConfigurableEnvironment springEnv) {
if(IQLEnv.isSpringProfileSet(springEnv)) {
return; // we seem to already have some profile set. let it be
}
// try to infer the appropriate Spring profile
// this JVM system property should be set in OPs managed JVMs
String indeedEnv = System.getProperty(indeedEnvironmentJVMProperty);
if(Strings.isNullOrEmpty(indeedEnv)) {
indeedEnv = "developer"; // assume this is not an OPs managed JVM and thus a developer station
}
final List<String> profiles = Lists.newArrayList(springEnv.getActiveProfiles());
profiles.add(indeedEnv);
springEnv.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}
protected String getWebappName() {
return "iql";
}
/** Config directory path inside the project in SVN.
* Should end with a /
*/
protected String getSVNConfigLocation() {
return "WEB-INF/config/";
}
protected List<String> getPropertyLocations(ConfigurableApplicationContext applicationContext) {
List<String> propertyLocations = Lists.newArrayList();
propertyLocations.addAll(getBasePropertyLocations(applicationContext));
propertyLocations.addAll(getPerEnvironmentPropertyLocations(applicationContext));
propertyLocations.addAll(getTomcatConfPropertyLocations(applicationContext));
propertyLocations.addAll(getTomcatContextPropertyLocations(applicationContext));
return propertyLocations;
}
protected List<String> getBasePropertyLocations(ConfigurableApplicationContext applicationContext) {
String configFile = getSVNConfigLocation() + getConfigFileName("base");
return Lists.newArrayList(configFile);
}
protected List<String> getPerEnvironmentPropertyLocations(ConfigurableApplicationContext applicationContext) {
final ConfigurableEnvironment springEnv = applicationContext.getEnvironment();
final IQLEnv env = IQLEnv.fromSpring(springEnv);
List<String> locations = Lists.newArrayList();
if(env != null) {
locations.add(getSVNConfigLocation() + getConfigFileName(env.id));
if(env.equals(IQLEnv.DEVELOPER)) { // allow optional local override file (e.g. for private auth data)
locations.add(getSVNConfigLocation() + getConfigFileName(env.id + "-private"));
}
}
return locations;
}
protected List<String> getTomcatConfPropertyLocations(ConfigurableApplicationContext applicationContext) {
String tomcatPropFile = getTomcatConfDir() + getConfigFileName(null);
return Lists.newArrayList(tomcatPropFile);
}
protected String getTomcatConfDir() {
return "file:" + System.getProperty("catalina.base") + "/conf/";
}
protected String getConfigFileName(String suffix) {
String fileName = getWebappName();
if(!Strings.isNullOrEmpty(suffix)) {
fileName += "-" + suffix;
}
fileName += ".properties";
return fileName;
}
protected List<String> getContextPropertiesParameterNames() {
return Lists.newArrayList(contextPropertiesParameterName, contextPropertiesParameterName2);
}
protected List<String> getTomcatContextPropertyLocations(ConfigurableApplicationContext applicationContext) {
if(!(applicationContext instanceof ConfigurableWebApplicationContext)) {
return Collections.emptyList();
}
ConfigurableWebApplicationContext webApplicationContext = (ConfigurableWebApplicationContext) applicationContext;
List<String> locations = Lists.newArrayList();
for(String propertiesParameterName : getContextPropertiesParameterNames()) {
final String tomcatContextPropertiesFile = webApplicationContext.getServletContext().getInitParameter(propertiesParameterName);
locations.add("file:" + tomcatContextPropertiesFile);
}
return locations;
}
protected boolean tryAddPropertySource(ConfigurableApplicationContext applicationContext, MutablePropertySources propSources, String filePath) {
if(filePath == null) {
return false;
}
Resource propertiesResource = applicationContext.getResource(filePath);
if(!propertiesResource.exists()) {
return false;
}
try {
ResourcePropertySource propertySource = new ResourcePropertySource(propertiesResource);
propSources.addFirst(propertySource);
} catch (IOException e) {
return false;
}
log.debug("Successfully added property source: " + filePath);
return true;
}
/**
* Can be overridden to add custom property sources
* @param applicationContext context to use for loading
* @param propSources where to append to
*/
protected void addPropertySources(ConfigurableApplicationContext applicationContext, MutablePropertySources propSources) { }
}