Reputation: 76
I am trying to compile an integration server using the springboot java framework. I am using Maven to compile a jar file, but I keep running into an issue with the way Maven stores jars in repositories.
The Jar I am working with is sapjco3.jar, which will crash on run (not package) because it needs its name to be sapjco3.jar. Because the Maven repo naming convention is:
/%groupId%/%artifactId%/%version%/%artifactId%-%version%.jar
The library ends up being called sapjco3-3.0.jar.
Is there some way to pop my sapjco3.jar into my application without using a repository (I haven't been able to add it to the java.library.path or by editing the entries and XML in the ~/.m2
directory). Otherwise, is there a way to script renaming it once it is inserted??
My platform is Ubuntu 14.04 LTS, by the way.
The crash:
Factory method 'createSapConnection' threw exception; nested exception is java.lang.ExceptionInInitializerError: JCo initialization failed with java.lang.ExceptionInInitializerError: Illegal JCo archive "sapjco3-3.0.jar". It is not allowed to rename or repackage the original archive "sapjco3.jar".
I just want this to build!!
Thanks
My POM file (missing the XML header, because StackOverflow doesn't parse it right):
<project>
<modelVersion>4.0.0</modelVersion>
<name>SampleIntegrationServer</name>
<description>Sample Integration Server</description>
<groupId>com.Sample</groupId>
<artifactId>SampleIntegrationServer</artifactId>
<packaging>jar</packaging>
<version>0.0.2</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud-aws-version>1.0.4.RELEASE</spring-cloud-aws-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-ws</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
<version>${spring-cloud-aws-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-messaging</artifactId>
<version>${spring-cloud-aws-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>net.sf.jt400</groupId>
<artifactId>jt400</artifactId>
<version>6.7</version>
</dependency>
<dependency>
<groupId>com.sap</groupId>
<artifactId>sapjco3</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.3.RELEASE</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<includeEmptyDirs>true</includeEmptyDirs>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Upvotes: 3
Views: 4937
Reputation: 92
One possible solution for this problem was for me to rename the dependency with the maven-dependency-plugin. For the tests I removed the original maven dependency from classpath and added the renamed (now sapjco3.jar) to the classpath again.
Step-1 : My local configuration is looks like this in pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Add the native JCO libraries to the class path -->
<classpathDependencyExcludes>
<classpathDependencyExcludes>${jco.group.id}:${jco.artifact.id}</classpathDependencyExcludes>
</classpathDependencyExcludes>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.directory}/dependency/sapjco3.jar</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
Step-2 : Add Jco libraries in build classpath as below :
NOTE : If you have Jco dependency <groupId>com.sap</groupId>
in pom.xml then please remove it, because application will read JCO lib from build classpath.
Upvotes: 1
Reputation: 1005
I solved the issue using shade plugin for maven. Having maven dependency for sapjco3 with scope provided:
<dependency>
<groupId>com.sap.conn.jco</groupId>
<artifactId>sapjco3</artifactId>
<version>${sapjco3.version}</version>
<scope>provided</scope>
</dependency>
Then the work is done by copying the library when shading and excluding it when the spring maven plugin adds it's version, so that we have the original one:
<build>
<finalName>price</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<stripVersion>true</stripVersion>
<includeArtifactIds>sapjco3</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>true</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
<resource>BOOT-INF/lib/sapjco3.jar</resource>
<file>sapjco3.jar</file>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.7.RELEASE</version>
<configuration>
<excludeGroupIds>com.sap.conn.jco</excludeGroupIds>
</configuration>
</plugin>
...
Upvotes: 1
Reputation: 4639
I solved this problem with custom layout
import org.springframework.boot.loader.tools.Layouts.Jar;
public class ResistantToSapjco3JarLayout extends Jar implements CustomLoaderLayout {
@Override
public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException {
// do Spring Boot defaults
writer.writeLoaderClasses();
URL jcoClassUrl = getClass().getClassLoader().getResource("com/sap/conn/jco/JCo.class");
if (jcoClassUrl == null) {
throw new IllegalStateException("No sapjco3 JAR on plugin classpath. "
+ "Make sure sapjco3 is added to spring-boot-maven-plugin dependency section");
}
if (!jcoClassUrl.toString().startsWith("jar:file")) {
throw new IllegalStateException("sapjco3 is not JAR");
}
JarURLConnection jarUrlConnection = (JarURLConnection) jcoClassUrl.openConnection();
JarFile sapjco3Jar = jarUrlConnection.getJarFile();
JarWriter jarWriter = (JarWriter) writer;
jarWriter.writeNestedLibrary("BOOT-INF/lib/",
new Library("sapjco3.jar", new File(sapjco3Jar.getName()), LibraryScope.COMPILE, false));
}
}
pom.xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layoutFactory implementation="com.company.ResistantToSapjco3JarLayoutFactory" />
<excludeArtifactIds>sapjco3</excludeArtifactIds>
</configuration>
<dependencies>
<dependency>
<groupId>com.company</groupId>
<artifactId>resistant-to-sapjco3</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>sapjco3</groupId>
<artifactId>sapjco3</artifactId>
<version>3.0.13</version>
</dependency>
</dependencies>
</plugin>
ResistantToSapjco3JarLayoutFactory
public class ResistantToSapjco3JarLayoutFactory implements LayoutFactory {
@Override
public Layout getLayout(File source) {
return new ResistantToSapjco3JarLayout();
}
}
Upvotes: 2