Java tutorial
/* * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.apimgt.impl.soaptorest; import com.fasterxml.jackson.databind.module.SimpleModule; import io.swagger.inflector.examples.ExampleBuilder; import io.swagger.inflector.examples.models.Example; import io.swagger.inflector.processors.JsonNodeExampleSerializer; import io.swagger.models.HttpMethod; import io.swagger.models.Model; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.RefModel; import io.swagger.models.Swagger; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.Parameter; import io.swagger.models.parameters.QueryParameter; import io.swagger.parser.SwaggerParser; import io.swagger.util.Json; import io.swagger.util.Yaml; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder; import org.wso2.carbon.apimgt.impl.soaptorest.template.RESTToSOAPMsgTemplate; import org.wso2.carbon.apimgt.impl.soaptorest.util.SOAPToRESTConstants; import org.wso2.carbon.apimgt.impl.soaptorest.util.SequenceUtils; import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.registry.core.RegistryConstants; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.registry.core.session.UserRegistry; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; 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 java.io.StringWriter; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.wso2.carbon.apimgt.impl.utils.APIUtil.handleException; /** * Class uses to generate api sequences for soap to rest conversion. */ public class SequenceGenerator { private static final Logger log = LoggerFactory.getLogger(SequenceGenerator.class); /** * Generates in/out sequences from the swagger given * * @param swaggerStr swagger string * @param apiDataStr api data as json string * @throws APIManagementException */ public static void generateSequencesFromSwagger(String swaggerStr, String apiDataStr) throws APIManagementException { Swagger swagger = new SwaggerParser().parse(swaggerStr); Map<String, Model> definitions = swagger.getDefinitions(); // Configure serializers SimpleModule simpleModule = new SimpleModule().addSerializer(new JsonNodeExampleSerializer()); Json.mapper().registerModule(simpleModule); Yaml.mapper().registerModule(simpleModule); Map<String, Path> paths = swagger.getPaths(); for (String pathName : paths.keySet()) { Path path = paths.get(pathName); Map<HttpMethod, Operation> operationMap = path.getOperationMap(); for (HttpMethod httpMethod : operationMap.keySet()) { Map<String, String> parameterJsonPathMapping = new HashMap<>(); Map<String, String> queryParameters = new HashMap<>(); Operation operation = operationMap.get(httpMethod); String operationId = operation.getOperationId(); //get vendor extensions Map<String, Object> vendorExtensions = operation.getVendorExtensions(); Object vendorExtensionObj = vendorExtensions.get("x-wso2-soap"); String soapAction = SOAPToRESTConstants.EMPTY_STRING; String namespace = SOAPToRESTConstants.EMPTY_STRING; String soapVersion = SOAPToRESTConstants.EMPTY_STRING; if (vendorExtensionObj != null) { soapAction = (String) ((LinkedHashMap) vendorExtensionObj).get("soap-action"); namespace = (String) ((LinkedHashMap) vendorExtensionObj).get("namespace"); soapVersion = (String) ((LinkedHashMap) vendorExtensionObj) .get(SOAPToRESTConstants.Swagger.SOAP_VERSION); } String soapNamespace = SOAPToRESTConstants.SOAP12_NAMSPACE; if (StringUtils.isNotBlank(soapVersion) && SOAPToRESTConstants.SOAP_VERSION_11.equals(soapVersion)) { soapNamespace = SOAPToRESTConstants.SOAP11_NAMESPACE; } List<Parameter> parameters = operation.getParameters(); for (Parameter parameter : parameters) { String name = parameter.getName(); if (parameter instanceof BodyParameter) { Model schema = ((BodyParameter) parameter).getSchema(); if (schema instanceof RefModel) { String $ref = ((RefModel) schema).get$ref(); if (StringUtils.isNotBlank($ref)) { String defName = $ref.substring("#/definitions/".length()); Model model = definitions.get(defName); Example example = ExampleBuilder.fromModel(defName, model, definitions, new HashSet<String>()); String jsonExample = Json.pretty(example); try { org.json.JSONObject json = new org.json.JSONObject(jsonExample); SequenceUtils.listJson(json, parameterJsonPathMapping); } catch (JSONException e) { log.error("Error occurred while generating json mapping for the definition", e); } } } } if (parameter instanceof QueryParameter) { String type = ((QueryParameter) parameter).getType(); queryParameters.put(name, type); } } //populates body parameter json paths and query parameters to generate api sequence parameters populateParametersFromOperation(operation, definitions, parameterJsonPathMapping, queryParameters); Map<String, String> payloadSequence = createPayloadFacXMLForOperation(parameterJsonPathMapping, queryParameters, namespace, SOAPToRESTConstants.EMPTY_STRING, operationId); try { String[] propAndArgElements = getPropertyAndArgElementsForSequence(parameterJsonPathMapping, queryParameters); if (log.isDebugEnabled()) { log.debug("properties string for the generated sequence: " + propAndArgElements[0]); log.debug("arguments string for the generated sequence: " + propAndArgElements[1]); } org.json.simple.JSONArray arraySequenceElements = new org.json.simple.JSONArray(); //gets array elements for the sequence to be used getArraySequenceElements(arraySequenceElements, parameterJsonPathMapping); Map<String, String> sequenceMap = new HashMap<>(); sequenceMap.put("args", propAndArgElements[0]); sequenceMap.put("properties", propAndArgElements[1]); sequenceMap.put("sequence", payloadSequence.get(operationId)); RESTToSOAPMsgTemplate template = new RESTToSOAPMsgTemplate(); String inSequence = template.getMappingInSequence(sequenceMap, operationId, soapAction, namespace, soapNamespace, arraySequenceElements); String outSequence = template.getMappingOutSequence(); saveApiSequences(apiDataStr, inSequence, outSequence, httpMethod.toString().toLowerCase(), pathName); } catch (APIManagementException e) { handleException("Error when generating sequence property and arg elements for soap operation: " + operationId, e); } } } } private static void populateParametersFromOperation(Operation operation, Map<String, Model> definitions, Map<String, String> parameterJsonPathMapping, Map<String, String> queryParameters) { List<Parameter> parameters = operation.getParameters(); for (Parameter parameter : parameters) { String name = parameter.getName(); if (parameter instanceof BodyParameter) { Model schema = ((BodyParameter) parameter).getSchema(); if (schema instanceof RefModel) { String $ref = ((RefModel) schema).get$ref(); if (StringUtils.isNotBlank($ref)) { String defName = $ref.substring("#/definitions/".length()); Model model = definitions.get(defName); Example example = ExampleBuilder.fromModel(defName, model, definitions, new HashSet<String>()); String jsonExample = Json.pretty(example); try { org.json.JSONObject json = new org.json.JSONObject(jsonExample); SequenceUtils.listJson(json, parameterJsonPathMapping); } catch (JSONException e) { log.error("Error occurred while generating json mapping for the definition: " + defName, e); } } } } if (parameter instanceof QueryParameter) { String type = ((QueryParameter) parameter).getType(); queryParameters.put(name, type); } } } private static void saveApiSequences(String apiDataStr, String inSequence, String outSequence, String method, String resourcePath) throws APIManagementException { JSONParser parser = new JSONParser(); boolean isTenantFlowStarted = false; org.json.simple.JSONObject apiData; try { apiData = (org.json.simple.JSONObject) parser.parse(apiDataStr); String provider = (String) apiData.get("provider"); String name = (String) apiData.get("name"); String version = (String) apiData.get("version"); if (provider != null) { provider = APIUtil.replaceEmailDomain(provider); } provider = (provider != null ? provider.trim() : null); name = (name != null ? name.trim() : null); version = (version != null ? version.trim() : null); String tenantDomain = MultitenantUtils.getTenantDomain(APIUtil.replaceEmailDomainBack(provider)); if (tenantDomain != null && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { isTenantFlowStarted = true; PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); } RegistryService registryService = ServiceReferenceHolder.getInstance().getRegistryService(); int tenantId; UserRegistry registry; try { tenantId = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager() .getTenantId(tenantDomain); APIUtil.loadTenantRegistry(tenantId); registry = registryService.getGovernanceSystemRegistry(tenantId); String resourceInPath = APIConstants.API_LOCATION + RegistryConstants.PATH_SEPARATOR + provider + RegistryConstants.PATH_SEPARATOR + name + RegistryConstants.PATH_SEPARATOR + version + RegistryConstants.PATH_SEPARATOR + SOAPToRESTConstants.SequenceGen.SOAP_TO_REST_IN_RESOURCE + resourcePath + SOAPToRESTConstants.SequenceGen.RESOURCE_METHOD_SEPERATOR + method + SOAPToRESTConstants.SequenceGen.XML_FILE_EXTENSION; String resourceOutPath = APIConstants.API_LOCATION + RegistryConstants.PATH_SEPARATOR + provider + RegistryConstants.PATH_SEPARATOR + name + RegistryConstants.PATH_SEPARATOR + version + RegistryConstants.PATH_SEPARATOR + SOAPToRESTConstants.SequenceGen.SOAP_TO_REST_OUT_RESOURCE + resourcePath + SOAPToRESTConstants.SequenceGen.RESOURCE_METHOD_SEPERATOR + method + SOAPToRESTConstants.SequenceGen.XML_FILE_EXTENSION; SequenceUtils.saveRestToSoapConvertedSequence(registry, inSequence, method, resourceInPath); SequenceUtils.saveRestToSoapConvertedSequence(registry, outSequence, method, resourceOutPath); } catch (UserStoreException e) { handleException("Error while reading tenant information", e); } catch (RegistryException e) { handleException("Error while creating registry resource", e); } catch (APIManagementException e) { handleException( "Error while saving the soap to rest converted sequence for resource path: " + resourcePath, e); } } catch (ParseException e) { handleException("Error occurred while parsing the sequence json", e); } finally { if (isTenantFlowStarted) { PrivilegedCarbonContext.endTenantFlow(); } } } private static Map<String, String> createPayloadFacXMLForOperation(Map<String, String> parameterJsonPathMapping, Map<String, String> queryPathParamMapping, String namespace, String prefix, String operationId) throws APIManagementException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); TransformerFactory transformerFactory = TransformerFactory.newInstance(); DocumentBuilder docBuilder; StringWriter stringWriter = new StringWriter(); try { Transformer transformer = transformerFactory.newTransformer(); docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); Element rootElement = doc.createElementNS(namespace, SOAPToRESTConstants.SequenceGen.NAMESPACE_PREFIX + SOAPToRESTConstants.SequenceGen.NAMESPACE_SEPARATOR + operationId); doc.appendChild(rootElement); int count = 1; for (String parameter : parameterJsonPathMapping.keySet()) { String parameterType = parameterJsonPathMapping.get(parameter); String[] parameterTreeNodes = parameter.split("\\."); Element prevElement = rootElement; int elemPos = 0; int length = parameterType.equals(SOAPToRESTConstants.ParamTypes.ARRAY) ? parameterTreeNodes.length - 1 : parameterTreeNodes.length; for (int i = 0; i < length; i++) { String parameterTreeNode = parameterTreeNodes[i]; if (StringUtils.isNotBlank(parameterTreeNode)) { Element element = doc.createElementNS(namespace, SOAPToRESTConstants.SequenceGen.NAMESPACE_PREFIX + SOAPToRESTConstants.SequenceGen.NAMESPACE_SEPARATOR + parameterTreeNode); if (doc.getElementsByTagName(element.getTagName()).getLength() > 0) { prevElement = (Element) doc.getElementsByTagName(element.getTagName()).item(0); } else { if (elemPos == length - 1) { element.setTextContent(SOAPToRESTConstants.SequenceGen.PROPERTY_ACCESSOR + count); count++; } prevElement.appendChild(element); prevElement = element; } elemPos++; } } } count = 1; if (parameterJsonPathMapping.size() == 0) { for (String queryParam : queryPathParamMapping.keySet()) { Element element = doc.createElementNS(namespace, SOAPToRESTConstants.SequenceGen.NAMESPACE_PREFIX + SOAPToRESTConstants.SequenceGen.NAMESPACE_SEPARATOR + queryParam); element.setTextContent(SOAPToRESTConstants.SequenceGen.PROPERTY_ACCESSOR + count); count++; rootElement.appendChild(element); } } else if (parameterJsonPathMapping.size() > 0 && queryPathParamMapping.size() > 0) { log.warn("Query parameters along with the body parameter is not allowed"); } transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(SOAPToRESTConstants.SequenceGen.INDENT_PROPERTY, SOAPToRESTConstants.SequenceGen.INDENT_VALUE); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(new DOMSource(doc), new StreamResult(stringWriter)); } catch (ParserConfigurationException e) { handleException("Error occurred when building in sequence xml", e); } catch (TransformerConfigurationException e) { handleException("Error in transport configuration", e); } catch (TransformerException e) { handleException("Error occurred when transforming in sequence xml", e); } if (log.isDebugEnabled()) { log.debug("parameter mapping for used in payload factory for soap operation:" + operationId + " is " + stringWriter.toString()); } Map<String, String> paramMap = new HashMap<>(); paramMap.put(operationId, stringWriter.toString()); return paramMap; } private static String[] getPropertyAndArgElementsForSequence(Map<String, String> parameterJsonPathMapping, Map<String, String> queryPathParamMapping) throws APIManagementException { String argStr = SOAPToRESTConstants.EMPTY_STRING; String propertyStr = SOAPToRESTConstants.EMPTY_STRING; for (String parameter : parameterJsonPathMapping.keySet()) { String parameterType = parameterJsonPathMapping.get(parameter); String paramElements = SequenceGenerator.createParameterElements(parameter, SOAPToRESTConstants.Swagger.BODY); String[] params = paramElements.split(SOAPToRESTConstants.SequenceGen.COMMA); if (!SOAPToRESTConstants.ParamTypes.ARRAY.equals(parameterType)) { propertyStr += params[0] + SOAPToRESTConstants.SequenceGen.NEW_LINE_CHAR; } argStr += params[1] + SOAPToRESTConstants.SequenceGen.NEW_LINE_CHAR; } if (MapUtils.isEmpty(parameterJsonPathMapping)) { for (String queryParam : queryPathParamMapping.keySet()) { String paramElements = SequenceGenerator.createParameterElements(queryParam, SOAPToRESTConstants.ParamTypes.QUERY); String[] params = paramElements.split(SOAPToRESTConstants.SequenceGen.COMMA); argStr += params[1] + SOAPToRESTConstants.SequenceGen.NEW_LINE_CHAR; propertyStr += params[0] + SOAPToRESTConstants.SequenceGen.NEW_LINE_CHAR; } } else if (parameterJsonPathMapping.size() > 0 && queryPathParamMapping.size() > 0) { log.warn("Query parameters along with the body parameter is not allowed"); } return new String[] { argStr, propertyStr }; } private static void getArraySequenceElements(org.json.simple.JSONArray array, Map<String, String> parameterJsonPathMapping) { for (String parameter : parameterJsonPathMapping.keySet()) { String parameterType = parameterJsonPathMapping.get(parameter); String[] parameterTreeNodes = parameter.split("\\."); if (SOAPToRESTConstants.ParamTypes.ARRAY.equals(parameterType)) { org.json.simple.JSONObject arrayObj = new org.json.simple.JSONObject(); arrayObj.put(SOAPToRESTConstants.SequenceGen.PROPERTY_NAME, parameter); arrayObj.put(SOAPToRESTConstants.SequenceGen.PARAMETER_NAME, parameterTreeNodes[parameterTreeNodes.length - 1]); array.add(arrayObj); } } } /** * Creates property and argument elements needed to inject to the sequence. * * @param jsonPathElement json path of a parameter * @param type type of the parameter (i.e query, body, path) * @return string of property and argument elements for a given parameter * @throws APIManagementException throws in xml to string transformation */ private static String createParameterElements(String jsonPathElement, String type) throws APIManagementException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder; StringWriter stringWriter = new StringWriter(); TransformerFactory transformerFactory = TransformerFactory.newInstance(); String property = SOAPToRESTConstants.EMPTY_STRING; String argument = SOAPToRESTConstants.EMPTY_STRING; try { Transformer transformer = transformerFactory.newTransformer(); docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); Element argElement = doc.createElement(SOAPToRESTConstants.SequenceGen.ARG_ELEMENT); Element propertyElement = doc.createElement(SOAPToRESTConstants.SequenceGen.PROPERTY_ELEMENT); argElement.setAttribute(SOAPToRESTConstants.SequenceGen.EVALUATOR_ATTR, SOAPToRESTConstants.SequenceGen.XML_FILE); String expressionAttr = SOAPToRESTConstants.SequenceGen.EXPRESSION_FUNC_DEF + jsonPathElement + SOAPToRESTConstants.SequenceGen.EXPRESSION_FUNC_DEF_CLOSING_TAG; argElement.setAttribute(SOAPToRESTConstants.SequenceGen.EXPRESSION_ATTR, expressionAttr); propertyElement.setAttribute(SOAPToRESTConstants.NAME_ATTRIBUTE, SOAPToRESTConstants.SequenceGen.REQ_VARIABLE + jsonPathElement); if (SOAPToRESTConstants.ParamTypes.QUERY.equals(type)) { propertyElement.setAttribute(SOAPToRESTConstants.SequenceGen.EXPRESSION_ATTR, SOAPToRESTConstants.SequenceGen.URL_OPERATOR + jsonPathElement); } else { propertyElement.setAttribute(SOAPToRESTConstants.SequenceGen.EXPRESSION_ATTR, SOAPToRESTConstants.SequenceGen.JSON_EVAL + SOAPToRESTConstants.SequenceGen.ROOT_OPERATOR + jsonPathElement + SOAPToRESTConstants.SequenceGen.CLOSING_PARANTHESIS); } transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(new DOMSource(propertyElement), new StreamResult(stringWriter)); property = stringWriter.toString(); stringWriter = new StringWriter(); transformer.transform(new DOMSource(argElement), new StreamResult(stringWriter)); argument = stringWriter.toString(); if (log.isDebugEnabled()) { log.debug("Argument element for request parameter: " + jsonPathElement + " and parameter type: " + type + " is: " + argument); } } catch (ParserConfigurationException e) { handleException("Error occurred when building in arg elements", e); } catch (TransformerConfigurationException e) { handleException("Error in transport configuration", e); } catch (TransformerException e) { handleException("Error occurred when transforming in sequence xml", e); } return property + SOAPToRESTConstants.SequenceGen.COMMA + argument; } }