All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.petals.binding.rest.config.RESTConsumesConfiguration Maven / Gradle / Ivy

There is a newer version: 2.4.0
Show newest version
/**
 * Copyright (c) 2007-2012 EBM WebSourcing, 2012-2020 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.binding.rest.config;

import static org.ow2.petals.binding.rest.RESTConstants.ConsumesParameter.SERVICE_BASE_PATH;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;

import org.glassfish.jersey.uri.UriTemplate;
import org.ow2.petals.binding.rest.config.incoming.IncomingPayloadGenerator;
import org.ow2.petals.binding.rest.config.incoming.OperationBuilder;
import org.ow2.petals.binding.rest.exchange.incoming.JBIMessage;
import org.ow2.petals.binding.rest.exchange.incoming.onjbiresponse.OnJBIResponse;
import org.ow2.petals.component.framework.api.configuration.AbstractConfigurationParameters;
import org.ow2.petals.component.framework.api.exception.PEtALSCDKException;
import org.ow2.petals.component.framework.jbidescriptor.generated.Consumes;
import org.ow2.petals.component.framework.su.ServiceUnitDataHandler;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ebmwebsourcing.easycommons.lang.StringHelper;
import com.ebmwebsourcing.easycommons.properties.PropertiesException;
import com.ebmwebsourcing.easycommons.properties.PropertiesHelper;
import com.ebmwebsourcing.easycommons.xml.XMLHelper;

public class RESTConsumesConfiguration extends RESTSUConfiguration {

    private static class JBIMessageTemplateMap {

        private final Map> jbiMessages = new HashMap<>();

        private final ReadWriteLock lock = new ReentrantReadWriteLock();

        private final Logger logger;

        /**
         * The custom place holders read from the component property file to be able to configure service units.
         */
        private final Properties componentPlaceholders;

        public JBIMessageTemplateMap(final Properties componentPlaceholders, final Logger logger) {
            this.componentPlaceholders = componentPlaceholders;
            this.logger = logger;
        }

        public void put(final JBIMessageTemplate jbiMessage) {

            final String httpMethod = jbiMessage.getHttpMethod();
            final String path = jbiMessage.getPath();

            Map jbiMessagesForHttpMethod = this.jbiMessages.get(httpMethod);
            if (jbiMessagesForHttpMethod == null) {
                jbiMessagesForHttpMethod = new HashMap<>();
            }

            try {
                final UriTemplate pathTemplate = new UriTemplate(
                        PropertiesHelper.resolveString(path, this.componentPlaceholders));
                if (jbiMessagesForHttpMethod.containsKey(pathTemplate)) {
                    throw new IllegalArgumentException(
                            "A JBI operation is already present for the specified HTTP Method and path");
                } else {
                    jbiMessagesForHttpMethod.put(pathTemplate, jbiMessage);
                }

                this.lock.writeLock().lock();
                try {
                    this.jbiMessages.put(httpMethod, jbiMessagesForHttpMethod);
                } finally {
                    this.lock.writeLock().unlock();
                }
            } catch (final PropertiesException e) {
                throw new IllegalArgumentException("An error occurs resolving placeholders", e);
            }
        }

        /**
         * @param httpMethod
         *            The HTTP method used on incoming HTTP request
         * @param path
         *            The path of the incoming HTTP request
         * @return
         */
        public JBIMessage get(final String httpMethod, final String path) {
            this.lock.readLock().lock();
            try {
                final Map jbiMessagesForHttpMethod = this.jbiMessages.get(httpMethod);
                if (jbiMessagesForHttpMethod != null) {
                    for (final Entry e : jbiMessagesForHttpMethod.entrySet()) {
                        final UriTemplate pathTemplate = e.getKey();
                        final Map templateVariables = new HashMap<>();
                        if (pathTemplate.match(path, templateVariables)) {
                            final JBIMessageTemplate template = e.getValue();
                            final JBIMessage jbiMessage = new JBIMessage(template);
                            for (Entry templateVariable : templateVariables.entrySet()) {
                                jbiMessage.addURIParameter(templateVariable.getKey(), templateVariable.getValue());
                            }
                            // we only are interested in the first path found!
                            return jbiMessage;
                        }
                    }
                }

                return null;
            } finally {
                this.lock.readLock().unlock();
            }
        }

        /**
         * Listener to apply changes when placeholders are reloaded
         */
        public void onPlaceHolderValuesReloaded() {
            this.lock.writeLock().lock();
            try {
                // For each HTTP method, we must update the map of message templates. These maps are replaced by new
                // ones containing updated URI templates
                for (final Entry> httpMethodEntry : this.jbiMessages
                        .entrySet()) {
                    final Map updatedJbiMessagesForHttpMethod = new HashMap<>();
                    for (final Entry currentJbiMessageEntry : httpMethodEntry
                            .getValue().entrySet()) {

                        final JBIMessageTemplate currentJbiMessage = currentJbiMessageEntry.getValue();
                        try {
                            final UriTemplate newPathTemplate = new UriTemplate(PropertiesHelper
                                    .resolveString(currentJbiMessage.getPath(), this.componentPlaceholders));
                            updatedJbiMessagesForHttpMethod.put(newPathTemplate, currentJbiMessage);

                        } catch (final PropertiesException e) {
                            this.logger.log(Level.WARNING,
                                    "A problem occurs resolving the path template '%s' against placeholders. Path template not updated. Try to reload placeholders again.",
                                    e);
                            updatedJbiMessagesForHttpMethod.put(currentJbiMessageEntry.getKey(), currentJbiMessage);
                        }
                    }

                    this.jbiMessages.put(httpMethodEntry.getKey(), updatedJbiMessagesForHttpMethod);
                }

            } finally {
                this.lock.writeLock().unlock();
            }
        }

    }

    private final JBIMessageTemplateMap jbiMessages;

    private final String serviceBasePath;

    private final Consumes consumes;

    private final AbstractConfigurationParameters extensions;

    public RESTConsumesConfiguration(final ServiceUnitDataHandler suDataHandler, final Consumes consumes,
            final XPath xpathBuilder, final Properties componentPlaceholders, final Logger logger)
            throws PEtALSCDKException {
        this.extensions = suDataHandler.getConfigurationExtensions(consumes);
        this.serviceBasePath = this.checkMandatoryParameter(SERVICE_BASE_PATH);
        final LogErrorListener xslLogErrorListener = new LogErrorListener(logger, suDataHandler.getName());
        this.jbiMessages = processSUConsumesMapping(consumes, xpathBuilder, suDataHandler.getInstallRoot(),
                xslLogErrorListener, componentPlaceholders, logger);
        this.consumes = consumes;
    }

    public String getServiceBasePath() {
        return this.serviceBasePath;
    }

    public Consumes getConsumes() {
        return this.consumes;
    }

    public JBIMessage getJBIMessage(final HttpServletRequest request) {

        // get the JBI operation from the method and the path
        final String httpMethod = request.getMethod();
        final String path = request.getPathInfo();
        final JBIMessage jbiMessage = this.jbiMessages.get(httpMethod, path);

        // add the request parameters
        if (jbiMessage != null) {
            final Map requestParameters = request.getParameterMap();
            for (final Entry parameter : requestParameters.entrySet()) {
                jbiMessage.addQueryParameter(parameter.getKey(),
                        org.ow2.petals.binding.rest.StringHelper.listToString(parameter.getValue()));
            }
        }

        return jbiMessage;
    }

    protected final String checkMandatoryParameter(final String parameter) throws PEtALSCDKException {
        final String parameterValue = this.extensions.get(parameter);
        if (StringHelper.isNullOrEmpty(parameterValue)) {
            RESTConfiguration.handleMissingMandatoryParameterError(parameter);
        }

        return parameterValue;
    }

    protected final boolean checkLinkedParameters(final List parameters) throws PEtALSCDKException {
        boolean allParameterDefined = true;
        boolean noneParameterDefined = true;

        final Iterator parametersIterator = parameters.iterator();
        StringBuilder parameterNameStr = null;
        while (parametersIterator.hasNext()) {
            final String parameterName = parametersIterator.next();
            final String parameterValue = this.extensions.get(parameterName);
            if (StringHelper.isNullOrEmpty(parameterValue)) {
                allParameterDefined = false;
            } else {
                noneParameterDefined = false;
            }
            if (parameterNameStr == null) {
                parameterNameStr = new StringBuilder(parameterName);
            } else if (parametersIterator.hasNext()) {
                parameterNameStr.append(", '" + parameterName + "'");
            } else {
                parameterNameStr.append(" and '" + parameterName + "'");
            }
        }

        if (!allParameterDefined && !noneParameterDefined) {
            throw new PEtALSCDKException(
                    String.format("The parameters '%s' must be defined together (or not defined)", parameterNameStr));
        }

        return allParameterDefined;
    }

    private static final JBIMessageTemplateMap processSUConsumesMapping(final Consumes consumes,
            final XPath xpathBuilder, final String suRootPath, final ErrorListener logErrorListener,
            final Properties componentPlaceholders, final Logger logger) throws PEtALSCDKException {
        final JBIMessageTemplateMap jbiMessages = new JBIMessageTemplateMap(componentPlaceholders, logger);

        try {
            final NodeList operationNodes = getSUOperationMappingNodes(consumes.getAny());
            int operationNb = operationNodes.getLength();
            for (int i = 0; i < operationNb; i++) {
                final Node operationNode = operationNodes.item(i);
                final Element operationElt = (Element) operationNode;

                final JBIMessageTemplate jbiMessage = OperationBuilder.build(operationElt, xpathBuilder, suRootPath,
                        logErrorListener, componentPlaceholders, logger);
                
                jbiMessage.log(logger);
                jbiMessage.verify();

                jbiMessages.put(jbiMessage);
            }
        } catch (final IllegalArgumentException e) {
            throw new PEtALSCDKException(e);
        }

        return jbiMessages;
    }

    public static class JBIMessageTemplate {

        private final QName jbiOperation;

        private final String httpMethod;

        private final String path;

        private final String xmlTemplate;

        private final XMLInputFactory requestConverterFactory;

        private final IncomingPayloadGenerator incomingPayloadGenerator;

        private final OnJBIResponse onJBIResponse;

        public JBIMessageTemplate(final QName jbiOperation, final String httpMethod, final String path,
                final Node xmlTemplate, final XMLInputFactory requestConverterFactory,
                final IncomingPayloadGenerator incomingPayloadGenerator, final OnJBIResponse onJBIResponse)
                throws TransformerException {
            this.jbiOperation = jbiOperation;
            this.httpMethod = httpMethod;
            this.path = path;
            this.xmlTemplate = XMLHelper.createStringFromDOMNode(xmlTemplate);
            this.requestConverterFactory = requestConverterFactory;
            this.incomingPayloadGenerator = incomingPayloadGenerator;
            this.onJBIResponse = onJBIResponse;
        }

        public QName getJbiOperation() {
            return this.jbiOperation;
        }

        public String getHttpMethod() {
            return this.httpMethod;
        }

        public String getPath() {
            return this.path;
        }

        public String getXmlTemplate() {
            return this.xmlTemplate;
        }

        public XMLInputFactory getRequestConverterFactory() {
            return this.requestConverterFactory;
        }

        public IncomingPayloadGenerator getIncomingPayloadGenerator() {
            return this.incomingPayloadGenerator;
        }

        public OnJBIResponse getOnJBIResponse() {
            return this.onJBIResponse;
        }

        public void log(final Logger log) {
            log.config("Rest resource:");
            log.config("\t- HTTP method: " + this.httpMethod);
            log.config("\t- HTTP path: " + this.path);
            if (this.incomingPayloadGenerator != null) {
                log.config("\t- Incoming payload generator: ");
                this.incomingPayloadGenerator.log("\t\t");
            } else {
                log.config("\t- No incoming payload generator");
            }
            log.config("\t- On JBI response: ");
            this.onJBIResponse.log("\t\t");
        }

        /**
         * Check the configuration
         * 
         * @throws PEtALSCDKException
         *             The configuration contains an error
         */
        public void verify() throws PEtALSCDKException {
            if (this.incomingPayloadGenerator != null) {
                this.incomingPayloadGenerator.verify();
            }
            this.onJBIResponse.verify();
        }
    }

    /**
     * Update configuration according to new values define for component place holders.
     */
    public void onPlaceHolderValuesReloaded() {

        this.jbiMessages.onPlaceHolderValuesReloaded();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy