org.apache.zeppelin.sap.universe.UniverseClient.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.zeppelin.sap.universe.UniverseClient.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.zeppelin.sap.universe;

import com.sun.org.apache.xpath.internal.NodeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * Client for API  SAP Universe
 */
public class UniverseClient {

    private static Logger logger = LoggerFactory.getLogger(UniverseClient.class);
    private static final String TOKEN_HEADER = "X-SAP-LogonToken";
    private static final String EL_FOLDER = "folder";
    private static final String EL_ITEM = "item";
    private static final String EL_NAME = "name";
    private static final String EL_PATH = "path";
    private static final String EL_ID = "id";
    private static final String EL_TECH_NAME = "technicalName";
    private static final String EL_ANSWER = "answer";
    private static final String EL_INFO = "info";
    private Map<String, String> tokens = new HashMap();
    private static final long DAY = 1000 * 60 * 60 * 24;
    private CloseableHttpClient httpClient;
    private String user;
    private String password;
    private String apiUrl;
    private String authType;
    private Header[] commonHeaders = { new BasicHeader("Accept", "application/xml"),
            new BasicHeader("Content-Type", "application/xml") };
    // <name, id>
    private final Map<String, UniverseInfo> universesMap = new ConcurrentHashMap();
    private final Map<String, Map<String, UniverseNodeInfo>> universeInfosMap = new ConcurrentHashMap();
    // for update the data (which was not updated a long time)
    private long universesUpdated = 0;
    private Map<String, Long> universesInfoUpdatedMap = new HashMap<>();

    private final String loginRequestTemplate = "<attrs xmlns=\"http://www.sap.com/rws/bip\">\n"
            + "<attr name=\"userName\" type=\"string\">%s</attr>\n"
            + "<attr name=\"password\" type=\"string\">%s</attr>\n" + "<attr name=\"auth\" type=\"string\" "
            + "possibilities=\"secEnterprise,secLDAP,secWinAD,secSAPR3\">%s</attr>\n" + "</attrs>";
    private final String createQueryRequestTemplate = "<query xmlns=\"http://www.sap.com/rws/sl/universe\" dataSourceType=\"%s\" "
            + "dataSourceId=\"%s\">\n" + "<querySpecification version=\"1.0\">\n" + "   <queryOptions>\n"
            + "            <queryOption name=\"duplicatedRows\" value=\"%s\"/>\n"
            + "            <queryOption name=\"maxRowsRetrieved\" activated=\"%s\" value=\"%d\"/>\n"
            + "  </queryOptions>" + "  <queryData>\n%s\n" + "     %s\n" + "  </queryData>\n"
            + "</querySpecification>\n" + "</query>\n";
    private final String filterPartTemplate = "<filterPart>%s\n</filterPart>";
    private final String errorMessageTemplate = "%s\n\n%s";
    private final String parameterTemplate = "<parameter type=\"prompt\">\n" + "%s\n" + "%s\n" + "%s\n" + "%s\n"
            + "</parameter>\n";
    private final String parameterAnswerTemplate = "<answer constrained=\"%s\" type=\"%s\">\n"
            + "            <info cardinality=\"%s\" keepLastValues=\"%s\"></info>\n" + "               <values>\n"
            + "     " + "                 <value>%s</value>\n" + "              </values>\n"
            + "        </answer>\n";

    public UniverseClient(String user, String password, String apiUrl, String authType, int queryTimeout) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(queryTimeout)
                .setSocketTimeout(queryTimeout).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(100);
        cm.closeIdleConnections(10, TimeUnit.MINUTES);
        httpClient = HttpClientBuilder.create().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
                .build();

        this.user = user;
        this.password = password;
        this.authType = authType;
        if (StringUtils.isNotBlank(apiUrl)) {
            this.apiUrl = apiUrl.replaceAll("/$", "");
        }
    }

    public void close() throws UniverseException {
        for (String s : tokens.keySet()) {
            closeSession(s);
        }
        try {
            httpClient.close();
        } catch (Exception e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(close all): Error close HTTP client", ExceptionUtils.getStackTrace(e)));
        }

    }

    public String createQuery(String token, UniverseQuery query) throws UniverseException {
        try {
            HttpPost httpPost = new HttpPost(String.format("%s%s", apiUrl, "/sl/v1/queries"));
            setHeaders(httpPost, token);
            String where = StringUtils.isNotBlank(query.getWhere())
                    ? String.format(filterPartTemplate, query.getWhere())
                    : StringUtils.EMPTY;
            httpPost.setEntity(new StringEntity(String.format(createQueryRequestTemplate,
                    query.getUniverseInfo().getType(), query.getUniverseInfo().getId(), query.getDuplicatedRows(),
                    query.getMaxRowsRetrieved().isPresent(), query.getMaxRowsRetrieved().orElse(0),
                    query.getSelect(), where), "UTF-8"));
            HttpResponse response = httpClient.execute(httpPost);

            if (response.getStatusLine().getStatusCode() == 200) {
                return getValue(EntityUtils.toString(response.getEntity()), "//success/id");
            }

            throw new UniverseException(
                    String.format(errorMessageTemplate, "UniverseClient " + "(create query): Request failed\n",
                            EntityUtils.toString(response.getEntity())));
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(create query): Request failed", ExceptionUtils.getStackTrace(e)));
        } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(create query): Response processing failed",
                    ExceptionUtils.getStackTrace(e)));
        }
    }

    public void deleteQuery(String token, String queryId) throws UniverseException {
        try {
            if (StringUtils.isNotBlank(queryId)) {
                HttpDelete httpDelete = new HttpDelete(String.format("%s%s%s", apiUrl, "/sl/v1/queries/", queryId));
                setHeaders(httpDelete, token);
                httpClient.execute(httpDelete);
            }
        } catch (Exception e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(delete query): Request failed", ExceptionUtils.getStackTrace(e)));
        }
    }

    public List<List<String>> getResults(String token, String queryId) throws UniverseException {
        HttpGet httpGet = new HttpGet(
                String.format("%s%s%s%s", apiUrl, "/sl/v1/queries/", queryId, "/data.svc/Flows0"));
        setHeaders(httpGet, token);
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new UniverseException(
                        String.format(errorMessageTemplate, "UniverseClient " + "(get results): Request failed\n",
                                EntityUtils.toString(response.getEntity())));
            }
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get results): Request failed", ExceptionUtils.getStackTrace(e)));
        }

        try (InputStream xmlStream = response.getEntity().getContent()) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(xmlStream);
            XPathFactory xPathfactory = XPathFactory.newInstance();
            XPath xpath = xPathfactory.newXPath();
            XPathExpression expr = xpath.compile("//feed/entry/content/properties");
            NodeList resultsNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            if (resultsNodes != null) {
                return parseResults(resultsNodes);
            } else {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get results): Response processing failed"));
            }
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get results): Request failed", ExceptionUtils.getStackTrace(e)));
        } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get results): Response processing failed",
                    ExceptionUtils.getStackTrace(e)));
        }
    }

    public String getToken(String paragraphId) throws UniverseException {
        try {
            if (tokens.containsKey(paragraphId)) {
                return tokens.get(paragraphId);
            }
            HttpPost httpPost = new HttpPost(String.format("%s%s", apiUrl, "/logon/long"));
            setHeaders(httpPost);

            httpPost.setEntity(
                    new StringEntity(String.format(loginRequestTemplate, user, password, authType), "UTF-8"));
            HttpResponse response = httpClient.execute(httpPost);
            String result = null;
            if (response.getStatusLine().getStatusCode() == 200) {
                result = getValue(EntityUtils.toString(response.getEntity()),
                        "//content/attrs/attr[@name=\"logonToken\"]");
                tokens.put(paragraphId, result);
            }

            return result;
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get token): Request failed", ExceptionUtils.getStackTrace(e)));
        } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get token): Response processing failed",
                    ExceptionUtils.getStackTrace(e)));
        }
    }

    public boolean closeSession(String paragraphId) throws UniverseException {
        try {
            if (tokens.containsKey(paragraphId)) {
                HttpPost httpPost = new HttpPost(String.format("%s%s", apiUrl, "/logoff"));
                setHeaders(httpPost, tokens.get(paragraphId));
                HttpResponse response = httpClient.execute(httpPost);
                if (response.getStatusLine().getStatusCode() == 200) {
                    return true;
                }
            }

            return false;
        } catch (Exception e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(close session): Request failed", ExceptionUtils.getStackTrace(e)));
        } finally {
            tokens.remove(paragraphId);
        }
    }

    public UniverseInfo getUniverseInfo(String universeName) {
        return universesMap.get(universeName);
    }

    public Map<String, UniverseNodeInfo> getUniverseNodesInfo(String token, String universeName)
            throws UniverseException {
        UniverseInfo universeInfo = universesMap.get(universeName);
        if (universeInfo != null && StringUtils.isNotBlank(universeInfo.getId())) {
            Map<String, UniverseNodeInfo> universeNodeInfoMap = universeInfosMap.get(universeName);
            if (universeNodeInfoMap != null && universesInfoUpdatedMap.containsKey(universeName)
                    && !isExpired(universesInfoUpdatedMap.get(universeName))) {
                return universeNodeInfoMap;
            } else {
                universeNodeInfoMap = new HashMap<>();
            }
            try {
                HttpGet httpGet = new HttpGet(
                        String.format("%s%s%s", apiUrl, "/sl/v1/universes/", universeInfo.getId()));
                setHeaders(httpGet, token);
                HttpResponse response = httpClient.execute(httpGet);

                if (response.getStatusLine().getStatusCode() == 200) {
                    try (InputStream xmlStream = response.getEntity().getContent()) {
                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                        DocumentBuilder builder = factory.newDocumentBuilder();
                        Document doc = builder.parse(xmlStream);
                        XPathFactory xPathfactory = XPathFactory.newInstance();
                        XPath xpath = xPathfactory.newXPath();
                        XPathExpression expr = xpath.compile("//outline/folder");
                        XPathExpression exprRootItems = xpath.compile("//outline/item");
                        NodeList universeInfoNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
                        NodeList universeRootInfoNodes = (NodeList) exprRootItems.evaluate(doc,
                                XPathConstants.NODESET);
                        if (universeInfoNodes != null) {
                            parseUniverseInfo(universeInfoNodes, universeNodeInfoMap);
                        }
                        if (universeRootInfoNodes != null) {
                            parseUniverseInfo(universeRootInfoNodes, universeNodeInfoMap);
                        }
                    } catch (Exception e) {
                        throw new UniverseException(String.format(errorMessageTemplate,
                                "UniverseClient " + "(get universe nodes info): Response processing failed",
                                ExceptionUtils.getStackTrace(e)));
                    }
                }
            } catch (IOException e) {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get universe nodes info): Request failed",
                        ExceptionUtils.getStackTrace(e)));
            }
            universeInfosMap.put(universeName, universeNodeInfoMap);
            universesInfoUpdatedMap.put(universeName, System.currentTimeMillis());

            return universeNodeInfoMap;
        }
        return Collections.emptyMap();

    }

    public void loadUniverses(String token) throws UniverseException {
        if (universesMap.isEmpty() || universesUpdated == 0 || isExpired(universesUpdated)) {
            Map<String, UniverseInfo> universes = new ConcurrentHashMap();
            loadUniverses(token, 0, universes);
            universesMap.clear();
            universesMap.putAll(universes);
            universesUpdated = System.currentTimeMillis();
        }
    }

    public void cleanUniverses() {
        universesMap.clear();
    }

    public void removeUniverseInfo(String universe) {
        universeInfosMap.remove(universe);
    }

    public Map<String, UniverseInfo> getUniversesMap() {
        return universesMap;
    }

    public List<UniverseQueryPrompt> getParameters(String token, String queryId) throws UniverseException {
        HttpGet httpGet = new HttpGet(String.format("%s%s%s%s", apiUrl, "/sl/v1/queries/", queryId, "/parameters"));
        setHeaders(httpGet, token);
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get parameters): Request failed\n",
                        EntityUtils.toString(response.getEntity())));
            }
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get parameters): Request failed", ExceptionUtils.getStackTrace(e)));
        }

        try (InputStream xmlStream = response.getEntity().getContent()) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(xmlStream);
            XPathFactory xPathfactory = XPathFactory.newInstance();
            XPath xpath = xPathfactory.newXPath();
            XPathExpression expr = xpath.compile("//parameters/parameter");
            NodeList parametersNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            if (parametersNodes != null) {
                return parseParameters(parametersNodes);
            } else {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get parameters): Response processing failed"));
            }
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get parameters): Response processing failed",
                    ExceptionUtils.getStackTrace(e)));
        } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get parameters): Response processing failed",
                    ExceptionUtils.getStackTrace(e)));
        }
    }

    public void setParametersValues(String token, String queryId, List<UniverseQueryPrompt> parameters)
            throws UniverseException {
        HttpPut httpPut = new HttpPut(String.format("%s%s%s%s", apiUrl, "/sl/v1/queries/", queryId, "/parameters"));
        setHeaders(httpPut, token);
        HttpResponse response = null;
        try {
            StringBuilder request = new StringBuilder();
            request.append("<parameters>\n");
            for (UniverseQueryPrompt parameter : parameters) {
                String answer = String.format(parameterAnswerTemplate, parameter.getConstrained(),
                        parameter.getType(), parameter.getCardinality(), parameter.getKeepLastValues(),
                        parameter.getValue());
                String id = parameter.getId() != null ? String.format("<id>%s</id>\n", parameter.getId())
                        : StringUtils.EMPTY;
                String technicalName = parameter.getTechnicalName() != null
                        ? String.format("<technicalName>%s</technicalName>\n", parameter.getTechnicalName())
                        : StringUtils.EMPTY;
                String name = parameter.getTechnicalName() != null
                        ? String.format("<name>%s</name>\n", parameter.getName())
                        : StringUtils.EMPTY;
                request.append(String.format(parameterTemplate, id, technicalName, name, answer));
            }
            request.append("</parameters>\n");

            httpPut.setEntity(new StringEntity(request.toString(), "UTF-8"));

            response = httpClient.execute(httpPut);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(set parameters): Request failed\n",
                        EntityUtils.toString(response.getEntity())));
            }
        } catch (IOException e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(set parameters): Request failed", ExceptionUtils.getStackTrace(e)));
        }
    }

    private void loadUniverses(String token, int offset, Map<String, UniverseInfo> universesMap)
            throws UniverseException {
        int limit = 50;
        HttpGet httpGet = new HttpGet(
                String.format("%s%s?offset=%s&limit=%s", apiUrl, "/sl/v1/universes", offset, limit));
        setHeaders(httpGet, token);
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
        } catch (Exception e) {
            throw new UniverseException(String.format(errorMessageTemplate,
                    "UniverseClient " + "(get universes): Request failed", ExceptionUtils.getStackTrace(e)));
        }
        if (response != null && response.getStatusLine().getStatusCode() == 200) {
            try (InputStream xmlStream = response.getEntity().getContent()) {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(xmlStream);
                XPathFactory xPathfactory = XPathFactory.newInstance();
                XPath xpath = xPathfactory.newXPath();
                XPathExpression expr = xpath.compile("//universe");
                NodeList universesNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
                if (universesNodes != null) {
                    int count = universesNodes.getLength();
                    for (int i = 0; i < count; i++) {
                        Node universe = universesNodes.item(i);
                        if (universe.hasChildNodes()) {
                            NodeList universeParameters = universe.getChildNodes();
                            int parapetersCount = universeParameters.getLength();
                            String id = null;
                            String name = null;
                            String type = null;
                            for (int j = 0; j < parapetersCount; j++) {
                                Node parameterNode = universeParameters.item(j);
                                parameterNode.getNodeName();
                                if (parameterNode.getNodeType() == Node.ELEMENT_NODE) {
                                    if (parameterNode.getNodeName().equalsIgnoreCase("id")) {
                                        id = parameterNode.getTextContent();
                                        continue;
                                    }
                                    if (parameterNode.getNodeName().equalsIgnoreCase("name")) {
                                        name = parameterNode.getTextContent();
                                        continue;
                                    }
                                    if (parameterNode.getNodeName().equalsIgnoreCase("type")) {
                                        type = parameterNode.getTextContent();
                                        continue;
                                    }
                                }
                            }
                            if (StringUtils.isNotBlank(type)) {
                                name = name.replaceAll(String.format("\\.%s$", type), StringUtils.EMPTY);
                            }
                            universesMap.put(name, new UniverseInfo(id, name, type));
                        }
                    }
                    if (count == limit) {
                        offset += limit;
                        loadUniverses(token, offset, universesMap);
                    }
                }
            } catch (IOException e) {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get universes): Response processing failed",
                        ExceptionUtils.getStackTrace(e)));
            } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
                throw new UniverseException(String.format(errorMessageTemplate,
                        "UniverseClient " + "(get universes): Response processing failed",
                        ExceptionUtils.getStackTrace(e)));
            }
        }
    }

    private boolean isExpired(Long lastUpdated) {
        if (lastUpdated == null || System.currentTimeMillis() - lastUpdated > DAY) {
            return true;
        }

        return false;
    }

    private void setHeaders(HttpRequestBase request) {
        setHeaders(request, null);
    }

    private void setHeaders(HttpRequestBase request, String token) {
        request.setHeaders(commonHeaders);
        if (StringUtils.isNotBlank(token)) {
            request.addHeader(TOKEN_HEADER, token);
        }
    }

    private String getValue(String response, String xPathString)
            throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
        try (InputStream xmlStream = new ByteArrayInputStream(response.getBytes())) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(xmlStream);
            XPathFactory xPathfactory = XPathFactory.newInstance();
            XPath xpath = xPathfactory.newXPath();
            XPathExpression expr = xpath.compile(xPathString);
            Node tokenNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            if (tokenNode != null) {
                return tokenNode.getTextContent();
            }
        }
        return null;
    }

    private List<UniverseQueryPrompt> parseParameters(NodeList parametersNodeList) {
        List<UniverseQueryPrompt> parameters = new ArrayList<>();
        if (parametersNodeList != null) {
            int count = parametersNodeList.getLength();
            for (int i = 0; i < count; i++) {
                Node parameterNode = parametersNodeList.item(i);
                Node type = parameterNode.getAttributes().getNamedItem("type");
                if (type != null && type.getTextContent().equalsIgnoreCase("prompt")
                        && parameterNode.hasChildNodes()) {
                    NodeList parameterInfoNodes = parameterNode.getChildNodes();
                    int childNodesCount = parameterInfoNodes.getLength();
                    String name = null;
                    Integer id = null;
                    String cardinality = null;
                    String constrained = null;
                    String valueType = null;
                    String technicalName = null;
                    String keepLastValues = null;
                    for (int j = 0; j < childNodesCount; j++) {
                        Node childNode = parameterInfoNodes.item(j);
                        String childNodeName = childNode.getNodeName();
                        if (childNodeName.equalsIgnoreCase(EL_NAME)) {
                            name = childNode.getTextContent();
                            continue;
                        }
                        if (childNodeName.equalsIgnoreCase(EL_ID)) {
                            id = Integer.parseInt(childNode.getTextContent());
                            continue;
                        }
                        if (childNodeName.equalsIgnoreCase(EL_TECH_NAME)) {
                            technicalName = childNode.getTextContent();
                            continue;
                        }
                        if (childNodeName.equalsIgnoreCase(EL_ANSWER)) {
                            NamedNodeMap answerAttributes = childNode.getAttributes();
                            if (answerAttributes.getNamedItem("constrained") != null) {
                                constrained = answerAttributes.getNamedItem("constrained").getTextContent();
                            }
                            if (answerAttributes.getNamedItem("type") != null) {
                                valueType = answerAttributes.getNamedItem("type").getTextContent();
                            }
                            NodeList answerNodes = childNode.getChildNodes();
                            int answerCount = answerNodes.getLength();
                            for (int k = 0; k < answerCount; k++) {
                                Node answerChildNode = answerNodes.item(k);
                                String answerChildNodeName = answerChildNode.getNodeName();
                                if (answerChildNodeName.equalsIgnoreCase(EL_INFO)) {
                                    NamedNodeMap infoAttributes = answerChildNode.getAttributes();
                                    if (infoAttributes.getNamedItem("cardinality") != null) {
                                        cardinality = infoAttributes.getNamedItem("cardinality").getTextContent();
                                    }
                                    if (infoAttributes.getNamedItem("keepLastValues") != null) {
                                        keepLastValues = infoAttributes.getNamedItem("keepLastValues")
                                                .getTextContent();
                                    }
                                    break;
                                }
                            }
                            continue;
                        }
                    }
                    if (name != null && id != null && cardinality != null) {
                        parameters.add(new UniverseQueryPrompt(id, name, cardinality, constrained, valueType,
                                technicalName, keepLastValues));
                        break;
                    }
                }
            }
        }

        return parameters;
    }

    private List<List<String>> parseResults(NodeList resultsNodeList) {
        List<List<String>> results = new ArrayList<>();
        if (resultsNodeList != null) {
            int count = resultsNodeList.getLength();
            for (int i = 0; i < count; i++) {
                Node node = resultsNodeList.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE && node.hasChildNodes()) {
                    NodeList properties = node.getChildNodes();
                    if (properties != null) {
                        int countProperties = properties.getLength();
                        List<String> headers = new ArrayList<>();
                        List<String> row = new ArrayList<>();
                        // first property is id
                        for (int j = 1; j < countProperties; j++) {
                            Node propertyNode = properties.item(j);
                            if (i == 0) {
                                headers.add(propertyNode.getNodeName().replaceAll("^\\w*:", StringUtils.EMPTY));
                            }
                            row.add(propertyNode.getTextContent());
                        }
                        if (i == 0) {
                            results.add(headers);
                        }
                        results.add(row);
                    }
                }
            }
        }

        return results;
    }

    private void addAttributesToDimension(Node universeNode, Map<String, UniverseNodeInfo> nodes) {
        final NodeList attributeNodes = universeNode.getChildNodes();
        final int attributeNodesCount = attributeNodes.getLength();

        for (int i = 0; i < attributeNodesCount; ++i) {
            final Node attributeNode = attributeNodes.item(i);

            if (attributeNode.getNodeName().equalsIgnoreCase(EL_ITEM)) {
                final NodeList childNodes = attributeNode.getChildNodes();
                final int childNodesCount = childNodes.getLength();

                String nodeId = null;
                String nodeName = null;
                String nodePath = null;
                for (int j = 0; j < childNodesCount; j++) {
                    Node childNode = childNodes.item(j);
                    if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                        switch (childNode.getNodeName().toLowerCase()) {
                        case EL_NAME:
                            nodeName = childNode.getTextContent();
                            break;
                        case EL_ID:
                            nodeId = childNode.getTextContent();
                            break;
                        case EL_PATH:
                            nodePath = childNode.getTextContent();
                            break;
                        }
                    }
                }
                StringBuilder key = new StringBuilder();
                if (StringUtils.isNotBlank(nodeName)) {
                    String nodeType = null;
                    String[] parts = nodePath.split("\\\\");
                    List<String> path = new ArrayList();
                    for (String part : parts) {
                        String[] p = part.split("\\|");
                        if (p.length == 2) {
                            if (p[1].equalsIgnoreCase("folder") || p[1].equalsIgnoreCase("dimension")) {
                                path.add(p[0]);
                            } else {
                                nodeName = p[0];
                                nodeType = p[1];
                            }
                        }
                    }
                    final String folder = StringUtils.join(path, "\\");
                    key.append("[");
                    key.append(StringUtils.join(path, "].["));
                    key.append(String.format("].[%s]", nodeName));
                    nodes.put(key.toString(), new UniverseNodeInfo(nodeId, nodeName, nodeType, folder, nodePath));
                }
            }
        }
    }

    private void parseUniverseInfo(NodeList universeInfoNodes, Map<String, UniverseNodeInfo> nodes) {
        if (universeInfoNodes != null) {
            int count = universeInfoNodes.getLength();
            for (int i = 0; i < count; i++) {
                Node node = universeInfoNodes.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE && node.hasChildNodes()) {
                    String name = node.getNodeName();
                    NodeList childNodes = node.getChildNodes();
                    int childNodesCount = childNodes.getLength();
                    if (name.equalsIgnoreCase(EL_FOLDER)) {
                        NodeSet list = new NodeSet();
                        for (int j = 0; j < childNodesCount; j++) {
                            Node childNode = childNodes.item(j);
                            if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.hasChildNodes()) {
                                String childNodeName = childNode.getNodeName();
                                if (childNodeName.equalsIgnoreCase(EL_FOLDER)
                                        || childNodeName.equalsIgnoreCase(EL_ITEM)) {
                                    list.addNode(childNode);
                                }
                            }
                        }
                        if (list.getLength() > 0) {
                            parseUniverseInfo(list, nodes);
                        }
                    } else if (name.equalsIgnoreCase(EL_ITEM)) {
                        String nodeId = null;
                        String nodeName = null;
                        String nodePath = null;
                        for (int j = 0; j < childNodesCount; j++) {
                            Node childNode = childNodes.item(j);
                            if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                                String childNodeName = childNode.getNodeName();
                                if (childNodeName.equalsIgnoreCase(EL_NAME)) {
                                    nodeName = childNode.getTextContent();
                                    continue;
                                }
                                if (childNodeName.equalsIgnoreCase(EL_ID)) {
                                    nodeId = childNode.getTextContent();
                                    continue;
                                }
                                if (childNodeName.equalsIgnoreCase(EL_PATH)) {
                                    nodePath = childNode.getTextContent();
                                    continue;
                                }
                            }
                        }
                        String folder = null;
                        StringBuilder key = new StringBuilder();
                        if (StringUtils.isNotBlank(nodeName)) {
                            String nodeType = null;
                            if (StringUtils.isNotBlank(nodePath)) {
                                String[] parts = nodePath.split("\\\\");
                                List<String> path = new ArrayList();
                                for (String part : parts) {
                                    String[] p = part.split("\\|");
                                    if (p.length == 2) {
                                        if (p[1].equalsIgnoreCase("folder")) {
                                            path.add(p[0]);
                                        } else {
                                            nodeName = p[0];
                                            nodeType = p[1];
                                            if (p[1].equalsIgnoreCase("dimension")) {
                                                addAttributesToDimension(node, nodes);
                                            }
                                        }
                                    }
                                }
                                folder = StringUtils.join(path, "\\");
                                if (path.isEmpty()) {
                                    key.append(String.format("[%s]", nodeName));
                                } else {
                                    key.append("[");
                                    key.append(StringUtils.join(path, "].["));
                                    key.append(String.format("].[%s]", nodeName));
                                }
                            }
                            nodes.put(key.toString(),
                                    new UniverseNodeInfo(nodeId, nodeName, nodeType, folder, nodePath));
                        }
                    }
                }
            }
        }
    }
}