AEM 项目结构

TIP
熟悉基本的AEM项目原型useFileVault Content Maven插件,因为本文是在这些知识和概念的基础上进行构建的。

本文概述了Adobe Experience Manager Maven项目要与AEM as a Cloud Service兼容所需的更改,需要确保这些项目尊重可变和不可变内容的拆分。 此外,建立依赖关系以创建无冲突的确定性部署,并将它们打包到可部署结构中。

AEM应用程序部署必须由单个AEM包组成。 此包继而应包含子包,这些子包包含应用程序正常运行所需的所有内容,包括代码、配置和任何支持的基线内容。

AEM要求将​ content ​和​ code ​分开,这意味着单个内容包​ 不能 ​部署到存储库的​ 同时 /apps和运行时可写区域(例如,/content/conf/home或非/apps区域)。 相反,应用程序必须将代码和内容分离到离散包中,以部署到 AEM 中。

本文档中概述的包结构与本地开发部署​ AEM 云服务部署兼容。

TIP
本文档中概述的配置由AEM项目Maven Archetype 24或更高版本提供。

存储库的可变与不可变区域 mutable-vs-immutable

AEM的/apps/libs区域被视为​ 不可变,因为在AEM启动(即在运行时)后无法对其进行更改(创建、更新、删除)。 在运行时更改不可变区域的任何尝试都将失败。

存储库中的其他所有内容,/content/conf/var/etc/oak:index/system/tmp等都是​ 可变的 ​区域,这意味着可在运行时更改这些区域。

WARNING
与在AEM的早期版本中一样,不应修改/libs。 只有AEM产品代码可以部署到/libs

Oak索引 oak-indexes

Oak索引(/oak:index)由AEM as a Cloud Service部署过程管理。 这是因为Cloud Manager必须等到部署任何新索引并完全重新索引后,才能切换到新代码图像。

因此,尽管Oak索引在运行时是可变的,但必须将其部署为代码,以便在安装任何可变包之前可以安装这些索引。 因此/oak:index配置是代码包的一部分,而不是内容包的一部分,如下所述

TIP
有关AEM as a Cloud Service中索引的更多详细信息,请参阅内容搜索和索引

Experience Manager的项目包结构

此图概述了推荐的项目结构和包部署对象。

推荐的应用程序部署结构如下:

代码包/OSGi包

  • 将生成OSGi捆绑包Jar文件,并直接将其嵌入所有项目中。

  • ui.apps包包含要部署的所有代码,并且只部署到/appsui.apps包的常见元素包括但不限于:

NOTE
必须将相同的代码部署到所有环境中。 此代码可确保某个置信度级别,以便暂存环境中的验证也处于生产状态。 有关详细信息,请参阅运行模式部分。

内容包

  • ui.content包包含所有内容和配置。 内容包包含不在ui.appsui.config包中的所有节点定义,换言之,包含不在/apps/oak:index中的所有节点定义。 ui.content包的常见元素包括但不限于:

    • 上下文感知配置
      • /conf
    • 必需的复杂内容结构(即基于并扩展过去在Repo Init中定义的基准内容结构的内容构建)。
      • /content/content/dam等。
    • 受控制的标记分类
      • /content/cq:tags
    • 旧版etc节点(理想情况下,将这些节点迁移到非/etc位置)
      • /etc

容器包

  • all包是一个容器包,其中仅包含可部署的项目、OSGI捆绑Jar文件、ui.appsui.configui.content包作为嵌入。 all包不得具有​ 自己的任何内容或代码,而是将所有部署委派到存储库的子包或OSGi捆绑包Jar文件。

    包现在使用Maven FileVault Package Maven插件的嵌入配置而非<subPackages>配置包含。

    对于复杂的Experience Manager部署,可能需要在AEM中创建表示特定站点或租户的多个ui.appsui.configui.content项目/包。 如果完成了此方法,请确保遵循可变和不可变内容之间的拆分,并将所需的内容包和OSGi捆绑Jar文件作为子包嵌入到all容器内容包中。

    例如,复杂的部署内容包结构可能如下所示:

    • all内容包嵌入以下包,以创建单个部署构件

      • common.ui.apps部署​ 站点A和站点B均所需的代码
      • 网站A所需的site-a.core OSGi包Jar
      • site-a.ui.apps部署站点A所需的代码
      • site-a.ui.config部署站点A所需的OSGi配置
      • site-a.ui.content部署站点A所需的内容和配置
      • 站点B所需的site-b.core OSGi包Jar
      • site-b.ui.apps部署站点B所需的代码
      • site-b.ui.config部署站点B所需的OSGi配置
      • site-b.ui.content部署站点B所需的内容和配置
  • ui.config包包含所有OSGi配置

    • 被视为代码并属于OSGi包,但不包含常规内容节点。 因此,它被标记为容器包装

    • 包含特定于运行模式的OSGi配置定义的组织文件夹

      • /apps/my-app/osgiconfig
    • 公共OSGi配置文件夹,其中包含适用于所有目标AEM as a Cloud Service部署目标的默认OSGi配置

      • /apps/my-app/osgiconfig/config
    • 运行特定于模式的OSGi配置文件夹,其中包含适用于所有目标AEM as a Cloud Service部署目标的默认OSGi配置

      • /apps/my-app/osgiconfig/config.<author|publish>.<dev|stage|prod>
    • Repo Init OSGi配置脚本

      • Repo Init是部署(可变)内容的推荐方法,这些内容在逻辑上是AEM应用程序的一部分。 Repo Init OSGi配置应放置在如上所述的相应config.<runmode>文件夹中,并用于定义:

        • 基线内容结构
        • 用户
        • 服务用户
        • ACL(权限)

额外的应用程序包 extra-application-packages

如果AEM部署使用其他AEM项目(项目本身由自己的代码和内容包组成),则其容器包应嵌入到项目的all包中。

例如,包含两个供应商AEM应用程序的AEM项目可能如下所示:

  • all内容包嵌入以下包,以创建单个部署构件

    • AEM应用程序所需的core OSGi包Jar
    • ui.apps部署AEM应用程序所需的代码
    • ui.config部署AEM应用程序所需的OSGi配置
    • ui.content部署AEM应用程序所需的内容和配置
    • vendor-x.all部署供应商X应用程序所需的一切(代码和内容)
    • vendor-y.all部署供应商Y应用程序所需的一切(代码和内容)

包类型 package-types

将为其声明的包类型标记包。 包类型有助于阐明包的用途和部署。

  • 容器包必须将其packageType设置为container。 容器包不得包含常规节点。 仅允许OSGi包、配置和子包。 AEM as a Cloud Service中的容器不允许使用安装挂接
  • 代码(不可变)包必须将其packageType设置为application
  • 内容(可变)包必须将其packageType设置为content

有关详细信息,请参阅下面的Apache Jackrabbit FileVault - Package Maven插件文档Apache Jackrabbit包类型FileVault Maven配置片段

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

将包标记为通过AdobeCloud Manager进行部署 marking-packages-for-deployment-by-adoube-cloud-manager

默认情况下,AdobeCloud Manager会收集由Maven内部版本生成的所有包。 但是,由于容器(all)包是包含所有代码和内容包的单个部署对象,因此您必须确保仅​ 部署 ​容器(all)包。 要确保这一点,Maven 内部版本生成的其他包必须使用 <properties><cloudManagerTarget>none</cloudManageTarget></properties> 的 FileVault Content Package Maven Plug-In 配置进行标记。

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

存储库初始 repo-init

Repo Init提供定义JCR结构的说明或脚本,这些说明或脚本涵盖了公用节点结构(如文件夹树)以及用户、服务用户、组和ACL定义。

Repo Init的主要优势在于,它们具有执行由其脚本定义的所有操作的隐式权限。 而且,此类脚本会在部署生命周期的早期调用,以确保在运行代码时存在所有必需的JCR结构。

虽然Repo Init脚本本身作为脚本存在于ui.config项目中,但它们可以而且应该用于定义以下可变结构:

  • 基线内容结构
  • 服务用户
  • 用户
  • ACL

Repo Init脚本存储为RepositoryInitializer OSGi工厂配置的scripts个条目。 因此,它们可以通过运行模式隐式定位,从而在AEM Author和AEM Publish Services的Repo Init脚本之间甚至环境之间(Dev、Stage和Prod)实现差异。

Repo Init OSGi配置最好采用.config OSGi配置格式编写,因为它们支持多行,这与使用.cfg.json定义OSGi配置的最佳实践不同。

在定义用户和组时,只有组被视为应用程序的一部分,并且是其功能的组成部分。 您仍然可以在AEM中在运行时定义“组织用户和组”。 例如,如果自定义工作流将工作分配给指定的组,请在AEM应用程序中通过Repo Init定义该组。 但是,如果分组仅是组织性的,例如“Wendy's Team”和“Sean's Team”,则最好在AEM中在运行时定义和管理这些组。

TIP
存储库初始化脚本​ 必须 ​在内联scripts字段中定义,否则references配置不起作用。

Apache Sling Repo Init文档上提供了Repo Init脚本的完整词汇。

TIP
有关完整代码片段,请参阅下面的Repo Init代码片段部分。

存储库结构包 repository-structure-package

代码包需要配置FileVault Maven插件的配置以引用强制结构依赖关系正确性的<repositoryStructurePackage>(以确保一个代码包不会安装在另一个代码包上)。 您可以为您的项目创建自己的存储库结构包。

仅代码包需要,这意味着任何标有<packageType>application</packageType>的包。

要了解如何为应用程序创建存储库结构包,请参阅开发存储库结构包

内容包(<packageType>content</packageType>) ​需要此存储库结构包。

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

在容器软件包中嵌入子包 embeddeds

内容或代码包将放置在一个特殊的“side-car”文件夹中,并可使用FileVault Maven插件的<embeddeds>配置定向到AEM author和/或AEM Publish上进行安装。 不要使用<subPackages>配置。

常见用例包括:

  • AEM作者用户和AEM发布用户之间的ACL/权限不同
  • 仅用于支持AEM创作活动的配置
  • 仅需要在AEM创作实例上运行的代码,例如与后台系统的集成

嵌入包

要定位AEM创作、AEM发布或两者,包将嵌入到特殊文件夹位置的all容器包中,格式如下:

/apps/<app-name>-packages/(content|application|container)/install(.author|.publish)?

划分此文件夹结构:

  • 第一级文件夹​ 必须为 /apps

  • 第二级文件夹表示应用程序,该应用程序已将-packages后固定到文件夹名称。 通常,所有子包都嵌入在仅有一个第二级文件夹下,但可以创建任意数量的第二级文件夹以最好地表示应用程序的逻辑结构:

    • /apps/my-app-packages
    • /apps/my-other-app-packages
    • /apps/vendor-packages
    note warning
    WARNING
    按照惯例,子包嵌入式文件夹的名称带有后缀-packages。 此命名可确保部署代码和内容包​ 不是 ​部署任何子包/apps/<app-name>/...的目标文件夹,从而导致破坏性的循环安装行为。
  • 第三级文件夹必须为
    applicationcontentcontainer

    • application文件夹包含代码包
    • content文件夹包含内容包
    • container文件夹包含AEM应用程序可能包含的任何额外应用程序包
      此文件夹名称对应于它包含的包类型
  • 第4级文件夹包含子包,并且必须是以下包之一:

    • install,以便您同时在​ 上安装 AEM作者和AEM发布
    • install.author,以便在AEM作者上安装​ only
    • install.publish,以便在AEM发布上安装​ only
      install.authorinstall.publish受支持的目标。 不支持其 他运行模式

例如,包含AEM创作和发布特定包的部署可能如下所示:

  • all容器包嵌入以下包,以创建单个部署对象

    • 嵌入到/apps/my-app-packages/application/install中的ui.apps将代码部署到AEM作者和AEM发布
    • 嵌入到/apps/my-app-packages/application/install.author中的ui.apps.author将代码仅部署到AEM作者
    • /apps/my-app-packages/content/install中嵌入的ui.content将内容和配置部署到AEM创作和AEM发布
    • /apps/my-app-packages/content/install.publish中嵌入的ui.content.publish将内容和配置仅部署到AEM发布
TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

容器包的过滤器定义 container-package-filter-definition

由于在容器包中嵌入了代码和内容子包,因此必须将嵌入的目标路径添加到容器项目的filter.xml中。 这样可以确保在构建容器包时将嵌入的包包含在容器包中。

只需为包含要部署的子包的任何第二级文件夹添加<filter root="/apps/<my-app>-packages"/>条目即可。

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

嵌入第三方包 embedding-3rd-party-packages

所有包必须可通过Adobe的公共Maven对象存储库或可访问的公共、可引用的第三方Maven对象存储库使用。

如果第三方包位于​ Adobe的公共Maven对象存储库 ​中,则无需进一步配置即可AdobeCloud Manager以解决对象。

如果第三方包位于​ 公共第三方Maven对象存储库 ​中,则必须在该项目的pom.xml中注册此存储库,并将其嵌入到上面概述的方法之后

第三方应用程序/连接器应使用其all包作为项目容器(all)包中的容器进行嵌入。

添加Maven依赖项遵循标准Maven实践,嵌入第三方工件(代码和内容包)的步骤如上所述🔗

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

ui.content包中ui.apps之间的包依赖关系 package-dependencies

为确保正确安装包,建议建立包间依赖关系。

一般规则是包含可变内容的包(ui.content)应依赖于支持呈现和使用可变内容的不可变代码(ui.apps)。

此常规规则的显着例外是如果不可变代码包(ui.apps或任何其他包),only ​包含OSGi包。 如果是,则任何AEM包都不应声明其依赖项。 原因是​ ​包含OSGi捆绑包的不可变代码包未向AEM 包管理器注册。 因此,任何依赖于它的AEM包都存在未满足的依赖关系,无法安装。

TIP
有关完整的代码片段,请参阅下面的POM XML代码片段部分。

内容包依赖项的常见模式包括:

简单部署包依赖关系 simple-deployment-package-dependencies

简单用例将ui.content可变内容包设置为依赖于ui.apps不可变代码包。

  • all没有依赖项

    • ui.apps没有依赖项
    • ui.content依赖于ui.apps

复杂的部署包依赖关系 complex-deploxment-package-dependencies

复杂的部署会根据简单的用例进行扩展,并设置相应可变内容和不可变代码包之间的依赖关系。 根据需要,还可以在不可变代码包之间建立依赖关系。

  • all没有依赖项

    • common.ui.apps.common没有依赖项
    • site-a.ui.apps依赖于common.ui.apps
    • site-a.ui.content依赖于site-a.ui.apps
    • site-b.ui.apps依赖于common.ui.apps
    • site-b.ui.content依赖于site-b.ui.apps

本地开发和部署 local-development-and-deployment

本文中概述的项目结构和组织是​ 完全兼容的 ​本地开发AEM实例。

POM XML片段 pom-xml-snippets

以下是Maven pom.xml配置片段,可添加到Maven项目中以与上述建议保持一致。

包类型 xml-package-types

作为子包部署的代码和内容包必须声明​ 应用程序 ​或​ 内容 ​的包类型,具体取决于它们包含的内容。

容器包类型 container-package-types

容器all/pom.xml项目​ ​声明<packageType>

代码(不可变)包类型 immutable-package-types

代码包必须将其packageType设置为application

ui.apps/pom.xml中,filevault-package-maven-plugin插件声明的<packageType>application</packageType>生成配置指令声明了其包类型。

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.jackrabbit</groupId>
      <artifactId>filevault-package-maven-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        <group>${project.groupId}</group>
        <name>my-app.ui.apps</name>
        <packageType>application</packageType>
        <accessControlHandling>merge</accessControlHandling>
        <properties>
          <cloudManagerTarget>none</cloudManagerTarget>
        </properties>
      </configuration>
    </plugin>
    ...

内容(可变)包类型 mutable-package-types

内容包必须将其packageType设置为content

ui.content/pom.xml中,filevault-package-maven-plugin插件声明的<packageType>content</packageType>生成配置指令声明了其包类型。

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.jackrabbit</groupId>
      <artifactId>filevault-package-maven-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        <group>${project.groupId}</group>
        <name>my-app.ui.content</name>
        <packageType>content</packageType>
        <accessControlHandling>merge</accessControlHandling>
        <properties>
          <cloudManagerTarget>none</cloudManagerTarget>
        </properties>
      </configuration>
    </plugin>
    ...

将包标记为AdobeCloud Manager部署 cloud-manager-target

在生成包的每个项目中, ​容器 (all) 项目外,将 <cloudManagerTarget>none</cloudManagerTarget> 添加到 filevault-package-maven-plugin 插件声明的 <properties> 配置中,以确保 Adobe Cloud Manager ​部署这些它们。容器(all)包应该是通过Cloud Manager部署的单个包,该包又嵌入了所有必需的代码和内容包。

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.jackrabbit</groupId>
      <artifactId>filevault-package-maven-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        ...
        <properties>
          <cloudManagerTarget>none</cloudManagerTarget>
        </properties>
      </configuration>
    </plugin>
    ...

存储库初始 snippet-repo-init

包含Repo Init脚本的Repo Init脚本是通过scripts属性在RepositoryInitializer OSGi工厂配置中定义的。 由于这些脚本是在OSGi配置中定义的,因此可以使用常用的../config.<runmode>文件夹语义通过运行模式轻松限定其作用域。

由于脚本通常是多行声明,因此在.config文件中定义脚本比基于JSON的.cfg.json格式更容易。

/apps/my-app/config.author/org.apache.sling.jcr.repoinit.RepositoryInitializer-author.config

scripts=["
    create service user my-data-reader-service

    set ACL on /var/my-data
        allow jcr:read for my-data-reader-service
    end

    create path (sling:Folder) /conf/my-app/settings
"]

scripts OSGi属性包含由Apache Sling的Repo Init语言定义的指令。

存储库结构包 xml-repository-structure-package

ui.apps/pom.xml以及声明代码包(<packageType>application</packageType>)的任何其他pom.xml中,将以下存储库结构包配置添加到FileVault Maven插件。 您可以为您的项目创建自己的存储库结构包。

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.jackrabbit</groupId>
      <artifactId>filevault-package-maven-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        ...
        <repositoryStructurePackages>
          <repositoryStructurePackage>
              <groupId>${project.groupId}</groupId>
              <artifactId>ui.apps.structure</artifactId>
              <version>${project.version}</version>
          </repositoryStructurePackage>
        </repositoryStructurePackages>
      </configuration>
    </plugin>
    ...

在容器软件包中嵌入子包 xml-embeddeds

all/pom.xml中,将以下<embeddeds>指令添加到filevault-package-maven-plugin插件声明。 请记住,不要 ​使用<subPackages>配置。 原因是它包含/etc/packages中的子包,而不是/apps/my-app-packages/<application|content|container>/install(.author|.publish)?

...
<plugin>
  <groupId>org.apache.jackrabbit</groupId>
  <artifactId>filevault-package-maven-plugin</artifactId>
  <extensions>true</extensions>
  <configuration>
      ...
      <embeddeds>

          <!-- Include the application's ui.apps and ui.content packages -->
          <!-- Ensure the artifactIds are correct -->

          <!-- OSGi Bundle Jar file that deploys to BOTH AEM Author and AEM Publish -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.core</artifactId>
              <type>jar</type>
              <target>/apps/my-app-packages/application/install</target>
          </embedded>

          <!-- Code package that deploys to BOTH AEM Author and AEM Publish -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.ui.apps</artifactId>
              <type>zip</type>
              <target>/apps/my-app-packages/application/install</target>
          </embedded>

           <!-- OSGi configuration code package that deploys to BOTH AEM Author and AEM Publish -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.ui.config</artifactId>
              <type>zip</type>
              <target>/apps/my-app-packages/application/install</target>
          </embedded>

          <!-- Code package that deploys ONLY to AEM Author -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.ui.apps.author</artifactId>
              <type>zip</type>
              <target>/apps/my-app-packages/application/install.author</target>
          </embedded>

          <!-- Content package that deploys to BOTH AEM Author and AEM Publish -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.ui.content</artifactId>
              <type>zip</type>
              <target>/apps/my-app-packages/content/install</target>
          </embedded>

          <!-- Content package that deploys ONLY to AEM Publish -->
          <embedded>
              <groupId>${project.groupId}</groupId>
              <artifactId>my-app.ui.content.publish-only</artifactId>
              <type>zip</type>
              <target>/apps/my-app-packages/content/install.publish</target>
          </embedded>

          <!-- Include any other extra packages  -->
          <embedded>
              <groupId>com.vendor.x</groupId>
              <artifactId>vendor.plug-in.all</artifactId>
              <type>zip</type>
              <target>/apps/vendor-packages/container/install</target>
          </embedded>
      <embeddeds>
  </configuration>
</plugin>
...

容器包的过滤器定义 xml-container-package-filters

all项目的filter.xml (all/src/main/content/jcr_root/META-INF/vault/definition/filter.xml)中,包含 ​要部署的包含子包的任何-packages文件夹:

<filter root="/apps/my-app-packages"/>

如果在嵌入目标中使用多个/apps/*-packages,则必须在此处枚举所有这些。

第三方Maven存储库 xml-3rd-party-maven-repositories

WARNING
添加更多Maven存储库可能会延长Maven构建时间,因为会检查其他Maven存储库的依赖项。

在Reactor项目的pom.xml中,添加任何必要的第三方公共Maven存储库指令。 完整的<repository>配置应该可以从第三方存储库提供程序中获得。

<repositories>
  ...
  <repository>
      <id>3rd-party-repository</id>
      <name>Public Third-Party Repository</name>
      <url>https://repo.3rdparty.example.com/...</url>
      <releases>
          <enabled>true</enabled>
          <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
          <enabled>false</enabled>
      </snapshots>
  </repository>
  ...
</repositories>

ui.content包中ui.apps之间的包依赖关系 xml-package-dependencies

ui.content/pom.xml中,将以下<dependencies>指令添加到filevault-package-maven-plugin插件声明。

...
<plugin>
  <groupId>org.apache.jackrabbit</groupId>
  <artifactId>filevault-package-maven-plugin</artifactId>
  <extensions>true</extensions>
  <configuration>
      ...
      <dependencies>
        <!-- Declare the content package dependency in the ui.content/pom.xml on the ui.apps project -->
        <dependency>
            <groupId${project.groupId}</groupId>
            <artifactId>my-app.ui.apps</artifactId>
            <version>${project.version}</version>
        </dependency>
      </dependencies>
    ...
  </configuration>
</plugin>
...

清除容器项目的目标文件夹 xml-clean-container-package

all/pom.xml中,添加在Maven生成之前清理目标目录的maven-clean-plugin插件。

<plugins>
  ...
  <plugin>
    <artifactId>maven-clean-plugin</artifactId>
    <executions>
      <execution>
        <id>auto-clean</id>
        <!-- Run at the beginning of the build rather than the default, which is after the build is done -->
        <phase>initialize</phase>
        <goals>
          <goal>clean</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
  ...
</plugins>

其他资源 additional-resources

recommendation-more-help
fbcff2a9-b6fe-4574-b04a-21e75df764ab