来自 Adobe 的用户体验专家 AEM 之:第三方包的部署
CQ 的构建基于 Apache Sling,而 Apache Sling 这是构建于一个 OSGi 容器之上 (确切来讲是 Apache Felix)。OSGi 容器的行为 (就类文件的加载方式以及在 classpath 中可用) 和大多数 Java 开发者所习惯那样略有不同。要确保类能够在 OSGi 容器中可用,需要使用一个特定的方式对它们进行打 jar 包,包括向标准的 MANIFEST.MF
·
CQ 的构建基于
Apache Sling,而 Apache Sling 则是构建于一个
OSGi 容器之上 (确切来讲是 Apache Felix)。
OSGi 容器的行为 (就类文件的加载方式以及在 classpath 中可用) 和大多数 Java 开发者所习惯那样略有不同。
要确保类能够在 OSGi 容器中可用,需要使用一个特定的方式对它们进行打 jar 包,包括向标准的 MANIFEST.MF 文件中添加额外的元数据。这样带来的一个问题就是由其他开发者所创建的库对于 OSGi 来讲这一附加信息是缺失的,因此他们的 jar 文件也就无法部署在 CQ 中。
本文将详细介绍如何将非 OSGi 库简单可靠地暴露在 CQ 中。关于创建并部署 OSGi bundle 到 CQ 的详细步骤请参考博客《 构建并部署 OSGi bundle》。
接下来把该 bundle 将要提供给 CQ 的第三方库依赖添加进来。确保使用 property 将这些依赖的版本进行声明,因为稍后会在 POM 中对它们进行重新定义:
之后是对 bundle 插件的配置:
有很多选项可以对该插件的行为进行控制。而关键要素在于 <_exportcontents> 标签。这一设置定义了第三方 jar 的哪些包要暴露给 OSGi 环境。 .* 后缀表示该包下的所有子包都将被包含进来。将每个第三方 jar 的版本信息也包含进来很重要 - 否则这些包将会以你创建的 bundle 的版本进行标记,这几乎肯定是不正确的而且很可能会引起 OSGi 容器内包解析的问题。
对于其他设置的解释参考博客《 构建并部署 OSGi bundle》。
依赖和插件配置好以后,只需简单运行标准的 Maven package/install 来生成 OSGi bundle。在构建好的 bundle 内部, META-INF/MANIFEST.MF 现在已经将所需要的 OSGi 元数据包含进来了,示例如下:
Manifest-Version: 1.0
Export-Package: org.apache.commons.codec;version="1.3",org.apache.comm
ons.codec.net;uses:="org.apache.commons.codec,org.apache.commons.code
c.binary";version="1.3",org.apache.commons.codec.language;uses:="org.
apache.commons.codec";version="1.3",org.apache.commons.codec.digest;u
ses:="org.apache.commons.codec.binary";version="1.3",org.apache.commo
ns.codec.binary;uses:="org.apache.commons.codec";version="1.3",org.ap
ache.commons.validator.javascript;version="1.3.1",org.apache.commons.
validator.resources;version="1.3.1",org.apache.commons.validator.util
;uses:="org.apache.commons.beanutils,org.apache.commons.logging,org.a
pache.commons.collections,org.apache.commons.validator";version="1.3.
1",org.apache.commons.validator.routines;version="1.3.1",org.apache.c
ommons.validator;uses:="org.apache.commons.validator.util,org.apache.
oro.text.perl,org.apache.commons.beanutils,org.apache.commons.collect
ions,org.apache.commons.logging,org.apache.commons.digester,org.xml.s
ax,org.apache.commons.digester.xmlrules";version="1.3.1"
Embed-Directory: OSGI-INF/lib
Bundle-ClassPath: .,OSGI-INF/lib/commons-validator-1.3.1.jar,OSGI-INF/
lib/commons-codec-1.3.jar
Built-By: craig
Tool: Bnd-1.43.0
Bundle-Name: com.headwire.cqblueprints - cqblueprints-examples-wrap3rd
partylib
Created-By: Apache Maven Bundle Plugin
Build-Jdk: 1.6.0_26
Bundle-Version: 5.3.2.SNAPSHOT
Bnd-LastModified: 1311886098106
Bundle-ManifestVersion: 2
Bundle-Activator: com.headwire.cqblueprints.examples.wrap3rdpartylib.A
ctivator
Bundle-SymbolicName: com.headwire.cqblueprints.examples-wrap3rdpartyli
b
Import-Package: com.squeakysand.osgi.framework,org.apache.commons.bean
utils,org.apache.commons.collections,org.apache.commons.digester,org.
apache.commons.digester.xmlrules,org.apache.commons.logging,org.apach
e.oro.text.perl,org.xml.sax
Embed-Dependency: *;scope=compile|runtime
注意看 Export-Package 属性的值。它包含了本次封装的所有来自第三方 jar 的包,这些包将要暴露给 OSGi 容器。
此外也注意一下 Import-Package 属性的值,它不仅声明了该 bundle 中的代码可能会有的依赖,还声明了绑定进来的第三方 jar 的依赖。为了避免 OSGi 容器为绑定的 jar 提供依赖,将 bundle 中封装的 jar 的所有传递依赖也要包含进来 - 这样的话该 bundle 就是完全子包含的了可以被部署到任何容器而不会有任何问题。可以通过给该 bundle 插件添加一个额外的配置标签来轻松实现这一点:
<Embed-Transitive>true</Embed-Transitive>
现在这个 bundle 就可以部署到一个 CQ 实例了。关于这一过程的自动化实现细节参考博客《 构建并部署 OSGi bundle》。 该 bundle 所暴露的包的所有的类对于其他 bundle 以及 CQ 实例内部代码来讲都是不可见的,所以无须担心同一第三方包的引入所带来的包冲突的问题,撸起袖子去开发吧:)
原文链接: Deploying 3rd Party Libraries。
作者简介
CQBlueprints 网站旨在能够对使用 Adobe CQ 的架构师和开发人员提供一些帮助。尽管在 Adobe 的官方网站上已经有了很多关于 CQ 的相关信息,但是那些信息主要专注于 CQ 自身的产品。当你真正去落地 CQ 的时候你还得去考虑很多额外的课题(诸如,如何自主开发?开发的东西如何才能在部署环境下工作?)。 CQBlueprints 网站针对的正是这些问题。
OSGi 容器的行为 (就类文件的加载方式以及在 classpath 中可用) 和大多数 Java 开发者所习惯那样略有不同。
要确保类能够在 OSGi 容器中可用,需要使用一个特定的方式对它们进行打 jar 包,包括向标准的 MANIFEST.MF 文件中添加额外的元数据。这样带来的一个问题就是由其他开发者所创建的库对于 OSGi 来讲这一附加信息是缺失的,因此他们的 jar 文件也就无法部署在 CQ 中。
本文将详细介绍如何将非 OSGi 库简单可靠地暴露在 CQ 中。关于创建并部署 OSGi bundle 到 CQ 的详细步骤请参考博客《 构建并部署 OSGi bundle》。
一个很 low 的解决方案
对于这个问题的一个解决方案可能是拿到第三方 jar 文件,解压,向 manifest 文件中添加额外的信息,重新打包并部署到 CQ。这样做主要的问题至少有三个:- 如果该第三方 jar 文件被签过名,这么修改的结果将导致该签名失效,而任何相关的校验工具会因此产生警告甚至失败。总和检查也会有类似的问题。
- 第三方 jar 有新版更新时,这种手工 "升级" 过程需要进行再次调整。
- 在团队协作环境中,将修改后的 jar 提供给每个人都是会有问题的。可能它会被推送到团队库里并覆盖掉了那个没有修改的 jar - 大部分的库管理工具不会喜欢你这么干 (原因见第 1 点)。或者,也许每个团队成员能够把修改后的 jar 推送到自己本地库里,但是在他们每次进行构建的时候 Maven 可能要报告问题了 (原因还是去见第 1 点)。
推荐方案
理想方案是创建你自己的封装有一个或多个这种非 OSGi jar 的 OSGi bundle。这么干有以下好处:- 可以使用 Apache Maven 去自动化执行整个过程,因此这是可靠且可重复的。
- 原始的第三方 jar 没有被修改。
- 当第三方有一个新版本的 jar 时,只需要调整 POM 中的依赖。然后简单地发布该 bundle 的一个新版。
- 由于该 bundle 是一个对的 Maven artifact,它可以很方便地进行团队成员内部共享,而不会有签名以及总和检查之类的问题。
<?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>
<parent>
<groupId>com.headwire.cqblueprints</groupId>
<artifactId>parent</artifactId>
<version>5.3.2-SNAPSHOT</version>
</parent>
<artifactId>cqblueprints-examples-wrap3rdpartylib</artifactId>
<packaging>bundle</packaging>
<name>${project.groupId} - ${project.artifactId}</name>
<description>
Demonstrates how to easily deploy the classes contained in a pre-packaged vanilla jar file into an OSGi
container, when the jar does not contain the necessary OSGi manifest headers.
</description>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.5</version>
<extensions>true</extensions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接下来把该 bundle 将要提供给 CQ 的第三方库依赖添加进来。确保使用 property 将这些依赖的版本进行声明,因为稍后会在 POM 中对它们进行重新定义:
<properties>
<commons-codec.version>1.3</commons-codec.version>
<commons-validator.version>1.3.1</commons-validator.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>${commons-validator.version}</version>
</dependency>
</dependencies>
之后是对 bundle 插件的配置:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Activator>com.headwire.cqblueprints.examples.wrap3rdpartylib.Activator</Bundle-Activator>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Embed-Directory>OSGI-INF/lib</Embed-Directory>
<Private-Package>com.headwire.cqblueprints.examples.wrap3rdpartylib</Private-Package>
<_exportcontents>
org.apache.commons.codec.*;version=${commons-codec.version},
org.apache.commons.validator.*;version=${commons-validator.version}
</_exportcontents>
</instructions>
</configuration>
</plugin>
有很多选项可以对该插件的行为进行控制。而关键要素在于 <_exportcontents> 标签。这一设置定义了第三方 jar 的哪些包要暴露给 OSGi 环境。 .* 后缀表示该包下的所有子包都将被包含进来。将每个第三方 jar 的版本信息也包含进来很重要 - 否则这些包将会以你创建的 bundle 的版本进行标记,这几乎肯定是不正确的而且很可能会引起 OSGi 容器内包解析的问题。
对于其他设置的解释参考博客《 构建并部署 OSGi bundle》。
依赖和插件配置好以后,只需简单运行标准的 Maven package/install 来生成 OSGi bundle。在构建好的 bundle 内部, META-INF/MANIFEST.MF 现在已经将所需要的 OSGi 元数据包含进来了,示例如下:
Manifest-Version: 1.0
Export-Package: org.apache.commons.codec;version="1.3",org.apache.comm
ons.codec.net;uses:="org.apache.commons.codec,org.apache.commons.code
c.binary";version="1.3",org.apache.commons.codec.language;uses:="org.
apache.commons.codec";version="1.3",org.apache.commons.codec.digest;u
ses:="org.apache.commons.codec.binary";version="1.3",org.apache.commo
ns.codec.binary;uses:="org.apache.commons.codec";version="1.3",org.ap
ache.commons.validator.javascript;version="1.3.1",org.apache.commons.
validator.resources;version="1.3.1",org.apache.commons.validator.util
;uses:="org.apache.commons.beanutils,org.apache.commons.logging,org.a
pache.commons.collections,org.apache.commons.validator";version="1.3.
1",org.apache.commons.validator.routines;version="1.3.1",org.apache.c
ommons.validator;uses:="org.apache.commons.validator.util,org.apache.
oro.text.perl,org.apache.commons.beanutils,org.apache.commons.collect
ions,org.apache.commons.logging,org.apache.commons.digester,org.xml.s
ax,org.apache.commons.digester.xmlrules";version="1.3.1"
Embed-Directory: OSGI-INF/lib
Bundle-ClassPath: .,OSGI-INF/lib/commons-validator-1.3.1.jar,OSGI-INF/
lib/commons-codec-1.3.jar
Built-By: craig
Tool: Bnd-1.43.0
Bundle-Name: com.headwire.cqblueprints - cqblueprints-examples-wrap3rd
partylib
Created-By: Apache Maven Bundle Plugin
Build-Jdk: 1.6.0_26
Bundle-Version: 5.3.2.SNAPSHOT
Bnd-LastModified: 1311886098106
Bundle-ManifestVersion: 2
Bundle-Activator: com.headwire.cqblueprints.examples.wrap3rdpartylib.A
ctivator
Bundle-SymbolicName: com.headwire.cqblueprints.examples-wrap3rdpartyli
b
Import-Package: com.squeakysand.osgi.framework,org.apache.commons.bean
utils,org.apache.commons.collections,org.apache.commons.digester,org.
apache.commons.digester.xmlrules,org.apache.commons.logging,org.apach
e.oro.text.perl,org.xml.sax
Embed-Dependency: *;scope=compile|runtime
注意看 Export-Package 属性的值。它包含了本次封装的所有来自第三方 jar 的包,这些包将要暴露给 OSGi 容器。
此外也注意一下 Import-Package 属性的值,它不仅声明了该 bundle 中的代码可能会有的依赖,还声明了绑定进来的第三方 jar 的依赖。为了避免 OSGi 容器为绑定的 jar 提供依赖,将 bundle 中封装的 jar 的所有传递依赖也要包含进来 - 这样的话该 bundle 就是完全子包含的了可以被部署到任何容器而不会有任何问题。可以通过给该 bundle 插件添加一个额外的配置标签来轻松实现这一点:
<Embed-Transitive>true</Embed-Transitive>
现在这个 bundle 就可以部署到一个 CQ 实例了。关于这一过程的自动化实现细节参考博客《 构建并部署 OSGi bundle》。 该 bundle 所暴露的包的所有的类对于其他 bundle 以及 CQ 实例内部代码来讲都是不可见的,所以无须担心同一第三方包的引入所带来的包冲突的问题,撸起袖子去开发吧:)
原文链接: Deploying 3rd Party Libraries。
作者简介
CQBlueprints 网站旨在能够对使用 Adobe CQ 的架构师和开发人员提供一些帮助。尽管在 Adobe 的官方网站上已经有了很多关于 CQ 的相关信息,但是那些信息主要专注于 CQ 自身的产品。当你真正去落地 CQ 的时候你还得去考虑很多额外的课题(诸如,如何自主开发?开发的东西如何才能在部署环境下工作?)。 CQBlueprints 网站针对的正是这些问题。
更多推荐
已为社区贡献6条内容
所有评论(0)