Nathaniel Waddell
Nathaniel Waddell

Reputation: 76

sapjco3.jar has issues with Maven's naming convention (Springboot)

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

Answers (3)

Kirtee Chaudhary
Kirtee Chaudhary

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 : enter image description here

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

dzezzz
dzezzz

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

michaldo
michaldo

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

Related Questions