All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.anc.constants.Constants Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright 2011 The American National Corpus
 *
 * 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 org.anc.constants;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * The base class used for declaring project wide constant values.
 * 
 * 

Each sub-class defines {@code public final} fields that will be * initialized with values from a properties file. The default values to use * if the properties file does not exist are specified with @Default annotations on each * field. *

 * package org.anc.example;
 * public class MyConstants extends Constants
 * {
 *    @Default("Hello world.")
 *    public final String HELLO_WORLD = null;
 *    @Default("8")
 *    public final Integer NTHREADS = null;
 *    
 *    public MyConstants()
 *    {
 *       super.init();
 *    }
 * }
 * 
*

Each public field in the subclass must be initialized to null and the subclass * constructor(s) must call {@code super.init()}. The Constants subclass can contain * fields of type String, Boolean, Integer, Float, and Double. However, the value specified * by the @Default annotation is always a String. * *

An instance of the subclass can then be created (usually as a static final * field of the application object) to access the defined constants. *

 * public class Application
 * {
 *    public static final MyConstants CONST = new MyConstants();
 *    
 *    public void run()
 *    {
 *       System.out.println(CONST.HELLO_WORLD);
 *    }
 * }
 * 
*

* The {@code Constants} class will look for the properties file {@code conf/machineName/class.name.properties} * where: *

    *
  • machineName is the value of the environment variable HOSTNAME (Unix based OSes) or COMPUTERNAME (Windows)
  • *
  • class.name is the fully qualified Java class name. *
*

The {@code Constants} class will first look for the properties file on the * file system and then on the class path. If a properties file can not be found * the fields will be initialized with the values from the @Default annotations. *

* A sub-class can also specify the properties file to use with the {@code init(String)} * method. The String parameter passed to the {@code init} method should be the name of a * Java system property or an OS environmental variable. The {@code Constants} class * will first try {@code System.getProperty} and then {@code System.getenv} to obtain * a file name. If neither property has been set the above method is used to locate * the properties file. For example, *

 * // In MyConstants.java
 * package org.anc.example;
 * public class MyConstants extends Constants
 *    {
 *       @Default("Hello world")
 *       public final String HELLO_WORLD = null;
 *       
 *       public MyConstants()
 *       {
 *          super.init("org.anc.hello");
 *       }
 *       
 *       public static void main(String[] args)
 *       {
 *          MyConstants constants = new MyConstants();
 *          System.out.println(constants.HELLO_WORLD);
 *       }
 *    }
 * 
 * # In /home/anc/hello.properties
 * HELLO_WORLD=Bonjour le monde.
 * 
 * # From the command line:
 * > java -cp MyConstants.jar -Dorg.anc.hello=/home/anc/hello.properties org.anc.example.MyConstants
 * > Bonjour le monde
 * 
*

A properties file containing the default values can be generated by * creating an instance of the class and calling the {@code save()} method. This is * convenient when you want to create properties files for use with other machines. *

 * public static void main(String[] args)
 * {
 *    MyConstants constants = new MyConstants();
 *    constants.save();
 * }
 * 
* * * @author Keith Suderman * */ public abstract class Constants { private static final long serialVersionUID = 1L; /** * Symbol table used to resolve variable names during initialization. * After initialization is complete this should be set back to null to * release the memory. */ private Map variables = null; /** * Annotation used to provide a default value for constants. * * @author Keith Suderman */ @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Default { String value(); } public void save() throws IOException { String name = getName(); File file = new File(name); File parent = file.getParentFile(); if (!parent.exists()) { if (!parent.mkdirs()) { throw new FileNotFoundException("Unable to create " + parent.getPath()); } } save(file); } public void save(String path) throws IOException { save(new File(path)); } public void save(File file) throws IOException { Properties props = new Properties(); Class subclass = this.getClass(); for (Field field : subclass.getDeclaredFields()) { String name = field.getName(); if (isPublicFinalString(field) || isPublicFinalInteger(field) || isPublicFinalFloat(field) || isPublicFinalDouble(field) || isPublicFinalBoolean(field)) { try { props.put(name, field.get(this).toString()); } catch (Exception e) { throw new IOException("Unable to save field : " + name, e); } } } OutputStream os = new FileOutputStream(file); try { System.out.println("Wrote " + file.getPath()); props.store(os, "Constants."); } finally { os.close(); } } protected Properties getProperties(String propName) throws FileNotFoundException, IOException { Properties props = new Properties(); String propValue = null; if (propName != null) { propValue = System.getProperty(propName); if (propValue != null) { props.load(new FileReader(propValue)); return props; } propValue = System.getenv(propName); if (propValue != null) { props.load(new FileReader(propValue)); return props; } } propValue = getName(); InputStream in = null; // First try to find the properties file on the file system. File propFile = new File(propValue); if (propFile.exists()) { in = new FileInputStream(propFile); } // Then try the class path. if (in == null) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = Constants.class.getClassLoader(); } in = loader.getResourceAsStream(propValue); if (in == null) { //throw new FileNotFoundException("Properties " + propValue + " not found."); return new Properties(); } } // 'in' can not be null or an exception would have been thrown above. props.load(in); return props; } protected String getName() { Class subclass = this.getClass(); String name = null; if (name == null) { name = System.getenv("COMPUTERNAME"); } if (name == null) { name = System.getenv("HOSTNAME"); } if (name == null) { name = System.getProperty("user.name"); } if (name == null) { name = "constants"; } return "conf/" + name.toLowerCase() + "/" + subclass.getName() + ".properties"; } protected void init() { init(null); } protected void init(String propertyName) { variables = new HashMap(); Properties props; try { props = getProperties(propertyName); } catch (Exception e) { System.err.println("Unable to load properties from " + propertyName); e.printStackTrace(); props = new Properties(); } Class subclass = this.getClass(); Field[] fields = subclass.getDeclaredFields(); for (Field field : fields) { String value = getInitValue(props, field); if (value == null) { continue; } if (isPublicFinalString(field)) { set(field, value); } else if (isPublicFinalInteger(field)) { set(field, new Integer(value)); } else if (isPublicFinalFloat(field)) { set(field, new Float(value)); } else if (isPublicFinalDouble(field)) { set(field, new Double(value)); } else if (isPublicFinalBoolean(field)) { set(field, new Boolean(value)); } } variables = null; } private String getInitValue(Properties props, Field field) { String sValue = props.getProperty(field.getName()); if (sValue == null) { Default defaultValue = field.getAnnotation(Default.class); if (defaultValue == null) { // This is definitely a programming error. //throw new RuntimeException("Missing @Default annotation on " // + field.getName()); // NO, it may not be a programming error. Groovy adds fields to // classes which will not contain Default annotations. return null; } sValue = defaultValue.value(); } return replaceVariables(sValue); } private String replaceVariables(String input) { int index = input.indexOf('$'); while (index >= 0) { int end = input.indexOf('/', index); if (end < index) { end = input.length(); } String key = input.substring(index + 1, end); String value = variables.get(key); if (value != null) { String prefix = input.substring(0, index); input = prefix + value + input.substring(end); index = input.indexOf('$', prefix.length()); } else { index = input.indexOf('$', end); } } return input; } private void set(Field field, Object value) { try { field.setAccessible(true); field.set(this, value); if (value instanceof String) { variables.put(field.getName(), value.toString()); } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } protected static boolean isPublicFinalString(Field field) { return isType(String.class, field); } protected static boolean isPublicFinalInteger(Field field) { return isType(Integer.class, field); } protected static boolean isPublicFinalDouble(Field field) { return isType(Double.class, field); } protected static boolean isPublicFinalFloat(Field field) { return isType(Float.class, field); } protected static boolean isPublicFinalBoolean(Field field) { return isType(Boolean.class, field); } private static boolean isType(Class theClass, Field field) { int flags = field.getModifiers(); return field.getType().equals(theClass) && Modifier.isPublic(flags) && Modifier.isFinal(flags) && !Modifier.isStatic(flags); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy