問題現(xiàn)象描述
項(xiàng)目中使用了activiti作為工作流引擎线定,為了使用更加方便受楼,自己開發(fā)了一套流程設(shè)置功能酒繁。先構(gòu)建BpmnModel
對(duì)象孽惰,再使用RepositoryService
提供的deploy方法動(dòng)態(tài)部署到activiti中膳凝。
功能開發(fā)完成后碑隆,開發(fā)本地測(cè)試和一輪測(cè)試之前都是OK的。然后前幾日蹬音,在回歸測(cè)試的時(shí)候上煤,出現(xiàn)了某些用戶可以成功部署,某些用戶無法成功部署的“奇怪”現(xiàn)象著淆。觀察后臺(tái)無法部署的后臺(tái)錯(cuò)誤日志:
org.activiti.bpmn.exceptions.XMLException: cvc-datatype-valid.1.2.1: '0785bf15-8d5d-11e7-8f8f-5254004a5833_39abc790-91fa-11e7-9e50-5254004a5833_loan' is not a valid value for 'NCName'.
at org.activiti.bpmn.converter.BpmnXMLConverter.convertToBpmnModel(BpmnXMLConverter.java:280)
at org.activiti.engine.impl.bpmn.parser.BpmnParse.execute(BpmnParse.java:187)
at org.activiti.engine.impl.bpmn.deployer.BpmnDeployer.deploy(BpmnDeployer.java:136)
at org.activiti.engine.impl.persistence.deploy.DeploymentManager.deploy(DeploymentManager.java:58)
at org.activiti.engine.impl.cmd.DeployCmd.execute(DeployCmd.java:106)
at org.activiti.engine.impl.cmd.DeployCmd.execute(DeployCmd.java:37)
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:24)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:57)
at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:31)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:40)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:35)
at org.activiti.engine.impl.RepositoryServiceImpl.deploy(RepositoryServiceImpl.java:79)
at org.activiti.engine.impl.repository.DeploymentBuilderImpl.deploy(DeploymentBuilderImpl.java:156)
at com.jinzay.platform.activiti.helper.service.ActivitiEngineService.repositoryDeployment(ActivitiEngineService.java:158)
at com.jinzay.ees.mst.service.api.MstProcessConfigController.deploy(MstProcessConfigController.java:282)
at com.jinzay.ees.mst.service.api.MstProcessConfigController$$FastClassBySpringCGLIB$$e14ce3a5.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:669)
at com.jinzay.ees.mst.service.api.MstProcessConfigController$$EnhancerBySpringCGLIB$$f5ce0d0c.deploy(<generated>)
at sun.reflect.GeneratedMethodAccessor556.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: '0785bf15-8d5d-11e7-8f8f-5254004a5833_39abc790-91fa-11e7-9e50-5254004a5833_loan' is not a valid value for 'NCName'.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:452)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.reportSchemaError(XMLSchemaValidator.java:3230)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processOneAttribute(XMLSchemaValidator.java:2825)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.processAttributes(XMLSchemaValidator.java:2762)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2050)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:740)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:380)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2787)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:118)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
at com.sun.org.apache.xerces.internal.jaxp.validation.StreamValidatorHelper.validate(StreamValidatorHelper.java:155)
at com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:116)
at javax.xml.validation.Validator.validate(Validator.java:124)
at org.activiti.bpmn.converter.BpmnXMLConverter.validateModel(BpmnXMLConverter.java:214)
at org.activiti.bpmn.converter.BpmnXMLConverter.convertToBpmnModel(BpmnXMLConverter.java:269)
... 97 common frames omitted
問題解決過程
這個(gè)報(bào)錯(cuò)的意思大概是:
xml sechma校驗(yàn)中劫狠,字符串'0785bf15-8d5d-11e7-8f8f-5254004a5833_39abc790-91fa-11e7-9e50-5254004a5833_loan'不是合法的'NCName'類型拴疤。
于是想著百度看看NCName
是什么類型,有什么約束独泞。然后似乎沒有一篇能說明白呐矾。試探了多次,發(fā)現(xiàn):
原來是不能以數(shù)字開頭懦砂!
為了證實(shí)想法蜒犯,借助谷歌終于弄清楚了。NCName只能以下劃線(_)或字母開頭荞膘,只能包含中劃線(-)罚随、下劃線、字母和數(shù)字羽资,我們的流程ID是按單位ID+'_'+部門ID
規(guī)則拼接的淘菩,而單位ID是隨機(jī)生成的字符串,所以有的是以數(shù)字開頭的就無法部署屠升。最終潮改,我們更改了單位ID的 生成規(guī)則,保證是字母開頭(多么簡單粗暴的solution...)弥激。
常用的XML內(nèi)置類型
-
XML Schema 內(nèi)置簡單類型:
- string 字符數(shù)據(jù)類型
模式處理器在處理string類型的值時(shí)进陡,會(huì)將值中所有空白字符包括空格、制表符微服、回車換行都保留。
- normalizedString
派生自string缨历,與string不同的地方:模式處理器對(duì)空白的處理方式以蕴,處理器會(huì)將讀到的 每一個(gè)回車、換行和制表符替換為單個(gè)空格字符辛孵。
- token
派生自normalizedString丛肮,模式處理器對(duì) 于此類型的內(nèi)容時(shí),會(huì)用單個(gè)空格來替換讀到的每一個(gè)回車魄缚、換行和制表符宝与,替換后,每一組連續(xù)的空格被壓縮為一個(gè)空格字符冶匹,而所有前導(dǎo)和后導(dǎo)空格被刪除习劫。
- Name
派生自token,它表示一個(gè)XML名稱嚼隘,即該類型的值必須以字母诽里、下劃線(_)或冒號(hào)(:)開頭,而且只能包含字母飞蛹、數(shù)字谤狡、連字符灸眼、下劃線、冒號(hào)或句號(hào)(.)墓懂。
- NCName
繼承自Name焰宣,表示一個(gè)XML無冒號(hào)的名稱,即該名稱中不能使用冒號(hào)捕仔。它與Name類似宛徊,唯一的區(qū)別就是不能使用冒號(hào)。
-
數(shù)字類型
- float和double(單精度和雙精度浮點(diǎn)數(shù))
float數(shù)據(jù)類型表示IEEE單精度32位浮點(diǎn)數(shù)逻澳,而double表示IEEEE雙精度64位浮點(diǎn)數(shù)闸天。
- decimal
數(shù)據(jù)類型decimal表示任意精度的小數(shù),不同的模式處理器支持的有效位數(shù)是不一樣的斜做,不過XML Schema推薦標(biāo)準(zhǔn)中要求符合規(guī)范的模式處理器必須至少支持18位有效數(shù)字苞氮。
- integer
數(shù)據(jù)類型integer派生自decimal,表示一個(gè)任意大的整數(shù)瓤逼。從integer數(shù)據(jù)類型派生了其他十二中內(nèi)置的整數(shù)類型笼吟。
-
日期和時(shí)間類型
- date
用于表示日期。date的格式為:YYYY-MM-DD霸旗。要表示9999年之后的年份贷帮,在年值左邊添加數(shù)字,但不允許前導(dǎo)0;表示公元前的年,可以在年值錢添加負(fù)號(hào)-圆裕⌒鹛剩可以在日期值之后添加時(shí)區(qū)表達(dá)式,或者字母‘Z’,表示UTC時(shí)間。
- time
表示時(shí)間。time的格式是:hh:mm:ss.sss沃但。時(shí)間是24小時(shí)制》鹣牛可以在時(shí)間值之后添加可選的時(shí)間表達(dá)式宵晚,或者添加字母‘Z’,表示是UTC時(shí)間维雇。
- dateTime
表示完整的日期和時(shí)間淤刃,即date+time,本類型的格式為YYYY-MM-DDThh:mm:ss.sss谆沃,字母T作為日期和時(shí)間的分割钝凶。可以在dateTime值之后添加可選的時(shí)區(qū)表達(dá)式,或者添加字符’Z’耕陷。
- gYear
表示一個(gè)特定的陽歷年掂名,格式為: YYYY
- gYearMonth
表示一個(gè)特定年的某月,格式: YYYY-MM
- gMonth
表示某個(gè)月哟沫,格式為: --MM
- gMonthDay
表示某個(gè)月的某天饺蔑,格式: --MM-DD (8) gDay 表示某天,格式為: ---DD
- duration
表示持續(xù)時(shí)間嗜诀。格式為:PnYnMnDTnHnMnS猾警。 示例:P2Y3M4DT23H12M15.6S
-
其他數(shù)據(jù)類型
- boolean
表示邏輯值,有效值是true,false,0,1
- anyURI
表示一個(gè)統(tǒng)一資源標(biāo)示符的引用隆敢。
- QName
表示XML名稱空間限定名发皿。Qname值包括一個(gè)名稱空間前綴和本地部分(這兩部分都是NCName類型),中間用冒號(hào)分隔拂蝎。