View Javadoc

1   /**
2    * Copyright 2010-2013 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.common.util.config.service;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Properties;
22  
23  import org.apache.commons.lang3.StringUtils;
24  import org.kuali.common.util.Assert;
25  import org.kuali.common.util.CollectionUtils;
26  import org.kuali.common.util.JAXBUtil;
27  import org.kuali.common.util.LocationUtils;
28  import org.kuali.common.util.Mode;
29  import org.kuali.common.util.ModeUtils;
30  import org.kuali.common.util.Project;
31  import org.kuali.common.util.ProjectUtils;
32  import org.kuali.common.util.PropertyUtils;
33  import org.kuali.common.util.config.ConfigUtils;
34  import org.kuali.common.util.config.ContextConfig;
35  import org.kuali.common.util.config.Location;
36  import org.kuali.common.util.config.ProjectConfig;
37  import org.kuali.common.util.config.ProjectConfigContainer;
38  import org.kuali.common.util.config.ProjectConfigContainerNullifier;
39  import org.kuali.common.util.nullify.Nullifier;
40  import org.kuali.common.util.property.Constants;
41  import org.kuali.common.util.property.processor.OverrideProcessor;
42  import org.springframework.util.PropertyPlaceholderHelper;
43  
44  public abstract class AbstractCachingConfigService implements ConfigService {
45  
46  	private static final String METAINF = "META-INF";
47  	private static final String CLASSPATH = "classpath:";
48  	private static final String CLASSPATH_PREFIX_KEY = "classpath.prefix";
49  	private static final String CONFIG = "config";
50  	private static final String PROPS = "metadata.properties";
51  	private static final String DELIMITER = ":";
52  	private static final PropertyPlaceholderHelper HELPER = Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER;
53  
54  	@Override
55  	public Properties getProperties(String configId) {
56  		return getProperties(configId, (Properties) null);
57  	}
58  
59  	@Override
60  	public Properties getProperties(List<String> configIds) {
61  		return getProperties(configIds, null);
62  	}
63  
64  	@Override
65  	public Properties getProperties(String configId, Properties overrides) {
66  		return getProperties(CollectionUtils.toEmptyList(configId), overrides);
67  	}
68  
69  	@Override
70  	public Properties getProperties(List<String> configIds, Properties overrides) {
71  		List<ProjectConfig> requests = ConfigUtils.getProjectConfigs(CollectionUtils.toEmptyList(configIds));
72  		return loadProperties(requests, PropertyUtils.toEmpty(overrides));
73  	}
74  
75  	protected abstract ProjectConfigContainer getCachedConfig(String groupId, String artifactId);
76  
77  	protected abstract void clearCache();
78  
79  	protected abstract ProjectConfigContainer getProjectConfig(String content, String encoding);
80  
81  	protected abstract String getFilename();
82  
83  	protected Properties loadProperties(List<ProjectConfig> requests, Properties overrides) {
84  		// Convert the ConfigRequest objects into Location objects
85  		List<Location> locations = getLocations(requests);
86  		// Allocate some storage
87  		Properties properties = new Properties();
88  		// Get system/environment properties
89  		Properties global = PropertyUtils.getGlobalProperties();
90  		// Cycle through our list of locations
91  		for (Location location : locations) {
92  			// Combine properties we've already loaded with overrides and global properties
93  			Properties resolver = PropertyUtils.combine(properties, overrides, global);
94  			// Use the combined properties to resolve any placeholders in the location
95  			String resolvedLocation = HELPER.replacePlaceholders(location.getValue(), resolver);
96  			// If the location exists, load it
97  			if (LocationUtils.exists(resolvedLocation)) {
98  				Properties loaded = PropertyUtils.load(resolvedLocation, location.getEncoding());
99  				new OverrideProcessor(Mode.INFORM, loaded, 2).process(properties);
100 			} else {
101 				// Take appropriate action for missing locations (ignore, inform, warn, or error out)
102 				ModeUtils.validate(location.getMissingMode(), "Non-existent location [" + resolvedLocation + "]");
103 			}
104 		}
105 		// Override the loaded properties with overrides properties
106 		new OverrideProcessor(Mode.INFORM, overrides, 2).process(properties);
107 		// Override everything with system/environment properties
108 		new OverrideProcessor(Mode.INFORM, global, 2).process(properties);
109 		// Decrypt them
110 		PropertyUtils.decrypt(properties);
111 		// Resolve them, throw an exception if any values cannot be fully resolved
112 		PropertyUtils.resolve(properties);
113 		// Return what we've found
114 		return properties;
115 	}
116 
117 	protected Properties getBaseFilterProperties() {
118 		return new Properties();
119 	}
120 
121 	protected ProjectConfigContainer loadMetadata(String groupId, String artifactId) {
122 		Project project = ProjectUtils.loadProject(groupId, artifactId);
123 		String location = getMetadataConfigFilePath(project, getFilename());
124 
125 		// Throw an exception if they are asking for config metadata that doesn't exist
126 		Assert.exists(location, "[" + location + "] does not exist");
127 		Properties properties = getBaseFilterProperties();
128 		Properties projectProperties = getFilterProperties(project);
129 		properties.putAll(projectProperties);
130 		String content = getFilteredContent(location, properties, project.getEncoding());
131 		return getProjectConfig(content, project.getEncoding());
132 	}
133 
134 	protected List<Location> getLocations(List<ProjectConfig> configs) {
135 		List<Location> locations = new ArrayList<Location>();
136 		for (ProjectConfig config : configs) {
137 			List<Location> requestLocations = findLocations(config);
138 			locations.addAll(requestLocations);
139 		}
140 		return locations;
141 	}
142 
143 	protected List<Location> findLocations(ProjectConfig request) {
144 		ProjectConfigContainer config = getCachedConfig(request.getGroupId(), request.getArtifactId());
145 		if (StringUtils.isBlank(request.getContextId())) {
146 			return new ArrayList<Location>(CollectionUtils.toEmptyList(config.getLocations()));
147 		} else {
148 			String[] tokens = StringUtils.split(request.getContextId(), DELIMITER);
149 			List<ContextConfig> contexts = config.getContexts();
150 			ContextConfig context = null;
151 			for (String token : tokens) {
152 				context = findContextConfig(contexts, token);
153 				contexts = context.getContexts();
154 			}
155 			return context.getLocations();
156 		}
157 	}
158 
159 	protected ContextConfig findContextConfig(List<ContextConfig> contexts, String contextId) {
160 		for (ContextConfig context : contexts) {
161 			if (StringUtils.equals(contextId, context.getId())) {
162 				return context;
163 			}
164 		}
165 		throw new IllegalArgumentException("Unknown contextId [" + contextId + "]");
166 	}
167 
168 	protected String getMetadataConfigFilePath(Project project, String filename) {
169 		String resourcePath = ProjectUtils.getResourcePath(project);
170 		return CLASSPATH + METAINF + "/" + resourcePath + "/" + CONFIG + "/" + filename;
171 	}
172 
173 	protected String getFilteredContent(String location, Properties properties, String encoding) {
174 		PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}", ":", true);
175 		String originalContent = LocationUtils.toString(location, encoding);
176 		String filteredContent = helper.replacePlaceholders(originalContent, properties);
177 		return filteredContent;
178 	}
179 
180 	protected Properties getFilterProperties(Project project) {
181 		String classpathPrefix = ProjectUtils.getClassPathPrefix(project);
182 		Properties duplicate = PropertyUtils.duplicate(project.getProperties());
183 		duplicate.setProperty(CLASSPATH_PREFIX_KEY, classpathPrefix);
184 		String location = getMetadataConfigFilePath(project, PROPS);
185 		Properties metadata = new Properties();
186 		if (LocationUtils.exists(location)) {
187 			metadata = PropertyUtils.load(location, project.getEncoding());
188 		}
189 		duplicate.putAll(metadata);
190 		return duplicate;
191 	}
192 
193 	protected void store(File file, ProjectConfigContainer config) {
194 
195 		Assert.notNull(file, "file is null");
196 		Assert.notNull(config, "config is null");
197 
198 		ProjectConfigContainer clone = new ProjectConfigContainer(config);
199 
200 		Nullifier nullifier = new ProjectConfigContainerNullifier(clone);
201 		nullifier.nullify();
202 
203 		JAXBUtil.write(clone, file);
204 	}
205 
206 }