使用内容组件创建博客文章时报错。
异常信息:
ERROR: Could not complete the Create a new Blog Entry [file:/F:/ofbiz/apache-ofbiz-16.11.02/applications/content/minilang/blog/BlogServices.xml#createBlogEntry]
process [problem invoking the [createTextContent] service with the map named [createText] containing
[{dataTemplateTypeId=NONE, ownerContentId=10030, mapKey=ARTICLE, description=自助机维护描述, locale=zh, contentName=自助机维护, contentPurposeTypeId=ARTICLE, userLogin=[GenericEntity:UserLogin][createdStamp,2017-05-26 09:27:16.0(java.sql.Timestamp)][createdTxStamp,2017-05-26 09:27:16.0(java.sql.Timestamp)][currentPassword,$SHA$RmNEvG$6nKzKSOBnQy3BZohBuYkSklOtw8(java.lang.String)][disabledBy,null()][disabledDateTime,2017-10-18 10:09:20.0(java.sql.Timestamp)][enabled,Y(java.lang.String)][externalAuthId,null()][hasLoggedOut,N(java.lang.String)][isSystem,null()][lastCurrencyUom,null()][lastLocale,zh(java.lang.String)][lastTimeZone,null()][lastUpdatedStamp,2017-11-06 07:59:33.0(java.sql.Timestamp)][lastUpdatedTxStamp,2017-11-06 07:59:33.0(java.sql.Timestamp)][partyId,admin(java.lang.String)][passwordHint,Auto-Generated Password(java.lang.String)][requirePasswordChange,N(java.lang.String)][successiveFailedLogins,0(java.lang.Long)][userLdapDn,null()][userLoginId,admin(java.lang.String)], textData=自助机维护常见维护方式 自助机维护常见维护方式 自助机维护常见维护方式, statusId=CTNT_INITIAL_DRAFT, contentAssocTypeId=SUB_CONTENT, contentIdFrom=10034, partyId=admin, dataResourceTypeId=ELECTRONIC_TEXT}]
: 找不到下列必须的参数: [IN] [createElectronicText.dataResourceId]]
分析查找原因:
根据异常提示信息,我们知道错误是发生在BlogServices.xml#createBlogEntry服务调用createTextContent服务时,传的参数createText不包含dataResourceId,所以异常了。
下面按流程查找相关配置:
1.提交时发出post请求
2.controller.xml根据请求映射进行相应的处理
<request-map uri="createBlogArticle">
<security https="true" auth="true"/>
<event type="service" invoke="createBlogEntry"/>
<response name="error" type="view" value="EditBlogArticle"/>
<response name="success" type="view" value="blogContent"/>
</request-map>
3.通过ofbiz的服务引擎查看createBlogEntry服务的定义
从上面的定义可以看到,dataResourceId并不是必须要输入的参数。接着继续查找原因。
4.查看createBlogEntry服务的具体实现(apache-ofbiz-16.11.02\applications\content\minilang\blog\BlogServices.xml)
<simple-method method-name="createBlogEntry" short-description="Create a new Blog Entry">
<set field="contentAssocTypeId" value="PUBLISH_LINK"/>
<set field="ownerContentId" from-field="parameters.blogContentId"/>
<set field="contentIdFrom" from-field="parameters.blogContentId"/>
<if-empty field="parameters.statusId">
<set field="parameters.statusId" value="CTNT_INITIAL_DRAFT"/>
</if-empty>
<if-empty field="parameters.templateDataResourceId">
<set field="parameters.templateDataResourceId" value="BLOG_TPL_TOPLEFT"/>
</if-empty>
<!-- determine of we need to create complex template structure or simple content structure -->
<if-empty field="parameters.contentName">
<add-error>
<fail-property resource="ContentUiLabels" property="ContentArticleNameIsMissing"/>
</add-error>
</if-empty>
<check-errors/>
<!-- complex template structure (image & text) -->
<set field="createMain.dataResourceId" from-field="parameters.templateDataResourceId"/>
<set field="createMain.contentAssocTypeId" from-field="contentAssocTypeId"/>
<set field="createMain.contentName" from-field="parameters.contentName"/>
<set field="createMain.description" from-field="parameters.description"/>
<set field="createMain.statusId" from-field="parameters.statusId"/>
<set field="createMain.contentIdFrom" from-field="contentIdFrom"/>
<set field="createMain.partyId" from-field="userLogin.partyId"/>
<set field="createMain.ownerContentId" from-field="ownerContentId"/>
<set field="createMain.dataTemplateTypeId" value="SCREEN_COMBINED"/>
<set field="createMain.mapKey" value="MAIN"/>
<call-service service-name="createContent" in-map-name="createMain">
<result-to-field result-name="contentId" field="contentId"/>
</call-service>
<!-- reset contentIdFrom to new contentId -->
<set field="contentAssocTypeId" value="SUB_CONTENT"/>
<set field="contentIdFrom" from-field="contentId"/>
<if-not-empty field="parameters._uploadedFile_fileName">
<!-- upload a picture -->
<set field="createImage.dataResourceTypeId" value="LOCAL_FILE"/>
<set field="createImage.dataTemplateTypeId" value="NONE"/>
<set field="createImage.mapKey" value="IMAGE"/>
<set field="createImage.ownerContentId" from-field="ownerContentId"/>
<set field="createImage.contentName" from-field="parameters.contentName"/>
<set field="createImage.description" from-field="parameters.description"/>
<set field="createImage.statusId" from-field="parameters.statusId"/>
<set field="createImage.contentAssocTypeId" from-field="contentAssocTypeId"/>
<set field="createImage.contentIdFrom" from-field="contentIdFrom"/>
<set field="createImage.partyId" from-field="userLogin.partyId"/>
<set field="createImage.isPublic" value="Y"/>
<set field="createImage.uploadedFile" from-field="parameters.uploadedFile"/>
<set field="createImage._uploadedFile_fileName" from-field="parameters._uploadedFile_fileName"/>
<set field="createImage._uploadedFile_contentType" from-field="parameters._uploadedFile_contentType"/>
<call-service service-name="createContentFromUploadedFile" in-map-name="createImage">
<result-to-field result-name="contentId" field="imageContentId"/>
</call-service>
</if-not-empty>
<if-not-empty field="parameters.articleData">
<!-- create text data -->
<set field="createText.dataResourceTypeId" value="ELECTRONIC_TEXT"/>
<set field="createText.contentPurposeTypeId" value="ARTICLE"/>
<set field="createText.dataTemplateTypeId" value="NONE"/>
<set field="createText.mapKey" value="MAIN"/>
<set field="createText.ownerContentId" from-field="ownerContentId"/>
<set field="createText.contentName" from-field="parameters.contentName"/>
<set field="createText.description" from-field="parameters.description"/>
<set field="createText.statusId" from-field="parameters.statusId"/>
<set field="createText.contentAssocTypeId" from-field="contentAssocTypeId"/>
<set field="createText.textData" from-field="parameters.articleData"/>
<set field="createText.contentIdFrom" from-field="contentIdFrom"/>
<set field="createText.partyId" from-field="userLogin.partyId"/>
<set field="createText.mapKey" value="ARTICLE"/>
<log level="info" message="calling createTextContent with map: ${createText}"/>
<call-service service-name="createTextContent" in-map-name="createText">
<result-to-field result-name="contentId" field="textContentId"/>
</call-service>
</if-not-empty>
<if-not-empty field="contentId">
<if-not-empty field="parameters.summaryData">
<!-- create the summary data -->
<set field="createSummary.dataResourceTypeId" value="ELECTRONIC_TEXT"/>
<set field="createSummary.contentPurposeTypeId" value="ARTICLE"/>
<set field="createSummary.dataTemplateTypeId" value="NONE"/>
<set field="createSummary.mapKey" value="SUMMARY"/>
<set field="createSummary.ownerContentId" from-field="ownerContentId"/>
<set field="createSummary.contentName" from-field="parameters.contentName"/>
<set field="createSummary.description" from-field="parameters.description"/>
<set field="createSummary.statusId" from-field="parameters.statusId"/>
<set field="createSummary.contentAssocTypeId" from-field="contentAssocTypeId"/>
<set field="createSummary.textData" from-field="parameters.summaryData"/>
<set field="createSummary.contentIdFrom" from-field="contentIdFrom"/>
<set field="createSummary.partyId" from-field="userLogin.partyId"/>
<call-service service-name="createTextContent" in-map-name="createSummary"/>
</if-not-empty>
</if-not-empty>
<field-to-result field="contentIdFrom" result-name="contentId"/>
<field-to-result field="parameters.blogContentId" result-name="blogContentId"/>
</simple-method>
从上面可以看到parameters.articleData不为空时,会调用createTextContent服务,就是这个服务调用时createText中没有包含dataResourceId,导致报错了。
5.查看createTextContent服务的定义(具体方法参照上面的办法,利用ofbiz的服务引擎):
<service name="createTextContent" engine="group" auth="true">
<description>Creates a Text Document DataResource and Content Records</description>
<!-- uses createContent internally; additonal permission(s) not necessary -->
<group>
<invoke name="createDataText" result-to-context="true"/>
<invoke name="createContent" result-to-context="true"/>
</group>
</service>
这个服务时个服务组,会调用2个服务createDataText和createContent。
6.查看createDataText服务的定义
<!-- generic create data text service; looks at the type id to determine if ELECTRONIC_TEXT is necessary -->
<service name="createDataText" engine="route" auth="true">
<description>Uses ECA to decide if we should call createElectronicText or just createDataResource (SHORT_TEXT)</description>
<implements service="createDataResource"/>
<implements service="createElectronicText"/>
</service>
这个是一个route类型的服务,可以看到它继承了createDataResource和createElectronicText服务接口。而调用createElectronicText服务必须输入参数dataResourceId,否则就会报错。
<!-- ElectronicText services -->
<service name="createElectronicText" default-entity-name="ElectronicText" engine="entity-auto" invoke="create" auth="true">
<description>Create a ElectronicText</description>
<permission-service service-name="genericDataResourcePermission" main-action="CREATE"/>
<implements service="createDataResource"/>
<auto-attributes include="pk" mode="INOUT" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
<override name="dataResourceTypeId" default-value="ELECTRONIC_TEXT"/>
<override name="textData" allow-html="any"/>
</service>
<!-- DataResource services -->
<service name="createDataResource" default-entity-name="DataResource" engine="simple"
location="component://content/minilang/data/DataServices.xml" invoke="createDataResource" auth="true">
<description>Create a DataResource</description>
<permission-service service-name="genericDataResourcePermission" main-action="CREATE"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
<attribute name="dataResourceId" type="String" mode="INOUT" optional="true"/>
<attribute name="targetOperationList" type="List" mode="IN" optional="true"/>
<attribute name="contentPurposeList" type="List" mode="IN" optional="true"/>
<attribute name="skipPermissionCheck" type="String" mode="IN" optional="true"/>
<attribute name="roleTypeId" type="String" mode="IN" optional="true"/>
<attribute name="partyId" type="String" mode="IN" optional="true"/>
<attribute name="dataResourceId" type="String" mode="OUT" optional="false"/>
<attribute name="dataResource" type="org.apache.ofbiz.entity.GenericValue" mode="OUT" optional="true"/>
<attribute name="uploadedFile" type="java.nio.ByteBuffer" mode="IN" optional="true"/>
<override name="objectInfo" allow-html="any"/>
<override name="dataResourceName" allow-html="any"/>
</service>
7.查看ElectronicText实体的定义(路径:apache-ofbiz-16.11.02\applications\datamodel\entitydef\content-entitymodel.xml)
<entity entity-name="ElectronicText"
package-name="org.apache.ofbiz.content.data"
title="Electronic Text Entity">
<field name="dataResourceId" type="id-ne"></field>
<field name="textData" type="very-long"></field>
<prim-key field="dataResourceId"/>
<relation type="one" fk-name="DATA_REC_TEXT" rel-entity-name="DataResource">
<key-map field-name="dataResourceId"/>
</relation>
</entity>
从上面可以看到dataResourceId是dataResourceId实体的主键,而在createElectronicText服务的定义中,却有下面的配置:
<auto-attributes include="pk" mode="INOUT" optional="false"/>
说明dataResourceId是必须输入的参数。
问题的原因找到了,那么解决办法也就出来了,有2种办法:
1. 直接把上面的配置false修改true,即调用该服务主键可以不是必选参数,会自动生成主键。
<auto-attributes include="pk" mode="INOUT" optional="true"/>
2. 调用createTextContent服务时,传入的createText参数中包含dataResourceId。
具体采用哪一种,就要看dataResourceId在之前是否有其他实体使用并生成了,如果有,则构造createText时吧它传进去,否则就需要修改一下createElectronicText服务的定义。
经过分析,应该是使用第一种比较好。
理由:在apache-ofbiz-16.11.02\applications\content\servicedef\secas.xml中如下定义:
<!-- electronic text; needs dataResourceId -->
<eca service="createElectronicText" event="invoke">
<condition field-name="dataResourceId" operator="is-empty"/>
<set field-name="dataResourceTypeId" value="ELECTRONIC_TEXT"/>
<action service="createDataResource" mode="sync" result-to-context="true"/>
</eca>
说明当dataResourceId为null时,会自动触发该触发器,然后调用createDataResource服务添加记录。即调用createElectronicText服务时是允许dataResourceId为null的。
最终解决方案:
打开文件:apache-ofbiz-16.11.02\applications\content\servicedef\services_data.xml
修改createElectronicText服务的定义。
<!-- ElectronicText services -->
<service name="createElectronicText" default-entity-name="ElectronicText" engine="entity-auto" invoke="create" auth="true">
<description>Create a ElectronicText</description>
<permission-service service-name="genericDataResourcePermission" main-action="CREATE"/>
<implements service="createDataResource"/>
<!-- <auto-attributes include="pk" mode="INOUT" optional="false"/> -->
<auto-attributes include="pk" mode="INOUT" optional="true"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
<override name="dataResourceTypeId" default-value="ELECTRONIC_TEXT"/>
<override name="textData" allow-html="any"/>
</service>
经过测试,修改后重启ofbiz,再次发布文章能正常使用了。