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