Jakarta Commons is a Jakarta subproject that creates and maintains independent packages unrelated to any other framework or product. The packages are a collection of components that serve small, useful purposes in their own right, and are usually server-centric.
The first installment of this series divided these components into five categories and described the Web and Trivial categories. The second article covered the XML and Packages categories. This article describes the final category, Utilities. Note that these categorizations are purely for organizational reasons.
The Utilities category contains the BeanUtils, Logging, DBCP, Pool, and Validator components.
Summary: Provides utilities for working with dynamic JavaBeans.
Where: Main Page, Binaries, Source.
|
Source Code: Download the source code for the example application. This .zip file contains: |
When: When your application requires dynamic access to JavaBeans without any knowledge of pre-compiled accessors and modifiers. The JavaBeans must conform to the naming design patterns in the JavaBeans specification.
Example Applications: BeanUtilsDemo.java, AppLayer1Bean.java, AppLayer2Bean.java, SubBean.java require commons-beanutils.jar, commons-logging.jar, and commons-collections.jar in the CLASSPATH.
Description:
In dynamic Java application development environments, it is not always
possible to know ahead in time about the various getter and setter methods for
your JavaBeans. Even when you might know the names of the methods, you may
find it cumbersome to write a setXXX or getXXX method
to set or get each and every property of a bean. Consider the case of almost identical beans being transferred from one application layer to
another. Do you call bean1.setXXX(bean2.getXXX()) for each and
every property? You could, but you don't have to. BeanUtils does it for you!
BeanUtils helps the developer with dynamic JavaBean creation, modification, and
copying.
BeanUtils can act on JavaBeans that satisfy the following conditions:
getXXX and setXXX methods. For Boolean properties,
isXXX and setXXX are allowed. These properties can be
read/write only, which means that only get or only set for a property is
allowed, too.BeanInfo
class associated with your JavaBean.Let us start with a simple example.
To get and set simple properties, use the PropertyUtils.getSimpleProperty(Object bean, String name) and PropertyUtils.setSimpleProperty(Object bean, String name, Object value) methods, respectively, as shown below with the help of AppLayer1Bean.java and AppLayer2Bean.java as our test JavaBeans.
PropertyUtils.setSimpleProperty(app1Bean, "intProp1",
new Integer(10));
System.err.println("App1LayerBean, stringProp1: " +
PropertyUtils.getSimpleProperty(app1Bean, "stringProp1"));
Why would you use these methods, when you could just as simply get (and set)
the values of these beans by calling the methods directly on the beans
(app1Bean.getStringProp1() or
app1Bean.setIntProp1(10))? You may not always know the names of
these properties in advance in your code, and therefore may not know the right
methods to call. These property names might come from variables set by another
process or an external application. So, for example, if you accessed the name
of the property of a bean and stored it in a variable, you could pass the
variable name to PropertyUtils. This reduces a dependency on the
developer to know the right method names in advance.
What happens when the properties are not simple data types? For example,
your bean might have a collection or a map as a property. In these cases, use
PropertyUtils.getIndexedProperty or
PropertyUtils.getMappedProperty. Indexed properties require you to
pass the index of the value within the collection that you want to get or set.
Mapped properties require you to pass the key of the value you want to get or
set. For example:
PropertyUtils.setIndexedProperty(
app1Bean, "listProp1[1]", "New String value 1");
System.err.println("App1LayerBean, listProp1[1]: " +
PropertyUtils.getIndexedProperty(app1Bean, "listProp1[1]"));
Notice how, for indexed properties, the value of the index is passed within square brackets. The example above sets the value at index 1 of the list
in the bean app1Bean to New String value 1, while the
line after that retrieves the same value at index 1. An alternate way of doing
the same thing is to use the methods
PropertyUtils.setIndexedProperty(Object bean, String name, int index,
Object value) and PropertyUtils.getIndexedProperty(Object bean,
String name, int index), where the index is passed as a method parameter.
Similar methods exist for mapped properties where you pass the key, not the
index, to get and set mapped values.
Finally, your bean might itself have other beans as properties. What happens
when you want to get or set the property of the bean that is contained as a
property within your primary bean? Use the
PropertyUtils.getNestedProperty(Object bean, String name) and
PropertyUtils.setNestedProperty(Object bean, String name, Object
value) methods, as shown below.
// accessing and setting nested properties
PropertyUtils.setNestedProperty(
app1Bean, "subBean.stringProp",
"Hello from SubBean, set via Nested Property Access");
System.err.println(
PropertyUtils.getNestedProperty(app1Bean, "subBean.stringProp"));
As you can see, the contained bean's property is accessed via a dot nomenclature.
You can use a combination of nested, mapped, and indexed properties to any
depths you like. To use a combination of these different property-access methods,
use PropertyUtils.getProperty(Object bean, String name) and
PropertyUtils.setProperty(Object bean, String name, Object value).
For example, this would allow you to make method calls such as:
PropertyUtils.setProperty(app1Bean, "subBean.listProp[0]",
"Some Value");
This combines nested and indexed property access into one call.
BeanUtils is often used when dynamically accessing the request parameters
for a web-based system. In fact, BeanUtils arose out of a need to transfer the
request parameters dynamically into system JavaBeans in the Struts project. A user-filled form
is transferred into a Map in code, where the parameter names form
the keys and the parameter values are the values from by the user, and a simple
BeanUtils.populate transfers these values into a system bean.
Finally, BeanUtils provides a one-step method to copy values from one bean into another:
// let's copy app1Bean to app2Bean
BeanUtils.copyProperties(app2Bean, app1Bean);
In this package, there are several other useful methods that I have not covered here. BeanUtils is one of the better-documented components. I encourage you to have a look at the Javadocs for this package for descriptions of the rest of the methods.
|
Summary: Wrapper library around a set of popular logging implementations.
Where: Main Page, Binaries, Source.
When: When your application requires more than one logging implementation, or you anticipate such a need in the future.
Example Application: LoggingDemo.java, commons-logging.properties requires commons-logging.jar in the CLASSPATH. Requires log4j.jar, in certain cases.
Description:
Logging enables your applications to debug and trace their behaviors at any
point in time. Logging is an integral part of any application, so there are
many third-party logging implementations that eliminate the need for you to
write your own logging API. In fact, even the JDK comes with a prebuilt logging
API. With such a plethora of choices (log4j, JDK, Logkit, et cetera), the choice
of a particular logging API to use within your own application comes down to
selecting the one that best suits your requirements. However, a case can be
made for instances where the choice of a logging API may not be compatible
within applications, either because of company requirements or incompatibilities
with existing architecture. The idea of the Logging component is to wrap the
requirement of logging within a set of standard APIs where the underlying
implementation can change or differ. The developer simply uses this API to make
the log requests. The API decides, based on available logging architectures, to
direct these logging calls to the appropriate handler. Thus, the Logging
component, as far as the developer is concerned, is independent of any
particular logging implementation.
If you are familiar with using log4j (also see this log4j article), using Commons-Logging should not be a problem. Even if you are not familiar with it, using Commons-Logging requires you to import two classes,
create a static instance of a Log, and log away. The relevant code
bits are shown below:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class LoggingDemo {
private static Log log = LogFactory.getLog(LoggingDemo.class);
// ...
}
An interesting thing happens when you call LogFactory.getLog().
A process of discovery is started to find the required Logging implementation,
according to the following scheme. Note that irrespective of how the desired
implementation is found, it should be a class that implements the
Log interface and is available in the CLASSPATH.
Commons-Logging comes prebuilt with Jdk14Logger,
Log4JLogger, LogKitLogger, NoOpLogger
(which simply swallows all messages), and a SimpleLog.
Commons-Logging looks for a configuration file called
commons-logging.properties in the CLASSPATH. This
file must define, at the minimum, the property
org.apache.commons.logging.Log, and it should be equal to the fully
qualified name of one of the implementations of the Log interface
listed above.
If a configuration file cannot be found with the right property above,
Commons-Logging looks for a system property called
org.apache.commons.logging.Log.
If there is no system property with the above name, Commons-Logging looks
for log4j classes in the CLASSPATH. By the simple act of finding
these classes in the CLASSPATH, Commons-Logging assumes that you
are using log4j. However, note that log4j still needs to be configured for its
properties in its own log4j.properties file.
If none of the above is found and if the application is running on JRE 1.4 and above, the application defaults to using the JRE1.4's logging mechanism.
Finally, if none of the above is valid and the application is not running
on JRE 1.4 and above, the application uses a built-in SimpleLog,
which writes everything to System.err.
Once the desired logging implementation has been obtained, you can start logging within your environment, based on your rules and degree of severity of your log messages. Using a standard API abstracts from the underlying mechanism and the same call is translated into implementation-specific calls.
|
Related Reading Jakarta Struts Pocket Reference |
The supplied demo file simply prints an information message and tells you
which logging implementation was used to do so. Try running this file in different
environments, for example, run the file on its own without specifying any
properties, and you will default to Jdk14Logger. Run it by specifying the system
property as -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog,
and you will see that SimpleLog is used to print the message.
Finally, try putting Log4 classes in the CLASSPATH. If you have
set the correct configuration for log4j in a log4j.properties
file, you will get the message created with Log4JLogger.
|
Summary: A library that allows you to maintain a pool of objects.
Where: Main Page, Binaries, Source.
When: Whenever you need to maintain a pool of object instances.
Example Applications: PoolDemo.java and MyObjectFactory.java require commons-pool.jar and commons-collections.jar in the CLASSPATH.
Description:
Pool serves the purpose of defining a set of interfaces for object-pooling mechanisms. It also provides some general-purpose pool implementations and some base classes that you could use to create your own pooling architecture.
Object pools may not be new to most developers. Many developers have, at some time or other, used a database pooling mechanism for access to databases. Object pools allow you to instantiate a set of objects within a pool as part of configuration and startup, allowing for shorter response times when these objects are actually needed within your application. These objects can be returned to the pool once they've been used, remaining available to any other calling application.
The Pool component allows you to create Object (Instance) pools without tying you to one particular implementation. Several implementations are provided with the component, and you can create your own, if so required.
Three types of base classes make up the Pool component:
ObjectPool, an interface that defines and maintains a pool,
ObjectPoolFactory, responsible for creating instances of
ObjectPool, and PoolableObjectFactory, which defines
a set of lifecycle methods for instances to be used in the
ObjectPool. A variation on these classes is the
KeyedXXX interface. This interface allows you to create several
object pools for different types of objects, only differentiated with the help
of a Key. In a way, the KeyedXXX interface is a
Map implementation of the normal ObjectPool.
As stated before, there are several generic implementations prebuilt within
the Pool component. One of these is the GenericObjectPool, and I
will illustrate its usage with the help of an example.
Create a PoolableObjectFactory. This factory defines how
your objects are created, destroyed, and validated.
import org.apache.commons.pool.PoolableObjectFactory;
public class MyObjectFactory implements PoolableObjectFactory {
private static int counter;
// returns a new string
public Object makeObject() {
return String.valueOf(counter++);
}
public void destroyObject(Object obj) {}
public boolean validateObject(Object obj) { return true; }
public void activateObject(Object obj) {}
public void passivateObject(Object obj) {}
}
Notice that we are creating a pool of String objects with an
incrementing number, and that our validations always return true. You can
implement the other methods, if required.
Using this PoolableObjectFactory, create a
GenericObjectPool, using default values for maxActive,
maxIdle, and so on.
// create a GenericObjectPool using MyObject factory and default
// values for the maxActive, maxIdle etc.
GenericObjectPool pool = new GenericObjectPool(new MyObjectFactory());
Borrow an object from it.
System.err.println("Borrowed: " + pool.borrowObject());
Return it.
pool.returnObject("0");
You can use various methods to find the state of the pool.
// so what's the number of active (borrowed objects) ?
System.err.println("Active objects: " + pool.getNumActive());
The example file PoolDemo.java contains the full source code.
|
Summary: Database Connection Pooling, based on the Pool component.
Where: Main Page, Binaries, Source.
When: Whenever access is required to a relational database.
Example Application: DBCPDemo.java requires commons-dbcp.jar, commons-pool.jar, and commons-collections.jar in the CLASSPATH. You will also require access to a database and the JDBC drivers for accessing that database. The example application tests connection to a MySQL server using the MySQL JDBC driver. Note that you will require the nightly version of the binaries, as the official release does not contain some of the required classes. Finally, when this example is run, make sure that you set the system property for the JDBC driver you are using (-Djdbc.drivers=com.mysql.jdbc.Driver).
Description:
DBCP provides a database-connection pooling mechanism based on the Pool component. Its usage is slightly more involved than regular connection pooling mechanisms, as the idea was to provide a generic architecture available as a pseudo-JDBC driver. However, since we have already covered the basics of the Pool component, understanding the usage for DBCP should be easier.
Create a GenericObjectPool class:
GenericObjectPool pool = new GenericObjectPool(null);
Recall from the discussion on the Pool component that the
GenericObjectPool requires a PoolableObjectFactory to
create the instances of our Objects that need to be pooled. This,
in DBCP's case, is provided by the PoolableConnectionFactory as
shown below:
Create the PoolableConnectionFactory:
DriverManagerConnectionFactory cf =
new DriverManagerConnectionFactory(
"jdbc:mysql://host/db", "username", "password");
PoolableConnectionFactory pcf =
new PoolableConnectionFactory(
CF, pool, null, "SELECT * FROM mysql.db", false, true);
Now, all we need to do is to create and register the
PoolingDriver.
new PoolingDriver().registerPool("myPool", pool);
We are now ready to get connections out of this Connection Pool. Note that
it has been created with default values for maxActive,
maxIdle, et cetera. You can set these properties to what you
require while creating the GenericObjectPool class in step 1 above. DBCPDemo.java gives the
complete example.
Summary: API that combines commonly used validations of user input.
Where: Main Page, Binaries, Source.
When: Whenever you need to validate JavaBeans on a regular basis.
Example Application: ValidatorDemo.java, MyValidator.java, MyFormBean.java, validation.xml require commons-validator.jar, commons-beanutils.jar, commons-collections.jar, commons-digester.jar, and commons-logging.jar in the CLASSPATH.
Description:
If you have developed a web application using Struts before, you've come across the Validator package. It makes the job of performing validations on user input quite easy, and provides a single interface for locale-specific error messages. However, Validator is not just of use in web applications — it can be quite easily used at other places where JavaBeans are used.
Validator allows you to define validations for your user input fields, provide internationalization support in form of locale-specific error messages, and create custom Validators. There are several prebuilt validators that you can use, and you can create your own if the custom validators do not serve your purpose.
Validation rules and validation methods are defined using XML files. (The definitions can be in one or many files. It is a good idea to separate them, though.) The validation methods file defines the validators to be used and names the class that actually implements the validator. This class does not need to implement any specific interfaces or extend another class. It should simply conform to the definition, as specified in the method file.
Let us construct a validator of our own that simply checks if a
String property of a bean contains a specific character
(*).
import org.apache.commons.validator.*;
public class MyValidator {
public static boolean validateContainsChar(Object bean, Field field) {
// first get the value of this bean property as a string
String val = ValidatorUtil.getValueAsString(bean, field.getProperty());
// now return true or false based on the presence of the '*' sign
return ((val.indexOf('*') == -1) ? false : true);
}
}
The ValidatorUtil class provides useful methods to get the
values of bean properties as easily manipulable String values.
This validator now needs to be defined in an XML file:
<!-- This defines the validator methods that we are implementing -->
<global>
<validator name="containsStar"
classname="MyValidator"
method="validateContainsChar"
methodParams="java.lang.Object, org.apache.commons.validator.Field" />
</global>
Notice that we define the exact method structure, including the parameters that it expects. To use this validator in a method of your own, follow these steps:
Add the validator rules that we want to implement in the XML file above:
<!-- This defines the validator rules -->
<formset>
<!-- This checks if the form bean's name property implements
the containsPercent method -->
<form name="myFormBean">
<field property="name" depends="containsStar">
<arg0 key="myFormBean.name" />
</field>
</form>
</formset>
As you can see, all validation rules are enclosed in the
formset element, following which the form for which the
validations are to be performed is listed with the individual validations. In
our case, we want the name property of myFormBean to
be validated so that it passes the containsStar validation (so
that it contains the character *).
Create a Validator and initialize it, based on the XML
file:
// load the validator xml files
InputStream in = getClass().getResourceAsStream("validator.xml");
// create a ValidatorResources
ValidatorResources resources = new ValidatorResources();
// and load resources in
ValidatorResourcesInitializer.initialize(resources, in);
// now create the Validator
Validator validator = new Validator(resources, "myFormBean");
validator.addResource(Validator.BEAN_KEY, bean);
Validate the bean:
// finally validate
ValidatorResults results = validator.validate();
The results of the validation are passed as an instance of
ValidatorResults. It contains a hashmap of individual
ValidationResult objects for each property for which validation
was requested.
Process the ValidationResults:
// the result object may contain the results of validation on other form
// properties as well. For each property we can get the result individually
ValidatorResult result = results.getValidatorResult("name");
// and for each property, we can check for individual validations
// for example, did the name property pass the containsStar validation?
System.err.println(
"Contains Star validation passed for \'name\' property?" +
result.isValid("containsStar"));
On each ValidationResult instance, we can query whether it
passed or failed a particular validation. For example, in the code above the
result instance for the name property is queried for
the containsStar validation by the code
result.isValid('containsStart').
Validator is quite useful in web applications, as it reduces the repetitive
task of validations to be performed on user input by providing a set of
prebuilt Validators. These include, but are not limited to, range checking,
limitations on types and sizes of input values, and email and locale testing.
Further, you can extend and create your own Validators to add to
this list.
This concludes the third and final installment of coverage of Jakarta Commons. Although these articles covered only the basics of each component, I hope that they have given enough information for you to start exploring them further. Good Luck!
Vikram Goyal is the author of Pro Java ME MMAPI.
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.