1

I'm currently trying to add a URLStreamHandler so I can handle URLs with custom protocols. This works fine when run locally. When deployed to AWS Lambda I get:

java.net.MalformedURLException: unknown protocol: baas

I'm following the "Handler" approach to registering the URLStreamHandler.

I even went as far as copying the code from URL.getURLStreamHandler(String) and added logging into my own code that is run by Lambda:

(Note: this is from the Java 8 source - I realise now that this might not be representative because AWS Lambda uses a Java 11 runtime).

URLStreamHandler handler = null;
String packagePrefixList = null;

packagePrefixList
    = java.security.AccessController.doPrivileged(
    new sun.security.action.GetPropertyAction(
            "java.protocol.handler.pkgs",""));
if (packagePrefixList != "") {
    packagePrefixList += "|";
}

// REMIND: decide whether to allow the "null" class prefix
// or not.
packagePrefixList += "sun.net.www.protocol";

LOG.debug("packagePrefixList: " + packagePrefixList);

StringTokenizer packagePrefixIter =
    new StringTokenizer(packagePrefixList, "|");

while (handler == null &&
       packagePrefixIter.hasMoreTokens()) {

    String packagePrefix =
      packagePrefixIter.nextToken().trim();
    try {
        String clsName = packagePrefix + "." + "baas" +
          ".Handler";
        Class<?> cls = null;
        LOG.debug("Try " + clsName);
        try {
            cls = Class.forName(clsName);
        } catch (ClassNotFoundException e) {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            if (cl != null) {
                cls = cl.loadClass(clsName);
            }
        }
        if (cls != null) {
            LOG.debug("Instantiate " + clsName);
            handler  =
              (URLStreamHandler)cls.newInstance();
        }
    } catch (Exception e) {
        // any number of exceptions can get thrown here
        LOG.debug(e);
    }
}

This prints (in Cloudwatch logs):

packagePrefixList: com.elsten.bliss|sun.net.www.protocol (BaasDriver.java:94, thread main)
Try com.elsten.bliss.baas.Handler (BaasDriver.java:108, thread main)
Instantiate com.elsten.bliss.baas.Handler (BaasDriver.java:118, thread main)
com.elsten.bliss.baas.Handler constructor (Handler.java:55, thread main) 

So, when run from my own code, in Lambda, it works.

However, the very next line of logging:

java.lang.IllegalArgumentException: URL is malformed: baas://folder: java.lang.RuntimeException
java.lang.RuntimeException: java.lang.IllegalArgumentException: URL is malformed: baas://folder
    ...
Caused by: java.net.MalformedURLException: unknown protocol: baas
    at java.base/java.net.URL.<init>(Unknown Source)
    at java.base/java.net.URL.<init>(Unknown Source)
    at java.base/java.net.URL.<init>(Unknown Source)

So it seems odd the same code is failing when run in URL. The main difference I can think of is the parent classloader used to load URL and my code are different, and so there's some sort of class loading issue.

The SPI approach can't be used because Lambda doesn't extract META-INF folders!

Dan Gravell
  • 7,855
  • 7
  • 41
  • 64

1 Answers1

0

Initially I thought the old URL.setURLStreamHandlerFactory(URLStreamHandlerFactory) approach was to be avoided, but it turns out this has been improved in recent Java versions, and so I have fallen back to that.

Specifically, a default fallback URLStreamHandlerFactory which is capable of handling streams to http, https, file et al is used as a fallback if the custom one provided cannot handle a stream.

This is a workaround though - it would be interesting to know why the class cannot be loaded.

Dan Gravell
  • 7,855
  • 7
  • 41
  • 64
  • `cls = Class.forName(clsName);` <- here. Uses the caller's class loader to find the class - which is either the platform class loader or null. You can try to use a `module-info.java` to declare a service, but not sure if AWS Lambda supports that. – Johannes Kuhn Apr 30 '20 at 13:58
  • Yeah - but as there appears to be little about how class loading works in Lambda I wasn't sure of how that was behaving. I suggest promoting your comment to an answer so someone can test it. – Dan Gravell Apr 30 '20 at 14:01