【问题标题】:Programatically build and launch contained applications based on Maven dependecies基于 Maven 依赖项以编程方式构建和启动包含的应用程序
【发布时间】:2014-10-31 10:27:31
【问题描述】:

我想从用户那里获取几个 Maven 依赖项列表,将它们解析并加载为包含的应用程序。步骤如下:

  1. 收集所有 Maven 依赖项的列表 (DONE)
  2. 使用 Aether (DONE) 解决所有依赖项
  3. 用 Aether 解析类路径 (DONE)
  4. 将上述内容捆绑在一个单独的“容器”中(以便可以使用具有潜在冲突版本的不同 Maven 依赖项)。
  5. 与其他列表重复。

提供一些上下文:我想在 UIMA 的上下文中使用上述内容,以便能够运行依赖于具有不同版本的不同库集的不同(自然语言处理)管道。我的目标是创建一个注释服务器,其中定义了(Maven)依赖项和可以以 RESTful 方式调用的管道。管道(及其相应的依赖项)应各自在包含的类路径环境中运行(以避免类路径冲突)。

OSGi 是要走的路吗?基于类路径(:=已解析的 jar 列表),我可以构建一个 OSGi 包并部署它吗?全部以编程方式?我无法控制 maven 依赖项(它们是 UIMA 组件,仅此而已),因此无法在其中添加 OSGi 元数据。

【问题讨论】:

    标签: java maven osgi aether


    【解决方案1】:

    maven-assembly-plugin 结合 maven 配置文件会为您解决这个问题吗?

    您可以根据每个配置文件以不同的方式过滤依赖项。您可以使用配置文件特定的程序集描述符文档并生成要放置在战争中的自定义清单。您正在描述一个 J2EE Web 应用程序 (war) 程序集——它们将在 servlet 容器内的防火墙类加载器中运行,因此您可以基于相同的源生成一堆它们(只需改变 Web 应用程序上下文和 WEB-基于每个配置文件的 INF/lib。

    例如,将它们拖放到同一个 Tomcat 服务器中,您就可以开始使用了。这是你的意思吗?

    HTH,

    尼克

    【讨论】:

    • 谢谢@nrapopor,听起来是个好主意。您能否详细说明 servlet 容器内的“防火墙”类加载器:我知道 UIMA 使用了一些 Class.forName() 调用——它会工作吗?好吧,我想我会试试看。再次感谢
    【解决方案2】:

    您当然可以创建一个包含 jar 列表的包,将所有这些放在包自己的类路径中,然后将该包部署到 OSGi 容器中。您可能确实需要创建一个 BundleActivator(这是该包的入口点,就像传统 Java 的 main 方法一样)。

    然后您说您有多个这样的捆绑包,我是否正确理解您希望将每个捆绑包部署在单独的容器中?如果是这样,您可以使用某种 REST 库为每个包提供一个 REST 端点,或者您可以使用 OSGi 远程服务来发布可以被其他容器发现的服务。

    我不确定这是否是您的意思,所以我也不确定 OSGi 是否是正确的方法。根据您的描述,您既不使用服务(使用 OSGi 的一个非常重要的原因,因为它将应用程序的各个部分相互分离),也不打算为组件创建不同的包(使用 OSGi 的另一个重要原因)。您几乎是在描述一种目前被宣传为“微服务”的架构风格。能否详细说明一下?

    【讨论】:

    • 谢谢马塞尔。该捆绑软件只会公开几个 REST 端点,不会公开其他服务。
    【解决方案3】:

    根据您的用例,我建议您查看Java ServiceLoader API。 ServiceLoader API 允许您定义一个接口,并从不同的自包含 JAR 加载该接口的实现。您可以将不同的库构建到它们自己的 jar 中,通过接口公开您需要的方法,并从您的 Java 程序中独立加载它们。 ServiceLoader 甚至会列出可供您使用的不同实现。

    来自文档: 假设我们有一个服务类型 com.example.CodecSet,它旨在表示某些协议的编码器/解码器对集合。在这种情况下,它是一个具有两个抽象方法的抽象类:

    public abstract Encoder getEncoder(String encodingName);
    public abstract Decoder getDecoder(String encodingName);
    

    如果提供者不支持给定的编码,每个方法都会返回一个适当的对象或 null。典型的供应商支持不止一种编码。 如果 com.example.impl.StandardCodecs 是 CodecSet 服务的实现,那么它的 jar 文件还包含一个名为

    META-INF/services/com.example.CodecSet
    

    这个文件包含一行:

    com.example.impl.StandardCodecs        # Standard codecs
    

    CodecSet 类在初始化时创建并保存单个服务实例:

    private static ServiceLoader<CodecSet> codecSetLoader
            = ServiceLoader.load(CodecSet.class);
    

    为了定位给定编码名称的编码器,它定义了一个静态工厂方法,该方法遍历已知和可用的提供者,仅在找到合适的编码器或提供者用完时返回。

    public static Encoder getEncoder(String encodingName) {
            for (CodecSet cp : codecSetLoader) {
                    Encoder enc = cp.getEncoder(encodingName);
                    if (enc != null)
                            return enc;
            }
            return null;
    }
    

    getDecoder 方法的定义类似。

    【讨论】:

      【解决方案4】:

      这听起来很像您可以使用Apache Stanbol。它是一个专注于内容语义增强的框架,但可用于任何涉及内容的基于 Web 的工作流程。您可以定义管道来处理和/或存储您的数据。有一些使用 Apache Tika 和 OpenNLP 的 NLP 组件。据我所知,您还可以集成 UIMA。它使用 RESTful 服务并基于 OSGI。


      如果 Stanbol 不适合您的用例并且您需要推出自己的应用程序,我认为 OSGI 仍然是可行的方法。

      根据您的用例,您可以将捆绑包部署到容器中,也可以简单地将 OSGI 框架嵌入到加载您创建的捆绑包的小型启动器应用程序中。

      许多 Maven 工件已经包含 OSGI 元数据。大多数情况下,您可以使用maven-dependency-plugin 将它们复制到您的包目录,然后直接将它们作为 OSGI 包加载。

      可以将非 OSGI 依赖项嵌入到需要它们的包中。还应该可以设置一些 maven 插件来修改清单以添加一些基于 maven 工件 id 和版本的元数据,并将依赖项重新打包为包(但这不会一直有效,因为 Maven pom 版本并且包的版本并不总是相同的)。

      可以使用maven-bundle-plugin 捆绑用户代码和任何所需的依赖项。它可以为您生成清单。

      对于 REST 接口,我通常会推荐 JAX-RS(Jersey 或 Apache CXF DOSGI),但我还没有对这些框架使用编程方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-05-03
        • 2012-09-20
        • 1970-01-01
        • 2021-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多