There is no exception in the local operation of the Spring Boot project, and an error is reported when deploying to the Linux server: java.lang.ClassNotFoundException

Keywords: Java snapshot Maven xml

One background

Recently, we are using spring boot to develop project A, which introduces module B developed by small partners. It starts service locally and runs well. When it is deployed to the server, an error will be reported as soon as it runs: Caused by: java.lang.ClassNotFoundException.

Note: there are many reasons for this error, such as: package conflict, class conflict, package does not exist, etc. I just list one of them here. After all, I have been stuck for half a day, so I think it is necessary to share it.

Two reasons

Let's see a weird phenomenon first. I used other project C to refer to the same dependency B, deployed it to the server, and it works well. Is my project OK? Scrutinize:

1) Project C is deployed to the server. The jar package under / app/C-service/lib is:

B-api-1.0.0-20191224.024308-5.jar
B-dao-1.0.0-20191223.073120-2.jar

2) Project C is deployed to the server, / app/C-service/C-service.jar/META-INF/MANIFEST.MF. The scanning path is:

lib/B-api-1.0.0-20191224.024308-5.jar
lib/B-dao-1.0.0-20191223.073120-2.jar

And:

1) My project A is deployed to the server. The jar package under / app/A-service/lib is:

B-api-1.0.0-SNAPSHOT.jar
B-dao-1.0.0-SNAPSHOT.jar

2) My project A is deployed to the server, / app/A-service/A-service.jar/META-INF/MANIFEST.MF. The scanning path is:

lib/B-api-1.0.0-20191224.024308-5.jar
lib/B-dao-1.0.0-20191223.073120-2.jar

It is found that the path of the jar package scanned in MANIFEST.MF is inconsistent with the name of the actual extracted jar package (one with time stamp and one with SNAPSHOT), so the jar package cannot be scanned, resulting in an error: Caused by: java.lang.ClassNotFoundException.

So what is the cause of this tragedy? Chance coincidence:

1. The module of my small partner is SNAPSHOT, such as 1.0.0-SNAPSHOT. When I pull the jar package, I pull B-api-1.0.0-20191224.024308-5.jar:

<dependency>
    <groupId>com.xxx.xxx</groupId>
    <artifactId>B-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

2. My project A can run locally because I didn't pack mvn clean package -Dmaven.test.skip=true at that time. When IDEA runs the project locally, the scan path of jar package is the same as that of the actual pulled jar package. After I pack, extract the A-service.zip package, and the local operation will also report an error;

3. The problem is the difference between before and after project A is packaged. Why is the name of jar package in the lib / directory after being unpacked by MANIFEST.MF and A-service.zip different?

4. Finally, by comparing the configuration files of project C and project A, we find the differences: there is an extra line in the assembly.xml of project A: < outputfilenamemapping > ${artifact. Artifactid} - ${artifact. Baseversion}. ${artifact. Extension} < / outputfilenamemapping >

<dependencySets>
    <dependencySet>
       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>
        <useProjectArtifact>false</useProjectArtifact>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
</dependencySets>

5. Get rid of this line. The problem has been solved. But why? The pulled jar package and the jar package scanned in MANIFEST.MF have time stamps. I always feel that they are not handsome enough and that something is wrong. Then I looked for the following relevant information in the word outputFileNameMapping, and found another elder brother: < useuniqueversions > false < / useuniqueversions >. This line of configuration is placed in pom.xml of the project Service module (the module where the Application.java startup class is located):

<!-- Pack jar On file, configure manifest Documents, adding lib Bag jar rely on -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>${main.class}</mainClass>
                <useUniqueVersions>false</useUniqueVersions>
            </manifest>
        </archive>
        <excludes>
            <exclude>*.yml</exclude>
            <exclude>*.properties</exclude>
        </excludes>
    </configuration>
</plugin>

6. explain:

1) < outputfilenamemapping >;

2) < useuniqueversions > false < / useuniqueversions > is used to generate the MANIFEST.MF file. If the version of the jar package under lib / is XXX snapshot, do you want to take a unique version timestamp? If you configure it as false (the default is true), it should be B-api-1.0.0-20191224.024308-5.jar. After configuration, the jar package is renamed as b-api-1.0.0-sn APSHOT.jar;

Just:

1) In project C, outputFileNameMapping and useUniqueVersions are not configured. The default value is used to make the jar package name under lib / consistent with that under MANIFEST.MF (with time stamp);

2) In my project A, not only the configuration files copied from where are configured with outputFileNameMapping (without time stamp), but also the useUniqueVersions (with time stamp) are not configured, resulting in inconsistency;

So: either there are two configurations, or there are none, or there will be inconsistencies, leading to errors.

Three solution

Personally, I think the jar package name is more elegant without time stamp, so I recommend outputFileNameMapping and useUniqueVersions, as follows:

1. Add configuration in assembly.xml: < outputfilenamemapping > ${artifact. Artifactid} - ${artifact. Baseversion}. ${artifact. Extension} < / outputfilenamemapping >

<dependencySets>
    <dependencySet>
        <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>
        <useProjectArtifact>false</useProjectArtifact>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
</dependencySets>

2. Add configuration in pom.xml of the module where Application.java is started: < useuniqueversions > false < / useuniqueversions >

<!-- Pack jar On file, configure manifest Documents, adding lib Bag jar rely on -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>${main.class}</mainClass>
                <useUniqueVersions>false</useUniqueVersions>
            </manifest>
        </archive>
        <excludes>
            <exclude>*.yml</exclude>
            <exclude>*.properties</exclude>
        </excludes>
    </configuration>
</plugin>

4. References

1)Apache Assembly: https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html
2)Pippo Deployment: http://www.pippo.ro/doc/deployment.html
 
From: Pippo Deployment: http://www.pippo.ro/doc/deployment.html
Snapshot Workaround
If you are using a SNAPSHOT version of Pippo as described in the Maven section, a small workaround is necessary due to a Maven bug:
1) Add <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping> to the dependencySet element inside assembly.xml
2) Add <useUniqueVersions>false</useUniqueVersions> to the maven-jar-plugin's manifest section inside pom.xml

have a good time ~: -)

Posted by landonmkelsey on Sun, 29 Dec 2019 00:20:28 -0800