SLF4J Logging in Eclipse Plugins

Developing with Maven and pure Java libraries all the time, I never thought it could be a problem to issue a few log statements when developing an Eclipse plugin. But it looks like in the imaginary of an Eclipse developer everything is always inside the Eclipse environment and nothing is outside the Eclipse universe.

If you search for the above headline using Google, one of the first articles you’ll find is one about the “platform logging facility”. But what about 3rd libraries? They cannot use an Eclipse-based logging framework.

In my libraries I use the SLF4J API and leave it up to the user to decide what logging implementation (Log4J, Logback, JDK) he or she wants to use. And that’s exactly what I want to do in Eclipse. It was hard to figure out exactly how to do it, but here are the pieces of that puzzle.

Phase 1: Development

This describes the steps during the development phase of your custom plugin.

Step 1: Get your libaries into a P2 repository

Everything you want to use in Eclipse has to be installed from a P2 repository. But most of the libaries I use are in a Maven repository. As far as I know there is no such thing as a main P2 repository similar to the “Maven Central,” and all libraries I found in P2 repositories were pretty old. So you have to create one by yourself.

Luckily there is a Maven plugin called p2-maven-plugin that converts all your Maven JARs into a single P2 repository. You can upload the plugin to a folder of your website or simply install it from your local hard drive.

For this example you’ll need the following libraries:

  • org.slf4j:slf4j-api:1.6.6
  • org.slf4j:slf4j-log4j12:1.6.6
  • log4j:log4j:1.2.17
  • org.ops4j.pax.logging:pax-logging-api:1.7.0
  • org.ops4j.pax.logging:pax-logging-service:1.7.0
  • org.ops4j.pax.confman:pax-confman-propsloader:0.2.2

Format “groupId:artifactid:version” is as used for the “p2-maven-plugin.” To skip this step you could also use http://www.fuin.org/p2-repository/.

Step 2: Install the SLF4J API in the Eclipse IDE

  1. Select “Help / Install New Software…”.
    Eclipse / Help / Install
  2. Add the P2 repository URL and install the “slf4j-api”—you could directly use the folder from Step 1 with a file URL like this: “file:/pathtoyour/p2-repository/”.Instal Slf4J API
  3. Add the freshly installed “slf4j.api” to your MANIFEST.MF.Dependencies in MANIFEST.MF
  4. Start using SLF4J logs in your code as usual.

Phase 2: Production

This describes the tasks a user of your custom plugin has to complete to start logging with Log4J. The following assumes that your custom plugin is already installed.

Step 1: Install the log libraries in the Eclipse IDE

  1. Select “Help / Install New Software…”.Eclipse / Help / Install
  2. Install the “Equinox Target Components” from the Eclipse Update Site.Install Equinox Target Components
  3. Add the P2 repository URL and install the following plugins:
    • Apache Log4j
    • OPS4J Pax ConfMan–Properties Loader
    • OPS4J Pax Logging–API
    • OPS4J Pax Logging–Service

    Install Log Libs

Step 2: Configure PAX Logging

  1. Set the location for your log configuration in the “eclipse.ini” as “vmarg"

    -vmargs
    -Xms40m
    -Xmx512m
    -Dbundles.configuration.location=<config-dir>

  2. Create a folder named “services” in the above “config-dir.”
  3. Create Log4J properties named “org.ops4j.pax.logging.properties” in “services.”

    log4j.rootLogger=INFO, FILE
    log4j.appender.FILE=org.apache.log4j.FileAppender
    log4j.appender.FILE.File=<path-to-your-log>/example.log
    log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.FILE.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss,SSS} [%t] %-5p %c %x - %m%n
    log4j.logger.your.package=DEBUG

Step 3: Activate PAX Logging

  1. Open the “Console” view.Show Console View
  2. Select the “Host OSGI Console.”Select OSGI Console
  3. Start the following bundles:
    start org.eclipse.equinox.cm
    start org.ops4j.pax.logging.pax-logging-api
    start org.ops4j.pax.logging.pax-logging-service
    start org.ops4j.pax.configmanager

    Start logging bundles

Now you should be able to see your log statements in the configured “example.log” file.

Step 4: Changing the configuration

If you want to change the configuration in the “org.ops4j.pax.logging.properties”, simply restart the PAX Configmanager in the OSGI console:

stop org.ops4j.pax.configmanager
start org.ops4j.pax.configmanager

Happy Logging!

Maven Tycho uses old JAR files

During the development of SrcMixins4J I faced a strange problem with Tycho: I updated one of the libraries (srcmixins4j-core.jar) without incrementing the version number. I know this is not the standard “-SNAPSHOT” way, but I never had a problem with this before. Maven can handle such updates easily. Inside Eclipse everything was fine, but building with Maven outside Eclipse still referenced the old version and I got compile errors.

It took me some time to figure out that Tycho stores a second copy of the referenced JARs in the Maven repository. There is a folder named “p2/osgi/bundle”.

After deleting the “p2/osgi/bundle/org.fuin.srcmixins4j.core” folder, Tycho finally loaded the new version.

Service-Oriented UI

In large software development projects, service-oriented architecture is very common because it provides a functional interface that can be used by different teams or departments. The same principles should be applied when creating user interfaces.

In the case of a large company that has, among others, a billing department and a customer management department, an organizational chart might look like this:

Large company

If the billing department wants to develop a new dialog for creating invoices, it might look like this:

As you can see, the screen above references a customer in the upper part. Clicking the “..” button right behind the short name text field will open the below dialog that allows the user to select the customer:

After pressing “Select” the customer data is shown in the invoice form.

It’s also possible to select a customer by simply entering a customer number or typing a short name into the text fields on the invoice screen. If a unique short name is entered, no selection dialog appears at all. Instead, the customer data is displayed directly. Only an ambiguous short name results in opening the customer selection screen.

The customer functionality will be provided by developers who belong to the customer management team. A typical approach involves the customer management development team providing some services while the billing department developers create the user interface and call these services.

However, this approach involves a stronger coupling between these two distinct departments than is actually necessary. The invoice only needs a unique ID for referencing the customer data. Developers creating the invoice dialog don’t really want to know how the customer data is queried or what services are used in the background to obtain that information.

The customer management developers should provide the complete part of the UI that displays the customer ID and handles the selection of the customer:

Using JSF 2, this is easy to achieve with composite components. The logical interface between the customer management department and the billing department consists of three parts:

  • Composite component (XHTML)
  • Backing bean for the composite component
  • Listener interface for handling the selection results

Provider (customer management departement)

Composite component:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:composite="http://java.sun.com/jsf/composite"
	xmlns:ice="http://www.icesoft.com/icefaces/component"
	xmlns:ace="http://www.icefaces.org/icefaces/components"
	xmlns:icecore="http://www.icefaces.org/icefaces/core">

<ui:composition>

	<composite:interface name="customerSelectionPanel" displayName="Customer Selection Panel" 
	                     shortDescription="Select a customer using it's number or short name">
		<composite:attribute name="model" type="org.fuin.examples.soui.view.CustomerSelectionBean" required="true" />		
	</composite:interface>

	<composite:implementation>
	
		<ui:param name="model" value="#{cc.attrs.model}"/>
	
		<ice:form id="customerSelectionForm">
			<icecore:singleSubmit submitOnBlur="true" />
			<h:panelGroup id="table" layout="block">

				<table>

					<tr>
						<td><h:outputLabel for="customerNumber"
								value="#{messages.customerNumber}" /></td>
						<td><h:inputText id="customerNumber"
								value="#{model.id}" required="false" /></td>
						<td>&nbsp;</td>
						<td><h:outputLabel for="customerShortName"
								value="#{messages.customerShortName}" /></td>
						<td><h:inputText id="customerShortName"
								value="#{model.shortName}" required="false" /></td>
						<td><h:commandButton action="#{model.select}"
								value="#{messages.select}" /></td>
					</tr>

					<tr>
						<td><h:outputLabel for="customerName"
								value="#{messages.customerName}" /></td>
						<td colspan="5"><h:inputText id="customerName"
								value="#{model.name}" readonly="true" /></td>
					</tr>

				</table>

			</h:panelGroup>
		</ice:form>

	</composite:implementation>

</ui:composition>

</html>

Backing bean for the composite component:

package org.fuin.examples.soui.view;

import java.io.Serializable;

import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang.ObjectUtils;
import org.fuin.examples.soui.model.Customer;
import org.fuin.examples.soui.services.CustomerService;
import org.fuin.examples.soui.services.CustomerShortNameNotUniqueException;
import org.fuin.examples.soui.services.UnknownCustomerException;

@Named
@Dependent
public class CustomerSelectionBean implements Serializable {

	private static final long serialVersionUID = 1L;

	private Long id;

	private String shortName;

	private String name;

	private CustomerSelectionListener listener;

	@Inject
	private CustomerService service;

	public CustomerSelectionBean() {
		super();
		listener = new DefaultCustomerSelectionListener();
	}

	public Long getId() {
		return id;
	}

	public void setId(final Long id) {
		if (ObjectUtils.equals(this.id, id)) {
			return;
		}
		if (id == null) {
			clear();
		} else {
			clear();
			this.id = id;
			try {
				final Customer customer = service.findById(this.id);
				changed(customer);
			} catch (final UnknownCustomerException ex) {
				FacesUtils.addErrorMessage(ex.getMessage());
			}
		}
	}

	public String getShortName() {
		return shortName;
	}

	public void setShortName(final String shortNameX) {
		final String shortName = (shortNameX == "") ? null : shortNameX;
		if (ObjectUtils.equals(this.shortName, shortName)) {
			return;
		}
		if (shortName == null) {
			clear();
		} else {
			if (this.id != null) {
				clear();
			}
			this.shortName = shortName;
			try {
				final Customer customer = service
						.findByShortName(this.shortName);
				changed(customer);
			} catch (final CustomerShortNameNotUniqueException ex) {
				select();
			} catch (final UnknownCustomerException ex) {
				FacesUtils.addErrorMessage(ex.getMessage());
			}
		}
	}

	public String getName() {
		return name;
	}

	public CustomerSelectionListener getConnector() {
		return listener;
	}

	public void select() {
		// TODO Implement...
	}

	public void clear() {
		changed(null);
	}

	private void changed(final Customer customer) {
		if (customer == null) {
			this.id = null;
			this.shortName = null;
			this.name = null;
			listener.customerChanged(null, null);
		} else {
			this.id = customer.getId();
			this.shortName = customer.getShortName();
			this.name = customer.getName();
			listener.customerChanged(this.id, this.name);
		}
	}

	public void setListener(final CustomerSelectionListener listener) {
		if (listener == null) {
			this.listener = new DefaultCustomerSelectionListener();
		} else {
			this.listener = listener;
		}
	}

	public void setCustomerId(final Long id) throws UnknownCustomerException {
		clear();
		if (id != null) {
			clear();
			this.id = id;
			changed(service.findById(this.id));
		}
	}

	private static final class DefaultCustomerSelectionListener implements
			CustomerSelectionListener {

		@Override
		public final void customerChanged(final Long id, final String name) {
			// Do nothing...
		}

	}

}

Listener interface for handling results:

package org.fuin.examples.soui.view;

/**
 * Gets informed if customer selection changed.
 */
public interface CustomerSelectionListener {

	/**
	 * Customer selection changed.
	 *
	 * @param id New unique customer identifier - May be NULL.
	 * @param name New customer name - May be NULL.
	 */
	public void customerChanged(Long id, String name);

}

User (billing departement)

The invoice bean simply uses the customer selection bean by injecting it, and connects to it using the listener interface:

package org.fuin.examples.soui.view;

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.New;
import javax.inject.Inject;
import javax.inject.Named;

@Named("invoiceBean")
@SessionScoped
public class InvoiceBean implements Serializable {

	private static final long serialVersionUID = 1L;

	@Inject @New
	private CustomerSelectionBean customerSelectionBean;

	private Long customerId;

	private String customerName;

	@PostConstruct
	public void init() {
		customerSelectionBean.setListener(new CustomerSelectionListener() {
			@Override
			public final void customerChanged(final Long id, final String name) {
				customerId = id;
				customerName = name;
			}
		});
	}

	public CustomerSelectionBean getCustomerSelectionBean() {
		return customerSelectionBean;
	}

	public String getCustomerName() {
		return customerName;
	}

}

Finally, in the invoice XHTML, the composite component is used and linked to the injected backing bean:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:fuin="http://fuin.org/examples/soui/facelets"
      xmlns:customer="http://java.sun.com/jsf/composite/customer">

    <ui:composition template="/WEB-INF/templates/template.xhtml">
        
        <ui:param name="title" value="#{messages.invoiceTitle}" />
    
        <ui:define name="header"></ui:define>
    
        <ui:define name="content">
        	<customer:selection-panel model="#{invoiceBean.customerSelectionBean}" />
        </ui:define>

        <ui:define name="footer"></ui:define>

    </ui:composition>
    
</html>

Summary
In conclusion, parts of the user interface that reference data from other departments should be the responsibility of the department that delivers the data. Any changes in the providing code can then be easily made without any changes to the using code. Another important benefit of this method is the harmonization of the application’s user interface. Controls and panels that display the same data always look the same. Every department can also create a repository of its provided user interface components, making the process of designing a new dialog as easy as putting the right components together.

Interfaces with default implementation – Mixins with AspectJ

Some weeks ago, I implemented several classes for the CQRS Meta Model. I found myself repeating the same code over and over again, as it was not possible to extend the same base class.

Damn… I wished Java had Mixins! After a short look a Qi4j, which seems a bit too heavy for my little use case, I remembered AspectJ’s Inter-type declarations. You can declare members (fields, methods, and constructors) that are owned by other types!

So here we go – Let’s define some behavior:

/**
 * Something that has a comment.
 */
public interface Commentable {

    public void setComment(String comment);

    public String getComment();

}

Now we’re going to create an aspect that provides the default implementation for the above interface:

/**
 * Implements the behavior of an object that has a comment assigned.
 */
public aspect CommentableAspect {

    private String Commentable.comment;

    public final String Commentable.getComment() {
        return this.comment;
    }
    
    public final void Commentable.setComment(final String comment) {
        this.comment = comment;
    }
}

All you have to do now is to implement the interface:

/**
 * Class with a comment field. 
 */
public class TestClass implements Commentable {

    // All methods are already implemented by 
    // simply adding the interface!
    
}

That’s it! All the necessary fields and methods are now added by AspectJ. You can now concentrate on composing behavior instead of thinking about a hierarchy of subclasses.

If you’d like to, it’s also possible to override the provided default methods (Caution: You’d have to remove the “final” from the aspect!) . I personally prefer the Design for Extension principle, and my methods are always abstract, final, or have an empty implementation.

Combining Strong Typing and Bean Validation (JSR 303)

Sometimes, it’s nice to use strong typing instead of repeating the same checks all over the layers and tiers. The interesting thing is that making a class robust against misuse is very similar to using Java Bean Validation.

A classical approach may look like this:

public class User {

    private static final Pattern PATTERN = Pattern.compile("[a-z][0-9a-z_\\-]*");

    private String name;

    public User(String name) {
        super();
        if (name == null) {
            throw new IllegalArgumentException("name == null");
        }
        String trimmed = name.trim().toLowerCase();
        if (trimmed.length() == 0) {
            throw new IllegalArgumentException("length name == 0");
        }
        if (trimmed.length() < 3) {
            throw new IllegalArgumentException("length name < 3");
        }
        if (trimmed.length() > 20) {
            throw new IllegalArgumentException("length name > 20");
        }
        if (!PATTERN.matcher(trimmed).matches()) {
            throw new IllegalArgumentException("name pattern violated");
        }
        this.name = trimmed;
    }

}

Using Bean Validation, we could create a custom constraint instead:

@Size(min = 3, max = 20)
@Pattern(regexp = "[a-z][0-9a-z_\\-]*")
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface UserName {

    String message() default "{org.fuin.blog.UserName.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

The User class now looks much better:

public class User {

    @NotNull
    @UserName
    private String name;

    public User(String name) {
        super();
        this.name = name;
    }

}

But now, the object has lost the ability to protect itself against misuse. It’s no longer a robust object. Maybe someone uses a validator to check if the object is valid; maybe not. In any case, it’s always possible to create invalid objects of this kind.

How about combining both techniques?

Let’s rename the UserName annotation into UserNameStr because it actually works on a string, and this way, we can also avoid a name clash with a new strong type we will create soon:

@Size(min = 3, max = 20)
@Pattern(regexp = "[a-z][0-9a-z_\\-]*")
@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface UserNameStr {

    String message() default "{org.fuin.blog.UserNameStr.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

Next, we create a base type for all such String based on strong typing:

public abstract class AbstractStringBasedType<T extends AbstractStringBasedType<T>> implements Comparable<T>, Serializable {

    private static final long serialVersionUID = 0L;

    private static final Validator VALIDATOR;

    static {
        VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
    }

    public final int hashCode() {
        return nullSafeToString().hashCode();
    }

    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final T other = (T) obj;
        return nullSafeToString().equals(other.nullSafeToString());
    }

    public final int compareTo(final T other) {
        return this.nullSafeToString().compareTo(other.nullSafeToString());
    }

    public final int length() {
        return nullSafeToString().length();
    }

    protected final void requireValid(final T value) {
        final Set<ConstraintViolation<T>> constraintViolations = VALIDATOR.validate(value);
        if (constraintViolations.size() > 0) {
            final StringBuffer sb = new StringBuffer();
            for (final ConstraintViolation<T> constraintViolation : constraintViolations) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("[" + constraintViolation.getPropertyPath() + "] "
                        + constraintViolation.getMessage() + " {"
                        + constraintViolation.getInvalidValue() + "}");
            }
            throw new IllegalArgumentException(sb.toString());
        }
    }

    private String nullSafeToString() {
        final String str = toString();
        if (str == null) {
            return "null";
        }
        return str;
    }

    public abstract String toString();

}

The refactored UserName class now uses the Bean Validation API to perform a constraint check at the end of the constructor, which means one is no longer able to create invalid objects:

public final class UserName extends AbstractStringBasedType<UserName> {

    private static final long serialVersionUID = 0L;

    @NotNull
    @UserNameStr
    private final String userName;

    public UserName(final String userName) {
        super();
        this.userName = userName;

        // Always the last line in the constructor!
        requireValid(this);
    }

    public String toString() {
        return userName;
    }

}

The refactored User class is now even simpler and contains only a @NotNull annotation on the name property:

public class User {

    // Only null check here, because all other
    // checks are done by user name itself
    @NotNull
    private UserName name;

    public User(UserName name) {
        super();
        this.name = name;
    }

}

Here is a simple example using the UserName type:

public class Example {

    public static void main(String[] args) {

        Locale.setDefault(Locale.ENGLISH);

        try {
            new UserName(null);
        } catch (IllegalArgumentException ex) {
            // [userName] may not be null {null}
        }

        try {
            new UserName("");
        } catch (IllegalArgumentException ex) {
            // [userName] must match "[a-z][0-9a-z_\-]*" {},
            // [userName] size must be between 3 and 20 {}
        }

        try {
            new UserName("_a1");
        } catch (IllegalArgumentException ex) {
            // [userName] must match "[a-z][0-9a-z_\-]*" {_a1}
        }

        // Valid name
        System.out.println(new UserName("john-2_a"));

    }

}

If we don’t want to use the strong typing, it’s easy to use only annotations. This ability is helpful especially where you have to deal with non-Java clients. In these situations, a DTO may contain only a simple annotated String:

public class UserDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @UserNameStr
    private String name;

    public UserDTO(String name) {
        super();
        this.name = name;
    }

}

Now strong types and Bean Validation can live in peaceful coexistence within your application. It’s a good idea to check if your GUI controls support such JS303 enhanced strong types before you start using it. Otherwise, you may lose the ability to validate upfront on the client (e.g. checking input length based on the @Size annotation).