view gnv/src/main/java/de/intevation/gnv/artifactdatabase/client/DefaultArtifactDatabaseClient.java @ 1022:28a0628b11b0

Added license file and license header. gnv/trunk@1258 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Tue, 02 Nov 2010 17:15:08 +0000
parents 397b4cb8589b
children fca4b5eb8d2f
line wrap: on
line source
/*
 * Copyright (c) 2010 by Intevation GmbH
 *
 * This program is free software under the LGPL (>=v2.1)
 * Read the file LGPL.txt coming with the software for details
 * or visit http://www.gnu.org/licenses/ if it does not exist.
 */

package de.intevation.gnv.artifactdatabase.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.log4j.Logger;
import org.restlet.Client;
import org.restlet.data.ClientInfo;
import org.restlet.data.Language;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import de.intevation.gnv.artifactdatabase.client.exception.ArtifactDatabaseClientException;
import de.intevation.gnv.artifactdatabase.client.exception.ArtifactDatabaseInputException;
import de.intevation.gnv.artifactdatabase.objects.Artifact;
import de.intevation.gnv.artifactdatabase.objects.ArtifactDescription;
import de.intevation.gnv.artifactdatabase.objects.ArtifactFactory;
import de.intevation.gnv.artifactdatabase.objects.ArtifactObject;
import de.intevation.gnv.artifactdatabase.objects.ArtifactStatisticsSet;
import de.intevation.gnv.artifactdatabase.objects.DefaultArtifactStatisticValue;
import de.intevation.gnv.artifactdatabase.objects.DefaultArtifactStatisticsSet;
import de.intevation.gnv.artifactdatabase.objects.DefaultExportMode;
import de.intevation.gnv.artifactdatabase.objects.DefaultOutputMode;
import de.intevation.gnv.artifactdatabase.objects.DefaultOutputParameter;
import de.intevation.gnv.artifactdatabase.objects.ExportMode;
import de.intevation.gnv.artifactdatabase.objects.InputParameter;
import de.intevation.gnv.artifactdatabase.objects.OutputMode;
import de.intevation.gnv.artifactdatabase.objects.OutputParameter;
import de.intevation.gnv.artifactdatabase.objects.ParametrizedArtifactFactory;
import de.intevation.gnv.artifactdatabase.objects.ParametrizedArtifactObject;
import de.intevation.gnv.artifactdatabase.objects.map.Layer;
import de.intevation.gnv.artifactdatabase.objects.map.MapService;
import de.intevation.gnv.propertiesreader.PropertiesReader;
import de.intevation.gnv.propertiesreader.PropertiesReaderFactory;
import de.intevation.gnv.util.XMLUtils;

/**
 * The default implementation of <code>ArtifactDatabaseClient</code> which
 * implements methods to communicate via HTTP protocol with the artifact server.
 *
 * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a>
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class DefaultArtifactDatabaseClient implements ArtifactDatabaseClient {
    /**
     * The URI of the namespace of the artifacts.
     */
    public final static String NAMESPACE_URI = "http://www.intevation.de/2009/artifacts";

    /**
     * The XML prefix for the artifacts namespace.
     */
    public final static String NAMESPACE_PREFIX = "art";

    /**
     * Xpath expression to get general exception messages related to server
     * errors.
     */
    public static final String EXCEPTION_GENERAL =
        "/art:exceptionreport/art:exception/text()";

    /**
     * Xpath expression to get exception messages related to invalid user input.
     */
    public static final String EXCEPTION_USER_INPUT =
        "/art:exceptionreport/art:exception/art:input/text()";

    /**
     * the logger, used to log exceptions and additonaly information
     */
    private static Logger log = Logger
            .getLogger(DefaultArtifactDatabaseClient.class);

    /**
     * The Databases which could be used
     */
    private static Collection<String> artifactDatabases = null;

    /**
     * Is the Class initialized?
     */
    private static boolean initialized = false;

    /**
     * Request locale
     */
    private Locale locale;


    /**
     * Constructor
     */
    public DefaultArtifactDatabaseClient() {

    }

    public Collection<ArtifactObject> getArtifactFactories()
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        Collection<ArtifactObject> resultValues = null;
        if (!initialized) {
            this.initialize();
        }
        try {
            log.debug("DefaultArtifactDatabaseClient.getArtifactFactories");
            Iterator<String> it = artifactDatabases.iterator();
            while (it.hasNext()) {
                String server = it.next();
                String url = server + "/factories";
                Document document = this.doGetRequest(url);
                if (resultValues == null) {
                    resultValues = this.getArtifactFactories(document, server,null);
                } else {
                    resultValues.addAll(this.getArtifactFactories(document,
                            server,null));
                }
            }
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
        return resultValues;
    }


    /**
     * Returns a collection of artifact factories retrieved by <i>server</i>.
     *
     * @param document
     * @param server
     * @param geometry
     * @return a collection of factories.
     */
    private Collection<ArtifactObject> getArtifactFactories(Document document,
                                                            String server,
                                                            String geometry) {
        NodeList artifactFactories = XMLUtils.getNodeSetXPath(document,
                "/art:result/art:factories/art:factory");
        Collection<ArtifactObject> resultValues = new ArrayList<ArtifactObject>(
                artifactFactories.getLength());
        if (artifactFactories != null) {
            for (int i = 0; i < artifactFactories.getLength(); i++) {
                Node artifactFactoryNode = artifactFactories.item(i);
                String name = XMLUtils.getStringXPath(artifactFactoryNode,
                        "@art:name");
                String description = XMLUtils.getStringXPath(
                        artifactFactoryNode, "@art:description");

                NodeList parameterNodeList = XMLUtils.getNodeSetXPath(
                                                            artifactFactoryNode,
                                                            "art:parameter");

                if ((parameterNodeList != null &&
                    parameterNodeList.getLength() > 0) ||
                    geometry != null){
                    Collection<String> parameters =
                          new ArrayList<String>(parameterNodeList.getLength());
                    for (int j = 0; j < parameterNodeList.getLength(); j++){
                        Element parameterNode = (Element)parameterNodeList.item(j);
                        parameters.add(parameterNode.getAttribute("id"));
                    }
                    ParametrizedArtifactObject pao =
                                    new ParametrizedArtifactFactory(name,
                                                                    description,
                                                                    server);
                    pao.addParameters("parameter", parameters);
                    resultValues.add(pao);
                    if (geometry != null){
                        Collection<String> geometryList = new ArrayList<String>(1);
                        geometryList.add(geometry);
                        pao.addParameters("geometry",geometryList);
                    }
                }else{
                    ArtifactObject artifactFactory  = new ArtifactFactory(name,
                                                                    description,
                                                                    server);
                    resultValues.add(artifactFactory);
                }
            }
        }
        log.debug("Artifact Factories: " + resultValues.size());
        return resultValues;
    }


    private Document doGetRequest(String requestUrl)
    throws IOException, ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        return this.doGetRequest(requestUrl, null);
    }


    private Document doGetRequest(String requestUrl, Document requestBody)
    throws IOException, ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        XMLUtils xmlUtils = new XMLUtils();
        Representation output = doGetRequestInternal(requestUrl, requestBody);
        Document document = XMLUtils.readDocument(output.getStream());
        this.check4ExceptionReport(document);
        return document;
    }

    protected Request initialize(Request request) {
        return initialize(request, locale);
    }

    /**
     * Initialize the <code>ClientInfo</code> object of <i>request</i> with the
     * given <i>locale</i>.
     *
     * @param request The request object.
     * @param locale The locale.
     * @return the prepared request object.
     */
    protected Request initialize(Request request, Locale locale) {
        if (locale != null) {
            ClientInfo clientInfo = request.getClientInfo();
            String lang = locale.getLanguage();
            Language language = new Language(lang);
            List<Preference<Language>> acceptedLanguages =
                new ArrayList<Preference<Language>>(2);
            acceptedLanguages.add(new Preference(language,     1.0f));
            acceptedLanguages.add(new Preference(Language.ALL, 0.5f));
            clientInfo.setAcceptedLanguages(acceptedLanguages);
        }
        return request;
    }


    private Representation doGetRequestInternal(String requestUrl,
                                                Document requestBody) throws IOException {
        Client client = new Client(Protocol.HTTP);
        Request request = initialize(new Request(Method.GET, requestUrl));
        if (requestBody != null) {
            String documentBody = XMLUtils
                    .writeDocument2String(requestBody);
            Representation representation = new StringRepresentation(
                    documentBody);
            request.setEntity(representation);
        }
        Response response = client.handle(request);
        if (response.getStatus().getCode() != 200){
            throw new IOException(response.getStatus().getDescription());
        }
        Representation output = response.getEntity();
        return output;
    }


    private InputStream doPostRequest(String requestUrl, Document requestBody)
                                                                              throws IOException {
        
        log.debug("URL: "+ requestUrl);
        log.debug("Body");
        log.debug(XMLUtils.writeDocument2String(requestBody));
        
        Client client = new Client(Protocol.HTTP);
        Request request = initialize(new Request(Method.POST, requestUrl));
        String documentBody = XMLUtils.writeDocument2String(requestBody);
        Representation representation = new StringRepresentation(documentBody);
        request.setEntity(representation);
        Response response = client.handle(request);
        if (response.getStatus().getCode() != 200){
            throw new IOException(response.getStatus().getDescription());
        }
        Representation output = response.getEntity();
        return output.getStream();
    }

    private synchronized void initialize() {
        if (!initialized) {
            PropertiesReader pr = PropertiesReaderFactory.getInstance()
                    .getPropertiesReader();
            int count = Integer.parseInt(pr.getPropertieValue(
                    ARTIFACTDATABASE_COUNT_ID, "0"));
            artifactDatabases = new ArrayList<String>(count);
            for (int i = 0; i < count; i++) {
                artifactDatabases.add(pr.getPropertieValue(
                        ARTIFACTDATABASE_URL_ID + "." + (i + 1), "N/N"));
            }
            initialized = true;
        }

    }


    /**
     * Creates a new artifact with the given <i>artifactFactory</i>. A POST call
     * is sent to the artifact server which creates a new artifact.
     *
     * @param artifactFactory The artifact factory used to create a new
     * artifact.
     * @return the created artifact of the artifact server.
     * @throws ArtifactDatabaseClientException
     * @throws ArtifactDatabaseInputException
     */
    public ArtifactObject createNewArtifact(ArtifactObject artifactFactory)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {

        try {
            Document request = this.createCreateRequestBody(artifactFactory);
            Document result = doPostRequest(artifactFactory, request, "create");
            return this.getArtifact(result);
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
    }

    /**
     * Returns a new artifact defined by uuid and hash values of the document
     * returned by the artifact server after creating a new artifact.
     *
     * @param document Contains information about the server-side created
     * artifact.
     * @return a new artifact object.
     */
    private ArtifactObject getArtifact(Document document) {
        XMLUtils xmlUtils = new XMLUtils();
        String uuid = XMLUtils.getStringXPath(document, "/art:result/art:uuid/@value");
        String hash = XMLUtils.getStringXPath(document, "/art:result/art:hash/@value");
        log.info("NEW Artifact: " + uuid + " / " + hash);
        return new Artifact(uuid, hash);
    }

    private Document createCreateRequestBody(ArtifactObject artifactFactory) {
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createRootNode(document);
        Element typeNode = this.createArtifactElement(document, "type");
        typeNode.setAttribute("name", "create");
        rootNode.appendChild(typeNode);

        Element factoryNode = this.createArtifactElement(document, "factory");
        factoryNode.setAttribute("name", artifactFactory.getId());
        rootNode.appendChild(factoryNode);

        if (artifactFactory instanceof ParametrizedArtifactObject){
            Map<String, Collection<String>> parameterMap =
                ((ParametrizedArtifactObject)artifactFactory).getParameters();
            if (parameterMap != null && !parameterMap.isEmpty() ){
                Iterator<String> keyIt = parameterMap.keySet().iterator();
                while (keyIt.hasNext()){
                    String key = keyIt.next();
                    Iterator<String> valueIt = parameterMap.get(key).iterator();
                    while (valueIt.hasNext()){
                        String value = valueIt.next();
                        Element parameterNode =
                             this.createArtifactElement(document, "parameter");
                        parameterNode.setAttribute("name", key);
                        parameterNode.setAttribute("value", value);
                        factoryNode.appendChild(parameterNode);
                    }
                }
            }
        }
        return document;
    }

    private Element createRootNode(Document document) {
        Element rootNode = this.createArtifactElement(document, "action");
        document.appendChild(rootNode);
        return rootNode;
    }


    private Element createArtifactElement(Document document, String name) {
        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
            document,
            NAMESPACE_URI,
            NAMESPACE_PREFIX
        );

        return creator.create(name);
    }


    private Document doPostRequest(ArtifactObject artifactFactory,
        Document request, String suburl)
    throws IOException, ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        XMLUtils xmlUtils = new XMLUtils();
        String url = ((ArtifactFactory) artifactFactory).getDataBaseUrl();
        InputStream is = this.doPostRequest(url + "/" + suburl, request);
        Document result = XMLUtils.readDocument(is);
        this.check4ExceptionReport(result);
        return result;
    }


    /**
     * Retrieve the current artifact description after calling the describe
     * operation of the artifact server.
     *
     * @param artifactFactory
     * @param currentArtifact
     * @param includeUI Set this option to true, if the user interface
     * description is required.
     * @return the artifact description.
     * @throws ArtifactDatabaseClientException
     * @throws ArtifactDatabaseInputException
     */
    public ArtifactDescription getCurrentStepDescription(
        ArtifactObject artifactFactory,
        ArtifactObject currentArtifact,
        boolean includeUI)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        try {
            String url = this.getArtifactUrl(artifactFactory, currentArtifact);
            Document request = this.createDescribeRequestBody(currentArtifact,
                                                              includeUI);
            InputStream describeResult = this.doPostRequest(url, request);

            return this.readDescription(XMLUtils.readDocument(describeResult),
                                        currentArtifact);
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
    }


    private Document createDescribeRequestBody(ArtifactObject currentArtifact,
                                                         boolean includeUI){
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createRootNode(document);

        Element typeNode = this.createArtifactElement(document, "type");
        typeNode.setAttribute("name", "describe");
        rootNode.appendChild(typeNode);

        Element uuidNode = this.createArtifactElement(document, "uuid");
        uuidNode.setAttribute("value", currentArtifact.getId());
        rootNode.appendChild(uuidNode);

        Element hashNode = this.createArtifactElement(document, "hash");
        hashNode.setAttribute("value", currentArtifact.getHash());
        rootNode.appendChild(hashNode);
        Element includeUINode = this.createArtifactElement(document, "include-ui");
        includeUINode.appendChild(document.createTextNode(""+includeUI));
        rootNode.appendChild(includeUINode);
        return document;
    }


    private String getArtifactUrl(ArtifactObject artifactFactory,
                                  ArtifactObject currentArtifact) {
        String url = ((ArtifactFactory) artifactFactory).getDataBaseUrl()
                     + "/artifact/" + currentArtifact.getId();
        return url;
    }

    private ArtifactDescription readDescription(Document document,
        ArtifactObject artifact)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        // Check if there was an Error or Exception reported from the
        // ArtifactDatabase
        this.check4ExceptionReport(document);
        if (artifact instanceof ArtifactDescription) {
            ArtifactDescription ad = (ArtifactDescription) artifact;
            Node uiNode = XMLUtils.getNodeXPath(document, "/art:result/art:ui");
            Node outputNode = XMLUtils
                    .getNodeXPath(document, "/art:result/art:outputs");

            Map<String, OutputMode> outputModes = null;
            if (outputNode != null) {
                NodeList outputModesNodes = XMLUtils.getNodeSetXPath(
                        outputNode, "art:output");
                if (outputModesNodes != null) {
                    outputModes = new HashMap<String, OutputMode>(
                            outputModesNodes.getLength());
                    for (int i = 0; i < outputModesNodes.getLength(); i++) {
                        Node outputModeNode = outputModesNodes.item(i);
                        String name = XMLUtils.getStringXPath(outputModeNode,
                                "@name");
                        String mimeType = XMLUtils.getStringXPath(
                                outputModeNode, "@mime-type");

                        NodeList parameterNodes = XMLUtils.getNodeSetXPath(
                                outputModeNode, "art:parameter/art:parameter");
                        Collection<OutputParameter> parameter = null;
                        if (parameterNodes != null) {
                            parameter = new ArrayList<OutputParameter>(
                                    parameterNodes.getLength());
                            for (int j = 0; j < parameterNodes.getLength(); j++) {
                                Node outputParameterNode = parameterNodes
                                        .item(j);
                                parameter.add(new DefaultOutputParameter(
                                        XMLUtils.getStringXPath(
                                                outputParameterNode, "@name"),
                                        XMLUtils.getStringXPath(
                                                outputParameterNode, "@value"),
                                        XMLUtils.getStringXPath(
                                                outputParameterNode, "@name"),
                                        XMLUtils.getStringXPath(
                                                outputParameterNode, "@type")));
                            }
                        }

                        NodeList exportNodes = XMLUtils.getNodeSetXPath(
                            outputModeNode, "art:exports/art:export");
                        Map exports = null;

                        if (exportNodes != null) {
                            int size = exportNodes.getLength();
                            exports = new HashMap<String, ExportMode>(size);

                            for (int k = 0; k < size; k++) {
                                Node export = exportNodes.item(k);
                                String expName = XMLUtils.getStringXPath(
                                    export, "@name");
                                exports.put(expName, new DefaultExportMode(
                                    expName,
                                    XMLUtils.getStringXPath(export, "@description"),
                                    XMLUtils.getStringXPath(export, "@mime-type")));
                            }
                        }

                        outputModes.put(name, new DefaultOutputMode(name,
                                mimeType, parameter, exports));
                    }

                }
            }

            String currentState = XMLUtils.getStringXPath(document,
                    "/art:result/art:state/@name");
            NodeList statesList = XMLUtils.getNodeSetXPath(document,
                    "/art:result/art:reachable-states/art:state/@name");
            Collection<String> reachableStates = new ArrayList<String>(
                    statesList.getLength());
            for (int i = 0; i < statesList.getLength(); i++) {
                reachableStates.add(statesList.item(i).getNodeValue());
            }

            NodeList inputNodes = XMLUtils.getNodeSetXPath(document,
                    "/art:result/art:model/art:input");
            if (inputNodes != null) {
                Collection<String> inputParameter = new ArrayList<String>(
                        inputNodes.getLength());
                for (int i = 0; i < inputNodes.getLength(); i++) {
                    Node inputNode = inputNodes.item(i);
                    String name = XMLUtils.getStringXPath(inputNode, "@name");
                    inputParameter.add(name);
                }
                ad.setInputParameter(inputParameter);
            }

            ad.setOutputModes(outputModes);
            ad.setCurrentOut(outputNode);
            ad.setCurrentUI(uiNode);
            ad.setCurrentState(currentState);
            ad.setReachableStates(reachableStates);
            return ad;
        } else {
            log.error("Artifact must be Instance of ArtifactDescription");
            throw new ArtifactDatabaseClientException(
                    "Artifact must be Instance of ArtifactDescription");
        }

    }


    /**
     * Feed, advance and describe in one single method.
     *
     * @param artifactFactory The factory which created the current artifact.
     * @param currentArtifact The current artifact.
     * @param target The target state for advance.
     * @param inputParameter Input data for feed.
     * @return the artifact description after calling feed and advance.
     * @throws ArtifactDatabaseClientException if a general error occured in
     * the artifact server.
     * @throws ArtifactDatabaseInputException if the input data was invalid used
     * for feed.
     */
    public ArtifactDescription doNextStep(
        ArtifactObject             artifactFactory,
        ArtifactObject             currentArtifact,
        String                     target,
        Collection<InputParameter> inputParameter)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {

        try {
            // 1 Feed
            this.doFeed(artifactFactory, currentArtifact, inputParameter);

            // 2. Noch einmal Describe um das jetzt zu erreichende Ziel zu ermitteln
            ArtifactDescription ad = getCurrentStepDescription(
                artifactFactory,
                new Artifact(currentArtifact.getId(),
                currentArtifact.getHash()),
                false
            );

            target = ad.getReachableStates().iterator().next();

            // 3 Advance
            doAdvance(artifactFactory, currentArtifact, target);

            // 3 Describe
            return this.getCurrentStepDescription(artifactFactory,
                    currentArtifact,true);
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
    }


    /**
     * Call the advance operation of the artifact server to step to the next
     * step of the parameterization.
     *
     * @param factory The artifact factory which created the current artifact.
     * @param artifact The current artifact.
     * @param target The target state.
     * @throws IOException if an error occured while reading/writing from/to
     * stream.
     * @throws ArtifactDatabaseClientException if a general error occured in
     * the artifact server.
     * @throws ArtifactDatabaseInputException if the input data was invalid.
     */
    public void doAdvance(
        ArtifactObject factory,
        ArtifactObject artifact,
        String         target)
    throws IOException, ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        String url                = getArtifactUrl(factory, artifact);
        Document advanceDocument  = createAdvanceRequestBody(artifact, target);
        InputStream advanceResult = doPostRequest(url, advanceDocument);
        check4ExceptionReport(XMLUtils.readDocument(advanceResult));
    }

    private Document createFeedRequestBody(
                                           ArtifactObject currentArtifact,
                                           Collection<InputParameter> inputParameter) {
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createRootNode(document);

        Element typeNode = this.createArtifactElement(document, "type");
        typeNode.setAttribute("name", "feed");
        rootNode.appendChild(typeNode);

        Element uuidNode = this.createArtifactElement(document, "uuid");
        uuidNode.setAttribute("value", currentArtifact.getId());
        rootNode.appendChild(uuidNode);

        Element hashNode = this.createArtifactElement(document, "hash");
        hashNode.setAttribute("value", currentArtifact.getHash());
        rootNode.appendChild(hashNode);

        Node dataNode = this.createParameterNodes(inputParameter, document,
                "data");
        rootNode.appendChild(dataNode);

        return document;
    }


    private Node createParameterNodes(
                                      Collection<InputParameter> inputParameter,
                                      Document document, String nodeName) {
        Element dataNode = this.createArtifactElement(document, nodeName);

        if (inputParameter != null) {
            Iterator<InputParameter> it = inputParameter.iterator();
            while (it.hasNext()) {
                InputParameter ip = it.next();
                String name = ip.getName();
                String[] values = ip.getValues();
                if (values != null) {
                    for (int i = 0; i < values.length; i++) {
                        String value = values[i];
                        Element inputNode = this.createArtifactElement(
                                document, "input");
                        inputNode.setAttribute("name", name);
                        inputNode.setAttribute("value", value);
                        dataNode.appendChild(inputNode);
                    }
                }
            }
        }
        return dataNode;
    }


    /**
     * Creates the xml body for the advance request.
     *
     * @param currentArtifact
     * @param target
     * @return the advance xml document.
     */
    private Document createAdvanceRequestBody(ArtifactObject currentArtifact,
                                              String target) {
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createRootNode(document);

        Element typeNode = this.createArtifactElement(document, "type");
        typeNode.setAttribute("name", "advance");
        rootNode.appendChild(typeNode);

        Element uuidNode = this.createArtifactElement(document, "uuid");
        uuidNode.setAttribute("value", currentArtifact.getId());
        rootNode.appendChild(uuidNode);

        Element hashNode = this.createArtifactElement(document, "hash");
        hashNode.setAttribute("value", currentArtifact.getHash());
        rootNode.appendChild(hashNode);
        Element targetNode = this.createArtifactElement(document, "target");
        targetNode.setAttribute("name", target);
        rootNode.appendChild(targetNode);
        return document;
    }

    /**
     * Call the out operation of the artifact server and writes the result to
     * <i>stream</i>.
     *
     * @param exportMode
     * @param inputParameter
     */
    public void doOutput(
        ArtifactObject artifactFactory,
        ArtifactObject currentArtifact,
        OutputStream   stream,
        String         targetName,
        String         exportMode,
        String         mimeType,
        Collection<InputParameter> inputParameter)
    throws ArtifactDatabaseClientException
    {
        try {
            Document requestBody = this.createOutRequestBody(
                currentArtifact,
                targetName,
                exportMode,
                mimeType,
                inputParameter);

            String requestUrl = this.getArtifactUrl(artifactFactory,
                    currentArtifact)
                                + "/" + targetName;
            InputStream is = this.doPostRequest(requestUrl, requestBody);

            byte[] b = new byte[4096];
            int i = -1;
            while ((i = is.read(b)) > 0) {
                stream.write(b, 0, i);
            }
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
    }


    /**
     * Call the out method of the artifact server with a specific export mode.
     *
     * @param artifactFactory
     * @param currentArtifact
     * @param out
     * @throws ArtifactDatabaseClientException if an error occured while
     * reading/writing from/to stream.
     */
    public void doExport(
        ArtifactObject artifactFactory,
        ArtifactObject currentArtifact,
        OutputStream   out)
    throws ArtifactDatabaseClientException
    {
        InputStreamReader reader  = null;
        OutputStreamWriter writer = null;

        try {
            String url = ((ArtifactFactory) artifactFactory).getDataBaseUrl();
            url       += "/export/" + currentArtifact.getId();

            Representation response = doGetRequestInternal(url, null);
            InputStream in          = response.getStream();

            reader = new InputStreamReader(in);
            writer = new OutputStreamWriter(out);

            int    c;
            char[] buffer = new char[512];
            while ((c = reader.read(buffer)) > 0) {
                writer.write(buffer, 0, c);
            }

        }
        catch (Exception e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
        finally {
            try {
                reader.close();
            } catch (IOException ioe) {}

            try {
                writer.flush();
            } catch (IOException ioe) {}

            try {
                writer.close();
            } catch (IOException ioe) {}
        }
    }


    /**
     * Start to import an artifact from xml document.
     *
     * @param factory
     * @param document The artifact as xml document.
     * @return A status description of the operation (success or failure).
     * @throws ArtifactDatabaseClientException
     * @throws IOException
     */
    public Document doImport(
        ArtifactFactory factory,
        Document        document)
    throws ArtifactDatabaseClientException, IOException
    {
        String url           = factory.getDataBaseUrl() + "/import";
        InputStream response = doPostRequest(url, document);

        return XMLUtils.readDocument(response);
    }


    /**
     * Creates the xml request body used for an out-call of the artifact server.
     *
     * @param currentArtifact
     * @param target
     * @param exportMode
     * @param mimeType
     * @param inputParameter
     * @return the request body.
     */
    private Document createOutRequestBody(
        ArtifactObject             currentArtifact,
        String                     target,
        String                     exportMode,
        String                     mimeType,
        Collection<InputParameter> inputParameter)
    {
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createRootNode(document);

        Element typeNode = this.createArtifactElement(document, "type");
        typeNode.setAttribute("name", "out");
        rootNode.appendChild(typeNode);

        Element uuidNode = this.createArtifactElement(document, "uuid");
        uuidNode.setAttribute("value", currentArtifact.getId());
        rootNode.appendChild(uuidNode);

        Element hashNode = this.createArtifactElement(document, "hash");
        hashNode.setAttribute("value", currentArtifact.getHash());
        rootNode.appendChild(hashNode);

        Element outNode = this.createArtifactElement(document, "out");
        outNode.setAttribute("name", target);
        rootNode.appendChild(outNode);

        Element exportNode = this.createArtifactElement(document, "export");
        exportNode.setAttribute("name", exportMode);
        outNode.appendChild(exportNode);

        Element mimeTypeNode = this.createArtifactElement(document, "mime-type");
        mimeTypeNode.setAttribute("value", mimeType);
        outNode.appendChild(mimeTypeNode);

        Node parameterNode = this.createParameterNodes(inputParameter,
                document, "params");
        outNode.appendChild(parameterNode);

        return document;
    }


    /**
     * Calls the feed operation of the artifact server.
     *
     * @param artifactFactory The artifact factory which created the current
     * artifact.
     * @param currentArtifact The current artifact.
     * @param inputParameter The user input.
     * @throws ArtifactDatabaseClientException
     * @throws ArtifactDatabaseInputException
     */
    public void doFeed(
        ArtifactObject artifactFactory,
        ArtifactObject currentArtifact,
        Collection<InputParameter> inputParameter)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {

        try {
            Document feedDocument = this.createFeedRequestBody(currentArtifact,
                    inputParameter);
            String url = this.getArtifactUrl(artifactFactory, currentArtifact);
            InputStream feedResult = this.doPostRequest(url, feedDocument);
            Document feedResultDocument = XMLUtils
                    .readDocument(feedResult);
            this.check4ExceptionReport(feedResultDocument);
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }
    }

    private void check4ExceptionReport(Document document)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        String message = XMLUtils.getStringXPath(document,
            EXCEPTION_GENERAL);
        if (message != null) {
            throw new ArtifactDatabaseClientException(message);
        }

        String input = XMLUtils.getStringXPath(document, EXCEPTION_USER_INPUT);
        if (input != null) {
            throw new ArtifactDatabaseInputException(input);
        }
    }


    /**
     * Call the specific wms export mode of the out operation of the artifact
     * server.
     *
     * @param factory
     * @param artifact
     * @param inputParameter
     * @return a document that contains information about the wms service and
     * the created layer.
     * @throws ArtifactDatabaseClientException
     * @throws ArtifactDatabaseInputException
     */
    public Document publishWMS(
        ArtifactObject             factory,
        ArtifactObject             artifact,
        Collection<InputParameter> inputParameter
    ) throws ArtifactDatabaseClientException, ArtifactDatabaseInputException{
        log.debug("Start wms publishing...");

        String target     = "wms";
        String requestURL = getArtifactUrl(factory, artifact) + "/" + target;
        Document request  = createOutRequestBody(
            artifact, target, "", "text/xml", inputParameter);
        try {
            InputStream input = doPostRequest(requestURL, request);
            Document result   = XMLUtils.readDocument(input);
            this.check4ExceptionReport(result);
            return result;
        }
        catch (IOException ioe) {
            log.error(ioe, ioe);
            throw new ArtifactDatabaseClientException(ioe);
        }
    }


    public Collection<ArtifactStatisticsSet> calculateStatistics(
        ArtifactObject artifactFactory,
        ArtifactObject currentArtifact)
    throws ArtifactDatabaseClientException
    {
        log.debug("DefaultArtifactDatabaseClient.calculateStatistics");
        Collection<ArtifactStatisticsSet> resultValues = null;

        try {
            String targetName = "statistics";
            String requestUrl = this.getArtifactUrl(artifactFactory,
                    currentArtifact) + "/" + targetName;
            Document requestBody = this.createOutRequestBody(currentArtifact,
                    targetName, "", "text/xml", null);
            XMLUtils xmlUtils = new XMLUtils();
            InputStream is = this.doPostRequest(requestUrl, requestBody);
            Document resultDocument = XMLUtils.readDocument(is);
            if (resultDocument != null) {

                NodeList statisticSetNodes = XMLUtils.getNodeSetXPath(resultDocument,
                                                  "/art:statistics/art:statistic");
                resultValues = new ArrayList<ArtifactStatisticsSet>
                                          (statisticSetNodes.getLength());
                for (int i = 0; i < statisticSetNodes.getLength(); i++) {
                    Element statisticSetNode = (Element)statisticSetNodes.item(i);
                    String name = statisticSetNode.getAttribute("name");
                    ArtifactStatisticsSet set =
                               new DefaultArtifactStatisticsSet(name);
                    NodeList resultNodes = XMLUtils.getNodeSetXPath(statisticSetNode,
                            "art:statistic-value");
                    if (resultNodes != null) {
                        for (int j = 0; j < resultNodes.getLength(); j++) {
                            Element statisticNode = (Element)resultNodes.item(j);
                            String statisticName = statisticNode.
                                                        getAttribute("name");
                            String statisticValue = statisticNode.
                                                        getAttribute("value");
                            set.addStatisticValues(
                                   new DefaultArtifactStatisticValue
                                               (statisticName,statisticValue));
                        }
                    }
                    resultValues.add(set);
                }
            }
        } catch (IOException e) {
            log.error(e, e);
            throw new ArtifactDatabaseClientException(e);
        }

        return resultValues;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    public Locale getLocale() {
        return locale;
    }


    public Collection<ArtifactObject> getArtifactFactoryMetaInformation(
        Collection<MapService> mapServices,
        String geometry,
        String srs)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        log.debug("DefaultArtifactDatabaseClient.getArtifactFactories");
        Collection<ArtifactObject> resultValues = null;
        try {
            this.initialize();
            Iterator<String> it = artifactDatabases.iterator();
            Document requestBody = this.createMetaDataRequestBody(mapServices,
                                                                  geometry,
                                                                  srs);
            while (it.hasNext()) {
                String server = it.next();
                String url = server + "/service/metadata/";
                log.debug(url);

                InputStream resultStream = this.doPostRequest(url, requestBody);
                resultValues = this.parseMetaDataResult(resultStream, server,geometry);
            }
        } catch (IOException e) {
            log.error(e,e);
        }
        return resultValues;
    }

    private Collection<ArtifactObject> parseMetaDataResult(
        InputStream inputStream,
        String      server,
        String      geometry)
    throws ArtifactDatabaseClientException, ArtifactDatabaseInputException {
        XMLUtils xmlUtils = new XMLUtils();
        Document document = XMLUtils.readDocument(inputStream);
        this.check4ExceptionReport(document);
        return this.getArtifactFactories(document, server,geometry);
    }

    private Document createMetaDataRequestBody(Collection<MapService> mapServices,
                                               String geometry,
                                               String srs){
        log.debug("DefaultArtifactDatabaseClient.createMetaDataRequestBody");
        Document document = XMLUtils.newDocument();
        Node rootNode = this.createArtifactElement(document, "GetMetaData");
        document.appendChild(rootNode);

        if (geometry != null){
            Element locationNode = this.createArtifactElement(document, "location");
            locationNode.setAttribute("srs", srs);
            locationNode.appendChild(document.createTextNode(geometry));
            rootNode.appendChild(locationNode);
        }
        Element mapServicesNode = this.createArtifactElement(document,
                                                             "mapservices");
        Iterator<MapService> it = mapServices.iterator();
        while (it.hasNext()){
            MapService mapService = it.next();
            Element mapServiceNode = this.createArtifactElement(document,
                                                                "mapservice");
            mapServiceNode.setAttribute("id", mapService.getID());
            mapServiceNode.setAttribute("type", mapService.getType());
            mapServiceNode.setAttribute("url", mapService.getURL());
            if (mapService.getLayer() != null){
                Iterator<Layer> layer = mapService.getLayer().iterator();
                while (layer.hasNext()){
                    Layer tmpLayer = layer.next();
                    Element layerNode = this.createArtifactElement(document,
                                                                   "layer");
                    layerNode.setAttribute("id", tmpLayer.getID());
                    layerNode.setAttribute("name", tmpLayer.getName());
                    layerNode.setAttribute("isgrouplayer",
                                           ""+tmpLayer.isGroupLayer());
                    layerNode.setAttribute("parentid", tmpLayer.parentID());
                    mapServiceNode.appendChild(layerNode);
                }
            }
            mapServicesNode.appendChild(mapServiceNode);
        }
        rootNode.appendChild(mapServicesNode);
        return document;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org