Thursday, November 26, 2015

Perl Data::Dumper for Java

In Perl, if you want to display the contents of a data structure in a human readable format you can use the great Data::Dumper module. In PHP there is the var_dump() function. But what to do in Java? You can use the XStream library:

"XStream uses reflection to discover the structure of the object graph to serialize at run time, and doesn't require modifications to objects. It can serialize internal fields, including private and final, and supports non-public and inner classes." - Wikipedia

If you use Maven to manage your depedencies just add the following artifact to you POM:

<dependency>
    <groupid>com.thoughtworks.xstream</groupid>
    <artifactid>xstream</artifactid>
    <version>1.4.8</version>
</dependency>

The XStream library can be used as follows to print the contents of an arbitrary data structure nicely formatted to the console.

XStream dumper = new XStream(new JsonHierarchicalStreamDriver());
System.out.println(dumper.toXML(lockMap));

The output:

{"map": [
  [
    {
      "longitude": 110649,
      "latitute": 107551,
      "outer-class": {}
    },
    [
      {
        "unixTimestamp": 1448570378 ,
        "temperature": 10.87,
        "outer-class": {
          "@reference": "../../../com.example.CoordinateRepository$CoordinateKey/outer-class"
        }
      },
      {
        "unixTimestamp": 1448570409,
        "temperature": 10.89,
        "outer-class": {
          "@reference": "../../../com.example.CoordinateRepository$CoordinateKey/outer-class"
        }
      }
    ]
  ]
]}

If you know any other libraries, approaches or solutions for this problem just let me know by dropping a comment below or sending me an email. I will update this post accordingly.

Monday, November 23, 2015

Logback and conditional processing of configuration files in an OSGi environment

Problem

As stated in the Logback documentation conditional processing in configuration files requires the Janino library. When running in an OSGi environment (e.g. an Eclipse RCP application) simply placing commons-compiler.jar and janino.jar on your application's class path is not sufficient enough. You will get the following exception:

<...>
12:50:47,754 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
12:50:47,755 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [stdout] to Logger[ROOT]
12:50:47,755 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [file] to Logger[ROOT]
12:50:47,810 |-ERROR in ch.qos.logback.core.joran.conditional.IfAction - 
  Failed to parse condition [property("xxx.instance").equals("SIM_LOCAL")] org.codehaus.janino.JaninoRuntimeException:
  Cannot load class 'ch.qos.logback.core.joran.conditional.PropertyWrapperForScripts' through the parent loader
  at org.codehaus.janino.JaninoRuntimeException
<...>

This exception occurs because in an OSGi environment the class loader of the org.codehaus.janino bundle can not load classes from the ch.qos.logback.core bundle. See here and here for a primer on OSGi class loading.

Solution

The org.codehaus.janino bundle would need a "Require-Bundle: ch.qos.logback.core" directive in it's manifest file in order for the bundle class loader to be able to load classes from the ch.qos.logback.core bundle. This can be achieved by extending the org.codehaus.janino bundle via a fragment (e.g. ch.qos.logback.core.janino.fragment). Below is the manifest of the fragment:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Janino Fragment (needed to uses Janino together with logback
in an OSGi environment)
Bundle-SymbolicName: ch.qos.logback.core.janino.fragment
Bundle-Version: 1.0.0
Fragment-Host: org.cohehaus.janino
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: ch.qos.logback.core
Bundle-Vendor: Danube Development

The bundle name org.cohehaus.janino in the bundle fragment directive is a typo in the upstream project. I am currently evaluating if it's possible to push such a fragment to the logback upstream project.

Using Git hooks together with JGit / Egit

Git hooks currently supported by JGit / EGit

Git currently supports 13 client-side hooks. Not all of them are supported by JGit / EGit. The following list outlines which client-side hooks have already been implemented in JGit: 
  • pre-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • post-rewrite
  • applypatch-msg
  • pre-applypatch
  • post-applypatch
  • pre-rebase
  • pre-push
  • post-checkout
  • post-merge
  • pre-auto-gc
As of JGit v4.2.0 support for hooks is only enabled when the JGit file system layer detects a POSIX or a Win32 + Cygwin file system.

Necessary prerequisites to use Git hooks when running under Windows

In order for the Git hooks to be called by JGit when running under Windows you have to do two things:
  1. Install Cygwin (do not forget to install the Git package)
  2. Add the Cygwin bin folder (e.g. cygwin64\system\bin) to the Windows PATH environment variable.
The next section outlines how the JGit file system layer actually checks on which file system it is running on. So in case if you have any problems when using with JGit and Git hooks I recommend reading on. 

Details about the JGit Filesystem layer

The JGit file system class hierachy is as follows:


            +----------------------+
            |abstract class        |
            |FS                    |
            +----------------------+
            |                      |
            |runHookIfPresent()    |
            |does not support hooks|
            +-----+------------+---+
                  ^            ^
                  |            |
                  |            |
                  |            |
+-----------------+----+    +--+---------------+
|class                 |    |class             |
|FS_Win32              |    |FS_POSIX          |
+----------------------+    +------------------+
|                      |    |                  |
|runHookIfPresent()    |    |runHookIfPresent()|
|does not support hooks|    |does support hooks|
+-----------------+----+    +------------------+
                  ^
                  |
                  +------+
                         |
               +---------+--------+
               |class             |
               |FS_Win32_Cygwin   |
               +------------------+
               |                  |
               |runHookIfPresent()|
               |does support hooks|
               +------------------+


The default implementation of runHookIfPresent(...) in FS does not support running hooks:

public ProcessResult runHookIfPresent(Repository repository,
        final String hookName,
        String[] args) throws JGitInternalException {
    return runHookIfPresent(repository, hookName, args, System.out, System.err,
            null);
}

And how does the FS layer decide if it's running on a Win32 or FS_Win32 + Cygwin file system? The presece of a Cygwin installation is determined by the isCygwin() function in the FS_Win32_Cygwin class:

/**
 * @return true if cygwin is found
 */
public static boolean isCygwin() {
    final String path = AccessController
            .doPrivileged(new PrivilegedAction() {
                public String run() {
                    return System.getProperty("java.library.path"); //$NON-NLS-1$
                }
            });
    if (path == null)
        return false;
    File found = FS.searchPath(path, "cygpath.exe"); //$NON-NLS-1$
    if (found != null)
        cygpath = found.getPath();
    return cygpath != null;
}

It simply checks if the Windows PATH variable contains the file cygpath.exe. (Reminder: The java.library.path is pre-populated with the PATH entries when running under Windows.)