36
votes

I got the error Unable to locate NamespaceHandler when using context:annotation-config running (java -jar) a jar assembled by the maven-assembly-plugin and containing my project and all its dependencies.

As other people correctly spotted on the forum.springsource.org thread (message #7/8) the problem occurs because the files META-INF/spring.handlers and META-INF/spring.schemas that are present in different jars, get overwritten when the maven-assembly-plugin repackages the jars in a single file.

Looking at the content of two spring-*.jar files you can see the files sits in the same position relatively to the classpath

$ jar tf spring-oxm-3.0.3.RELEASE.jar
META-INF/spring.handlers
META-INF/spring.schemas
org/springframework/oxm/GenericMarshaller.class
...

$ jar tf spring-context-3.0.3.RELEASE.jar
META-INF/spring.handlers
META-INF/spring.schemas
org/springframework/context/ApplicationContext.class

Isn't it is possible to put the META-INF folder in a specific package? If so the idea I'd suggest, (hope it's applicable) is to put the META-INF/spring.shemas and META-INF/spring.handlers files under the package they refer to.

$ jar tf spring-oxm-3.0.3.RELEASE.jar
org/springframework/oxm/META-INF/spring.schemas
org/springframework/oxm/META-INF/spring.handlers
org/springframework/oxm/GenericMarshaller.class
...

$ jar tf spring-context-3.0.3.RELEASE.jar
org/springframework/context/META-INF/spring.handlers
org/springframework/context/META-INF/spring.schemas
org/springframework/context/ApplicationContext.class

This way they won't conflict when merged in a single jar. What do you think about it?

2
Probably won't work. I would expect whatever code reads these files would use a ClassLoader.getResource() call, which looks in the root of the classpath. Or (more likely) explicitly examines the files in the classpath.Anon
Yes, it won't work by just moving the files in that location without changing the code that loads it. I wonder if that's a better way to organize that information, as it keep things in separate packages (aka. namespaces). I'd like to submit a request for enhancement to SpringSrouce if the idea is good.protoboolean
You might want to mark the 26+ question as an answer @Xan.Gray

2 Answers

87
votes

I managed to get rid of the bug using the shader plugin instead of the (buggy) assembler plugin:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>at.seresunit.lecturemanager_connector.App</mainClass>
                            </transformer>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.handlers</resource>
                            </transformer>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.schemas</resource>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

I think I found the solution on the springsource forums.. it has been quite some time since I looked it up.. can't really remember the author. Kudos to him anyways :p

cheers

2
votes

With Ant.

<!--define couple of properties to identify spring jar files-->
<property name="spring-beans-jar" value="spring-beans-4.0.5.RELEASE.jar"/>
<property name="spring-context-jar" value="spring-context-4.0.5.RELEASE.jar"/>

<!--other properties-->


<target name="dist" depends="compile" description="Prepare distribution">
    <!--dump spring-context into build classes (or some place else)-->
    <unjar src="${lib.dir}/${spring-context-jar}" dest="${build.classes.dir}"/>

    <!--dump spring-beans on top of it overwriting META-INF/spring.* files-->
    <unjar src="${lib.dir}/${spring-beans-jar}" dest="${build.classes.dir}"/>

    <!--get overwritten META-INF/spring.* files of spring-context to some other place-->
    <unjar src="${lib.dir}/${spring-context-jar}" dest="${build.tmp.dir}">
        <patternset>
            <include name="META-INF/spring.handlers"/>
            <include name="META-INF/spring.schemas"/>
            <include name="META-INF/spring.tooling"/>
        </patternset>
    </unjar>

    <!--skipped spring-beans/META-INF/spring.factories as its not present in spring-context-->
    <!--handled only spring-context and spring-beans as that's what I needed at this point-->

    <!--append content from spring-context/META-INF/spring.* files-->
    <concat destfile="${build.classes.dir}/META-INF/spring.handlers" append="true">
        <filelist dir="${build.tmp.dir}" files="META-INF/spring.handlers"/>
    </concat>
    <concat destfile="${build.classes.dir}/META-INF/spring.schemas" append="true">
        <filelist dir="${build.tmp.dir}" files="META-INF/spring.schemas"/>
    </concat>
    <concat destfile="${build.classes.dir}/META-INF/spring.tooling" append="true">
        <filelist dir="${build.tmp.dir}" files="META-INF/spring.tooling"/>
    </concat>

    <jar destfile="${build.dist.dir}/application.jar">
        <fileset dir="${build.classes.dir}"/>

        <!--include all .jar files except already extracted ones-->
        <zipgroupfileset dir="${lib.dir}" includes="*.jar"
                         excludes="${spring-beans-jar}, ${spring-context-jar}"/>
        <manifest>
            <attribute name="Main-Class" value="${main-class}"/>
        </manifest>
    </jar>
</target>