This project has retired. For details please refer to its
Attic page.
001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.oozie.service;
019
020 import org.apache.hadoop.conf.Configuration;
021 import org.apache.oozie.util.Instrumentable;
022 import org.apache.oozie.util.Instrumentation;
023 import org.apache.oozie.util.XLog;
024 import org.apache.oozie.util.XConfiguration;
025 import org.apache.oozie.ErrorCode;
026
027 import java.io.File;
028 import java.io.FileInputStream;
029 import java.io.IOException;
030 import java.io.InputStream;
031 import java.io.StringWriter;
032 import java.util.HashSet;
033 import java.util.Map;
034 import java.util.Set;
035 import java.util.Arrays;
036
037 /**
038 * Built in service that initializes the services configuration.
039 * <p/>
040 * The configuration loading sequence is identical to Hadoop configuration loading sequence.
041 * <p/>
042 * Default values are loaded from the 'oozie-default.xml' file from the classpath, then site configured values
043 * are loaded from a site configuration file from the Oozie configuration directory.
044 * <p/>
045 * The Oozie configuration directory is resolved using the <code>OOZIE_HOME<code> environment variable as
046 * <code>${OOZIE_HOME}/conf</code>. If the <code>OOZIE_HOME<code> environment variable is not defined the
047 * initialization of the <code>ConfigurationService</code> fails.
048 * <p/>
049 * The site configuration is loaded from the <code>oozie-site.xml</code> file in the configuration directory.
050 * <p/>
051 * The site configuration file name to use can be changed by setting the <code>OOZIE_CONFIG_FILE</code> environment
052 * variable to an alternate file name. The alternate file must ber in the Oozie configuration directory.
053 * <p/>
054 * Configuration properties, prefixed with 'oozie.', passed as system properties overrides default and site values.
055 * <p/>
056 * The configuration service logs details on how the configuration was loaded as well as what properties were overrode
057 * via system properties settings.
058 */
059 public class ConfigurationService implements Service, Instrumentable {
060 private static final String INSTRUMENTATION_GROUP = "configuration";
061
062 public static final String CONF_PREFIX = Service.CONF_PREFIX + "ConfigurationService.";
063
064 public static final String CONF_IGNORE_SYS_PROPS = CONF_PREFIX + "ignore.system.properties";
065
066 /**
067 * System property that indicates the configuration directory.
068 */
069 public static final String OOZIE_CONFIG_DIR = "oozie.config.dir";
070
071
072 /**
073 * System property that indicates the data directory.
074 */
075 public static final String OOZIE_DATA_DIR = "oozie.data.dir";
076
077 /**
078 * System property that indicates the name of the site configuration file to load.
079 */
080 public static final String OOZIE_CONFIG_FILE = "oozie.config.file";
081
082 private static final Set<String> IGNORE_SYS_PROPS = new HashSet<String>();
083 private static final String IGNORE_TEST_SYS_PROPS = "oozie.test.";
084
085 private static final String PASSWORD_PROPERTY_END = ".password";
086
087 static {
088 IGNORE_SYS_PROPS.add(CONF_IGNORE_SYS_PROPS);
089
090 //all this properties are seeded as system properties, no need to log changes
091 IGNORE_SYS_PROPS.add("oozie.http.hostname");
092 IGNORE_SYS_PROPS.add("oozie.http.port");
093
094 IGNORE_SYS_PROPS.add(Services.OOZIE_HOME_DIR);
095 IGNORE_SYS_PROPS.add(OOZIE_CONFIG_DIR);
096 IGNORE_SYS_PROPS.add(OOZIE_CONFIG_FILE);
097 IGNORE_SYS_PROPS.add(OOZIE_DATA_DIR);
098
099 IGNORE_SYS_PROPS.add(XLogService.OOZIE_LOG_DIR);
100 IGNORE_SYS_PROPS.add(XLogService.LOG4J_FILE);
101 IGNORE_SYS_PROPS.add(XLogService.LOG4J_RELOAD);
102 }
103
104 public static final String DEFAULT_CONFIG_FILE = "oozie-default.xml";
105 public static final String SITE_CONFIG_FILE = "oozie-site.xml";
106
107 private static XLog log = XLog.getLog(ConfigurationService.class);
108
109 private String configDir;
110 private String configFile;
111
112 private LogChangesConfiguration configuration;
113
114 public ConfigurationService() {
115 log = XLog.getLog(ConfigurationService.class);
116 }
117
118 /**
119 * Initialize the log service.
120 *
121 * @param services services instance.
122 * @throws ServiceException thrown if the log service could not be initialized.
123 */
124 public void init(Services services) throws ServiceException {
125 configDir = getConfigurationDirectory();
126 configFile = System.getProperty(OOZIE_CONFIG_FILE, SITE_CONFIG_FILE);
127 if (configFile.contains("/")) {
128 throw new ServiceException(ErrorCode.E0022, configFile);
129 }
130 log.info("Oozie home dir [{0}]", Services.getOozieHome());
131 log.info("Oozie conf dir [{0}]", configDir);
132 log.info("Oozie conf file [{0}]", configFile);
133 configFile = new File(configDir, configFile).toString();
134 configuration = loadConf();
135 }
136
137 public static String getConfigurationDirectory() throws ServiceException {
138 String oozieHome = Services.getOozieHome();
139 String configDir = System.getProperty(OOZIE_CONFIG_DIR, oozieHome + "/conf");
140 File file = new File(configDir);
141 if (!file.exists()) {
142 throw new ServiceException(ErrorCode.E0024, configDir);
143 }
144 return configDir;
145 }
146
147 /**
148 * Destroy the configuration service.
149 */
150 public void destroy() {
151 configuration = null;
152 }
153
154 /**
155 * Return the public interface for configuration service.
156 *
157 * @return {@link ConfigurationService}.
158 */
159 public Class<? extends Service> getInterface() {
160 return ConfigurationService.class;
161 }
162
163 /**
164 * Return the services configuration.
165 *
166 * @return the services configuration.
167 */
168 public Configuration getConf() {
169 if (configuration == null) {
170 throw new IllegalStateException("Not initialized");
171 }
172 return configuration;
173 }
174
175 /**
176 * Return Oozie configuration directory.
177 *
178 * @return Oozie configuration directory.
179 */
180 public String getConfigDir() {
181 return configDir;
182 }
183
184 private InputStream getDefaultConfiguration() throws ServiceException, IOException {
185 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
186 InputStream inputStream = classLoader.getResourceAsStream(DEFAULT_CONFIG_FILE);
187 if (inputStream == null) {
188 throw new ServiceException(ErrorCode.E0023, DEFAULT_CONFIG_FILE);
189 }
190 return inputStream;
191 }
192
193 private LogChangesConfiguration loadConf() throws ServiceException {
194 XConfiguration configuration;
195 try {
196 InputStream inputStream = getDefaultConfiguration();
197 configuration = new XConfiguration(inputStream);
198 File file = new File(configFile);
199 if (!file.exists()) {
200 log.info("Missing site configuration file [{0}]", configFile);
201 }
202 else {
203 inputStream = new FileInputStream(configFile);
204 XConfiguration siteConfiguration = new XConfiguration(inputStream);
205 XConfiguration.injectDefaults(configuration, siteConfiguration);
206 configuration = siteConfiguration;
207 }
208 }
209 catch (IOException ex) {
210 throw new ServiceException(ErrorCode.E0024, configFile, ex.getMessage(), ex);
211 }
212
213 if (log.isTraceEnabled()) {
214 try {
215 StringWriter writer = new StringWriter();
216 for (Map.Entry<String, String> entry : configuration) {
217 boolean maskValue = entry.getKey().endsWith(PASSWORD_PROPERTY_END);
218 String value = (maskValue) ? "**MASKED**" : entry.getValue();
219 writer.write(" " + entry.getKey() + " = " + value + "\n");
220 }
221 writer.close();
222 log.trace("Configuration:\n{0}---", writer.toString());
223 }
224 catch (IOException ex) {
225 throw new ServiceException(ErrorCode.E0025, ex.getMessage(), ex);
226 }
227 }
228
229 String[] ignoreSysProps = configuration.getStrings(CONF_IGNORE_SYS_PROPS);
230 if (ignoreSysProps != null) {
231 IGNORE_SYS_PROPS.addAll(Arrays.asList(ignoreSysProps));
232 }
233
234 for (Map.Entry<String, String> entry : configuration) {
235 String sysValue = System.getProperty(entry.getKey());
236 if (sysValue != null && !IGNORE_SYS_PROPS.contains(entry.getKey())) {
237 log.info("Configuration change via System Property, [{0}]=[{1}]", entry.getKey(), sysValue);
238 configuration.set(entry.getKey(), sysValue);
239 }
240 }
241 for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
242 String name = (String) entry.getKey();
243 if (!IGNORE_SYS_PROPS.contains(name)) {
244 if (name.startsWith("oozie.") && !name.startsWith(IGNORE_TEST_SYS_PROPS)) {
245 if (configuration.get(name) == null) {
246 log.warn("System property [{0}] no defined in Oozie configuration, ignored", name);
247 }
248 }
249 }
250 }
251
252 return new LogChangesConfiguration(configuration);
253 }
254
255 private class LogChangesConfiguration extends XConfiguration {
256
257 public LogChangesConfiguration(Configuration conf) {
258 for (Map.Entry<String, String> entry : conf) {
259 if (get(entry.getKey()) == null) {
260 setValue(entry.getKey(), entry.getValue());
261 }
262 }
263 }
264
265 public String[] getStrings(String name) {
266 String s = get(name);
267 return (s != null && s.trim().length() > 0) ? super.getStrings(name) : new String[0];
268 }
269
270 public String get(String name, String defaultValue) {
271 String value = get(name);
272 if (value == null) {
273 boolean maskValue = name.endsWith(PASSWORD_PROPERTY_END);
274 value = defaultValue;
275 String logValue = (maskValue) ? "**MASKED**" : defaultValue;
276 log.warn(XLog.OPS, "Configuration property [{0}] not found, using default [{1}]", name, logValue);
277 }
278 return value;
279 }
280
281 public void set(String name, String value) {
282 setValue(name, value);
283 boolean maskValue = name.endsWith(PASSWORD_PROPERTY_END);
284 value = (maskValue) ? "**MASKED**" : value;
285 log.info(XLog.OPS, "Programmatic configuration change, property[{0}]=[{1}]", name, value);
286 }
287
288 private void setValue(String name, String value) {
289 super.set(name, value);
290 }
291
292 }
293
294 /**
295 * Instruments the configuration service. <p/> It sets instrumentation variables indicating the config dir and
296 * config file used.
297 *
298 * @param instr instrumentation to use.
299 */
300 public void instrument(Instrumentation instr) {
301 instr.addVariable(INSTRUMENTATION_GROUP, "config.dir", new Instrumentation.Variable<String>() {
302 public String getValue() {
303 return configDir;
304 }
305 });
306 instr.addVariable(INSTRUMENTATION_GROUP, "config.file", new Instrumentation.Variable<String>() {
307 public String getValue() {
308 return configFile;
309 }
310 });
311 instr.setConfiguration(configuration);
312 }
313
314 /**
315 * Return a configuration with all sensitive values masked.
316 *
317 * @param conf configuration to mask.
318 * @return masked configuration.
319 */
320 public static Configuration maskPasswords(Configuration conf) {
321 XConfiguration maskedConf = new XConfiguration();
322 for (Map.Entry<String, String> entry : conf) {
323 String name = entry.getKey();
324 boolean maskValue = name.endsWith(PASSWORD_PROPERTY_END);
325 String value = (maskValue) ? "**MASKED**" : entry.getValue();
326 maskedConf.set(name, value);
327 }
328 return maskedConf;
329 }
330
331 }