13

tl;dr: How do I add the javax.activation.DataSource dependency to a server run through the App Engine Maven plugin?

I'm using the App Engine Maven plugin to deploy to a local App Engine server, and I'm trying to use the Blobstore API to upload a file. I'm using Java 11.

pom.xml

<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>io.happycoding</groupId>
  <artifactId>blobstore-test</artifactId>
  <version>1</version>
  <packaging>war</packaging>

  <properties>
    <!-- App Engine currently supports Java 8 -->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-spec</artifactId>
      <version>1.2.5</version>
    </dependency>

    <dependency>
      <groupId>com.google.appengine</groupId>
      <artifactId>appengine-api-1.0-sdk</artifactId>
      <version>1.9.59</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-maven-plugin</artifactId>
        <version>1.9.71</version>
      </plugin>
    </plugins>
  </build>
</project>

index.jsp

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>

    <form action="<%= BlobstoreServiceFactory.getBlobstoreService().createUploadUrl("/submit") %>" 
      method="POST"
      enctype="multipart/form-data">

      Enter some text:
      <br/>
      <textarea name="text"></textarea>
      <br/>
      Select a file:
      <input type="file" name="image">
      <br/>
      <input type="submit" value="Submit">
    </form>

  </body>
</html>

SubmitServlet.java

package io.happycoding.servlets;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/submit")
public class SubmitServlet extends HttpServlet {

  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) {
    System.out.println("data submitted");
  }
}

Command to run the local server: mvn appengine:devserver

I can successfully run a local server, and I can see that the form is rendered correctly, including the Blobstore URL.

However, when I submit the form, I get this error:

java.lang.NoClassDefFoundError: javax/activation/DataSource
    at com.google.appengine.api.blobstore.dev.UploadBlobServlet.handleUpload(UploadBlobServlet.java:171)
    at com.google.appengine.api.blobstore.dev.UploadBlobServlet.access$000(UploadBlobServlet.java:69)
    at com.google.appengine.api.blobstore.dev.UploadBlobServlet$1.run(UploadBlobServlet.java:115)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at com.google.appengine.api.blobstore.dev.UploadBlobServlet.doPost(UploadBlobServlet.java:112)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:867)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1623)
    at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:134)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:48)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.appengine.tools.development.jetty9.StaticFileFilter.doFilter(StaticFileFilter.java:123)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:366)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:349)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
    at com.google.appengine.tools.development.DevAppServerRequestLogFilter.doFilter(DevAppServerRequestLogFilter.java:44)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1602)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1588)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1345)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1557)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1247)
    at com.google.appengine.tools.development.jetty9.DevAppEngineWebAppContext.doScope(DevAppEngineWebAppContext.java:94)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at com.google.appengine.tools.development.jetty9.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:595)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:502)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:364)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ClassNotFoundException: javax.activation.DataSource
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
    at com.google.appengine.tools.development.DevAppServerClassLoader.loadClass(DevAppServerClassLoader.java:108)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    ... 57 more

Some Googling reveals quite a few questions about this problem:

The answers to these questions suggest adding a combination of these dependencies:

<dependency>
  <groupId>javax.activation</groupId>
  <artifactId>javax.activation-api</artifactId>
  <version>1.2.0</version>
</dependency>

<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

<dependency>
    <groupId>com.sun.activation</groupId>
    <artifactId>javax.activation</artifactId>
    <version>1.2.0</version>
</dependency>

I've tried adding each of these, and I've tried adding all of them. But no matter what, I still get the above error.

I understand why this is happening: Java 8 included the javax.activation.DataSource class, and it was subsequently removed. So since I'm using Java 11, that class is missing. But none of the dependencies I've tried so far seem to provide the missing class, possibly because I'm using the App Engine Maven plugin instead of running my own server?

How do I add this dependency to a project that uses the App Engine Maven plugin?

Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
  • Did you ever find a solution? I have the same issue after switching to Java 11, but using Java 8 for compiling the app engine project. The only way for me to resolve the issue on the local is to switch back to Java 8. – DFB Feb 16 '21 at 12:36
  • A clarification - When I include the activation-1.1.1.jar using the maven dependency, the error changes from `javax.activation.DataSource` not found to `javax/activation/DataContentHandler` not found. – DFB Feb 16 '21 at 12:48

3 Answers3

2

I'd first try adding

<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

as you pointed out, but my expectation is that you're getting the NoClassDefFound error because the javax.activation classes aren't getting added to the war by maven. I still mostly do my jar/war/ear building with Ant for this very reason: more verbose, but less magic. If you look in your project's /target folder you should be able to see the war file: if you do a jar tf <warfile> you should be able to see all the jar files added to WEB-INF/lib, and this will tell you if it is adding the activation jar.

Looking at https://github.com/GoogleCloudPlatform/appengine-maven-plugin, the recommended sample includes this plugin:

<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <archiveClasses>true</archiveClasses>
            </configuration>
        </plugin>

before the appengine-maven-plugin entry in the build section of your pom. I'd put that in and then see if it properly builds the war.

Femi
  • 64,273
  • 8
  • 118
  • 148
  • I have the same issue, and tried your recommendation, but it didn't help. I can see the activation-1.1.1.jar in my WEB-INF/lib, but still get he error the same error. I'm using jdk-11.0.2.jdk when I get the error. Switching back to jdk1.8.0_231.jdk resolves the issue. – DFB Feb 16 '21 at 12:38
  • This one is because Java 9 deprecated javax.activation (https://docs.oracle.com/javase/10/docs/api/java.activation-summary.html). You'll need to manually add it to your POM if you're using Java 9 or higher. – Femi Feb 18 '21 at 01:20
  • I've added it to the POM and confirmed that it's included in the WEB/INF/lib, but it still doesn't work. In addition, I also added the plugin which you mentioned, but that didn't help either. – DFB Feb 20 '21 at 12:06
  • This still works 11 years later :) – Sridhar Sarnobat Jul 30 '22 at 03:02
1

Google App Engine doesn't support Java 11. It only supports Java 8.

Zack
  • 301
  • 1
  • 11
  • Yes, I know that. I'm asking how I can deploy **locally** with a later version of Java. Is there some official documentation that says this is not supported? – Kevin Workman Mar 28 '19 at 16:39
0

I switched back to Java 8 on local environment. On AppEngine it works.

Jarda Pavlíček
  • 1,636
  • 17
  • 16