Unit testing default methods in interfaces

Java 8 has introduced a nice feature, default (so-called “defender”) methods in interfaces. These methods are implemented in the interface, but can be overridden.

Now how can we unit-test these default methods?

A naive approach would be: in your unit test class, just implement the interface and test the default method on that interface. Something like this:

public interface IntProvider{
    int getInt();
    default int getIntTimesTwo(){
        return getInt() * 2;
    }
}
@Test
public void testGetIntTimesTwo(){
 final int n = 4;
 IntProvider ip = () -> n;
 assertThat(ip.getIntTimesTwo(), is(n * 2));
}

This example is relatively pain-free, especially since the interface is functional and hence can be expressed as a lambda. If things are that simple, count your blessings and stop reading.

One problem I do see, however, is that introducing a new non-default method to the interface will break this test, in fact the test won’t even compile. I would argue that this is a sign of tight coupling, and hence bad software design.

One suggestion would be to use Mockito to mock the interface. It’s a great suggestion, and of course the first one I thought of. And in Mockito 2.x or higher, you can do it:

@Test
public void testGetIntTimesTwoWithMockito(){
    final int n = 4;
    IntProvider ip = Mockito.mock(IntProvider.class);
    Mockito.when(ip.getInt()).thenReturn(n);
    Mockito.when(ip.getIntTimesTwo()).thenCallRealMethod();
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

Nice!

Now my problem is: unfortunately I have to use Mockito 1.x, which doesn’t have this functionality. So another solution is to roll your own version. To this effect, we’ll look at JDK dynamic proxies. Since Java 1.5, you can instantiate an interface with an InvocationHandler, and Java 8 makes this look (relatively) painless:

@Test @Ignore // running this may crash your IDE
public void testGetIntTimesTwoWithProxies_thisDoesntWork() {
    final int n = 4;
    IntProvider ip = (IntProvider) Proxy.newProxyInstance(
            IntProvider.class.getClassLoader(),
            new Class<?>[]{IntProvider.class},
            (proxy, method, args) -> { // lambda InvocationHandler
                if (method.getName().equals("getInt")) {
                    return n;
                } else {
                    return method.invoke(proxy, args);
                }
            }
    );
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

Wouldn’t it be great if that worked? But it doesn’t. It gives you a StackOverflowError, as the else block will keep executing the method on the proxy and never get at the actual default method. Also, the InvocationHandler needs to take care needs to take care of the standard boilerplate methods as well, or you will get some very weird surprises when calling equals(), hashCode() or toString(). To that end, Guava provides a convenience class called AbstractInvocationHandler that handles the standard methods with sane defaults. Unfortunately that means we can’t use lambdas:

@Test @Ignore // running this may crash your IDE
public void testGetIntTimesTwoWithProxies_guava_thisStillDoesntWork() {
    final int n = 4;
    IntProvider ip = (IntProvider) Proxy.newProxyInstance(

            IntProvider.class.getClassLoader(),
            new Class<?>[]{IntProvider.class},
            new AbstractInvocationHandler() {
                protected Object handleInvocation(
                    Object proxy, Method method, Object[] args
                ) throws Exception {
                    if (method.getName().equals("getInt")) {
                        return n;
                    } else {
                        return method.invoke(proxy, args);
                    }
                }
            }
    );
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

So now we have working equals and hashCode, but still no default methods.

To get these to work, we have to use a MethodHandle, which we can obtain as follows:

return MethodHandles.lookup()
    .in(declaringClass)
    .unreflectSpecial(method, declaringClass)
    .bindTo(proxy)
    .invokeWithArguments(args);

[Source]

So we can rewrite our test like this:

@Test
public void testGetIntTimesTwoWithMethodHandle_thisSometimesWorks() {
    final int n = 4;
    IntProvider ip = (IntProvider) Proxy.newProxyInstance(
            IntProvider.class.getClassLoader(),
            new Class<?>[]{IntProvider.class},
            new AbstractInvocationHandler() {
                protected Object handleInvocation(
                        Object proxy, Method m, Object[] args
                ) throws Throwable {
                    if (m.isDefault()) {
                        Class<?> dc = m.getDeclaringClass();
                        return MethodHandles.lookup()
                                            .in(dc)
                                            .unreflectSpecial(m, dc)
                                            .bindTo(proxy)
                                            .invokeWithArguments(args);
                    } else if (m.getName().equals("getInt")) {
                        return n;
                    } else {
                        throw new UnsupportedOperationException();
                    }
                }
            }
    );
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

It looks to me like this should work, and frankly I don’t understand why it doesn’t, but it in some situations it doesn’t, because the MethodHandle thinks the method access level is wrong, and Method.setAccessible(true) also doesn’t help there.

Fortunately I came across this blog post by ZeroTurnaround, which provided the solution. The MethodHandles class actually doesn’t provide a factory method with the required access levels, so we need to invoke a constructor using reflection:

@Test
public void testGetIntTimesTwoWithMethodHandleWithReflection(){
    final int n = 4;
    IntProvider ip = (IntProvider) Proxy.newProxyInstance(
            IntProvider.class.getClassLoader(),
            new Class<?>[]{IntProvider.class},
            new AbstractInvocationHandler(){
                protected Object handleInvocation(
                        Object proxy, Method m, Object[] args
                ) throws Throwable {
                    if(m.isDefault()){
                        Class<?> dc = m.getDeclaringClass();
                        Constructor<Lookup> c =
                          Lookup.class // MethodHandles.Lookup
                            .getDeclaredConstructor(Class.class, int.class);
                        c.setAccessible(true);
                        return c.newInstance(dc, Lookup.PRIVATE)
                                .unreflectSpecial(m, dc)
                                .bindTo(proxy)
                                .invokeWithArguments(args);

                    }
                    else if(m.getName().equals("getInt")) return n;
                    else throw new UnsupportedOperationException();
                }
            }
    );
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

This version does work, but it’s an awful test. Waaaay to technical and involved. So let’s abstract the technical stuff away to a library method:

public final class ProxyHelper {


    /**
     * Create a proxy of the supplied interface, using the supplied
     * {@link InvocationHandler}, which will be wrapped
     * inside a {@link DefaultRespectingInvocationHandler}.
     * In effect this means that standard methods like equals /
     * hashcode / toString will be handled with sane defaults and
     * {@code default} methods will be called directly.
     * All other methods will be handled by the supplied
     * {@link InvocationHandler}.
     */
    public static <T> T mockNonDefaultMethods(
      Class<T> type, InvocationHandler h) {
        requireNonNull(type, "Type required");
        checkArgument(type.isInterface(),
          "Expected interface type but got %s", type.getName());
        requireNonNull(h, "Handler required");

        @SuppressWarnings("unchecked")
        T proxy = (T) newProxyInstance(
                          type.getClassLoader(),
                          new Class<?>[]{type},
                          wrapWithDefaults(h)
                  );

        return proxy;
    }

    private static InvocationHandler wrapWithDefaults(
      InvocationHandler inner) {

            return new DefaultRespectingInvocationHandler(inner);
    }


    private static class DefaultRespectingInvocationHandler
                 extends AbstractInvocationHandler {

        private final InvocationHandler inner;

        private DefaultRespectingInvocationHandler(
          InvocationHandler inner) {
            this.inner = inner;
        }

        @Override
        protected final Object handleInvocation(
          Object proxy, Method m, Object[] args) throws Throwable {
            if (m.isDefault()) {
                final Class<?> dc = m.getDeclaringClass();
                Constructor<Lookup> c = 
                    Lookup.class
                          .getDeclaredConstructor(
                                 Class.class, int.class);
                c.setAccessible(true);
                return c.newInstance(dc, PRIVATE)
                        .unreflectSpecial(m, dc)
                        .bindTo(proxy)
                        .invokeWithArguments(args);
            }
            return inner.invoke(proxy, m, args);
        }

    }
}

We’re using the decorator pattern here, wrapping the supplied InvocationHandler with one that takes care of both default and boilerplate methods, leaving only the “real” logic to the supplied handler.

And now we can rewrite our test in this significantly cleaner way:

@Test
public void testGetIntTimesTwoWithFactoryMethod() {
    final int n = 4;
    IntProvider ip = ProxyHelper.mockNonDefaultMethods(
      IntProvider.class, (proxy, method, args) -> {
        if (method.getName().equals("getInt")) {
            return n;
        }
        throw new UnsupportedOperationException();
    });
    assertThat(ip.getIntTimesTwo(), is(n * 2));
}

Obviously, all of this only makes sense if you have a large code base with several default methods you need to test like this. And also obviously, the Mockito way above is less painful!

Posted in Uncategorized | Leave a comment

How to compare types in Java

Here’s an overview of the different methods to identify a type or Object in Java.

There are three basic cases

  1. I want to detect if two variables point to Objects of the same type
  2. I want to detect if two types are compatible
  3. I want to detect if a given variable points to an Object of a given type

Check if two variables point to Objects of the same type

The first way is both the most useless and the most cumbersome, so lets’s get it over with fast:

boolean sameType = ob1.getClass() == obj2.getClass();

That was easy enough, but it gets messy as soon as one of the two may be null:

boolean sameType = (obj1 == obj2 || 
                     (obj1 != null
                      && obj2 != null
                      && obj1.getClass() == obj2.getClass())
                    );

Check if two types are compatible

In the following, I will be using the word type as a synonym for Class objects, which can actually be classes, interfaces, enums and annotations. In this section I’ll ignore null. If you can’t even get your types null-safe, you’re screwed anyway.

If you need two types to be identical, just compare them for identity. According to the Java Language Specification, reference types with equal declarations are actually the same Object

boolean sameType = type1 == type2;

If you need type1 to be a subtype of type2 (where subtype means any kind of extends or implements relation

boolean sameType = type2.isAssignableFrom(type1);
// Examples:
assert List.class.isAssignableFrom(ArrayList.class); // Class ArrayList implements Interface List
assert Collection.class.isAssignableFrom(List.class); // Interface List extends Interface Collection
assert AbstractList.class.isAssignableFrom(ArrayList.class); // Class ArrayList extends
                                                             // Class AbstractList

Check if a variable is of a given type

That’s easiest if you know the type at Compile time

boolean typeOrSubType = obj instanceof SomeType

There are two nice side effects of this check:

  1. it includes an implicit null check, there’s no need to separately check for null
  2. it generates a compile-time error if the types are not related:
String obj = // some code that may or may not initialize the String
boolean typeOrSubType = obj instanceof Integer // this generates a compile error, as the compiler
                                               // knows this can never be true

If you only know the type at runtime, the compiler can’t help you with static analysis, but you’re not completely lost.

Class<?> type = // get the type from somewhere. again, we'll assume the type is not null
boolean typeOrSubType = type.isInstance(obj);

Again, Class.isInstance(Object) gives you an implicit null check.

The above methods check whether the object is an instance of the type or a subtype. If you need to check for the exact type (without subtypes), you can use this code

boolean isExactType = obj != null && obj.getClass() == type;

The distinction between a) exact type and b) type or subtype is mostly important when it comes to overriding the equals(Object) method, depending on your interpretation of the equals() contract. This is discussed in Effective Java Item 8: Obey the general contract when overriding equals and has been discussed various times on StackOverflow, most broadly in the thread Overriding equals and hashCode in Java.

Special types

There are some special methods for some special types of types (pun intended).

Here’s how to detect enums:

enum Duck{
  HUEY, DEWEY, LOUIE;
}
assert Duck.class.isEnum(); // detect that a type is an enum
assert Enum.class.isAssignableFrom(Duck.class); // equivalent, but more cumbersome
Object obj = Duck.HUEY; // << you usually don't want to do this, but we need it for the next line
assert obj instanceof Enum;

Here’s how to detect arrays:

assert String[].class.isArray(); // works for Object arrays,
assert int[].class.isArray();    // for primitive arrays
assert int[][].class.isArray();  // and for multidimensional arrays

Note that you can only identify arrays via their types. There’s no legal instanceof check, so for checking a variable you need to check it for null first:

boolean isArray = obj != null && obj.getClass().isArray()

There’s also the Class.getComponentType() method for detecting an Array’s type. You can use that to detect a multidimensional array:

Class<?> componentType = type.getComponentType();
boolean isMultiDimensionalArray = componentType != null && componentType.isArray();

As you can see, this is rather ugly. Actually, code related to arrays is almost always ugly in Java, which is one of the reasons most people prefer to use Collections over arrays.

Here’s how to detect primitive types:

assert int.class.isPrimitive();

Note that there is no legal way to check the type of a primitive variable, as the type of primitive variables is always known to be the variable type. While an Object can actually be a String, and int can never be a long. Both of these generate compiler errors:

assert 1 instanceof int;
assert 1 instanceof Integer;

There’s also no easy way to detect if an Object is of one of the primitive wrapper types. If you have access to the Guava library, you can use Primitives.isWrapperType(Class) for this, though.

Posted in Uncategorized | Tagged , , | 2 Comments

Two Guava Collections

Today I wrote two different Guava Collections as answer to two different StackOverflow Questions:

Java data Structure and
Comparator and equals()

The first requested a Map where the keys are Enums and the values are sorted collections.

In my answer, I implemented a Guava MultiMap that does just that. It uses EnumMap internally if it knows the enum’s type and a HashMap or a user-supplied map otherwise (each of these three Constructors has a sibling with a custom comparator that sorts the values).

The second was apparently based on the first and dealt with the problem that the OP would like to use a SortedSet, but the business objects did not have a valid compareTo() / equals() relation, so I suggested that a sorted List would be better. I implemented such a List using Guava’s ForwardingList as base and redirecting the logic to an internal ArrayList.

Posted in Guava, Java | Leave a comment

Add system properties through spring XML configuration.

If you want to set system properties through XML configuration, here is a solution (inspired by this question):

Use two MethodInvokingFactorybeans, one to statically access System.getProperties() and one to call properties.putAll(map) on these properties. Here’s the configuration:

<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property
        name="targetObject">
        <!-- System.getProperties() -->
        <bean
            class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="java.lang.System" />
            <property name="targetMethod" value="getProperties" />
        </bean>
    </property>
    <property
        name="targetMethod"
        value="putAll" />
    <property
        name="arguments">
        <!-- The new Properties -->
        <util:properties>
            <prop
                key="my.key">myvalue</prop>
            <prop
                key="my.key2">myvalue2</prop>
            <prop
                key="my.key3">myvalue3</prop>

        </util:properties>
    </property>
</bean>

Of course I am not saying this is a best practice (far from it). But it’s probably the easiest way to add system properties using XML only.

Posted in Java, Spring | 2 Comments

Wicket behavior to guard changed form.

Triggered by the StackOverflow Question Wicket : Notify if page model has been changed., here is a wicket behavior that you can attach to a form and that will stop the user from leaving the site once he has changed some values of the form.

Here is the Javascript file (it requires that either Prototype or MooTools are available):


// initialize namespace
if(!window.wicket)window.wicket={};
if(!wicket.behaviors)wicket.behaviors={};
if(!wicket.behaviors.guardform)wicket.behaviors.guardform={};

// check whether prototype or mootools are installed
wicket.behaviors.guardform.prototypeinstalled = window.Prototype && window.Prototype.version;
wicket.behaviors.guardform.mootoolsinstalled = window.MooTools && window.MooTools.version;
if(wicket.behaviors.guardform.mootoolsinstalled && !window.Selectors){
	throw "MooTools must contain the Selectors api to use this behavior";
}

wicket.behaviors.guardform.dirty = false;

// default prompt, can be adjusted from the wicket behavior
wicket.behaviors.guardform.prompt = "You have changed values in the form.\n"
    + "Do you really want to edit and lose your changes?";

wicket.behaviors.guardform.attach=function(elem,event,handler){
	if(wicket.behaviors.guardform.mootoolsinstalled){
		elem.addEvent(event,handler);
	}else if(wicket.behaviors.guardform.prototypeinstalled){
		elem.observe(event,handler);
	}
	throw "Prototype or MooTools must be present to use this behavior";
}

wicket.behaviors.guardform.select=function(elem,args){
	if(wicket.behaviors.guardform.mootoolsinstalled){
		return elem.getElements(args);
	}else if(wicket.behaviors.guardform.prototypeinstalled){
		return elem.select(args);
	throw "Prototype or MooTools must be present to use this behavior";
}

wicket.behaviors.guardform.parent=function(elem,selector){
	if(wicket.behaviors.guardform.mootoolsinstalled){
		return elem.getParent(args);
	}else if(wicket.behaviors.guardform.prototypeinstalled){
		return elem.up(args);
	throw "Prototype or MooTools must be present to use this behavior";
}

wicket.behaviors.guardform.init=function(id){

	var obj = $(id);
	if(obj){
		var first = true;

		// attach onchange behavior for all child text components
		wicket.behaviors.guardform.select(
				obj,
				"input[type=text], " // text fields
				"textarea, " // text areas
				"select" // select controls
		).each(function(item){
			if(first){
				first=false;
			}
			wicket.behaviors.guardform.attach(item, "change", function(){
				wicket.behaviors.guardform.dirty = true;
			});
		});

		// attach window unload handler that will fire a prompt if form is dirty
		wicket.behaviors.guardform.attach(window, "beforeunload", function(){
			return (!wicket.behaviors.guardform.dirty ||
                                confirm(wicket.behaviors.guardform.prompt);
		});

		// now attach an 'undirty' function to the form
		wicket.behaviors.guardform.attach(
			obj,
			'submit',
			function(){
				wicket.behaviors.guardform.dirty = false;
			}
		);

	}

}

Then you need the actual wicket behavior, that you attach to the wicket form component (This version can only be attached to root forms, not nested forms, as they are internally remodeled to <div> tags). Here’s the Behavior class:

public class GuardDirtyFormBehavior extends AbstractBehavior{

    private Component component;

    private IModel promptModel = null;

    public GuardDirtyFormBehavior setPromptModel(final IModel promptModel){
        this.promptModel = promptModel;
        return this;
    }

    @Override
    public void bind(final Component component){
        if(!(component instanceof Form)){
            throw new WicketRuntimeException("Behavior must be attached to a form");
        }
        // if the model needs component wrapping, wrap it here
        if(promptModel instanceof IComponentAssignedModel<?>){
            promptModel=((IComponentAssignedModel<String>)promptModel).wrapOnAssignment(component);
        }
        this.component = component.setOutputMarkupId(true);
    }

    @Override
    public void onComponentTag(final Component component, final ComponentTag tag){
        if(!"form".equalsIgnoreCase(tag.getName())){
            throw new WicketRuntimeException("Behavior must be attached to a root form, not a nested form.");
        }
    }

    @Override
    public void renderHead(final IHeaderResponse response){
        response.renderJavascriptReference(new JavascriptResourceReference("GuardDirtyFormBehavior.js"));
        if(this.promptModel != null){
            final String prompt = this.promptModel.getObject();
            response.renderJavascript("wicket.behaviors.guardform.prompt='"
                + prompt + "';", "wicket.behaviors.guardform.prompt");
        }
        response.renderOnDomReadyJavascript("wicket.behaviors.guardform.init('"
            + this.component.getMarkupId() + "');");
    }

}

You can assign it like this:

public class Mypage extends WebPage{
    public Mypage(){
        add(
            new Form<Object>("form")
            .add(
                new TextField<String>("textField1"),
                new TextField<String>("textField2"),
                new TextField<String>("textField3"),
                new TextField<String>("textField4")
            )
            .add(new GuardDirtyFormBehavior())
        );
     }
}

You can also change the javascript prompt message using the wicket i18n mechanism by using setPromptModel together with a ResourceModel:

.add(new GuardDirtyFormBehavior()
    .setPromptModel(new ResourceModel("form.dirty.prompt"))
)

This will use the wicket resource loading mechanism to look up a localized version of the resource key ‘form.dirty.prompt’.

Posted in Java, Wicket | Leave a comment

Compile .less files to .css in maven (again using GMaven)

I have recently come across LessCSS, a CSS meta-language that introduces code re-usability. I have been looking for a solution like this for some years now, as I hate having to write the same stuff over and over again. So I immediately created some .less files in a current web project of mine, but when it came to automatically compiling them, things turned out to be less easy.

LessCss was originally written in ruby, so my first approach was to embed jruby in maven and do the compiling like that. However, JRuby is huge and it takes ages to start up, and also there are issues about file paths to the jruby.jar that are not allowed to contain spaces etc. All in all it was just a pain.

Then I had the idea to do the processing at runtime, not compile time, using a PackageResource in wicket. While doing some research for this I came across this blog post, where the author had done exactly this. However, I still believe this kind of pre-processing should be done at compile time, so I was very happy to see that the author uses a slightly different approach using the re-written javascript version of LessCss embedded in Mozilla Rhino. After some hacking on my part this solution works perfectly for me using GMaven. Basically I rewrote the inner workings of the visural less processing in groovy, so my language stack is this:

Java (Maven) ==> Groovy (GMaven) ==> JavaScript (Rhino)

So here is the groovy script that does the work:

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ScriptableObject;
import org.slf4j.Logger;

public class LessCssJsCompiler {

    private final String lessjs =
        new File("src/main/javascript/less.js").text;
    private final ContextFactory cf;
    private final Context c;
    private final ScriptableObject so;
    private final Logger log

    private final String runjs = """\
    var lessIt = function(css) {
        var result;
        var parser = new less.Parser();

        parser.parse(css, function (e, root) {
            result = root.toCSS();
        });
        return result;
    };
""";

    private File srcRoot;
    private File trgRoot;

    public LessCssJsCompiler(File src, File trg, Logger log){
        this.log = log;
        this.srcRoot=src.getAbsoluteFile();
        log.info "Source folder: ${this.srcRoot}";
        this.trgRoot=trg.getAbsoluteFile();
        log.info "Target folder: ${this.trgRoot}";
        this.cf = new ContextFactory();
        this.c = cf.enterContext();
        this.so = c.initStandardObjects();
        c.setOptimizationLevel(9);
        c.evaluateString(so, lessjs, "less.js", 1, null);
        c.evaluateString(so, runjs, "run.js", 1, null);
        processFolder(this.srcRoot);
    }

    void processFolder(File folder){
        folder.eachFile {
            if(it.isDirectory()){
                if(it.name != 'CVS'){ // or whatever your SCM is
                    processFolder it;
                }
            }else{
                if(it.name.endsWith('.less'))
                    processFile(it, getMirrorFile(it));
            }
        }
    }

    void processFile (File input, File output){
        if(output.exists() && output.lastModified() > input.lastModified()){
            log.info "File $output is up to date, skipping."
        }else{
            log.info "Compiling file $input to $output";
            output.parentFile.mkdirs();
            output.text = less(input.text);
        }
    }

    File getMirrorFile(File input){
        File parentFolder = new File(input.parentFile.absolutePath.
                replace(srcRoot.absolutePath, trgRoot.absolutePath));
        def output = new File(
                parentFolder,
                input.name.replace(".less", ".css")
                );
        log.info "Using output file $output for input file $input";
        return output;
    }

    public String less(String data) {
        String lessitjs = "lessIt(\"" +
               data.replace("\"", "\\\"")
                   .replaceAll( /\s+/, ' ' ) // strip line feeds
                   .replaceAll( /\/\*.*?\*\//, '' )+"\");"; //strip comments
        return this.c.evaluateString(
                         so, lessitjs, "lessitjs.js", 1, null).toString();
    }
}

The relevant part of the POM.xml is this:

inside dependencies:

<dependency>
    <groupId>rhino</groupId>
    <artifactId>js</artifactId>
    <version>1.7R2</version>
    <scope>provided</scope>
</dependency>

(you could also use a plugin dependency, but when I use a project dependency, eclipse will give me autocompletion etc)

inside build/plugins:

<plugin>
 <groupId>org.codehaus.groovy.maven</groupId>
 <artifactId>gmaven-plugin</artifactId>
 <version>1.0</version>
 <executions>
 <execution>
 <id>translate-css</id>
 <phase>process-resources</phase>
 <goals>
 <goal>execute</goal>
 </goals>
 <configuration>
 <scriptpath>
 <element>${pom.basedir}/src/main/groovy</element>
 </scriptpath>
 <source>
 <![CDATA[

 new LessCssJsCompiler(
     new File(pom.basedir, "src/main/java"),
     new File(pom.build.outputDirectory),
     log
 );
 ]]>
 </source>
 </configuration>
 </execution>
 </executions>
 </plugin>

In order for this to work, the less.js script (I am using this version) must be inside src/main/javascript and the groovy script must be inside src/main/groovy.

Also, you probably don’t want the .less files in your target folder, so you should remove them from resource processing

for src/main/resources:

<resource>
     <directory>src/main/resources</directory>
     <excludes>
         <exclude>**/*.less</exclude>
     </excludes>
</resource>

or for src/main/java (which is probably where stuff is if you use wicket):

<resource>
     <directory>src/main/java</directory>
     <excludes>
         <exclude>**/*.java</exclude>
         <exclude>**/*.less</exclude>
     </excludes>
</resource>
Posted in CSS, Groovy, JavaScript, Maven | 10 Comments

Deploy a folder hierarchy to a maven repository

Again, prompted by a Stackoverflow question, here is a means to deploy an entire directory including subdirectories to a maven repository.

I am using GMaven (groovy scripts embedded in maven), a great way to script and interact with maven without having to write plugins.

For a lot of what we’ll do, we need to access components from the underlying plexus container (Maven’s IOC system). Fortunately, GMaven provides several pre-defined variables:

project The maven project, with auto-resolving properties
pom Alias for project
session The executing MavenSession
settings The executing Settings
log A SLF4J Logger instance
ant An AntBuilder instance for easy access to Ant tasks
fail() A helper to throw MojoExecutionException

The way to access the plexus container is to call MavenSession.lookup(role), so you’lll be seeing a lot of code like this:


def repoFactory = session.lookup(
    'org.apache.maven.artifact.repository.ArtifactRepositoryFactory');

(Plexus role ids are usually equivalent to the qualified name of the interface that is returned)

My first Idea was to copy the functionality of the Maven Build-Helper Plugin‘ s attach-artifact mojo: attach artifacts to the current build, deploying or installing them as appropriate in the current maven lifecycle context. However, this mechanism relies on AttachedArtifacts, which must have the same groupId and artifactId as the containing project, which I didn’t think to be appropriate for a folder hierarchy.

Instead, I copied some of the code from the deploy:deploy-file mojo, grabbing repository information from the POM’s <distributionManagement> section.

Here are the plexus components we need:

def layout =  session.lookup('org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout');
def repoFactory = session.lookup(
    'org.apache.maven.artifact.repository.ArtifactRepositoryFactory');
def repository = repoFactory.createDeploymentArtifactRepository(
    pom.distributionManagement.repository.id,
    pom.distributionManagement.repository.url,
    layout, true );
def localRepository = session.localRepository;
def helper =
    session.lookup("org.apache.maven.project.MavenProjectHelper");
def afm = session.lookup(
    'org.apache.maven.artifact.handler.manager.ArtifactHandlerManager');
def factory = new org.apache.maven.artifact.factory.DefaultArtifactFactory();
factory.class.getDeclaredField("artifactHandlerManager").accessible = true;
factory.artifactHandlerManager=afm;
def deployer = session.lookup(
'org.apache.maven.artifact.deployer.ArtifactDeployer');

Then we grab some defaults from the <properties> block:

// initialize properties
def baseFolder = new File(pom.properties['deploy.basefolder']);
def groupId = pom.properties['deploy.groupId'];
def version = pom.properties['deploy.version'];

Now we iterate over all files, create an artifact and a temporary pom for each and deploy these using the Deployer component.


// iterate over all files recursively
baseFolder.eachFileRecurse{
    if(it.isDirectory())return;

    // use the file extension as packaging
    def packaging = it.name.replaceAll( /.+\./ , '' );
    // create the artifactId from the file's relative path
    def artifactId = it.absolutePath
        .replace(baseFolder.absolutePath, '')
        .substring(1)
        .replaceFirst( /\..*?$/ , '')
        .replaceAll( /\W+/ , '-' );

    // build a temporary artifact
    def artifact =
        factory.createBuildArtifact(
            groupId, artifactId, version, packaging );

    // create a pom file for the artifact
    def model = new org.apache.maven.model.Model();
    model.setModelVersion( "4.0.0" );
    model.setGroupId( groupId );
    model.setArtifactId( artifactId );
    model.setVersion( version );
    model.setPackaging( packaging );
    File pomFile = File.createTempFile( "mvndeploy", ".pom" );
    pomFile.deleteOnExit();
    fw = org.codehaus.plexus.util.WriterFactory.newXmlWriter( pomFile );
    new org.apache.maven.model.io.xpp3.MavenXpp3Writer().write( fw, model );
    org.apache.commons.io.IOUtils.closeQuietly( fw );

    def metadata =
        new org.apache.maven.project.artifact.ProjectArtifactMetadata(
                    artifact, pomFile );
    artifact.addMetadata( metadata );

    // deploy file
    deployer.deploy(it, artifact, repository, localRepository );
}

And here is the entire pom.xml:

<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>folderdeployer</artifactId>
    <packaging>jar</packaging>
    <version>SNAPSHOT</version>

    <properties>
        <!-- This is where your artifacts are -->
        <deploy.basefolder>c:\temp\stuff</deploy.basefolder>

        <!-- This will be used as groupId -->
        <deploy.groupId>my.groupid</deploy.groupId>

        <!-- this will be used as version -->
        <deploy.version>1.2.3</deploy.version>
    </properties>
    <build>
        <plugins>

            <plugin>
                <groupId>org.codehaus.groovy.maven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.0</version>
                <dependencies>
                    <dependency>
                        <groupId>commons-io</groupId>
                        <artifactId>commons-io</artifactId>
                        <version>1.4</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>deploy-files</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>execute</goal>
                        </goals>
                        <configuration>
                            <source>
                            <![CDATA[
// read components from plexus container
def layout = session.lookup(
    'org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout');
def repoFactory = session.lookup(
    'org.apache.maven.artifact.repository.ArtifactRepositoryFactory');
def repository = repoFactory.createDeploymentArtifactRepository(
    pom.distributionManagement.repository.id,
    pom.distributionManagement.repository.url,
    layout, true );
def localRepository = session.localRepository;
def helper =
    session.lookup("org.apache.maven.project.MavenProjectHelper");
def afm = session.lookup(
    'org.apache.maven.artifact.handler.manager.ArtifactHandlerManager');
def factory = new org.apache.maven.artifact.factory.DefaultArtifactFactory();
factory.class.getDeclaredField("artifactHandlerManager").accessible = true;
factory.artifactHandlerManager=afm;

def deployer = session.lookup(
    'org.apache.maven.artifact.deployer.ArtifactDeployer');

// initialize properties
def baseFolder = new File(pom.properties['deploy.basefolder']);
def groupId = pom.properties['deploy.groupId'];
def version = pom.properties['deploy.version'];

// iterate over all files recursively
baseFolder.eachFileRecurse{
    if(it.isDirectory())return;

    // packaging = file.extension
    def packaging = it.name.replaceAll( /.+\./ , '' );
    // artifactId = file.relativePath.replace '/' , '-'
    def artifactId = it.absolutePath
        .replace(baseFolder.absolutePath, '')
        .substring(1)
        .replaceFirst( /\..*?$/ , '')
        .replaceAll( /\W+/ , '-' );
    def artifact =
        factory.createBuildArtifact(
            groupId, artifactId, version, packaging );

    // create pom for artifact
    def model = new org.apache.maven.model.Model();
    model.setModelVersion( "4.0.0" );
    model.setGroupId( groupId );
    model.setArtifactId( artifactId );
    model.setVersion( version );
    model.setPackaging( packaging );
    File pomFile = File.createTempFile( "mvndeploy", ".pom" );
    pomFile.deleteOnExit();
    fw = org.codehaus.plexus.util.WriterFactory.newXmlWriter( pomFile );
    new org.apache.maven.model.io.xpp3.MavenXpp3Writer().write( fw, model );
    org.apache.commons.io.IOUtils.closeQuietly( fw );

    def metadata =
        new org.apache.maven.project.artifact.ProjectArtifactMetadata(
                    artifact, pomFile );
    artifact.addMetadata( metadata );

    // deploy file
    deployer.deploy(it, artifact, repository, localRepository );
}
                                    ]]>
                            </source>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <distributionManagement>
        <repository>
            <id>your repo id here</id>
            <url>scp://your.repo.url/here</url>
            <layout>default</layout>
        </repository>
    </distributionManagement>

</project>

Put it anywhere you want, edit the <properties> and the <distributionManagement> section and call it using

mvn package
Posted in Groovy, Maven | 1 Comment