public
Created

My config code

  • Download Gist
gistfile1.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
 
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Closeables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import javax.annotation.concurrent.ThreadSafe;
import java.io.*;
import java.util.Date;
import java.util.Properties;
 
/**
* @author eshioji@gmail.com
*/
@ThreadSafe
public class Config {
private static final Logger log = LoggerFactory.getLogger(Config.class);
private static final Joiner joiner = Joiner.on("\n\t");
public static final String CONFIG_FILE_NAME = "some.stuff";
private volatile ImmutableMap<Prop, Object> store;
 
 
public void load(String resourceName){
Properties properties = new Properties();
InputStream is = ClassLoader.getSystemResourceAsStream(resourceName);
try {
Preconditions.checkNotNull(is,"No config file found. You must include the config file "+CONFIG_FILE_NAME+" in the classpath");
properties.load(is);
ImmutableMap.Builder<Prop, Object> ret = ImmutableMap.builder();
for (Prop prop : Prop.values()) {
String val = properties.getProperty(prop.name());
if (val == null) {
val = prop._default;
if (val == null) {
throw new PropertiesException("No configured value nor a default value is available for " + prop + ". Please provide a configuration in " + CONFIG_FILE_NAME);
}
}
ret.put(prop, prop.parse(val));
}
ImmutableMap<Prop, Object> built = ret.build();
store = built;
log.info("Read configuration:\n\t" + joiner.join(built.entrySet()));
} catch (IOException e) {
log.error("Unable to read config file", e);
throw new PropertiesException(e);
} finally {
Closeables.closeQuietly(is);
}
}
 
public <T> T value(Prop prop, Class<T> type) {
Preconditions.checkState(store!=null,"Coding error; Please call load method before using this method.");
Object ret = store.get(prop);
Preconditions.checkArgument(ret.getClass() == type,"Coding error. You are specifying the wrong class. You should give:" + prop.type);
return (T)ret;
}
 
public static void main(String[] args) throws IOException {
if(args.length!=1){
throw new RuntimeException("Please provide output directory");
}
PrintWriter pw = new PrintWriter(new FileWriter(args[0] + File.separator + CONFIG_FILE_NAME + ".template"));
try {
pw.println("#Configuration file template for " + CONFIG_FILE_NAME + " generated on " + new Date());
for (Prop prop : Prop.values()) {
pw.println("# " + prop);
pw.println("# " + prop.name() + "=" + prop._default);
pw.println();
}
} finally {
Closeables.closeQuietly(pw);
}
}
}
 
 
 
 
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.ning.http.client.ProxyServer;
import com.ning.http.client.oauth.ConsumerKey;
 
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
 
/**
* Entity class for managing properties.</br>
* THE HARD CODED VALUES ARE DEFAULTS! VALUE IN THE PROPERTY FILE WILL OVERWRITE THEM</br>
*
* <h3>How to add a new property</h3>
* basic example</br>
<pre>
{@code
// name(default_value, type)
MAX_FAILURE("3",Integer.class),
}
* </pre>
 
* advanced example:</br>
* <pre>
{@code
//name // default value in string // type // description (optional)
WORKER_THREAD_NUM(""+Runtime.getRuntime().availableProcessors(), Integer.class, "Number of workers threads. Default value is number of available cores."),
}
* </pre>
*
* If your type has "valueOf" method, this is all you need.</br>
* If you want to use a type that has no "valueOf" method, you have to implement the {@link #doParse} method, like so:</br>
* <pre>
{@code
TWITTER_TOKEN_FILE("src/test/resources/dummy.dummy", File.class){
@Override
protected Object doParse(String value) {
File tokenFile = new File(value);
if(tokenFile.canRead()){
return tokenFile;
}else{
throw new PropertiesException("Configuration for " + this + " invalid. File "+tokenFile.getAbsolutePath() +" cannot be read.");
}
}
},
* </pre>
*
* If you provide null as the default value, the initialization fails unless you provide a configuration value in the property file.
* @author Enno Shioji (eshioji@gmail.com)
*/
public enum Prop {
 
// Simplest example
THREAD_NUM("10", Integer.class),
 
// Requires user to provide config
SECRET_KEY(null, String.class),
 
// Dynamic values
WORKER_THREAD_NUM(""+Runtime.getRuntime().availableProcessors(),Integer.class, "Number of workers. Default value is number of available cores."),
 
// In-build validation
TWITTER_TOKEN_FILE("src/test/resources/dummy.tokens", File.class){
@Override
protected Object doParse(String value) {
File tokenFile = new File(value);
if(tokenFile.canRead()){
return tokenFile;
}else{
throw new PropertiesException("Configuration for " + this + " invalid. File "+tokenFile.getAbsolutePath() +" cannot be read.");
}
}
},
 
// Some other examples
FOLLOW_REDIRECTS("true",Boolean.class),
HTTP_CONN_TIMEOUT_MS("12000",Integer.class),
HTTP_COMPRESSION_ENABLED("true",Boolean.class),
 
PROXY_ENABLED("false", Boolean.class),
PROXY_SERVER("secret.proxy:8080", ProxyServer.class){
@Override
protected Object doParse(String value) {
try{
new URI("http://" + value);
} catch (URISyntaxException e) {
throw new PropertiesException("Invalid config for " + this +" Was given:"+value +" was expecting something like " +_default+" (host:port)");
}
Splitter splitter = Splitter.on(":");
List<String> split = ImmutableList.copyOf(splitter.split(value));
if(split.size()!=2){
throw new PropertiesException("Invalid config for " + this +" Was given:"+value +" was expecting something like " +_default+" (host:port) (did you forgot to give port?)");
}
String host = split.get(0);
int port = Integer.valueOf(split.get(1));
return new ProxyServer(ProxyServer.Protocol.HTTP,host,port);
}
};
 
 
 
 
 
final String _default;
final Class<?> type;
final String desc;
 
/**
*
* @param _default
* @param type
*/
Prop(String _default, Class<?> type) {
this(_default, type, null);
}
 
Prop(String _default, Class<?> type, String desc) {
this._default = _default;
this.type = type;
this.desc = desc;
}
 
 
final Object parse(String value) {
return doParse(value);
}
 
protected Object doParse(String value) {
return valof(value);
}
 
private Object valof(String value) {
try {
if (this.type == String.class) {
return value;
}
Method valof = this.type.getDeclaredMethod("valueOf", String.class);
return valof.invoke(null, value);
} catch (InvocationTargetException e) {
throw new PropertiesException("Invalid input for " +this + ". Was given " + value + ". Please check your config. Was expecting something like " + _default);
}catch (NoSuchMethodException e) {
throw new AssertionError("Please implement a custom doParse method on " + this);
} catch (IllegalAccessException e) {
throw new AssertionError("Please implement a custom doParse method on " + this);
}catch(Exception e){
throw new PropertiesException("Invalid input for " +this + ". Was given " + value + ". Please check your config. Was expecting something like " + _default);
}
}
 
 
@Override
public String toString() {
return "Name:" + this.name() + " Type:" + this.type.getSimpleName() + " Default value=" + (this._default != null ? this._default : "No default value") +(this.desc != null ? " Description:" + this.desc : "");
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.