Reputation: 3363
I am building a project in Eclipse, using Apache CXF JAX-RS. When I run my main class in Eclipse, it works fine. When I build a jar with dependencies in maven, it doesn't work. This is my pom.xml (I am building by running "mvn clean compile assembly:single"):
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.theopentutorials.jaxrs</groupId>
<artifactId>JsonCxfProvider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.theopentutorials.jaxrs.calc.CalcRESTStartUp</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
This is my main class:
public static void main(String[] args) {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(ResultsXml.class);
sf.setResourceProvider(ResultsXml.class, new SingletonResourceProvider(new ResultsXml()));
sf.setAddress("http://localhost:9999/open/");
Server server = sf.create();
}
Where ResultsXml is basically an annotated pojo class. When running in Eclipse, I can make requests on localhost:9999/open/ and I get the JSON back that I expect. However, when I build in maven and then run with java -jar myjarfile.jar, I get the following error:
Exception in thread "main" org.apache.cxf.service.factory.ServiceConstructionException at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:197) at com.theopentutorials.jaxrs.calc.CalcRESTStartUp.main(CalcRESTStartUp.java:15) Caused by: org.apache.cxf.BusException: No DestinationFactory was found for the namespace http://cxf.apache.org/transports/http. at org.apache.cxf.bus.managers.DestinationFactoryManagerImpl.getDestinationFactory(DestinationFactoryManagerImpl.java:130) at org.apache.cxf.endpoint.ServerImpl.initDestination(ServerImpl.java:88) at org.apache.cxf.endpoint.ServerImpl.(ServerImpl.java:72) at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:155) ... 1 more
Everything I have been able to find on Google / StackOverflow so far suggests that this error comes from a missing META-INF/cxf/cxf.xml file - which makes sense, as I don't have one of those. But how then does it work in Eclipse?
Could it be that Eclipse is picking up a cxf.xml file from one of the dependency jars that happens to have the config I need, but when packaging with maven it picks them up in a different order and so doesn't work? I have tried to create my own cxf.xml file, but I'm not sure which one (the maven build logs suggest that between all my dependencies, there are about 12 copies of the file) to use - is there a way to find out which one Eclipse is picking up?
EDIT1
I tried using eclipse to export a runnable jar file with unpacked dependencies, when the eclipse-exported jar I get a similar but subtly different message:
Cannot find any registered HttpDestinationFactory from the Bus. org.apache.cxf.transport.http.HTTPTransportFactory
Exception in thread "main" org.apache.cxf.service.factory.ServiceConstructionException at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:199) at com.theopentutorials.jaxrs.calc.CalcRESTStartUp.main(CalcRESTStartUp.java:15) Caused by: java.io.IOException: Cannot find any registered HttpDestinationFactory from the Bus. at org.apache.cxf.transport.http.HTTPTransportFactory.getDestination(HTTPTransportFactory.java:295) at org.apache.cxf.endpoint.ServerImpl.initDestination(ServerImpl.java:93) at org.apache.cxf.endpoint.ServerImpl.(ServerImpl.java:72) at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:155)
EDIT2
I tried using eclipse to export a runnable jar file with 'package required libraries into jar' - so far this seems to be working. Is it possible to replicate this in maven?
Upvotes: 5
Views: 4677
Reputation: 31
This maven-shade-plugin configuration worked for me:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!-- Optional Start -->
<finalName>${artifactId}-${version}</finalName>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jar-with-dependencies</shadedClassifierName>
<!-- Optional End -->
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.logslie.main</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/cxf/cxf-extension-xml.xml</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/cxf/bus-extensions.txt</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/cxf/cxf-extension-http-jetty.xml</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/cxf/cxf-extension-http.xml</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/cxf/cxf-servlet.xml</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
<resource>META-INF/cxf/cxf.xml</resource>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
Upvotes: 3
Reputation: 3363
It seems the correct fix for this, which I discovered when I had the exact same problem with some Spring libraries, is to use the Maven Shade plugin:
http://maven.apache.org/plugins/maven-shade-plugin/
Because several cxf-* jars have files in them with the same names (eg META-INF/cxf/cxf.xml) a normal jar-with-dependencies build will include the first one, and then ignore all subsequent ones it finds as 'duplicate'. The shade plugin will concatenate these files together, so you end up with one large file at the end that contains all of the entries that you needed.
Upvotes: 3
Reputation: 125
I suppose you use the zip created by the maven assembly plugin. When using jar-with-dependencies, this plugin merges all the files from the dependency jars in one single jar file. If there is some file that have the same name and path in the various jars, only one of those duplicated files will be kept. In your case, I think it is the /META-INF/cxf/bus-extensions.txt from cxf-rt-transports-http-jetty jar file which doesn't appear in the resulting assembly zip file. In Eclipse, the merge of the jar files is not done so each bus-extensions.txt is loaded.
Upvotes: 1