This is a framework that has been developed during the last 4 years, adapting it to a lot of critical IT projects. It is studied to be easy to use, but heavly extensible and complex if needed, with a great care of performances.
The most interesting features are: runtime changes support, config caching and garbage collection, hierarchies, inheritance, config validation, and sql statement bind variables enforcement.
Thing to be implemented: Xstream (http://xstream.codehaus.org/) parameter-type support, eventually Database and LDAP config sources samples, user feedback will be very important.
From the XML point of view it is a hierarchy of "entities" and "components".
An entity can have child entities and child components. A component is always the child of an entity and cannot have childs. There is no limit on the level.
In the file-based implementation an entity is a "<entity name>.xml" file, or an "<entity name>" folder that contains an "entity.xml" file and eventually n "<childs>.xml" files.
Any .xml file represents an entity and contains entity's "parameters", "sql statements", and "components". Each component contains, component's "parameters" and "sql statements".
Actually there are 4 config implementation: file, web, classpath, but any inputstream capable source can be a repository for configs (LDAP/Database can be rapidly implemented on the data model basis).
An entity A can be child of entity B, and extend entity C, so it is located under entity B but inherits parameters, components and sql statements from entity C.
From the Java API point of view, there are n ConfigSet at top and eventually one Singleton ConfigSet.
Each ConfigSet contains n EntityConfig, and each EntityConfig contains n parameters, ComponentConfig, SQLStatement.
Components are simply sub-sections of an entity.
Parameters are generally java.lang.String, but the API, has a lot of cast methods, that makes really
effective the use of any other type, like:
my.beautifull.java.interface mbji= (my.beautifull.java.interface)config.getClassInstanceParameter("my-pluggable-interface");
where the parameter is:
<parameter name="my-pluggable-interface" value="my.beautifull.java.interface"/>.
SQLStatements are special java.lang.String that support a BindVariable position iterator,
so that a jdbc wrapper or an orm can know where to bind the value of fariable "foo" and "somethingelse":
select * from mytable where thiscolumn= ${foo} and eventhiscolumn= ${foo} and butthiscolumn= ${somethingelse}
the position iterator will say 1,2 for "foo" and 3 for "somethingelse":
The framework comes with an .xsd file to validate xml files, so that
when you initialize an entity it will throw exceptions if you define the
same parameter two-times or someting like this.
If you modify a "runtime change capable" source like a FileConfigSource,
the framework will change the value of the parameter in the cached instance of
the entity. Considering that each entity will exists only once in a JVM, every
referece to a parameter will be updated. The change controller can be started or
not.
A garbage collector can be activated to maintain a maximum of n entities cached in
the java heap space ona LRU basis. And even if the cache is active it is still possible
to set as non cacheable a specific entity, in its xml file.
Can obtain configs from any source.
Runtime changes: detects changed entities and update values on the fly.
Performance: a lot of tuning possibilities, config caching and garbage collection
Hierarchies: entities are ordered in trees
Inheritance: an entity can inherit parameters from another.
Validation: the xsd will detect twin parameters, components, sql statements.
Expression handlers: include complex pars, from any source, or java constants or copy parameters as part of sql statement or other parameters.
Flexibility: you can implement SourceManagers, ExpressionHandlers, almost anything is an interface that you can implement your way.
User feedback will drive development, submit your requests:
http://sourceforge.net/tracker/?group_id=198310
Source/Doc or Binary package (initial release):
http://sourceforge.net/project/showfiles.php?group_id=198310
or CVS (updated frequently):
http://sourceforge.net/cvs/?group_id=198310
Hint: Download samples in the source package
Write down your sample.xml:<?xml version="1.0" encoding="UTF-8"?> <entity xmlns="http://eforceconfig.sourceforge.net/XML/entity-config" name="sample"> <parameters> <parameter name="mypar" value="10" /> <parameter name="myclass" value="eforce.util.config.samples.HelloWorld" /> <parameter name="mytable" type="table"> <value name="a" value="av"/> <value name="b" value="bv"/> <value name="c" value="cv"/> <value name="d" value="dv"/> </parameter> <parameter name="mylongpar"> <![CDATA[ bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla %{constant:eforce.util.config.samples.HelloWorld.MYCONSTANT} bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla ]]> </parameter> </parameters> <sql> <!-- simple statement --> <statement name="mysql"> <![CDATA[ select * from dual where 1= ? ]]> </statement> <!-- statement with bind variable --> <statement name="mysql2" binding-type="named"> <![CDATA[ select * from dual where 1= ${MYBINDVAR} ]]> </statement> <!-- statement with expression literal that points to local paramter named "adir" see above --> <statement name="sqlwithexpr" binding-type="named"> <![CDATA[ select DECODE('%{local:parameter/mytable/c}','cv','itworks','fu..') from dual ]]> </statement> </sql> <components> <component name="mycomp"> <parameters> <parameter name="mylist" type="list"> <value value="a"/> <value value="b"/> <value value="c"/> <value value="d"/> </parameter> </parameters> <sql> <statement name="acmpstmt"> <![CDATA[ select * from dual ]]> </statement> </sql> </component> </components> </entity>
Use it in your class HelloWorld.java:
package eforce.util.config.samples; import java.util.Iterator; import java.util.Map; import eforce.util.config.ComponentConfig; import eforce.util.config.Config; import eforce.util.config.ConfigException; import eforce.util.config.EntityConfig; import eforce.util.config.initializers.ClassPathConfigInitializer; public class HelloWorld { public static final String MYCONSTANT= "*************YEA*************"; public static void main(String[] args) { Config config= new Config("samples"); try { config.init(new ClassPathConfigInitializer("eforce.util.config.samples")); } catch (ConfigException e) { e.printStackTrace(); } EntityConfig entity= config.getEntity("sample"); // get "mypar" parameter System.err.println("parameter mypar: '"+entity.getParameter("mypar")+"'"); // get "mypar" parameter as int if (entity.getIntParameter("mypar")>20) System.err.println("mypar is greater than 20"); else System.err.println("mypar is not greater than 20"); // get "mylongpar" parameter with java constant in it System.err.println("mylongpar:"); System.err.println(entity.getParameter("mylongpar")); // get "myclass" instance HelloWorld ew= (HelloWorld)entity.getClassInstanceParameter("myclass"); System.err.println("new HelloWorld instance: "+ew); // cycle through "mytable" values Iterator i= entity.getTableParameter("mytable").entrySet().iterator(); while (i.hasNext()) { Map.Entry e= (Map.Entry)i.next(); System.err.println("mytable key: "+e.getKey()+" value: "+e.getValue()); } // get component "mycomp" ComponentConfig cc= entity.getComponent("mycomp"); // cycle through "mylist" ordered values i= cc.getListParameter("mylist").iterator(); int cnt=0; while (i.hasNext()) System.err.println("mylist["+(cnt++)+"]: "+i.next()); // get "sqlwithexpr" with "mytable" value in it System.err.println("sqlwithexpr: "+entity.getSQLstmt("sqlwithexpr")); config.stop(); } }
Use it in a webapp .war:
Copy binary package content into WEB-INF/lib:
<war-root>/WEB-INF/lib/eforceconfig.jar eforceconfig-webapp.jar commons-lang-2.0.jar jakarta-oro-2.0.7.jar jakarta-regexp-1.3.jar log4j-1.2.8.jar
Or at container level (see your web container documentation).
Copy previous sample.xml into WEB-INF/entity-config:
<war-root>/WEB-INF/entity-config/sample.xml
Configure web.xml:
<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app id="salonepro" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>sample webapp</display-name>
<description>show how to plug eforceconfig into a webapp</description>
<listener>
<display-name>ConfigInitializer</display-name>
<listener-class>eforce.util.config.plugin.webapp.ConfigContextListener</listener-class>
</listener>
<context-param>
<param-name>eforce.util.config.CONFIGSET_NAME</param-name>
<param-value>samplewebapp</param-value>
</context-param>
<context-param>
<param-name>eforce.util.config.CACHE_SIZE</param-name>
<param-value>20</param-value>
</context-param>
<context-param>
<param-name>eforce.util.config.CHANGE_CONTROL_INTERVAL</param-name>
<param-value>10000</param-value>
</context-param>
<context-param>
<param-name>eforce.util.config.GARBAGE_COLLECTOR_INTERVAL</param-name>
<param-value>10000</param-value>
</context-param>
<context-param>
<param-name>eforce.util.config.START_CHANGE_CONTROL</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>eforce.util.config.START_GARBAGE_COLLECTOR</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>eforce.util.config.samples.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
Sample servlet HelloWorldServlet.java:
package eforce.util.config.samples.webapp; import java.io.IOException; import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import eforce.util.config.ComponentConfig; import eforce.util.config.Config; import eforce.util.config.EntityConfig; import eforce.util.config.samples.HelloWorld; public class HelloWorldServlet extends HttpServlet { private EntityConfig entity= Config.getConfigSet("samplewebapp").getEntity("sample"); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out= response.getWriter(); // get "mypar" parameter out.println("parameter mypar: '"+entity.getParameter("mypar")+"'<br>"); // get "mypar" parameter as int if (entity.getIntParameter("mypar")>20) out.println("mypar is greater than 20<br>"); else out.println("mypar is not greater than 20<br>"); // get "mylongpar" parameter with java constant in it out.println("mylongpar:<br>"); out.println(entity.getParameter("mylongpar")+"<br>"); // get "myclass" instance HelloWorld ew= (HelloWorld)entity.getClassInstanceParameter("myclass"); out.println("new HelloWorld instance: "+ew+"<br>"); // cycle through "mytable" values Iterator i= entity.getTableParameter("mytable").entrySet().iterator(); while (i.hasNext()) { Map.Entry e= (Map.Entry)i.next(); out.println("mytable key: "+e.getKey()+" value: "+e.getValue()+"<br>"); } // get component "mycomp" ComponentConfig cc= entity.getComponent("mycomp"); // cycle through "mylist" ordered values i= cc.getListParameter("mylist").iterator(); int cnt=0; while (i.hasNext()) out.println("mylist["+(cnt++)+"]: "+i.next()+"<br>"); // get "sqlwithexpr" with "mytable" value in it out.println("sqlwithexpr: "+entity.getSQLstmt("sqlwithexpr")+"<br>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
If you get:
org.xml.sax.SAXNotRecognizedException: Property: http://java.sun.com/xml/jaxp/properties/schemaLanguage
probably you are not using a JAXP 1.2 compliant SAX parser, try using xerces.
If you get any other problem please subimt a support request:
http://sourceforge.net/tracker/?group_id=198310
eforceconfig is developed by Andrea A.A. Gariboldi