Java tutorial
/** * Copyright (C) 2016 LibRec * <p> * This file is part of LibRec. * LibRec is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * LibRec is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with LibRec. If not, see <http://www.gnu.org/licenses/>. */ package net.librec.conf; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import net.librec.util.StringUtil; /** * Provides access to configuration parameters. * * <h3 id="Resources">Resources</h3> * <p> * Configurations are specified by resources. A resource contains a set of * name/value pairs as key-value data. Each resource is named by either a * <code>String</code> or by a {@link Path}. If named by a <code>String</code>, * then the classpath is examined for a file with that name. If named by a * <code>Path</code>, then the local filesystem is examined directly, without * referring to the classpath. * <p> * Unless explicitly turned off, Librec by default specifies two resources, * loaded in-order from the classpath: * <ol> * <li><tt> * <a href="{@docRoot}/../librec.properties"> * librec.properties</a></tt>: Read-only defaults for librec.</li> * <li><tt>librec.properties</tt>: Site-specific configuration for a given * librec installation.</li> * </ol> * Applications may add additional resources, which are loaded subsequent to * these resources in the order they are added. * * @author WangYuFeng */ public class Configuration implements Iterable<Map.Entry<String, String>> { private static final Log LOG = LogFactory.getLog(Configuration.class); private static final ConcurrentMap<ClassLoader, Map<String, Class<?>>> CACHE_CLASSES = new ConcurrentHashMap<ClassLoader, Map<String, Class<?>>>(); private Properties properties; private ClassLoader classLoader; { classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = Configuration.class.getClassLoader(); } } private boolean loadDefaults = true; /** * List of configuration resources. */ private ArrayList<Resource> resources = new ArrayList<Resource>(); private static final CopyOnWriteArrayList<String> defaultResources = new CopyOnWriteArrayList<String>(); static { ClassLoader cL = Thread.currentThread().getContextClassLoader(); if (cL == null) { cL = Configuration.class.getClassLoader(); } if (cL.getResource("librec.properties") != null) { LOG.warn("DEPRECATED: librec.properties found in the classpath. "); } // if (cL.getResource("driver.classes.props") != null) { // LOG.warn("DEPRECATED: driver.classes.props found in the classpath."); // } addDefaultResource("librec.properties"); // addDefaultResource("driver.classes.props"); } public static class Resource { private final Object resource; private final String name; public Resource(Object resource) { this(resource, resource.toString()); } public Resource(Object resource, String name) { this.resource = resource; this.name = name; } public String getName() { return name; } public Object getResource() { return resource; } @Override public String toString() { return name; } } /** * Add a default resource. Resources are loaded in the order of the * resources added. * * @param name file name. File should be present in the classpath. */ public static void addDefaultResource(String name) { synchronized (Configuration.class) { if (defaultResources.contains(name)) { return; } defaultResources.add(name); } } /** * Get an {@link Iterator} to go through the list of <code>String</code> * key-value pairs in the configuration. * * @return an iterator over the entries. */ public Iterator<Entry<String, String>> iterator() { Map<String, String> result = new HashMap<String, String>(); for (Map.Entry<Object, Object> item : getProps().entrySet()) { if (item.getKey() instanceof String && item.getValue() instanceof String) { result.put((String) item.getKey(), (String) item.getValue()); } } return result.entrySet().iterator(); } public synchronized void addResource(Resource resource) { resources.add(resource); loadProperty(getProps(), resource); } private void overlay(Properties to, Properties from) { for (Entry<Object, Object> entry : from.entrySet()) { to.put(entry.getKey(), entry.getValue()); } } /** * Set the <code>value</code> of the <code>name</code> property. * * @param name property name. * @param value property value. */ public void set(String name, String value) { getProps().setProperty(name, value); } public String get(String name) { return getProps().getProperty(name); } /** * Set the array of string values for the <code>name</code> property as as * comma delimited values. * * @param name property name. * @param values The values */ public void setStrings(String name, String... values) { set(name, StringUtil.arrayToString(values)); } /** * Get the comma delimited values of the <code>name</code> property as an * array of <code>String</code>s. If no such property is specified then * <code>null</code> is returned. * * @param name property name. * @return property value as an array of <code>String</code>s, or * <code>null</code>. */ public String[] getStrings(String name) { String valueString = get(name); return StringUtil.getStrings(valueString); } public Float getFloat(String name, Float defaultValue) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Float.valueOf(value); } else { return defaultValue; } } /** * Set the value of the <code>name</code> property to a <code>float</code>. * * @param name property name. * @param value property value. */ public void setFloat(String name, float value) { set(name, Float.toString(value)); } public Float getFloat(String name) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Float.valueOf(value); } else { return null; } } /** * Set the value of the <code>name</code> property to a <code>double</code>. * * @param name property name. * @param value property value. */ public void setDouble(String name, double value) { set(name, Double.toString(value)); } public Double getDouble(String name, Double defaultValue) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Double.valueOf(value); } else { return defaultValue; } } public Double getDouble(String name) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Double.valueOf(value); } else { return null; } } public String get(String name, String defaultValue) { String value = get(name); return StringUtils.isNotBlank(value) ? value : defaultValue; } /** * Set the value of the <code>name</code> property to an <code>long</code>. * * @param name property name. * @param value <code>int</code> value of the property. */ public void setLong(String name, long value) { set(name, Long.toString(value)); } public Long getLong(String name, Long defaultValue) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Long.valueOf(value); } else { return defaultValue; } } public Long getLong(String name) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Long.valueOf(value); } else { return null; } } /** * Set the value of the <code>name</code> property to an <code>int</code>. * * @param name property name. * @param value <code>int</code> value of the property. */ public void setInt(String name, int value) { set(name, Integer.toString(value)); } public Integer getInt(String name, Integer defaultValue) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Integer.valueOf(value); } else { return defaultValue; } } public Integer getInt(String name) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Integer.valueOf(value); } else { return null; } } /** * Set the array of int values for the <code>name</code> property as as * comma delimited values. * * @param name property name. * @param values The values */ public void setInts(String name, int[] values) { set(name, StringUtil.arrayToString(values)); } /** * Get the value of the <code>name</code> property as a set of * comma-delimited <code>int</code> values. * <p> * If no such property exists, an empty array is returned. * * @param name property name * @return property value interpreted as an array of comma-delimited * <code>int</code> values */ public int[] getInts(String name) { String[] strings = getTrimmedStrings(name); int[] ints = new int[strings.length]; for (int i = 0; i < strings.length; i++) { ints[i] = Integer.parseInt(strings[i]); } return ints; } /** * Get the comma delimited values of the <code>name</code> property as an * array of <code>String</code>s, trimmed of the leading and trailing * whitespace. If no such property is specified then an empty array is * returned. * * @param name property name. * @return property value as an array of trimmed <code>String</code>s, or * empty array. */ public String[] getTrimmedStrings(String name) { String valueString = get(name); return StringUtil.getTrimmedStrings(valueString); } /** * Set the value of the <code>name</code> property to a <code>boolean</code>. * * @param name property name. * @param value <code>boolean</code> value of the property. */ public void setBoolean(String name, boolean value) { set(name, Boolean.toString(value)); } public boolean getBoolean(String name) { String value = get(name); return StringUtils.isNotBlank(value) ? Boolean.valueOf(value) : false; } public boolean getBoolean(String name, boolean defaultValue) { String value = get(name); if (StringUtils.isNotBlank(value)) { return Boolean.valueOf(value); } else { return defaultValue; } } private synchronized Properties getProps() { if (properties == null) { properties = new Properties(); loadResources(properties, resources); } return properties; } private void loadResources(Properties properties, ArrayList<Resource> resources) { if (loadDefaults) { for (String resource : defaultResources) { loadProperty(properties, new Resource(resource)); } } for (Resource resource : resources) { loadProperty(properties, resource); } } private void loadProperty(Properties properties, Resource wrapper) { Object resource = wrapper.getResource(); try { InputStream fis; if (resource instanceof URL) { // an URL resource fis = ((URL) resource).openStream(); properties.load(fis); } else if (resource instanceof String) { // a CLASSPATH resource URL url = getResource((String) resource); if (url != null) { URLConnection connection = url.openConnection(); if (connection instanceof JarURLConnection) { // Disable caching for JarURLConnection to avoid sharing // JarFile // with other users. connection.setUseCaches(false); } fis = connection.getInputStream(); properties.load(fis); } } else if (resource instanceof Path) { // a file resource fis = new FileInputStream(new File(((Path) resource).toUri().getPath())); properties.load(fis); } else if (resource instanceof InputStream) { fis = (InputStream) resource; properties.load(fis); } else if (resource instanceof Properties) { overlay(properties, (Properties) resource); } } catch (IOException e) { e.printStackTrace(); } } public URL getResource(String name) { return classLoader.getResource(name); } /** * Load a class by name. * * @param name the class name. * @return the class object. * @throws ClassNotFoundException if the class is not found. */ public Class<?> getClassByName(String name) throws ClassNotFoundException { Map<String, Class<?>> map = CACHE_CLASSES.get(classLoader); if (map == null) { Map<String, Class<?>> newMap = new ConcurrentHashMap<String, Class<?>>(); map = CACHE_CLASSES.putIfAbsent(classLoader, newMap); if (map == null) { map = newMap; } } Class<?> clazz = map.get(name); if (clazz == null) { clazz = Class.forName(name, true, classLoader); if (clazz != null) { map.put(name, clazz); } } return clazz; } /** * Load a class by name. * * @param name the class name. * @param defaultName the default class. * @return the class object. * @throws ClassNotFoundException if the class is not found. */ public Class<?> getClassByName(String name, String defaultName) throws ClassNotFoundException { try { return getClassByName(name); } catch (ClassNotFoundException e) { LOG.error(e); return getClassByName(defaultName); } } }