/* * ContextFactory.java * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.hadoop.metrics; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.apache.commons.io.IOUtils; import org.apache.hadoop.metrics.spi.NullContext; import org.apache.hadoop.util.PropertiesFromJSON; import org.json.JSONException; import org.json.JSONObject; /** * Factory class for creating MetricsContext objects. To obtain an instance * of this class, use the static <code>getFactory()</code> method. */ public class ContextFactory { static final String PROPERTIES_FILE = "/hadoop-metrics.properties"; static final String PROPERTIES_FILE_CUSTOM = "/hadoop-metrics-custom.properties"; static final String JSON_FILE = System.getProperty("hadoop-metrics.json.configuration", "/config.materialized_JSON"); static final String DEFAULT_JSON_OBJECT_PATH = System.getProperty( "hadoop-metrics.json.default.object.path", "properties_files.hadoop-metrics.default"); static final String CUSTOM_JSON_OBJECT_PATH = System.getProperty( "hadoop_metrics.json.custom.object.path", null); private static final String CONTEXT_CLASS_SUFFIX = ".class"; private static final String DEFAULT_CONTEXT_CLASSNAME = "org.apache.hadoop.metrics.spi.NullContext"; private static ContextFactory theFactory = null; private final Map<String, Object> attributeMap = new HashMap<String, Object>(); private final Map<String, MetricsContext> contextMap = new HashMap<String, MetricsContext>(); // Used only when contexts, or the ContextFactory itself, cannot be // created. private static Map<String, MetricsContext> nullContextMap = new HashMap<String, MetricsContext>(); /** Creates a new instance of ContextFactory */ protected ContextFactory() { } /** * Returns the value of the named attribute, or null if there is no * attribute of that name. * * @param attributeName * the attribute name * @return the attribute value */ public Object getAttribute(String attributeName) { return attributeMap.get(attributeName); } /** * Returns the names of all the factory's attributes. * * @return the attribute names */ public String[] getAttributeNames() { String[] result = new String[attributeMap.size()]; int i = 0; // for (String attributeName : attributeMap.keySet()) { Iterator it = attributeMap.keySet().iterator(); while (it.hasNext()) { result[i++] = (String) it.next(); } return result; } /** * Sets the named factory attribute to the specified value, creating it * if it did not already exist. If the value is null, this is the same as * calling removeAttribute. * * @param attributeName * the attribute name * @param value * the new attribute value */ public void setAttribute(String attributeName, Object value) { attributeMap.put(attributeName, value); } /** * Removes the named attribute if it exists. * * @param attributeName * the attribute name */ public void removeAttribute(String attributeName) { attributeMap.remove(attributeName); } /** * Returns the named MetricsContext instance, constructing it if necessary * using the factory's current configuration attributes. * <p/> * * When constructing the instance, if the factory property * <i>contextName</i>.class</code> exists, its value is taken to be the name * of the class to instantiate. Otherwise, the default is to create an * instance of <code>org.apache.hadoop.metrics.spi.NullContext</code>, which * is a dummy "no-op" context which will cause all metric data to be * discarded. * * @param contextName * the name of the context * @return the named MetricsContext */ public synchronized MetricsContext getContext(String refName, String contextName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { MetricsContext metricsContext = contextMap.get(refName); if (metricsContext == null) { String classNameAttribute = refName + CONTEXT_CLASS_SUFFIX; String className = (String) getAttribute(classNameAttribute); if (className == null) { className = DEFAULT_CONTEXT_CLASSNAME; } Class contextClass = Class.forName(className); metricsContext = (MetricsContext) contextClass.newInstance(); metricsContext.init(contextName, this); contextMap.put(contextName, metricsContext); } return metricsContext; } public synchronized MetricsContext getContext(String contextName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { return getContext(contextName, contextName); } /** * Returns all MetricsContexts built by this factory. */ public synchronized Collection<MetricsContext> getAllContexts() { // Make a copy to avoid race conditions with creating new contexts. return new ArrayList<MetricsContext>(contextMap.values()); } /** * Returns a "null" context - one which does nothing. */ public static synchronized MetricsContext getNullContext(String contextName) { MetricsContext nullContext = nullContextMap.get(contextName); if (nullContext == null) { nullContext = new NullContext(); nullContextMap.put(contextName, nullContext); } return nullContext; } /** * Returns the singleton ContextFactory instance, constructing it if * necessary. * <p/> * * When the instance is constructed, this method checks if the file * <code>hadoop-metrics.properties</code> exists on the class path. If it * exists, it must be in the format defined by java.util.Properties, and all * the properties in the file are set as attributes on the newly created * ContextFactory instance. * * @return the singleton ContextFactory instance */ public static synchronized ContextFactory getFactory() throws IOException { if (theFactory == null) { theFactory = new ContextFactory(); theFactory.setAttributes(); } return theFactory; } /** * Used for unit tests */ public static synchronized void resetFactory() { theFactory = null; } private void setAttributes() throws IOException { // Try JSON first InputStream is = getClass().getResourceAsStream(JSON_FILE); if (is != null) { try { String jsonString = IOUtils.toString(is, "UTF-8"); JSONObject json = new JSONObject(jsonString); setAttributes(PropertiesFromJSON.getProperties(json, DEFAULT_JSON_OBJECT_PATH, CUSTOM_JSON_OBJECT_PATH)); return; } catch (JSONException e) { // Fall back to default configuration mechanism } } // Fail back to PROPERTIES_FILE is = getClass().getResourceAsStream(PROPERTIES_FILE); if (is != null) { setAttributes(is); } // PROPERTIES_FILE_CUSTOM will overwrite properties set in PROPERTIES_FILE is = getClass().getResourceAsStream(PROPERTIES_FILE_CUSTOM); if (is != null) { setAttributes(is); } } private void setAttributes(InputStream is) throws IOException { if (is != null) { Properties properties = new Properties(); properties.load(is); setAttributes(properties); } } private void setAttributes(Properties properties) { if (properties != null) { for (Map.Entry<Object, Object> keyValue : properties.entrySet()) { setAttribute((String) keyValue.getKey(), (String) keyValue.getValue()); } } } }