/**
* Copyright 2007-2011 非也
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation。
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses. *
*/
package org.fireflow.model.io.service;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang.StringUtils;
import org.fireflow.model.InvalidModelException;
import org.fireflow.model.data.Expression;
import org.fireflow.model.data.Input;
import org.fireflow.model.data.Output;
import org.fireflow.model.data.impl.ExpressionImpl;
import org.fireflow.model.data.impl.InputImpl;
import org.fireflow.model.data.impl.OutputImpl;
import org.fireflow.model.io.DeserializerException;
import org.fireflow.model.io.ModelElementNames;
import org.fireflow.model.io.SerializerException;
import org.fireflow.model.io.Util4Deserializer;
import org.fireflow.model.io.Util4Serializer;
import org.fireflow.model.servicedef.InterfaceDef;
import org.fireflow.model.servicedef.OperationDef;
import org.fireflow.model.servicedef.ServiceDef;
import org.fireflow.model.servicedef.impl.AbstractServiceDef;
import org.fireflow.model.servicedef.impl.CommonInterfaceDef;
import org.fireflow.model.servicedef.impl.OperationDefImpl;
import org.firesoa.common.schema.NameSpaces;
import org.firesoa.common.util.ScriptLanguages;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
*
* @author 非也 www.firesoa.com
*
*
*/
public abstract class ServiceParser implements ModelElementNames {
protected static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
.newInstance();
private static String CDATA_SECTION_ELEMENT_LIST = "";
public static final String JDK_TRANSFORMER_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
protected static boolean useJDKTransformerFactory = false;//需要规避bug
static {
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
if (JDK_TRANSFORMER_CLASS.equals(transformerFactory.getClass().getName())){
useJDKTransformerFactory = true;
}
documentBuilderFactory.setNamespaceAware(true);
StringBuffer buf = new StringBuffer();
buf.append("{").append(ModelElementNames.SERVICE_NS_URI).append("}").append(ModelElementNames.DESCRIPTION).append(" " )
.append("{").append(ModelElementNames.SERVICE_NS_URI).append("}").append(ModelElementNames.BODY).append(" ")
.append("{").append(ModelElementNames.RESOURCE_NS_URI).append("}").append(ModelElementNames.DESCRIPTION).append(" " )
.append("{").append(ModelElementNames.RESOURCE_NS_URI).append("}").append(ModelElementNames.BODY).append(" ");
CDATA_SECTION_ELEMENT_LIST = buf.toString();
}
private static Map<String, ServiceParser> parserCache = new HashMap<String, ServiceParser>();
public abstract ServiceDef deserializeService(Element element)
throws DeserializerException;
private static final String DEFAULT_CHARSET = "UTF-8";
/**
* 将service序列化成element,并将该element作为parentElement的子元素。
*
* @param service
* @param parentElement
* @throws SerializerException
*/
public abstract void serializeService(ServiceDef service,
Element parentElement) throws SerializerException;
/**
* 将service定义文件解析成ServiceDef列表
*
* @param in
* @return
* @throws IOException
* @throws InvalidModelException
*/
public static List<ServiceDef> deserialize(InputStream in)
throws IOException, DeserializerException, InvalidModelException {
DocumentBuilder documentBuilder = null;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(in);
return deserialize(document);
} catch (ParserConfigurationException e) {
throw new DeserializerException(e);
} catch (SAXException e) {
throw new DeserializerException(e);
}
}
public static List<ServiceDef> deserialize(Document document)
throws DeserializerException, InvalidModelException {
Element servicesElem = document.getDocumentElement();
List<ServiceDef> result = new ArrayList<ServiceDef>();
loadServices(result, servicesElem);
return result;
}
public static Document serializeToDOM(List<ServiceDef> services)
throws InvalidModelException, SerializerException {
try {
DocumentBuilder documentBuilder = documentBuilderFactory
.newDocumentBuilder();
Document document = documentBuilder.newDocument();
Element servicesElement = document.createElementNS(SERVICE_NS_URI,
SERVICE_NS_PREFIX + ":" + SERVICES);
document.appendChild(servicesElement);
// servicesElement.addNamespace(SERVICE_NS_PREFIX, SERVICE_NS_URI);
// servicesElement.addNamespace(XSD_NS_PREFIX, XSD_URI);
// servicesElement.addNamespace(XSI_NS_PREFIX, XSI_URI);
// QName qname = df.createQName("schemaLocation", "xsi", XSI_URI);
// servicesElement.addAttribute(qname, SERVICE_SCHEMA_LOCATION);
for (ServiceDef svc : services) {
ServiceParser parser = ServiceParser.getInstance(svc);
parser.serializeService(svc, servicesElement);
}
return document;
} catch (ParserConfigurationException e) {
throw new SerializerException(e);
} finally {
}
}
public static String serializeToXmlString(List<ServiceDef> services,String charset)
throws InvalidModelException, SerializerException{
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
serialize(services, out,charset);
} catch (IOException e) {
throw new SerializerException(e);
}
return out.toString();
}
public static String serializeToXmlString(List<ServiceDef> services)
throws InvalidModelException, SerializerException {
return serializeToXmlString(services,DEFAULT_CHARSET);
}
public static void writeServices(List<ServiceDef> services,
Element parentElement) throws SerializerException,
InvalidModelException {
Document document = parentElement.getOwnerDocument();
Element servicesElement = document.createElementNS(SERVICE_NS_URI,
SERVICE_NS_PREFIX + ":" + SERVICES);
parentElement.appendChild(servicesElement);
for (ServiceDef svc : services) {
ServiceParser parser = ServiceParser.getInstance(svc);
parser.serializeService(svc, servicesElement);
}
}
public static void serialize(List<ServiceDef> services, OutputStream out,String charset)
throws IOException, InvalidModelException, SerializerException {
try {
Document document = serializeToDOM(services);
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, charset);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, CDATA_SECTION_ELEMENT_LIST);
transformer.transform(new DOMSource(document),
new StreamResult(out));
out.flush();
} catch (TransformerConfigurationException e) {
throw new SerializerException(e);
} catch (TransformerException e) {
throw new SerializerException(e);
} finally {
}
}
public static void serialize(List<ServiceDef> services, OutputStream out)
throws IOException, InvalidModelException, SerializerException {
serialize(services,out,DEFAULT_CHARSET);
}
public static void loadServices(List<ServiceDef> servicesList,
Element servicesElement) throws DeserializerException,
InvalidModelException {
if (servicesElement == null)
return;
servicesList.clear();
NodeList children = servicesElement.getChildNodes();
int length = children.getLength();
for (int i = 0; i < length; i++) {
Node node = children.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
Element svcElm = (Element) node;
ServiceParser parser = ServiceParser.getInstance(svcElm);
ServiceDef svcDef = parser.deserializeService(svcElm);
servicesList.add(svcDef);
}
}
public static ServiceParser getInstance(Element element)
throws InvalidModelException {
String parserClass = element.getAttribute(PARSER_CLASS_NAME);
if (StringUtils.isEmpty(parserClass)) {
throw new InvalidModelException(
"Can not find the parser-class from the service element.");
}
return getConcretServiceParser(parserClass);
}
public static ServiceParser getInstance(ServiceDef service)
throws InvalidModelException {
String parserClass = service.getParserClassName();
if (StringUtils.isEmpty(parserClass)) {
throw new InvalidModelException(
"Parser class of the Service can NOT be empty.");
}
return getConcretServiceParser(parserClass);
}
protected static ServiceParser getConcretServiceParser(
String parserClassName) throws InvalidModelException {
ServiceParser parser = parserCache.get(parserClassName);
if (parser != null)
return parser;
try {
Class clz = Class.forName(parserClassName);
parser = (ServiceParser) clz.newInstance();
parserCache.put(parserClassName, parser);
return parser;
} catch (ClassNotFoundException e) {
throw new InvalidModelException(e);
} catch (InstantiationException e) {
throw new InvalidModelException(e);
} catch (IllegalAccessException e) {
throw new InvalidModelException(e);
} finally {
}
}
protected void writeCommonServiceAttribute(AbstractServiceDef service,
Element svcElem) {
svcElem.setAttribute(ID, service.getId());
svcElem.setAttribute(NAME, service.getName());
svcElem.setAttribute(PACKAGE_ID, service.getBizCategory());
svcElem.setAttribute(DISPLAY_NAME, service.getDisplayName());
svcElem.setAttribute(TARGET_NAMESPACE, service.getTargetNamespaceUri());
if (!StringUtils.isEmpty(service.getInvokerBeanName())) {
svcElem.setAttribute(INVOKER_BEAN_NAME,
service.getInvokerBeanName());
}
else if (!StringUtils.isEmpty(service.getInvokerClassName()) ) {
svcElem.setAttribute(INVOKER_CLASS_NAME,
service.getInvokerClassName());
}
svcElem.setAttribute(PARSER_CLASS_NAME, service.getParserClassName());
this.writeDescription(svcElem, service.getDescription());
}
protected void loadCommonServiceAttribute(AbstractServiceDef absService,
Element svcElem) {
absService.setName(svcElem.getAttribute(NAME));
absService.setDisplayName(svcElem.getAttribute(DISPLAY_NAME));
absService.setBizCategory(svcElem.getAttribute(PACKAGE_ID));
absService.setDescription(this.loadCDATA(Util4Deserializer.child(svcElem,
DESCRIPTION)));
String beanName = svcElem.getAttribute(INVOKER_BEAN_NAME);
if (!StringUtils.isEmpty(beanName)) {
absService.setInvokerBeanName(beanName);
}
String className = svcElem.getAttribute(INVOKER_CLASS_NAME);
if (!StringUtils.isEmpty(className)) {
absService.setInvokerClassName(className);
}
absService.setParserClassName(svcElem.getAttribute(PARSER_CLASS_NAME));
absService.setTargetNamespaceUri(svcElem.getAttribute(TARGET_NAMESPACE));
Element extendedAttributesElement = Util4Deserializer.child(svcElem,
EXTENDED_ATTRIBUTES);
this.loadExtendedAttributes(absService.getExtendedAttributes(),
extendedAttributesElement);
}
/**
* 实现common interface的反序列化
*/
protected InterfaceDef loadCommonInterface(ServiceDef service,Element element) {
CommonInterfaceDef _interface = new CommonInterfaceDef();
_interface.setName(element.getAttribute(NAME));
List<OperationDef> operations = _interface.getOperations();
List<Element> operationElements = Util4Deserializer.children(element,
OPERATION);
if (operationElements != null) {
for (Element operationElm : operationElements) {
OperationDefImpl op = new OperationDefImpl();
op.setOperationName(operationElm.getAttribute(NAME));
// 解析inputs
List<Input> inputs = op.getInputs();
Element inputsElement = Util4Deserializer.child(operationElm,
INPUTS);
List<Element> inputElements = Util4Deserializer.children(
inputsElement, INPUT);
if (inputElements != null) {
for (Element inputElm : inputElements) {
InputImpl input = new InputImpl();
input.setName(inputElm.getAttribute(NAME));
input.setDisplayName(inputElm.getAttribute(DISPLAY_NAME));
input.setDefaultValueAsString(
inputElm.getAttribute(DEFAULT_VALUE));
input.setDataPattern(inputElm.getAttribute(DATA_PATTERN));
String dataTypeStr = inputElm.getAttribute(DATA_TYPE);
if (dataTypeStr.charAt(0) == '{') {
input.setDataType(javax.xml.namespace.QName
.valueOf(dataTypeStr));
} else {
//TODO 待进一步处理
// service.getXmlSchemaCollection().getXmlSchemas()[0].get
// int index = dataTypeStr.indexOf(":");
// String prefix = dataTypeStr.substring(0, index);
// String localName = dataTypeStr.substring(index + 1);
// inputElm.getOwnerDocument().getn
// Namespace dom4jNs = inputElm
// .getNamespaceForPrefix(prefix);
// input.setDataType(new javax.xml.namespace.QName(
// dom4jNs.getURI(), localName, dom4jNs
// .getPrefix()));
}
inputs.add(input);
}
}
// 解析output
List<Output> outputs = op.getOutputs();
Element outputsElement = Util4Deserializer.child(operationElm,
OUTPUTS);
List<Element> outputElements = Util4Deserializer.children(
outputsElement, OUTPUT);
if (outputElements != null) {
for (Element outputElm : outputElements) {
OutputImpl output = new OutputImpl();
output.setName(outputElm.getAttribute(NAME));
output.setDisplayName(outputElm.getAttribute(DISPLAY_NAME));
String dataTypeStr = outputElm.getAttribute(DATA_TYPE);
if (dataTypeStr.charAt(0) == '{') {
output.setDataType(javax.xml.namespace.QName
.valueOf(dataTypeStr));
} else {
//待进一步处理
// int index = dataTypeStr.indexOf(":");
// String prefix = dataTypeStr.substring(0, index);
// String localName = dataTypeStr.substring(index + 1);
// Namespace dom4jNs = outputElm
// .getNamespaceForPrefix(prefix);
// output.setDataType(new javax.xml.namespace.QName(
// dom4jNs.getURI(), localName, dom4jNs
// .getPrefix()));
}
outputs.add(output);
}
}
operations.add(op);
}
}
return _interface;
}
/**
* 实现common interface的序列化
*/
protected Element writeCommonInterface(InterfaceDef _interface,Element svcElem) {
CommonInterfaceDef commonInterface = (CommonInterfaceDef) _interface;
Element element = Util4Serializer.addElement(svcElem, INTERFACE_COMMON);
element.setAttribute(NAME, commonInterface.getName());
List<OperationDef> operations = commonInterface.getOperations();
if (operations != null) {
for (OperationDef op : operations) {
Element opElm = Util4Serializer.addElement(element, OPERATION);
opElm.setAttribute(NAME, op.getOperationName());
List<Input> inputs = op.getInputs();
if (inputs != null) {
Element inputsElm = Util4Serializer.addElement(opElm,
INPUTS);
for (Input in : inputs) {
Element inElm = Util4Serializer.addElement(inputsElm,
INPUT);
inElm.setAttribute(NAME, in.getName());
if (!StringUtils.isEmpty(in.getDisplayName())) {
inElm.setAttribute(DISPLAY_NAME,
in.getDisplayName());
}
if (!StringUtils.isEmpty(in.getDataPattern())) {
inElm.setAttribute(DATA_PATTERN,
in.getDataPattern());
}
if (!StringUtils.isEmpty(in.getDefaultValueAsString())) {
inElm.setAttribute(DEFAULT_VALUE,
in.getDefaultValueAsString());
}
if (in.getDataType() != null) {
inElm.setAttribute(DATA_TYPE, in.getDataType()
.toString());
}
}
}
List<Output> outputs = op.getOutputs();
if (outputs != null) {
Element outputsElm = Util4Serializer.addElement(opElm,
OUTPUTS);
for (Output out : outputs) {
Element outElm = Util4Serializer.addElement(outputsElm,
OUTPUT);
outElm.setAttribute(NAME, out.getName());
if (!StringUtils.isEmpty(out.getDisplayName())) {
outElm.setAttribute(DISPLAY_NAME,
out.getDisplayName());
}
if (out.getDataType() != null) {
outElm.setAttribute(DATA_TYPE, out.getDataType()
.toString());
}
}
}
}
}
return element;
}
protected void loadExtendedAttributes(
Map<String, String> extendedAttributes,
Element extendedAttributesElement) {
if (extendedAttributesElement != null) {
List<Element> children = Util4Deserializer.children(
extendedAttributesElement, EXTENDED_ATTRIBUTE);
for (Element propElem : children) {
extendedAttributes.put(propElem.getAttribute(NAME),
propElem.getAttribute(VALUE));
}
}
}
protected void writeExtendedAttributes(
Map<String, String> extendedAttributes, Element serviceElement) {
if (extendedAttributes == null || extendedAttributes.isEmpty()) {
return;
}
Element extendedAttributesElement = Util4Serializer.addElement(
serviceElement, EXTENDED_ATTRIBUTES);
Iterator<String> iterator = extendedAttributes.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Element propElm = Util4Serializer.addElement(
extendedAttributesElement, EXTENDED_ATTRIBUTE);
propElm.setAttribute(NAME, key);
propElm.setAttribute(VALUE, extendedAttributes.get(key));
}
}
protected Expression createExpression(Element expressionElement){
if (expressionElement!=null){
ExpressionImpl exp = new ExpressionImpl();
exp.setLanguage(expressionElement.getAttribute(LANGUAGE));
exp.setName(expressionElement.getAttribute(NAME));
exp.setDisplayName(expressionElement.getAttribute(DISPLAY_NAME));
String dataTypeStr = expressionElement.getAttribute(DATA_TYPE);
QName qname = QName.valueOf(dataTypeStr);
exp.setDataType(qname);
Element bodyElement = Util4Deserializer.child(expressionElement, BODY);
if (bodyElement==null){
exp.setBody("");
}else{
exp.setBody(this.loadCDATA(bodyElement));
}
Element namespacePrefixUriMapElem = Util4Deserializer.child(expressionElement, NAMESPACE_PREFIX_URI_MAP);
if (namespacePrefixUriMapElem!=null){
List<Element> children = Util4Deserializer.children(namespacePrefixUriMapElem, ENTRY);
if (children!=null && children.size()>0){
for (Element elem : children){
String prefix = elem.getAttribute(NAME);
String uri = elem.getAttribute(VALUE);
exp.getNamespaceMap().put(prefix, uri);
}
}
}
return exp;
}else{
ExpressionImpl exp = new ExpressionImpl();
exp.setLanguage(ScriptLanguages.UNIFIEDJEXL.name());
exp.setDataType(new QName(NameSpaces.JAVA.getUri(), String.class
.getName(), NameSpaces.JAVA.getPrefix()));
exp.setName("WorkItemSubject");
exp.setDisplayName("工作项主题");// 需国际化
exp.setBody("");
return exp;
}
}
protected void writeExpression(Expression exp,Element parent){
if (exp==null) return;
Element expressionElem = Util4Serializer.addElement(parent, EXPRESSION);
if (exp.getName()!=null && !exp.getName().trim().equals("")){
expressionElem.setAttribute(NAME, exp.getName());
}
if (exp.getDisplayName()!=null && !exp.getDisplayName().trim().equals("")){
expressionElem.setAttribute(DISPLAY_NAME, exp.getDisplayName());
}
expressionElem.setAttribute(LANGUAGE, exp.getLanguage());
Document doc = parent.getOwnerDocument();
String body = exp.getBody()==null?"":exp.getBody();
CDATASection cdata = doc.createCDATASection(useJDKTransformerFactory?(" "+body):body);
Element bodyElem = Util4Serializer.addElement(expressionElem, BODY);
bodyElem.appendChild(cdata);
if(exp.getNamespaceMap()!=null && exp.getNamespaceMap().size()>0){
Element namespaceMapElem = Util4Serializer.addElement(expressionElem, NAMESPACE_PREFIX_URI_MAP);
Iterator<Map.Entry<String,String>> entrys = exp.getNamespaceMap().entrySet().iterator();
while(entrys.hasNext()){
Map.Entry<String,String> entry = entrys.next();
Element entryElem = Util4Serializer.addElement(namespaceMapElem, ENTRY);
entryElem.setAttribute(NAME, entry.getKey());
entryElem.setAttribute(VALUE, entry.getValue());
}
}
}
protected static boolean equalStrings(String s1, String s2) {
if (s1 == s2) {
return true;
}
s1 = s1 == null ? "" : s1.trim();
s2 = s2 == null ? "" : s2.trim();
return s1.equals(s2);
}
protected String loadCDATA(Element cdataElement){
if (cdataElement==null){
return "";
}else{
String data = cdataElement.getTextContent();
if (data==null)return data;
else{
if (useJDKTransformerFactory){
if (data.startsWith(" ")){
return data.substring(1);//去掉一个空格
}
}
return data;
}
}
}
protected void writeDescription(Element parent, String desc) {
if(desc==null || desc.trim().equals(""))return;
Document doc = parent.getOwnerDocument();
Element descElem = Util4Serializer.addElement(parent, DESCRIPTION);
CDATASection cdata = doc.createCDATASection(useJDKTransformerFactory?(" "+desc):desc);
descElem.appendChild(cdata);
}
}