The Holy Java

Building the right thing, building it right, fast

Archive for December, 2009

Compiling with AspectJ’s ajc compiler from Maven

Posted by Jakub Holý on December 10, 2009

I needed to compile AspectJ classes (.java with annotations) with ajc and I wanted to have that integrated into the Maven build process. I tried several means, finally finding the one that worked.

Note: I had to use ajc even though I was using the pure java 5 syntax based on annotations instead of the legacy AspectJ syntax due to bug in AspectJ (fixed in v. 1.6.6).

Failed Attempts

aspectj-maven-plugin v1.2

First I tried the AspectJ Maven plugin but it didn’t work for me because I had a special need – a project containing only an aspect while the plugin requires, if I remember correctly, also the advised sources to be in the project. A fix is available and should be included in the version 1.3 but the project doesn’t seem to be very active so who knows when it will be released. See MASPECTJ-7.

maven-compiler-plugin for aspectj

Next I tried the maven-compiler-plugin, which supports various back-ends including AspectJ with the dependency on plexus-compiler-aspectj and compilerId set to aspectj. Unfortunately the plexus-compiler-aspectj is quite out of date, supporting only AspectJ 1.3 while I needed 1.6.

Finally the Success: maven-antrun-plugin

Being failed by Maven plugins, I had to resort to the standard way of running ajc via Ant. Fortunately Maven has a very good integration of Ant and its tasks.

The only problem here was that Ant requires ${java.home} to point to JDK (and not JRE) to find javac. It doesn’t help to set the envrionmental variable JAVA_HOME to point to the JDK because Maven resets it to $JAVA_HOME/jre in the case – see MANTRUN-91. The solution is to add tools.jar (which includes javac classes) to maven-antrun-plugin’s dependencies.

The corresponding piece of pom.xml:

<build>
<plugins>
  <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <!-- Tell javac not to compile sources for antrun will do it -->
    <configuration>
        <excludes>
            <exclude>**/*.*</exclude>
        </excludes>
    </configuration>
  </plugin>

  <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.3</version>
    <dependencies>
      <dependency>
        <groupId>sun.jdk</groupId>
        <artifactId>tools</artifactId>
        <version>1.5.0</version>
        <scope>system</scope>
        <systemPath>${java.home}/../lib/tools.jar</systemPath>
      </dependency>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId> <!-- apectj ant plugin -->
        <version>1.6.0</version>
      </dependency>
    </dependencies>

    <executions>
      <execution>
        <phase>compile</phase>
        <goals>
          <goal>run</goal>
        </goals>
        <configuration>
          <tasks>
            <echo>AntRun: Compiling AspectJ classes with ajc, java.home=${java.home}</echo>
            <taskdef
              resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties"
              classpathref="maven.plugin.classpath" />

            <iajc
              srcDir="src/main/java"
              destDir="target/classes"
              verbose="true"
              showWeaveInfo="true"
              source="1.5"
              classpathRef="maven.compile.classpath"
              Xlint="ignore" >

              <exclude name="eu/ibacz/pbns/util/aspect/ExampleTimingAnnotatedAspect.java"/>
            </iajc>

          </tasks>
        </configuration>
      </execution>
    </executions>
  </plugin>
</plugins>
</build>

Note: maven-antrun-plugin v1.3 uses Ant 1.7.1 - see http://maven.apache.org/plugins/maven-antrun-plugin/dependencies.html

Conclusion

It's a bit tricky but very well possible to run AspectJ compiler instead of javac during a maven build. The most flexible way is to use the AspectJ Ant task and maven-antrun-plugin though in more standard cases the aspectj-maven-plugin can serve you well too.

Posted in Languages | Tagged: , , , | 1 Comment »

Troubleshooting logging configuration (Log4j, commons-logging)

Posted by Jakub Holý on December 7, 2009

Did it ever happen to you that your logging didn’t behave as expected? Here are some tips how to find out what’s going on.

Commons-logging (since 1.1)

Set the system property org.apache.commons.logging.diagnostics.dest to STDOUT (or STDERR or a file name); docs: -Dorg.apache.commons.logging.diagnostics.dest=STDOUT

Extract of a sample output (no commons-logging.properties):

...
[LogFactory from sun.misc.Launcher$AppClassLoader@934873913] [ENV] Application classpath (java.class.path): ..
...
[LOOKUP] No properties file of name 'commons-logging.properties' found
....
Discovering a Log implementation...
...
Log adapter 'org.apache.commons.logging.impl.Log4JLogger' from classloader java.net.URLClassLoader@32689826 has been selected for use.

Extract of a sample output (incorrect commons-logging.properties found):

...
[LogFactory from sun.misc.Launcher$AppClassLoader@934873913] [LOOKUP] Properties file found at 'jar:file:/myproject/lib/test/dbunit-embeddedderby-parenttest-sources.jar!/commons-logging.properties' with priority 0.0
.. [LOOKUP] Properties file at 'file:/myproject/web/WEB-INF/classes/commons-logging.properties' with priority 0.0 does not override file at 'jar:file:/myproject/lib/test/dbunit-embeddedderby-parenttest-sources.jar!/commons-logging.properties' with priority 0.0
.. [LOOKUP] Properties file of name 'commons-logging.properties' found at 'jar:file:/myproject/lib/test/dbunit-embeddedderby-parenttest-sources.jar!/commons-logging.properties"
.. Attempting to load user-specified log class 'org.apache.commons.logging.impl.SimpleLog'...
.. Log adapter 'org.apache.commons.logging.impl.SimpleLog' from classloader sun.misc.Launcher$AppClassLoader@934873913 has been selected for use.
...

Notice that Commons Logging uses the context classloader and not e.g. Class.getClassloader() to locate the implementation to use, which may occassionally lead to some problems.

PS: You may be better of not using commons-logging because of its classloader issues. (SLF4J may be better?)

Log4j

Set the system property log4j.debug to true for Log4j to log the location of the configuration file it's using and other useful information. You can also set it in in the log4j.properties file:

log4j.debug=true

Or, as mentioned above, pass it as a system property to Java, for example as in

java -Dlog4j.debug=true -jar YourApplication.jar

The debug information will be printed into the System.out, not in any log file you may have configured (Log4j can't use itself for this logging).

Example output:

log4j: Parsing for [root] with value=[INFO, CONSOLE, filelog].
log4j: Level token is [INFO].
log4j: Category root set to INFO
log4j: Parsing appender named "CONSOLE".
log4j: Parsing layout options for "CONSOLE".
log4j: Setting property [conversionPattern] to [%6rms [%p] ..%0.46c %x- %m%n].
log4j: End of parsing for "CONSOLE".
log4j: Parsed "CONSOLE" options.
log4j: Parsing appender named "filelog".
log4j: Parsing layout options for "filelog".
log4j: Setting property [conversionPattern] to [%6rms [%p] ..%0.46c %x- %m%n].
log4j: End of parsing for "filelog".
log4j: Setting property [file] to [/home/me/mylog.log].
log4j: Setting property [maxBackupIndex] to [5].
log4j: Setting property [maxFileSize] to [50MB].
log4j: setFile called: /home/me/mylog.log, true
log4j: setFile ended
log4j: Parsed "filelog" options.
log4j: Parsing for [eu.ibacz.lqs.uiradrupdater] with value=[DEBUG].
log4j: Level token is [DEBUG].
log4j: Category eu.ibacz.lqs.uiradrupdater set to DEBUG
log4j: Handling log4j.additivity.eu.ibacz.lqs.uiradrupdater=[null]
log4j: Finished configuring.

For the log4j.properties:

log4j.rootCategory=INFO, CONSOLE, filelog

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%6rms [%p] ..%0.46c %x- %m%n

log4j.appender.filelog=org.apache.log4j.RollingFileAppender
log4j.appender.filelog.File=${user.home}/mylog.log
log4j.appender.filelog.MaxFileSize=50MB
log4j.appender.filelog.MaxBackupIndex=5
log4j.appender.filelog.layout=org.apache.log4j.PatternLayout
log4j.appender.filelog.layout.ConversionPattern=%6rms [%p] ..%0.46c %x- %m%n

log4j.logger.eu.ibacz.lqs.uiradrupdater=DEBUG

Posted in Languages, Tools | Tagged: , , , , , | Comments Off