引入:
在我们第一次打开openCMS主工作区的时候,我们的请求地址是:http://localhost:8080/opencms/opencms/system/workplace/views/workplace.jsp, 它会打开一个类似如下的工作区界面:
细心的读者会发现,此工作区文件workplace.jsp在整个opencms.war中根本就没有,那么这个页面到底来自哪里呢?
解决:
我们做了调试,当请求到达opencms时候,它最早会进入OpenCmsServlet的doGet()方法,它进而会去调用OpenCmsCore的showResource()方法:
protected void showResource(HttpServletRequestreq,HttpServletResponse res) { CmsObject cms = null; try { cms = initCmsObject(req, res); …. // user is initialized, now deliver the requested resource CmsResource resource = initResource(cms, cms.getRequestContext().getUri(),req,res); if (resource != null){ // a file was read, go on process it m_resourceManager.loadResource(cms, resource, req, res); m_sessionManager.updateSessionInfo(cms, req); } } catch (Throwable t) { errorHandling(cms, req, res, t); } }
此方法从宏观上做了几件大事:
(1)初始化CMS对象和当前用户配置信息(这个我们没兴趣)
(2)根据请求URL获取Resource对象(这应该是一个对象ID)。
(3)让CmsResourceManager载入Resource对象(这应该是一个真实页面)
我们逐个分析。
亮点1: 如何根据请求URL获取Resource对象:
对于根据请求URL获取Resource对象,可以看到它首先调用了CmsResource的initResource()方法,而initResource()方法会调用CmsObject的readDefaultFile方法来从CMS的VFS(虚拟文件系统)中获取指定的Resource. 这个readDefaultFile会根据请求的资源URI来创建一个UUID,然后获取资源对象,如果获取不成功,则直接通过URI来获取资源对象。这里从调试过程可以看出,它是直接通过URI来获取资源对象:
而获取方式则是通过CmsSecurityManager的readResource()方法,它进而会委托CmsDriverManager的readResource()方法来从数据库中获取Resource对象:
public CmsResource readResource(CmsDbContext dbc, String resourcePath,CmsResourceFilter filter) throwsCmsDataAccessException { CmsUUID projectId =getProjectIdForContext(dbc); // please note: the filter will be applied in the security manager later CmsResource resource =getVfsDriver(dbc).readResource(dbc, projectId, resourcePath,filter.includeDeleted()); // context dates need to be updated updateContextDates(dbc, resource); // return the resource return resource; }
从调试结果可以看,它会构建以下的查询语句:
SELECT CMS_OFFLINE_STRUCTURE.STRUCTURE_ID,CMS_OFFLINE_STRUCTURE.RESOURCE_ID,CMS_OFFLINE_STRUCTURE.RESOURCE_PATH,CMS_OFFLINE_STRUCTURE.STRUCTURE_STATE,CMS_OFFLINE_STRUCTURE.DATE_RELEASED,CMS_OFFLINE_STRUCTURE.DATE_EXPIRED,CMS_OFFLINE_STRUCTURE.STRUCTURE_VERSION,CMS_OFFLINE_RESOURCES.RESOURCE_ID,CMS_OFFLINE_RESOURCES.RESOURCE_TYPE,CMS_OFFLINE_RESOURCES.RESOURCE_FLAGS,CMS_OFFLINE_RESOURCES.RESOURCE_STATE,CMS_OFFLINE_RESOURCES.DATE_CREATED,CMS_OFFLINE_RESOURCES.DATE_LASTMODIFIED,CMS_OFFLINE_RESOURCES.USER_CREATED,CMS_OFFLINE_RESOURCES.USER_LASTMODIFIED,CMS_OFFLINE_RESOURCES.PROJECT_LASTMODIFIEDASLOCKED_IN_PROJECT,CMS_OFFLINE_RESOURCES.RESOURCE_SIZE,CMS_OFFLINE_RESOURCES.DATE_CONTENT,CMS_OFFLINE_RESOURCES.SIBLING_COUNT,CMS_OFFLINE_RESOURCES.RESOURCE_VERSION,CMS_OFFLINE_RESOURCES.PROJECT_LASTMODIFIEDFROM CMS_OFFLINE_STRUCTURE,CMS_OFFLINE_RESOURCES WHERECMS_OFFLINE_STRUCTURE.RESOURCE_PATH='/system/workplace/views/workplace.jsp' ANDCMS_OFFLINE_STRUCTURE.RESOURCE_ID=CMS_OFFLINE_RESOURCES.RESOURCE_ID ORDER BYCMS_OFFLINE_STRUCTURE.STRUCTURE_STATE ASC
此查询的用意是根据请求的RESOURCE_PATH来查找CMS离线结构表CMS_OFFLINE_STRUCTURE(很好理解,因为这块是框架层面的,不是对外作为站点内容给用户看的,所以这些资源应该是offline的结构资源),获取对应的RESOURCE_ID和其他一些相关信息:
这样,我们就获得了资源ID为9c0a79be-f058-11d8-ba65-db38cc8f19d7 的资源,这就是请求workplace.jsp的资源ID。此资源对象的信息从调试器中获知为:
[org.opencms.file.CmsResource, path:/system/workplace/views/workplace.jsp, structure id840ccc67-11b7-11db-91cd-fdbae480bac9, resource id:9c0a79be-f058-11d8-ba65-db38cc8f19d7, type id: 4, folder: false, flags: 0, project:00000000-0000-0000-0000-000000000000, state: 0, date created: Mon Jun 2716:00:00 CST 2005, user created: c300ba5c-01e8-3727-b305-5dcc9ccae1ee, datelastmodified: Mon Jan 29 18:25:58 CST 2007, 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 3008:06:18 CST 2014, size: 1546, sibling count: 2, version: 1]
亮点2:如何根据Resource对象获得真实Resource并进行渲染:
这就是靠CmsResourceManager的loadResource()方法来做具体的文件读取,然后通过CmsSessionManager来更新会话了。具体略,其实质就是通过刚才的ResourceID查找CMS_CONTENT表获取对应的页面JSP文件,然后利用合适的加载器来加载。因为fileType是jsp,所以最终使用的加载器是CmsJspLoader.
我们查找数据库CMS_CONTENTS表,果然找到了对应的记录:
将该BLOB打开,我们查看内容:
发现它是用的frameset结构,之前会先外部引入一些js 文件,比如explorer.js,ajax.js,top_js.jsp,之后在frameset中,又会先包含top_head.jsp,主体和top_foot.jsp ,而这一切和我们从Firebug的网络视图中看到的是一样的:
总结:
(1)这些最终请求的资源文件都以BLOB形式存放在数据库中而不是直接在opencms.war中。
(2)从请求URI到资源ID获取通过查找CMS_OFFLINE_STRUCTURE和CMS_OFFLINE_RESOURCES这两张表。CmsResource,CmsObject,CmsSecurityManager,CmsDriverManager都参与其中。
(3)从资源ID到真实页面的JSP文件通过查找CMS_CONTENTS表,CmsResourceManager参与其中。