Adobe Experience Manager(AEM)构建于一个丰富的开源软件堆栈上,该堆栈会公开许多Java API,以供在开发过程中使用。 本文探讨了主要API以及何时以及为何应使用它们。
AEM基于4个主Java API集构建。
Adobe Experience Manager (AEM)
Apache Sling Web Framework
JCR(Apache Jackrabbit Oak)
OSGi(Apache Felix)
一般规则是首选按以下顺序使用API/抽象:
如果AEM提供的API比较好 Sling、JCR和OSGi. 如果AEM不提供API,则首选 Sling JCR和OSGi。
此顺序是一般规则,表示存在例外。 违反此规则的可接受原因包括:
众所周知的例外情况,如下所述。
更高级别的API中不提供必需的功能。
在现有代码(自定义或AEM产品代码)的上下文中操作,该代码本身使用的是不太首选的API,而移动到新API的成本是不合理的。
AEM API提供了特定于产品化用例的抽象概念和功能。
例如, AEM PageManager 和 页面 API提供了 cq:Page
AEM中表示网页的节点。
而这些节点可通过 Sling API作为资源,JCR API作为节点,AEM API为常见用例提供了抽象概念。 使用AEM API可确保产品之间的AEM行为保持一致,并可自定义和扩展到AEM。
AEM API具有一个包内首选项,该首选项由以下Java包按照优先顺序标识:
com.adobe.cq
com.adobe.granite
com.day.cq
com.adobe.cq
支持产品用例, com.adobe.granite
支持跨产品平台用例,例如工作流或任务(在产品之间使用):AEM Assets、站点等)。
com.day.cq
包含“原始”API。 这些API解决了在Adobe获取之前和/或围绕客户获取 Day CQ. 这些API受支持,不应避免使用,除非 com.adobe.cq
或 com.adobe.granite
提供(较新)替代方法。
新的抽象概念,例如 Content Fragments 和 Experience Fragments 是在 com.adobe.cq
空间而不是 com.day.cq
下文描述。
AEM支持多种查询语言。 3种主要语言包括 JCR-SQL2、 XPath和 AEM查询生成器.
最重要的问题是在整个代码库中维护一致的查询语言,以降低复杂性和了解成本。
与 Apache Oak 将它们转移到JCR-SQL2中以执行最终查询,与查询时间本身相比,转换到JCR-SQL2的时间可以忽略不计。
首选API是 AEM查询生成器,这是最高级别的抽象,为构建、执行和检索查询结果提供了一个强大的API,并提供了以下内容:
简单、参数化的查询构建(建模为映射的查询参数)
AEM谓词 支持常见查询要求
可扩展API,允许开发自定义 查询谓词
JCR-SQL2和XPath可以直接通过 Sling 和 JCR API,返回结果 Sling 资源 或 JCR节点,分别为。
AEM QueryBuilder API会泄漏ResourceResolver对象。 要缓解此漏洞,请遵循 代码示例.
Apache Sling 是支持AEM的RESTful Web框架。 Sling 提供HTTP请求路由、将JCR节点建模为资源、提供安全上下文等。
Sling 为扩展构建API具有额外的好处,这意味着增加使用构建的应用程序的行为通常会更简单、更安全 Sling API,而不是扩展性较差的JCR API。
作为访问JCR节点 Sling Resources 通过 值映射.
通过提供安全上下文 ResourceResolver.
通过ResourceResolver的创建和删除资源 创建/移动/复制/删除方法.
通过更新属性 ModiableValueMap.
构建请求处理构建基块
异步工作处理构建块
的 JCR(Java内容存储库)2.0 API 是JCR实施规范的一部分(对于AEM, Apache Jackrabbit Oak)。 所有JCR实施都必须符合并实施这些API,因此,是与AEM内容交互的最低级别API。
JCR本身是基于层次/树的NoSQL数据存储AEM用作其内容存储库。 JCR具有大量受支持的API,从内容CRUD到查询内容,不一而足。 尽管有这个强大的API,但与更高级别的AEM和 Sling 抽象。
与Apache Jackrabbit Oak API相比,始终更喜欢JCR API。 JCR API适用于 交互 具有JCR存储库,而Oak API适用于 实施 JCR存储库。
虽然JCR是AEM内容存储库,但其API并不是与内容交互的首选方法。 相反,首选AEM API(页面、资产、标记等) 或Sling资源API,因为它们提供了更好的抽象概念。
在AEM应用程序中,JCR API的会话和节点接口的广泛使用是代码气味。 确保 Sling 不应改用API。
JCR观察(侦听JCR事件)
创建深层节点结构
OSGi API与更高级别的API(AEM、 Sling、和JCR),并且很少需要使用OSGi API,并且需要高级AEM开发专业知识。
OSGi定义了所有OSGi容器必须实施和符合的规范。 AEM OSGi实施Apache Felix也提供了几个自己的API。
org.osgi
)覆盖Apache Felix API(org.apache.felix
)。用于声明OSGi服务和组件的OSGi批注。
用于动态代码内的OSGi API 取消/注册OSGi服务/组件.
以下是上述规则的常见例外。
在处理低级OSGi抽象(如在OSGi组件属性中定义或读取)时,提供的较新抽象 org.osgi
优先于较高级别的Sling基础。 相互竞争的Sling抽象概念尚未被标记为 @Deprecated
建议 org.osgi
替换。
另请注意OSGi配置节点定义首选 cfg.json
在 sling:OsgiConfig
格式。
首选 com.day.cq.dam.api
over com.adobe.granite.asset.api
.
com.day.cq
Assets API为AEM资产管理用例提供了更免费的工具。@SlingServlet
@SlingFilter
以下是有用的Java代码片段,它们使用讨论的API说明常见用例的最佳实践。 这些片段还说明了如何从较不首选的API移动到更首选的API。
自AEM 6.2起, Sling ResourceResolver为 AutoClosable
在 试用资源 语句。 使用此语法,即 resourceResolver .close()
不需要。
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
try (ResourceResolver resourceResolver = rrf.getResourceResolver(authInfo)) {
// Do work with the resourceResolver
} catch (LoginException e) { .. }
ResourceResolvers必须在 finally
块,则无法使用上面显示的自动关闭技术。
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
ResourceResolver resourceResolver = null;
try {
resourceResolver = rrf.getResourceResolver(authInfo);
// Do work with the resourceResolver
} catch (LoginException e) {
...
} finally {
if (resourceResolver != null) { resourceResolver.close(); }
}
Resource resource = ResourceResolver.getResource("/path/to/the/resource");
Resource resource = resourceResolver.getResource(node.getPath());
DamUtil.resolveToAsset(..)
解析下的任何资源 dam:Asset
按需要向上走树,以返回到资产对象。
Asset asset = DamUtil.resolveToAsset(resource);
使资源适应资产需要资源本身作为 dam:Asset
节点。
Asset asset = resource.adaptTo(Asset.class);
pageManager.getContainingPage(..)
解析下的任何资源 cq:Page
根据需要向上走树以返回到Page对象。
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");
使资源适应页面需要资源本身作为 cq:Page
节点。
Page page = resource.adaptTo(Page.class);
使用页面对象的getter获取已知属性(getTitle()
, getDescription()
等) 和 page.getProperties()
获取 [cq:Page]/jcr:content
用于检索其他属性的ValueMap。
Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());
资产API为从 [dam:Asset]/jcr:content/metadata
节点。 请注意,这不是ValueMap,不支持第2个参数(默认值和自动类型转换)。
Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");
当属性存储在AEM API(页面、资产)无法直接访问的位置(属性或相对资源)中时, Sling 资源和值映射可用于获取数据。
ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");
在这种情况下,AEM对象可能必须转换为 Sling Resource 以有效地找到所需属性或子资源。
Resource resource = page.adaptTo(Resource.class);
Resource resource = asset.adaptTo(Resource.class);
使用 Sling's ModiableValueMap 将属性写入节点。 这只能写入到即时节点(不支持相对属性路径)。
请注意 .adaptTo(ModifiableValueMap.class)
需要对资源具有写入权限,否则将返回null。
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");
resource.getResourceResolver().commit();
在AEM中正确定义和初始化页面时,需要始终使用PageManager创建页面模板。
String templatePath = "/conf/my-app/settings/wcm/templates/content-page";
boolean autoSave = true;
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
pageManager.create("/content/parent/path", "my-new-page", templatePath, "My New Page Title", autoSave);
if (!autoSave) { resourceResolver.commit(); }
ResourceResolver支持创建资源的基本操作。 创建更高级别的抽象概念(AEM页面、资产、标记等) 使用其各自经理提供的方法。
resourceResolver.create(parentResource, "my-node-name", new ImmutableMap.Builder<String, Object>()
.put("jcr:primaryType", "nt:unstructured")
.put("jcr:title", "Hello world")
.put("propertyName", "Other initial properties")
.build());
resourceResolver.commit();
ResourceResolver支持删除资源。 创建更高级别的抽象概念(AEM页面、资产、标记等) 使用其各自经理提供的方法。
resourceResolver.delete(resource);
resourceResolver.commit();