2

I can package a JAVA project I've written. One which uses the Gson library for JSON features. I'm very new to JAVA so I could be making a dumb mistake but here's what I've assumed:

In the source code I have:

import com.google.gson.Gson;

and then use this import like so:

Gson gson = new Gson();
String json = gson.toJson(result);

In my Maven pom.xml I've included the following in the dependency section:

<dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.0.0</version>
  </dependency>
  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

As I say, it does package to a JAR file with no errors (using Maven for packaging) but my JAR file is being used on AWS as a serverless function and so I believe what I need to do is include the Gson dependency as part of my JAR file (could be wrong). This seems to be backed up by the errors I get on AWS:

aws error

Having done some google searches it looked like maybe Maven's "shade" plugin might be the ticket. I added it into my pom.xml like so:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <artifactSet>
            <includes>
              <include>com/google/gson/**</include>
            </includes>
        </artifactSet>
      </configuration>
      <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
       </executions>
 </plugin>

But looking in the generated JAR file I see no difference. What do I need to do to get this to work?


The full POM file can be found here: pom.xml

ken
  • 8,763
  • 11
  • 72
  • 133
  • one suspicion i have is that my includes block is incorrect. what i’m still not groking is how maven does dep mgmt and so for instance, though i’ve imported Gson, where is that code and code structure actually brought into the local file system. In Javascript world you’d have a local copy of you deps in node_modules. – ken Mar 01 '18 at 18:55

2 Answers2

1

If you have dependencies that will be provided from runtime container, you should set scope provided to these dependencies.

  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
  </dependency>

And remove <artifactSet> section from plugin configuration, execute mvn package then jar with required dependencies will be created.

miskender
  • 7,460
  • 1
  • 19
  • 23
  • so I added the **scope** of "provided" for _com.amazonaws_ and I retained my dependency block for _com.google.code.gson_ (as illustrated above). I also removed the **artifactSet** and unfortunately I still get the same result. – ken Mar 01 '18 at 19:01
  • @ken Can you check contents of your jar file, to see if it is actually contains gson class files. And you dont have to specify compile scope for gson library. – miskender Mar 01 '18 at 19:04
  • it does not have gson classes. :( – ken Mar 01 '18 at 19:06
1

I'm unfamiliar with the shade plugin others have referenced. The way it sounds to me, you need an artifact that's an executable jar: a jar, with its dependencies.

Here's how I do that, using Maven:

<?xml version="1.0" encoding="UTF-8"?>
<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.company.groupId</groupId>
    <artifactId>artifact-id</artifactId>
    <version>1.0.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>

                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.1.0</version>

                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.company.App</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>attach-javadoc</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.0.1</version>
                <executions>
                    <execution>
                        <id>attach-source</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

What you want to pay attention to is the build plugin, maven-assembly-plugin. This tells Maven how to assemble/package the results of the build. In its configuration, you define the main class that contains the runnable application, which usually is going to be where your public static void main(String[] args) declaration is. You also define a descriptor-ref, which is a String that will be appended to the jar's name. So, you'd end up with artifactId-1.0.0-jar-with-dependencies.jar, using my POM as an example.

To further explain what's going on, without the changes I recommend, your POM is telling Maven to just build your code. As part of that, you declare dependencies, of which you have two right now: aws-lambda-java-core and gson. When you don't provide a scope, it defaults to compile scope. This tells Maven to grab that dependency when the program is compiled, so that the program can use that dependency's code. But, when packaging the build artifact of your program, Maven, by default, will not include those dependencies in the final jar; it expects that when you run the jar, you'll have those dependencies on your classpath.

By adding the assembly build plugin, you're changing those instructions to Maven. With that plugin, you're telling Maven that when it builds the program, it needs to assemble it in such a way that all declared dependencies are included (read: assembled) with the program, and to do that during the package phase of the build; you'll see these dependencies in the lib folder of the build artifact. And then, like I mentioned earlier, the descriptorRef is descriptive info that will be appended onto the build artifact's name.

As an aside, and not truly relevant to your question, I'd recommend looking into FasterXML for JSON handling and manipulation. So much more powerful, so much easier, and it's widely supported and used, which means it has a great community behind it.

liltitus27
  • 1,670
  • 5
  • 29
  • 46
  • Yeah apparently FasterXML is used within the AWS Lambda framework too. I was considering it but I'm such a newbie that I couldn't even figure out how to import that library correctly. My last attempt at Java was version 0.9 in 1996. :p – ken Mar 01 '18 at 19:16
  • Let me see if I can grok everything you have put in here. – ken Mar 01 '18 at 19:17
  • btw, I have my _plugins_ within a _pluginManagement_ block ... should I remove that? – ken Mar 01 '18 at 19:21
  • jackson dependencies usually consist of at least these three: com.fasterxml.jackson.core jackson-core ${jackson.version} com.fasterxml.jackson.core jackson-annotations ${jackson.version} com.fasterxml.jackson.core jackson-databind ${jackson.version} – liltitus27 Mar 01 '18 at 19:23
  • you shouldn't need pluginmanagement. try using my pom as a template, filling in your program info in artifact and group id. run `mvn clean package`, and try to execute `java -jar target/.jar` – liltitus27 Mar 01 '18 at 19:24
  • I was just removing pluginManagement when I saw this ... it appears this WAS causing problems as I'd already brought most of your config across but only after removing it did I see it build the JAR `-with dependencies`. – ken Mar 01 '18 at 19:26