Wednesday 13 April 2016

Various tips on Oracle Spatial

When creating a spatial index on a table with SDO_GEOMETRY, one of the required parameters is LAYER_GTYPE.

How to find GTYPE of SDO_GEOMETRY objects in a table:

select sdo_geometry.get_gtype(geom), count(*) from map_data.zip_geom group by sdo_geometry.get_gtype(geom)
/

Monday 11 April 2016

Configuring log4j to create a new log file at each Java program run.

For running standalone Java programs, such as jobs, that use Apache log4j logging, it is often useful to have a separate log file per each program execution.
The post covers a simple approach that can be used with a FileAppender by using a timestamp as part of the log file name injected from a system property.

Two different samples are provided. One can be used when the Java program is launched directly from a shell (manually or via a scheduler) and the other when launching it as an Ant task.

The log4j configuration is the same for both scenarios and is shown right below:

# A sample Log4j configuration demonstrating how to create a new log file 
# at each program start.
# Created:  Apr 6, 2016 by Vitali Tchalov

log4j.rootLogger=info, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %5p [%t] (%d) %c - %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=logs/job_${log.timestamp}.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d [%t] %5p %c - %m%n

The configuration uses a custom system property log.timestamp to append a unique (with a second precision) suffix to the log file name.

The way the property is set depends on how the Java program is launched.

Scenario 1 - when starting a plain regular Java program by directly invoking the java executable

1. Add a system property in a static block of the main class (i.e. the launching class with the main(String[]) method) prior to referencing any Logger.

static {
    System.setProperty("log.timestamp", 
        new  SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()));
}

Below is a complete class source code:

package com.forms2docx;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.log4j.Logger;

/**
 * A sample class to demonstrate a technique to configure Log4j to create a new log file at each program run.
 * 
 * To compile the sample program, specify the absolute path to a log4j.jar file, for example:
 * javac -d bin -cp ".;./lib/log4j-1.2.17.jar;" ./com/forms2docx/*.java
 *
 * To run with the static block that programmatically adds the log.timestamp property:
 * java -cp ".;./bin;./lib/log4j-1.2.17.jar;" com.forms2docx.Log4jNewFile
 *
 * To run with the log.timestamp property passed from the command line:
 * java -cp ".;./bin;./lib/log4j-1.2.17.jar;" -Dlog.timestamp=$(date +"%Y%m%d_%H%M%S") com.forms2docx.Log4jNewFile
 *
 * @author Vitali Tchalov
 */
public class Log4jNewFile {
    static {
       System.setProperty("log.timestamp", 
           new  SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()));
    }

    private static final Logger logger = Logger.getLogger(Log4jNewFile.class);

    public static void main(String[] args) {

        logger.info(String.format("Job has started at %s.", 
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));

        logger.info("The sample demonstrates how to configure Log4j to create a new file on every program run.");
 
    } 
}


To execute this program, compile and run from a shell:

java -cp ".;./bin;./lib/log4j-1.2.17.jar;" com.forms2docx.Log4jNewFile

Of course, a log4j jar file must reside on the classpath.

If modifying the source is not possible or desirable for whatever reason, it is also possible to supply the system property on the command line, like this:

java -cp ".;./bin;./lib/log4j-1.2.17.jar;" -Dlog.timestamp=$(date +"%Y%m%d_%H%M%S") com.forms2docx.Log4jNewFile

The command line above is for a UNIX system (e.g. Linux, Mac). It might be possible to adapt it for Windows too but formatting a date and time to a short format would be very cumbersome in Windows.

Scenario 2 - starting a Java program (job) as an Ant task.

These steps are required for launching a Java program as an Ant task:

1. Include <tstamp /> to the Ant build file
2. Add the following to the java task:
<sysproperty key="log.timestamp" value="${DSTAMP}_${TSTAMP}" />

Note, the DSTAMP and TSTAMP are standard variables defined by Ant.

An example of an Ant build file to launch a Java program as an Ant task: (requires a log4j.jar file on the classpath as well as Ant in the PATH):
<project name="Launch Java Ant task sample" basedir="." default="info">
    <echo message="Launching Java Ant task sample..." />

    <tstamp/>

    <target name="info">
     <echo message="The runJob Java task demonstrates creating a new log file at each run."/>
    </target>

    <target name="runJob" description="Demonstrates a new log file per each run.">
        <java 
                classname="com.forms2docx.Log4jNewFile"
                fork="true"
                failonerror="true">
            <jvmarg value='-Dlog4j.configuration=file:"${basedir}/log4j.properties"' />
            <jvmarg value='-server' />
            <sysproperty key="log.timestamp" value="${DSTAMP}_${TSTAMP}" />

            <classpath>
                <pathelement location="${basedir}/bin"/>
                <fileset dir="${basedir}/lib">
                    <include name="*.jar" />
                </fileset>
            </classpath>
        </java>

        <echo message="Task completed."/>          
    </target>
</project>
 
Note that by default, the TSTAMP is in "HHmm" format. When this precision is not sufficient, then a custom property with a required format can be added.
For example:
    <tstamp>
        <format property="tstamp-sec" pattern="HHmmss"/>
    </tstamp>


Then the sysproperty in the java task would look like this:

<sysproperty key="log.timestamp" value="${DSTAMP}_${tstamp-sec}" />
 
/* --- end --- */