Code & Iron

Posts Tagged "Java"

April 2, 2012

Making the Leap: Log4J to SLF4J

So. The team lead has come to you and said “time to switch project X over to SLF4J“. Or you’re the team lead and you just realized all your log statements only work with Log4J? What if that project dies …

  Show More

So. The team lead has come to you and said “time to switch project X over to SLF4J“. Or you’re the team lead and you just realized all your log statements only work with Log4J? What if that project dies out?

Freaking out? Worried you will have to manually edit lines upon lines of code? Time to hire a coop? Fear not brave coder. We will bring the full power of bulk editing and unix “diff” to bear on this one. (And a really experienced colleague of mine who helped me through this!)

For those who are unfamiliar, SLF4J is the Simple Logging Facade for Java. It is a layer on top of Log4J, Logback, and others that allows you to decouple your log statements from your underlying implementation. SLF4J is to Log4J as JPA is to Hibernate.

Also the interface it provides is absolutely delicious. No more checking isDebugEnabled() before a log statement to avoid performance hits, SLF4J does so automatically. Also, no more complex string concatenation inside logger.info(). In SLF4J the info, debug, error, trace, and fatal methods take objects and plug them into message placeholders “{}” for you, calling the toString() methods as appropriate.

So let’s switch your project over. First, check out a fresh working copy of the project you need to modify, let’s call it project-logging-dirty. Next, check out another working copy of the same project, in directory project-logging-clean.

Download the SLF4J Migrator Tool here:
It’s perfectly suited to changing from Log4J to SLF4J, but it has one problem: It touches every single line of every single.java file in your project, and screws up the whitespace more often that not. I personally hate losing svn/git history due to whitespace mods. I want to know who to svn blame (lol, bad joke) when I’m looking at this project in 6 months.

Run the SLF4J migrator against project-logging-dirty.


java -jar slf4j-migrator-1.6.4.jar

If you do an svn diff you will see a HUGE number of lines changed (which are mostly whitespace modifications). Let’s pull those changes by making a unified diff of the two directories, ignoring .svn and whitespace mods, and forward the output to a new file called slf4j.patch.


diff -udrEbBw -x.svn project-logging-dirty project-logging-clean > slf4j.patch

Change to project-logging-clean so that it’s your present working directory, and apply the patch!


patch -p1 -ZEBw --ignore-whitespace < ../slf4j.patch

Run svn diff again in project-logging-clean. You should see a clean changeover to SLF4J including LoggerFactory.getLogger statements and all the rest. Commit project-logging-clean once you've run all integration tests against the build. Delete project-logging-dirty, to never be seen again.

Caveat: When doing thread-specific concurrent programming, you may be using NDC (Nested Diagnostic Context), a stack of context specific log message parameters. NDC is unsafe as the log parameter stack could theoretically grow without bounds (memory leak anyone?). SLF4J does not support NDC, and switches to MDC (Map based, which is much more forgiving if you are writing to the log parameters forget to remove). Caution should be exercised when doing any thread specific logging: each map.put should have a corresponding map.remove, in a finally block. BUT take note: The SLF4J Migrator Tool cannot change over your NDC statements to MDC, you will need to do this manually.

Best of luck, I hope you have as much fun with SLF4J as I do!

  Posted by Matt Holtom on April 2, 2012  /  Tags: , , , , ,
January 25, 2012

The Maven Dependency Plugin, It’s Tree Time.

I recently inherited an external testing project that is about 6 months stale. It was written by a co-op who left a while ago. For those of your playing the home game, neither of these are good signs. It’s a …

  Show More

I recently inherited an external testing project that is about 6 months stale. It was written by a co-op who left a while ago. For those of your playing the home game, neither of these are good signs. It’s a suite of tests packaged in an executable jar that use selenium to automate a firefox (or any) browser. Its purpose is to automatically verify there have been no regressions in the target project since the last major feature set. Nifty right?

Only one issue. The project was started in, and run exclusively out of, eclipse. Specifically, all of the dependency information was created by modifying the eclipse build path settings. This means that a ton of relevant project information is stored in the .classpath file on a specific computer. To make matters worse, the .classpath, .settings, and .project files are commonly ignored globally by users of svn or git, to avoid muddying up the repository. If that one computer has a hard drive crash, or the co-op quits, dies, or goes on a road trip, we’re scuppered. The project now requires a goat sacrifice as part of the build process, it’s arcane magicka twisting just outside our mortal comprehension.

For the record: I have nothing against eclipse. I think it is a great IDE, and I use it every day. However, it is not, and never will be an enterprise level build/dependency management tool.

Enter the POM (Project Object Model). The pom.xml file is your first step to using Maven as a build and dependency management tool.

Create a basic pom.xml file at the root of your project, and make sure your directory conforms to the “convention over configuration” maven directory structure.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

From there, you can take advantage of the fact that every pom.xml automatically inherits all of the behaviour of the standard “super pom”, and bango, without even realizing it you’re using a fully featured build tool. To add a dependency (i.e. the junit 4 api) reference its groupId, artifactId, and version as follows:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.8.2</version>
  </dependency>
</dependencies>

Go ahead and build your project with the command:

mvn install

Notice that the dependency is downloaded, along with any other dependencies that IT in turn depends on, magic! No more DLL Hell for us (well, JAR hell, but you get the picture)! For the interested, this is called transitive dependency management: a->b->c therefore a->c.

Let’s take this one step further. To see a nicely formatted tree of your newly downloaded dependencies, add the maven dependency plugin to your pom.xml as follows:

    <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.4</version>
        </plugin>
    </plugins>
    <sourceDirectory>src/</sourceDirectory>
    </build>

Now try the command:

mvn dependency:tree

You should be looking at a somewhat simpler version of this:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Test MPG 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.4:tree (default-cli) @ test-mpg ---
[INFO] com.acme.myproj:test-proj:jar:1.0.0-SNAPSHOT
[INFO] +- junit:junit:jar:4.8.2:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.3:compile
[INFO] |  +- org.slf4j:slf4j-api:jar:1.6.3:compile
[INFO] |  - log4j:log4j:jar:1.2.16:compile
[INFO] +- org.seleniumhq.selenium:selenium-firefox-driver:jar:2.12.0:compile
[INFO] |  +- org.seleniumhq.selenium:selenium-remote-driver:jar:2.12.0:compile
[INFO] |  |  +- cglib:cglib-nodep:jar:2.1_3:compile
[INFO] |  |  - org.json:json:jar:20080701:compile
[INFO] |  - commons-io:commons-io:jar:2.0.1:compile
[INFO] +- org.seleniumhq.selenium:selenium-htmlunit-driver:jar:2.12.0:compile
[INFO] |  +- org.seleniumhq.selenium:selenium-api:jar:2.12.0:compile
[INFO] |  |  - com.google.guava:guava:jar:10.0.1:compile
[INFO] |  |     - com.google.code.findbugs:jsr305:jar:1.3.9:compile
[INFO] |  +- net.sourceforge.htmlunit:htmlunit:jar:2.9:compile
[INFO] |  |  +- xalan:xalan:jar:2.7.1:compile
[INFO] |  |  |  - xalan:serializer:jar:2.7.1:compile
[INFO] |  |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  |  +- org.apache.httpcomponents:httpmime:jar:4.1.2:compile
[INFO] |  |  +- commons-codec:commons-codec:jar:1.4:compile
[INFO] |  |  +- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.9:compile
[INFO] |  |  +- xerces:xercesImpl:jar:2.9.1:compile
[INFO] |  |  |  - xml-apis:xml-apis:jar:1.3.04:compile
[INFO] |  |  +- net.sourceforge.nekohtml:nekohtml:jar:1.9.15:compile
[INFO] |  |  +- net.sourceforge.cssparser:cssparser:jar:0.9.5:compile
[INFO] |  |  |  - org.w3c.css:sac:jar:1.3:compile
[INFO] |  |  - commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  - org.apache.httpcomponents:httpclient:jar:4.1.2:compile
[INFO] |     - org.apache.httpcomponents:httpcore:jar:4.1.2:compile
[INFO] - org.seleniumhq.selenium:selenium-java:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-android-driver:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-chrome-driver:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-ie-driver:jar:2.12.0:compile
[INFO]    |  - net.java.dev.jna:jna:jar:3.3.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-iphone-driver:jar:2.12.0:compile
[INFO]    - org.seleniumhq.selenium:selenium-support:jar:2.12.0:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.051s
[INFO] Finished at: Wed Jan 25 00:18:37 EST 2012
[INFO] Final Memory: 6M/81M
[INFO] ------------------------------------------------------------------------

Pretty cool right? Check that pom.xml into the repository, and bask in the fact that anyone in your organization can check your project out and build it straight out of the gate. Now say three Hail Mary’s and ten Bless Me Father’s, and swear off eclipse dependency management for good. No seriously, please don’t do it, or it might be you we sacrifice next time instead of the goat ­čśÇ

Take care,
Matt

  Posted by Matt Holtom on January 25, 2012  /  Tags: , ,
January 23, 2012

Source to the World!

Okay, time to really put wordpress through its paces. Let’s try a tad of java with *gasp* syntax highlighting! Here goes! That’s all folks,┬áTake care, Matt

  Show More

Okay, time to really put wordpress through its paces. Let’s try a tad of java with *gasp* syntax highlighting!

Here goes!

//HelloWorld.java
public class HelloWorld{
  public static void main(String args[]){
    System.out.println("Hello World");
  }
}

That’s all folks,┬áTake care,

Matt

  Posted by Matt Holtom on January 23, 2012  /  Tags: