Wednesday, February 20, 2008

Some useful ant tasks

Call another target within another buildfile
<ant antfile="../AnotherProject/build.xml" target="mytarget">


Call another target within the same buildfile. It can be used also for imported buildfile.
<antcall target="mytarget">


The complete list of Ant core tasks can be found here.

String Concatenation Comparison

I've concatenate two strings, an integer and a double in five different ways.
Here are the results (in nanosecs):
StringBuilder       37708
StringBuffer 95319
String 5389877
C-Like 10690022
MessageFormat 30410260

Saturday, February 16, 2008

The Java Logging API in brief (part II)

Now I'd like to debate with you about some logging best practise.
If your log messagge would not be logged with the current settings and it contains relative expensive operation like a string concatenation, it's better to precede it with a less expensive operation like a check:
if (logger.isLoggable(Level.FINEST)) {
logger.finest("log this: "+value);
}


To log an exception and its stacktrace use:
try {
// Test with an exception
throw new Exception();
} catch (Throwable e) {
// Log the exception
logger.log(Level.SEVERE, "your message", e);
}


You can also you when you enter in a method and with which parameters, if you throws exception in the method and when you exit and with with return value.
This is an example:
public int myMethod(String p1, int p2) {
log.entering(MyClass.class.getName(), "myMethod",
new Object[] { p1, p2 });

if (p2 < 0) {
Exception ex = new IllegalStateException();
log.throwing(this.getClass().getName(), "myMethod", ex);
}
int result = p2 * 2;
log.exiting(MyClass.class.getName(), "myMethod", result);

return result;
}

Note that this approach is very intrusive and inquinates you code logic, therefore it would be better to do this using an interceptor.

The Java Logging API in brief (part I)

In my previous post I've suggested a very primitive way to log error. Of course the right way to log your error, but also other information, is to use a really logger.
The most famous logger for the java environment is sure log4j but also the core jdk provides a logging api. The vantage of using the embedded java logging api is that you need no external jar.
If you already know the the log4j levels, this comparison table can help you:
log4J Java Logging
log4j.fatal("msg"); -
log4j.error("msg"); logger.severe("msg");
log4j.warn("msg"); logger.warning("msg");
log4j.info("msg"); logger.info("msg");
log4j.debug("msg"); logger.config("msg");
- logger.fine("msg");
- logger.finer("msg");
- logger.finest("msg");

To create a logger with the Java Logging API:
private static final Logger log =
Logger.getLogger(MyClass.class.getPackage().getName());

Note that I've called this logger as the package because this be my very practical in case of refactoring. Of couse, you are free to use the name that best fits your needings, for example the module name.
You can configure log level and everything else programmatically but the best and less intrusive way to do this is the logging.properties file.

A standard configuration is already in every java distribution in the folder $JAVA_HOME/jre/lib/ but you can use a different file by specifying a filename with the java.util.logging.config.file system property (for example java -Djava.util.logging.config.file=/path/to/log.properties).
This is the standard configuration as for jdk 6
############################################################
# Default Logging Configuration File
############################################################

############################################################
# Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

This configuration is pretty self-explanatory, note as you can set different log level for different logger.
You can write your own log formatter by subclassing the abstract class java.util.logging.Formatter and overriding the method format(LogRecord).

Redirect Std Error to a file

Sometimes may be convenient to save all outcomes from standar error to a file.
This is very easy to do and you need the following lines of code
String filename = "mylog.log";
PrintStream stream = new PrintStream(filename);
System.setErr(stream);

The problem with this approach is that there is no timestamp, like for normal log.
A simply workaround may be to redirect the standard error at every launch to a different file named with the timestamp and to save all the log files in the same folder.
String logFolderName = "log";
File logFolder = new File(logFolderName);
// Create the log folder if it non already exists
if (!logFolder.exists()) {
logFolder.mkdirs();
}
DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH:mm");
StringBuilder sb = new StringBuilder();
sb.append(logFolderName);
sb.append(System.getProperty("file.separator"));
sb.append("mystderr");
sb.append("_");
sb.append(df.format(new Date()));
sb.append(".log");
PrintStream stream = new PrintStream(sb.toString());
System.setErr(stream);

and your exception will be saved in file like this: log/mystderr_2008-02-16_22:09.log

Self Made Reports Alternative

There are many Reporting Tool for Java, here can you find an almost complete overview.

Anyway, if you're looking for a low-level, very flexible and self-made solution, can you consider to do your reports in XHTML (with or without a WYSWYG editor), populate it with data with template engine like Velocity.

Then you can trasform your XTML output in PDF with Flying Saucer. The Flying Saucer ist a XTML+CSS renderer to view or convert such document to pdf. It provide also very useful pagination capabilities, like page format, page number, page breaks, header and footer.

Last but not least, can you view or silent print your pdf report with another higt-quality open-souce tool like PDF-Renderer.

JUnit 4 in 30 seconds

JUnit is one of the most used Unit Testing Frameworks for java.
Beginning from the version 4, it provides some very intuitive annotations to write your test:

@BeforeClass
Methods are executed once before the first of the series of tests. External resources that are used by all tests should be initialised here (i.e.: a database connection).

@AfterClass
Methods are executed once after the last test has been run.Resources used by the tests that need to be released such as streams etc. should be set free here.

@Before
Methods are executed before every single test. For example can you init your test data.

@After
Methods are executed after every single test. For example can you delete the output of your test.

@Test
Methods are the actual test methods. Here can you use the Assertion.

@Test (expected = ArrayIndexOutOfBoundsException.class)
The Test Annotation can be extended by stating what exception should be thrown when the test is executed, the test fails if no exception or if the wrong exception is thrown.

@Ignore
Don't run tests, that are not yet implemented.