opencms 发布过程深入研究

引入:

比起创建Resource,发布过程要困难很多,我上周在support team时候曾经设想不通过调试器,光走读代码来明白其中的奥秘,后来因为堆栈太深而放弃了,现在有了调试器,终于把这些细节弄明白了,果然非常复杂。

细节分析:

在发布Resource时,它的入口是CmsPublishProject类的actionPublish()方法,发布过程复杂到变态,全包装在performDialogOperation()方法中:

/**
     * @seeorg.opencms.workplace.CmsMultiDialog#performDialogOperation()
     */
   protected boolean performDialogOperation() throws CmsException {
 
        CmsPublishList publishList =getSettings().getPublishList();
        if (publishList == null){
            thrownew CmsException(Messages.get().container(
                org.opencms.db.Messages.ERR_GET_PUBLISH_LIST_PROJECT_1,
                getProjectname()));
        }
        OpenCms.getPublishManager().publishProject(
            getCms(),
            new CmsHtmlReport(getLocale(),getCms().getRequestContext().getSiteRoot()),
            publishList);
        // wait 2 seconds, may be it finishes fast
        OpenCms.getPublishManager().waitWhileRunning(1500);
        returntrue;
}

宏观上看,它先获得workplace中所有资源的发布列表,因为本例中只有一个资源,所以调试的列表如下:

[
publish of project:ae97ff1d-7824-11e4-8c0e-28e347ffa92b
publish history ID:f5d6e16a-787e-11e4-8289-28e347ffa92b
resources: [[org.opencms.file.CmsResource, path:/sites/default/charles_study/publish_test1, structure idea051006-787e-11e4-8289-28e347ffa92b, resource id:ea051007-787e-11e4-8289-28e347ffa92b, type id: 1, folder: false, flags: 0,project: ae97ff1d-7824-11e4-8c0e-28e347ffa92b, state: 2, date created: Sun Nov30 18:52:01 CST 2014, user created: c300ba5c-01e8-3727-b305-5dcc9ccae1ee, datelastmodified: Sun Nov 30 18:52:09 CST 2014, user lastmodified:c300ba5c-01e8-3727-b305-5dcc9ccae1ee, date released: Thu Jan 01 08:00:00 CST1970, date expired: Sun Aug 17 15:12:55 CST 292278994, date content: Sun Nov 3018:52:01 CST 2014, size: 0, sibling count: 1, version: 1]]
folders: []
deletedFolders: []
]

其次,它通过CmsPublishManager的publishProject()方法来做对于待发布列表的资源的发布工作。

CmsPublishManager会调用CmsSecurityManager的publishProject()方法,最终调用CmsDriverManager的publishObject()方法来做发布的全部工作。这就是我们要讨论的重点。

重点探讨1: 从CmsDriverManager的publishObject()方法来研究发布过程。

发布过程代码很多也很深,从宏观上看,它做了这么几件事情:

a.  检查父目录,这主要用于是否存在该资产的父目录还没被发布的情况。

b.发起一个CmsEvent类的发布前事件(beforePublishEvent),该事件中除包含上面的发布资源列表(publishList)外还包含projectId,dbContext和一个空的发布报告对象(CmsHtmlReport,用于存储发布失败的错误),该事件中的数据如下:

{publishList=
[
publish of project: ae97ff1d-7824-11e4-8c0e-28e347ffa92b
publish history ID: c0488fed-7882-11e4-8289-28e347ffa92b
resources:
 [[org.opencms.file.CmsResource, 
path:/sites/default/charles_study/publish_test4, structure 
idbb3dab29-7882-11e4-8289-28e347ffa92b, resource 
id:bb3dab2a-7882-11e4-8289-28e347ffa92b, type id: 1, folder: false, 
flags: 0,project: ae97ff1d-7824-11e4-8c0e-28e347ffa92b, state: 2, date 
created: Sun Nov30 19:19:21 CST 2014, user created: 
c300ba5c-01e8-3727-b305-5dcc9ccae1ee, datelastmodified: Sun Nov 30 
19:19:24 CST 2014, user 
lastmodified:c300ba5c-01e8-3727-b305-5dcc9ccae1ee, date released: Thu 
Jan 01 08:00:00 CST1970, date expired: Sun Aug 17 15:12:55 CST 
292278994, date content: Sun Nov 3019:19:21 CST 2014, size: 0, sibling 
count: 1, version: 1]]
folders: []
deletedFolders: []
]
,
 
[email protected],[email protected],projectId=ae97ff1d-7824-11e4-8c0e-28e347ffa92b}

然后用CmsEventManager的fireCmsEvent(beforePublishEvent)方法来执行这次发布事件。

因为一个资源的发布会影响到opencms的其他组件的更新,所以这里使用“Observer”设计模式,它吧多个cms的其他组件加到CmsEventManager的监听器列表中,监听器有几百个,我就不一一列出了。

c.用发布锁CmsLock锁住所有发布列表中的资源。

d.调用CmsPublishEngine的enqueuePublishJob()方法来记录下发布的计划任务,并产生具体的发布报告。具体来说:

  1. 它创建一个具体的CmsPublishJobInfoBean对象,它其中包含了发布所需的全部细节:
[org.opencms.publish.CmsPublishJobInfoBean, history id:c0488fed-7882-11e4-8289-28e347ffa92b, project idae97ff1d-7824-11e4-8c0e-28e347ffa92b, project name: Offline, user id:c300ba5c-01e8-3727-b305-5dcc9ccae1ee, locale: en, flags: 0, size: 1, enqueue time:0, start time: 0, finish time: 0]

2. 调用CmsPublishQueue的add方法吧这个发布作业对象添加进去,它在其中会调用CmsDriverManager的createPublishJob()方法来创建具体的发布作业,最后会调用CmsProjectDriver类的createPublishJob()方法来做具体的数据库层面的操作。

其最后执行的SQL语句是:

INSERT INTO CMS_PUBLISH_JOBS(HISTORY_ID,PROJECT_ID,PROJECT_NAME,USER_ID,PUBLISH_LOCALE,PUBLISH_FLAGS,RESOURCE_COUNT,ENQUEUE_TIME,START_TIME,FINISH_TIME,PUBLISH_LIST) VALUES (‘6709c8a5-7887-11e4-8289-28e347ffa92b‘,‘ae97ff1d-7824-11e4-8c0e-28e347ffa92b‘,‘Offline‘,‘c300ba5c-01e8-3727-b305-5dcc9ccae1ee‘,‘en‘,0,1,1417348377907,0,0,x‘ACED00057372001D6F72672E6F70656E636D732E64622E436D735075626C6973684C697374DC35DFB34A32E7310C0000787077486709C8A5788711E4828928E347FFA92BAE97FF1D782411E48C0E28E347FFA92B0000000000000001FFFFFFFF000000016244C681788711E4828928E347FFA92B000000000000000078‘)

所以从这里看出,它会更新CMS_PUBLISH_JOBS表,把发布的若干信息,比如发布历史ID,项目ID,名称,用户ID,发布国际化标示,入发布队列时间,发布开始时间,结束时间,以及发布的列表都插入数据库。

3.调用CmsPublishListenerCollection的fireEnqueued()方法来把这次发布事件通知所有的监听组件,如果发布过程失败,则从CmsPublishJob的publishReport中可以拿到发布错误信息。

e.调用checkCurrentPublishJobThread()方法来做具体的站点发布。因为这里是多线程操作而不是同步走下去的,所以开始几次调试每次都找不到执行点,后来研究了下,发现具体的站点发布代码放在CmsPublishThread的run()中,它最终会调用CmsProjectDriver类的publishProject()方法来执行具体的发布。

  1. 它首先调用CmsHistoryDriver的writeProject()方法把指定的Project写入CMS_HISTORY_PROJECTS和CMS_HISTORY_PROJECTRESOURCES表:
// write an entry in the publish project log
m_driverManager.getHistoryDriver(dbc).writeProject(dbc, publishTag, System.currentTimeMillis());

写入CMS_HISTORY_PROJECTS表的SQL语句如下:

NSERT INTO CMS_HISTORY_PROJECTS(PUBLISH_TAG,PROJECT_ID,PROJECT_NAME,PROJECT_PUBLISHDATE,PROJECT_PUBLISHED_BY,USER_ID,GROUP_ID,MANAGERGROUP_ID,PROJECT_DESCRIPTION,DATE_CREATED,PROJECT_TYPE,PROJECT_OU)VALUES(62,‘ae97ff1d-7824-11e4-8c0e-28e347ffa92b‘,‘Offline‘,1417354083096,‘c300ba5c-01e8-3727-b305-5dcc9ccae1ee‘,‘c300ba5c-01e8-3727-b305-5dcc9ccae1ee‘,‘6dfffebb-0985-3cbd-8d53-a5df8681a9f3‘,‘4d9b473f-3b73-34f7-b80c-15f068c3b2be‘,‘TheOffline Project‘,1417305967147,0,‘/‘)

写入CMS_HISTORY_PROJECTRESOURCES表的SQL语句如下:

INSERT INTO CMS_HISTORY_PROJECTRESOURCES (PUBLISH_TAG,PROJECT_ID,RESOURCE_PATH)VALUES (62,‘ae97ff1d-7824-11e4-8c0e-28e347ffa92b‘,‘/‘)

2.它会执行具体的内容变更,它会遍历发布列表publishList, 然后对其中的目录和文件分别发布,其中目录在先。对于目录的发布,它是调用以下代码来实现:

projectDriver.publishFolder(
                            dbc,
                            report,
                            ++publishedFolderCount,
                            foldersSize,
                            onlineProject,
                            new CmsFolder(currentFolder),
                            publishList.getPublishHistoryId(),
publishTag);

而对于文件的发布,它是调用以下代码来实现:

projectDriver.publishFile(
                        dbc,
                        report,
                        ++publishedFileCount,
                        filesSize,
                        onlineProject,
                        currentResource,
                        publishedContentIds,
                        publishList.getPublishHistoryId(),
publishTag);

重点探讨2:发布文件的过程(其实发布目录差不多一样,只不过因为我用文件做例子,所以只能调试出文件的发布细节了)

总的来说,CmsProjectDriver类的publishFile()方法会调用CmsProjectDriver类的publishNewFile()方法,进而调用CmsProjectDriver类的publishFileContent()方法来执行具体发布的,具体步骤如下:

a. 从CMS_OFFLINE_CONTENTS表中获取给定ResourceID的内容。

byte[] offlineContent= m_driverManager.getVfsDriver(dbc).readContent(
                dbc,
                projectIdForReading,
                offlineResource.getResourceId());

它执行的SQL语句是:

SELECT CMS_OFFLINE_CONTENTS.FILE_CONTENT FROMCMS_OFFLINE_CONTENTS WHERECMS_OFFLINE_CONTENTS.RESOURCE_ID=‘d46e46f4-7893-11e4-8289-28e347ffa92b‘

b.从上步骤的Resource构建offlineFile, 并且克隆到newFile中。

// create the file online
            newFile = (CmsFile)offlineFile.clone();
newFile.setState(CmsResource.STATE_UNCHANGED);

c.创建cms的online resources和online structure 。通过以下代码

// update the online/offlinestructure and resource records of the file
 m_driverManager.getVfsDriver(dbc).publishResource(dbc, onlineProject, newFile, offlineFile)

其中更新CMS_ONLINE_RESOURCES的SQL语句如下:

INSERT INTO CMS_ONLINE_RESOURCES(RESOURCE_ID,RESOURCE_TYPE,RESOURCE_FLAGS,DATE_CREATED,USER_CREATED,DATE_LASTMODIFIED,USER_LASTMODIFIED,RESOURCE_STATE,RESOURCE_SIZE,DATE_CONTENT,PROJECT_LASTMODIFIED,SIBLING_COUNT,RESOURCE_VERSION)VALUES(‘d46e46f4-7893-11e4-8289-28e347ffa92b‘,1,0,1417353704758,‘c300ba5c-01e8-3727-b305-5dcc9ccae1ee‘,1417353704758,‘c300ba5c-01e8-3727-b305-5dcc9ccae1ee‘,0,0,1417353704758,‘ae97ff1d-7824-11e4-8c0e-28e347ffa92b‘,1,1)

更新CMS_ONLINE_STRUCTURE的SQL语句如下:

INSERT INTO CMS_ONLINE_STRUCTURE(STRUCTURE_ID,RESOURCE_ID,RESOURCE_PATH,STRUCTURE_STATE,DATE_RELEASED,DATE_EXPIRED,PARENT_ID,STRUCTURE_VERSION)VALUES (‘d46e46f3-7893-11e4-8289-28e347ffa92b‘,‘d46e46f4-7893-11e4-8289-28e347ffa92b‘,‘/sites/default/charles_study/publish-test-11‘,0,0,9223372036854775807,‘4bf8b750-785d-11e4-8289-28e347ffa92b‘,0)

d.接着上一步,更新cms的online resources和online structure的版本号。通过以下代码:

// update version numbers
 m_driverManager.getVfsDriver(dbc).publishVersions(dbc, offlineResource, !alreadyPublished);

其中更新CMS_ONLINE_RESOURCES的版本号的SQL语句如下:

UPDATE CMS_ONLINE_RESOURCES SET RESOURCE_VERSION = 1WHERE CMS_ONLINE_RESOURCES.RESOURCE_ID =‘d46e46f4-7893-11e4-8289-28e347ffa92b‘

更新CMS_ONLINE_STRUCTURE的SQL语句 如下:

UPDATE CMS_ONLINE_STRUCTURE SET STRUCTURE_VERSION = 0 WHERECMS_ONLINE_STRUCTURE.STRUCTURE_ID = ‘d46e46f3-7893-11e4-8289-28e347ffa92b‘

e.创建online文件的内容。通过以下代码:

// create/update the content
  m_driverManager.getVfsDriver(dbc).createOnlineContent(
                dbc,
                offlineFile.getResourceId(),
                offlineFile.getContents(),
                publishTag,
                true,
                needToUpdateContent);

它执行的SQL语句如下:

INSERT INTO CMS_CONTENTS(RESOURCE_ID,FILE_CONTENT,PUBLISH_TAG_FROM,PUBLISH_TAG_TO,ONLINE_FLAG) VALUES(‘d46e46f4-7893-11e4-8289-28e347ffa92b‘,x‘‘,62,62,1)

f.创建online文件的properties信息。通过以下代码:

m_driverManager.getVfsDriver(dbc).writePropertyObjects(dbc, onlineProject, newFile, offlineProperties);

它会把去写CMS_ONLINE_PROPERTIES文件,因为我的发布的文件没有配置properties,所以调试器跳过了这一段。

g.写入新的online访问控制列表。通过以下代码:

m_driverManager.getUserDriver(dbc).publishAccessControlEntries(
                dbc,
                dbc.currentProject(),
                onlineProject,
                offlineResource.getResourceId(),
                newFile.getResourceId());

它最终会去写CMS_ONLINE_ACCESSCONTROL。

最终,发布过程成功,前端会有一个模态对话框总结下这次发布:

总结:

(1)发布过程宏观上分为发布作业的记录和实际站点发布工作。

(2)发布过程是以事件驱动的,其发布涉及到的信息资源记录在beforePublishEvent中。

(3)发布作业会维护一个作业队列,然后把发布事件添加到发布队列中。发布作业的执行会更新CMS_PUBLISH_JOBS表。

(4)发布作业的结果会通知所有监听器,这些监听器是opencms的自带组件。

(5)对于实际站点发布的工作,是另开了线程来完成的,其线程使用的执行体是CmsPublishThread类。

(6)发布线程会把当前Project信息写入CMS_HISTORY_PROJECTS和CMS_HISTORY_PROJECTRESOURCES表。

(7)具体目录和文件的发布工作,是通过发布线程遍历待发布列表来依次执行的,先执行目录发布工作,再执行文件发布工作。但两者类似。

(8)执行文件的发布会从CMS_OFFLINE_CONTENTS表中获取给定ResourceID的内容,构建offlineFile,并克隆到newFile中,然后依次创建CMS_ONLINE_RESOURCES,CMS_ONLINE_STRUCTURE文件,并更新其版本号。再在以下表(CMS_CONTENTS,CMS_ONLINE_PROPERTIES,CMS_ONLINE_ACCESSCONTROL)中分别创建新条目。

时间: 2024-10-13 11:38:33

opencms 发布过程深入研究的相关文章

QT5.x应用在Mac OS X和Windows平台的发布过程

QT是一款非常牛逼的跨平台开发工具,目前可以开发Mac OS X.Windows.Linux.Android.iOS等平台的App.对于Android和iOS平台,发布相对容易,例如,Android平台是生成apk文件上传到Android设备的,所以直接安装apk文件即可.不过对于PC平台(Mac OS X.Windows和Linux)的应用,在发布时就显得麻烦些. 本文主要介绍Mac OS X和Windows平台的发布过程.这两个平台运行的应用都称为可执行程序.Windows平台可执行文件扩展

Qt入门之基础篇 ( 二 ) :Qt项目建立、编译、运行和发布过程解析

转载请注明出处:CN_Simo. 题解: 本篇内容主讲Qt应用从创建到发布的整个过程,旨在帮助读者能够快速走进Qt的世界. 本来计划是讲解Qt源码静态编译,如此的话读者可能并不能清楚地知道为何要静态编译,所以借此篇内容说明一下原由并为之后文章的学习做准备. 即使本片内容只是在围绕一个小小的HelloWorld程序开展,但还是希望朋友们不要急于求成,"欲速则不达". 文章整体思路: 我们循序渐进地来看,一个Qt应用的完成有以下一个重要的步骤: 项目创建->源码编译->程序运行

WebServices生成发布过程及常见问题的解决方法

春夏秋冬走健康之路看四季养生网 健康饮食 养生问题 母婴保健 养生小常识 3.下一步,我们需要将Myservice文件夹拷贝到C:\Inetpub\wwwroot目录下(重要).如下图所示  然后依次右击“我的电脑”->“管理”->“服务和应用程序”->“Internet信息服务(IIS)管理器”打开IIS管理器,或者在控制面板中,通过“管理工具”打开“Internet信息服务(IIS)管理器”,如下图所示 展开“网站”节点,右键点击“默认网站”节点,依次选择“新建”->“虚拟目录

关于3G移动通信网络中用户ip的配置过程的研究(中国电信cdma2000)

在RP口对ppp过程进行研究 PPP协商过程,如下图所示: 在建立ppp过程中pdsn需要与FAAA.HAAA交互.同时在分组数据业务进行过程中这种交互更加频繁,介绍如下,分为两种情况,简单ip,移动iP: 简单ip 类似于拨号上网,每次给MS分配的IP地址是动态可变的 实现MS作为主叫的分组数据呼叫,协议简单,容易实现 切换PPP链路时,需要中断正在进行的数据通信.示意图如下: 简单ip时用户在ipcp协商过程中从pdsn的ip地址池中取得ip地址,当切换pdsn时链接中断. 下图为在具体数据

Javascript的执行过程详细研究

下面我们以更形象的示例来说明JavaScript代码在页面中的执行顺序.如果说,JavaScript引擎的工作机制比较深奥是因为它属于底层行为,那么JavaScript代码执行顺序就比较形象了,因为我们可以直观感觉到这种执行顺序,当然JavaScript代码的执行顺序是比较复杂的,所以在深入JavaScript语言之前也有必要对其进行剖析. 1.1  按HTML文档流顺序执行JavaScript代码 首先,读者应该清楚,HTML文档在浏览器中的解析过程是这样的:浏览器是按着文档流从上到下逐步解析

Win7下的网站发布的小小研究

今天闲来无事研究了一下网站的发布,之前一直以为很难的样子,当真正实现了就觉得他也不过如此,现在来把我的研究结果分享一下,如果有问题望大家提出来! 首先发布网站我们要子啊本地的电脑上安装IIS,这个就不多说了,百度一下一大堆:现在说一下建网站的步骤: 打开“控制面板”-“管理工具”-Internet信息服务管理器(没有安装IIS没有的哦),如下图操作: 然后弹出对话框,输入网站的名字,选择网站的物理路径 我这里是发布到本地计算机,所以IP写的是我自己电脑的IP 点击“确定” 最后一步就是打开我们的

SQL Server发布订阅功能研究

前提:发布订阅只能是同一个内网的机器上才能使用,其实这个可以用配置管理器的别名功能设置之后就可以了,外网的也能通过这样的方式来搞. 配置过程参考老D的文章:http://www.cnblogs.com/daizhj/archive/2009/11/18/1605293.html SQL Server发布订阅功能测试: (我发觉这个功能很不行,为什么不行,我下面其中一台设置错误了就要全部重来,那么如果我下面有几百台机器,怎么重来?) 1.采用发布订阅机制只能是读写分离的方案,也就是说A发布B订阅,

iOS:记一次Mac OS X 测试版(OS X EL Capitan) APP发布过程

从遇到问题开始说起,发布时候,出现了 Invalid Toolchain. New apps and app updates must be built with the public (GM) versions of Xcode 6 or later, and iOS 8 SDK or later. Don't submit apps built with beta software. 说明xcode 必须是release版,好吧 beta版pass掉,然后下载xcode(6.4),继续发布

记一次结巴分词.net core 2.0版 nuget发布过程

最近用到分词考虑很久,选用了结巴分词,原因见博客Lucene.net(4.8.0) 学习问题记录五: JIEba分词和Lucene的结合,以及对分词器的思考 既然选好了,难就开始行动吧 . 查了.net core版的JIEba分词目前已经有人迁移了 https://github.com/linezero/jieba.NET 不过是net core 1.1版本,看到上边有issue也没有人处理,感觉好像作者不维护了. https://github.com/SilentCC/JIEba-netcor