I am using the maven plugin to shade and minimize a jar before I upload it to AWS lambda. However, I'm getting a runtime exception because of a missing class. As far as I know, this is due to some 'dynamic' class loading or something, but I'm not sure if A) there is a solution or B) what it might be, beyond efforts I've already made. I read some good [articles][1] on jar shading and I think I understand the general idea, but I can't find an example of my particular problem documented anywhere.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /home/ro007848/Workspace/geometry-service/target/geometry-service-1.0.0.jar with /home/ro007848/Workspace/geometry-service/target/geometry-service-1.0.0-shaded.jar
Below is the stack trace I see in cloudwatch:
Caused by: java.lang.RuntimeException: Unable to find function Length
at org.geotools.filter.FunctionFinder.findFunction(FunctionFinder.java:208)
at org.geotools.filter.FunctionFinder.findFunction(FunctionFinder.java:152)
at org.geotools.filter.FunctionFinder.findFunction(FunctionFinder.java:129)
at org.geotools.filter.FilterFactoryImpl.function(FilterFactoryImpl.java:819)
at org.geotools.feature.FeatureTypes.createLengthRestriction(FeatureTypes.java:148)
Here are the contents of my pom file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<filters>
<filter>....</filter>
<filter>
<artifact>org.geotools:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
..........
<filter>....</filter>
<filter>
<artifact>com.amazonaws:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<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>
When I take the jar that gets generated, unzip, and look inside,
➜ geotools git:(dev-S62039_geojson-to-shapefile_ro007848) ✗ pwd
/home/ro007848/Workspace/geometry-service/target/jar-shaded-simple/jar-contents/org/geotools
➜ geotools git:(dev-S62039_geojson-to-shapefile_ro007848) ✗ grep -r "Length"
this is what I find:
................
Binary file ows/wms/CRSEnvelope.class matches
Binary file filter/FilterSAXParser.class matches
Binary file filter/FilterDOMParser.class matches
Binary file filter/LengthFunction.class matches
Binary file filter/function/StaticGeometry.class matches
Binary file filter/function/FilterFunction_strLength.class matches
Binary file filter/function/FilterFunction_strToLowerCase.class matches
Binary file filter/function/FilterFunction_strToUpperCase.class matches
Binary file filter/function/FilterFunction_geomLength.class matches
Binary file filter/ExpressionDOMParser.class matches
Binary file referencing/util/CRSUtilities.class matches
Binary file referencing/crs/DefaultProjectedCRS.class matches
................
It appears as though there is a "Length" function there... If my exception isn't talking about this one, which one is it talking about? How do I being debugging this issue? Is there a 'scorched-earth' approach to setting <include>
filters in jar-shading plugin configuration so get this working?
Someone else suggested I try adding these classes explicitly in the shading configuration, like so:
<filter>
<artifact>org.geotools:gt-main</artifact>
<includes>
<include>org/geotools/filter/LengthFunction.class</include>
</includes>
</filter>
but that either doesn't work, or I'm not doing it right.
More info:
When I run integration tests for this code locally, there is no problem with execution. Whatever functions are needed are found. However, when I execute the same integration tests against my resources deployed in AWS (this one lambda backed by this one jar), that's when I get the failure. I find it difficult to uncover much in the debugger about what exactly I need to include because the code appears to do things 'dynamically' and I'm having trouble following it closely.
I'm at my wits end!
EDIT:
I took a look at the e-mail thread posted by @gerold-broser, but I'm not sure it is applicable because according to mvn help:effective-pom -Dverbose
I am on version 25.1 of geotools. The output says stuff like
<dependency>
<groupId>org.geotools</groupId> <!-- com.digitalglobe.p2020.common:common-dependencies:3.47.139813, line 922 -->
<artifactId>gt-coverage</artifactId> <!-- com.digitalglobe.p2020.common:common-dependencies:3.47.139813, line 923 -->
<version>25.1</version> <!-- com.digitalglobe.p2020.common:common-dependencies:3.47.139813, line 924 -->
</dependency>
``` and
```
<dg-geotools.version>25.1</dg-geotools.version> <!-- com.digitalglobe.p2020.common:common-dependencies:3.47.139813, line 218 -->
```
[1]: https://medium.com/@akhaku/java-class-shadowing-and-shading-9439b0eacb13
As is discussed in the GeoTools FAQ GeoTools relies on META-INF/services
files from each of the GeoTools modules used by your application. Unless you specifically instruct the Shade
plugin to merge these files then you will end up with the first or last one the maven sees (i forget which).
So add this to your pom's build
section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<!-- This bit merges the various GeoTools META-INF/services files -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
If you want to check it's worked then you need to check that META-INF/services/org.opengis.filter.expression.Function
includes org.geotools.filter.LengthFunction