miércoles, 29 de abril de 2009

Generating a java bean from a properties file

little introduction to .properties files


Well, this is my situation. In my work, often I'm in need of making configurable stuff so non-programmer users can configure certain aspect of a application only editing text files. Xml is great for that, specialy if you want to support complex data and formally define de format of the document with a DTD section. But, sometimes you need to define only few configurable string values and in these circunstance xml can be like killing a fly with a gun. A much more confortable way in these cases is to use .properties resources. These are plain text files with for example the following code:

username=fulano
password=xyz
serverUrl=http://192.168.1.111


In the most common situation you left these files inside your classpath and some java class will read them.

In the last example, a web service client will read the account information from a .properties file. For reading the properties, we can, for example, name the file account1.properties and store it in the same package as some WebServiceClientManager.class. In side this class we simply do


InputStream stream = this.getClass().getResourceAsStream("account1.properties");
java.util.Properties props = new java.util.Properties();
props.load(stream);
String username = props.get("username");
String password= props.get("password");
etc.


Note that java.util.Properties objects are java.utils.HashMap representing the key=value in the .properties file. Note that because of the last, the order of properties is not respected.

Generating java bean class code from .properties files



Well, in my case, I feel more confortable dealing with java objects than with maps (in which you have to work with string ids). So I've created the following class that is able to create a java class code (a java bean) with the properties expressed in a .properties file:

public class Properties2JavaBean {
private Properties props;
public String propertiesToBean(String className,
String packageName) throws IOException {
String attrDef = "\tString ", methodsDef="",
constructorSignature="\tpublic "+className+"(", constructorBody="{";
Iterator<object> it = props.keySet().iterator();
while (it.hasNext()) {
String k = (String) it.next();
attrDef+=k;
constructorSignature+="String "+k;
constructorBody+="\n\t\tthis."+k+"="+k+";";
if(it.hasNext()){
attrDef+=", ";
constructorSignature+=", ";
}
methodsDef+="\n\n"+buildGetter(k)+"\n"+buildSetter(k);
}
constructorSignature+=")";
constructorBody+="\n\t}";

return "package "+packageName+";\n" +
"/** this class was autogenerated from a .properties file"+
" by Properties2JavaBean */\n"+

"public class "+className+"{\n" +

""+attrDef+";\n"+
constructorSignature+constructorBody+"\n"+
methodsDef+"\n" +
"\n\n\tpublic static "+className+ " newInstante(){ " +
"\n\t\t"+propertiesToBeanConstructor(className, "inst")+
";\n\t\treturn inst;" +
"\n\t}\n" +
"}";
}
public String propertiesToBeanConstructor(String className,
String instanceName) throws IOException{
String s = className+" "+instanceName+" = new "+className+"(";
Iterator<object> it = props.keySet().iterator();
while (it.hasNext()) {
s+="\""+props.get(it.next())+"\"";
if(it.hasNext())
s+=", ";
}
return s+");";
}
public void load(String s) throws IOException {
load(stringToStream(s));
}
public void load(InputStream s) throws IOException {
props = new Properties();
props.load(s);
}
String buildGetter(String propName) {
return "\tpublic String get"+changeFirstLetterMayus(propName, true)+
"(){\n\t\treturn this."+propName+";\n\t}";
}
String buildSetter(String propName) {
return "\tpublic void set"+changeFirstLetterMayus(propName, true)+
"(String attr){\n\t\tthis."+propName+" = attr;\n\t}";
}
public static String changeFirstLetterMayus(String strInput, boolean mayus) {
if(mayus)
return strInput.substring(0,1).toUpperCase()+
strInput.substring(1,strInput.length());
else
return strInput.substring(0,1).toLowerCase()+
strInput.substring(1,strInput.length());
}
private static InputStream stringToStream(String s) {
return new ByteArrayInputStream(s.getBytes());
}
}



So, for example, if I execute this conversor for the .properties example listed above, the following java bean class code is created:

package org.sgx.claroscuro;
/** this class was autogenerated from a .properties file by Properties2JavaBean */
public class ConfigBean{
String serverUrl, password, username;
public ConfigBean(String serverUrl, String password, String username){
this.serverUrl=serverUrl;
this.password=password;
this.username=username;
}
public String getServerUrl(){
return this.serverUrl;
}
public void setServerUrl(String attr){
this.serverUrl = attr;
}
public String getPassword(){
return this.password;
}
public void setPassword(String attr){
this.password = attr;
}
public String getUsername(){
return this.username;
}
public void setUsername(String attr){
this.username = attr;
}
public static ConfigBean newInstante(){
ConfigBean inst = new ConfigBean(
"http://192.168.1.111", "xyz", "fulano");

return inst;
}
}


Nice, eh? As you can see, in the static newInstance() generated method this javabean is instantiated with the exacly the same information stored in the source .properties file.

So, if you are dealing with .properties files for your configuration stuff, and you are making javabeans manually, this class can be usefull... Use it as you wish.

No hay comentarios: