【问题标题】:Can't use "Handler" approach to adding a URLStreamHandler in AWS Lambda无法使用“处理程序”方法在 AWS Lambda 中添加 URLStreamHandler
【发布时间】:2020-04-30 21:51:25
【问题描述】:

我目前正在尝试添加一个 URLStreamHandler,以便可以使用自定义协议处理 URL。这在本地运行时工作正常。当部署到 AWS Lambda 时,我得到:

java.net.MalformedURLException: unknown protocol: baas

我正在关注"Handler" approach to registering the URLStreamHandler

我什至从URL.getURLStreamHandler(String) 复制代码并将日志添加到我自己的由 Lambda 运行的代码中:

(注意:这是来自 Java 8 源 - 我现在意识到这可能不具有代表性,因为 AWS Lambda 使用 Java 11 运行时)。

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);
    }
}

这会打印(在 Cloudwatch 日志中):

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) 

因此,当从我自己的代码在 Lambda 中运行时,它可以工作。

但是,下一行日志记录:

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)

因此,在URL 中运行相同的代码失败似乎很奇怪。我能想到的主要区别是用于加载URL 的父类加载器和我的代码不同,因此存在某种类加载问题。

SPI approach 无法使用,因为Lambda doesn't extract META-INF folders

【问题讨论】:

    标签: java aws-lambda classloader


    【解决方案1】:

    最初我认为应该避免使用旧的 URL.setURLStreamHandlerFactory(URLStreamHandlerFactory) 方法,但事实证明这在最近的 Java 版本中得到了改进,所以我又回到了那个。

    具体来说,如果提供的自定义后备无法处理流,则默认后备 URLStreamHandlerFactory 能够处理到 httphttpsfile 等的流。

    虽然这是一种解决方法 - 知道为什么无法加载该类会很有趣。

    【讨论】:

    • cls = Class.forName(clsName); module-info.java 来声明服务,但不确定 AWS Lambda 是否支持。
    • 是的 - 但由于 Lambda 中类加载的工作原理似乎很少,我不确定它的行为方式。我建议将您的评论推广到答案,以便有人可以对其进行测试。
    猜你喜欢
    • 2018-11-29
    • 2019-02-08
    • 2018-03-10
    • 2020-04-03
    • 2013-12-27
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 2016-09-28
    相关资源
    最近更新 更多