by.stub.yaml.YamlParser.java Source code

Java tutorial

Introduction

Here is the source code for by.stub.yaml.YamlParser.java

Source

/*
HTTP stub server written in Java with embedded Jetty
    
Copyright (C) 2012 Alexander Zagniotov, Isa Goksu and Eric Mrak
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package by.stub.yaml;

import by.stub.cli.ANSITerminal;
import by.stub.utils.ReflectionUtils;
import by.stub.utils.StringUtils;
import by.stub.yaml.stubs.StubHttpLifecycle;
import by.stub.yaml.stubs.StubRequest;
import by.stub.yaml.stubs.StubResponse;
import org.apache.commons.codec.binary.Base64;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class YamlParser {

    private static final String NODE_REQUEST = "request";

    private String loadedConfigAbsolutePath;
    private String yamlConfigFilename;

    private YamlParser() {

    }

    public YamlParser(final String yamlConfigFilename) {
        if (yamlConfigFilename == null) {
            throw new IllegalArgumentException("Given YAML config filename is null!");
        }
        this.yamlConfigFilename = yamlConfigFilename;
    }

    private Reader buildYamlReaderFromFilename() throws IOException {

        final File yamlFile = new File(yamlConfigFilename);
        final String filename = StringUtils.toLower(yamlFile.getName());

        if (!filename.endsWith(".yaml") && !filename.endsWith(".yml")) {
            throw new IOException(
                    String.format("The given filename %s does not ends with YAML or YML", yamlConfigFilename));
        }

        loadedConfigAbsolutePath = yamlFile.getAbsolutePath();

        return new InputStreamReader(new FileInputStream(yamlFile), StringUtils.utf8Charset());
    }

    //TODO Ability to get content from WWW via HTTP or ability to load non-textual files, eg.: images, PDFs etc.
    private String loadContentFromFile(final String filePath) throws IOException {

        final File contentFile = new File(filePath);
        if (!contentFile.isFile()) {
            throw new IOException(String.format("Could not load file from path: %s", filePath));
        }

        return StringUtils.inputStreamToString(new FileInputStream(contentFile));
    }

    public List<StubHttpLifecycle> parseAndLoad() throws Exception {
        return parseAndLoad(buildYamlReaderFromFilename());
    }

    @SuppressWarnings("unchecked")
    public List<StubHttpLifecycle> parseAndLoad(final Reader io) throws Exception {

        final List<StubHttpLifecycle> httpLifecycles = new LinkedList<StubHttpLifecycle>();
        final List<?> loadedYamlData = loadYamlData(io);

        if (loadedYamlData.isEmpty()) {
            return httpLifecycles;
        }

        for (final Object rawParentNode : loadedYamlData) {

            final LinkedHashMap<String, LinkedHashMap> parentNode = (LinkedHashMap<String, LinkedHashMap>) rawParentNode;

            final StubHttpLifecycle parentStub = new StubHttpLifecycle(new StubRequest(), new StubResponse());
            httpLifecycles.add(parentStub);

            mapParentYamlNodeToPojo(parentStub, parentNode);

            final String method = parentStub.getRequest().getMethod();
            final String url = parentStub.getRequest().getUrl();
            final String loadedMsg = String.format("Loaded: %s %s", method, url);
            ANSITerminal.loaded(loadedMsg);
        }

        return httpLifecycles;
    }

    @SuppressWarnings("unchecked")
    protected void mapParentYamlNodeToPojo(final StubHttpLifecycle parentStub,
            final LinkedHashMap<String, LinkedHashMap> parentNode) throws Exception {
        for (final Map.Entry<String, LinkedHashMap> parent : parentNode.entrySet()) {

            final LinkedHashMap<String, Object> httpSettings = (LinkedHashMap<String, Object>) parent.getValue();

            if (parent.getKey().equals(NODE_REQUEST)) {
                mapHttpSettingsToPojo(parentStub.getRequest(), httpSettings);
                continue;
            }

            mapHttpSettingsToPojo(parentStub.getResponse(), httpSettings);
        }
    }

    @SuppressWarnings("unchecked")
    protected void mapHttpSettingsToPojo(final Object target, final LinkedHashMap<String, Object> httpProperties)
            throws Exception {

        for (final Map.Entry<String, Object> pair : httpProperties.entrySet()) {

            final Object value = pair.getValue();
            final String propertyName = pair.getKey();

            if (value instanceof Map) {
                final Map<String, String> keyValues = encodeAuthorizationHeader((Map<String, String>) value);
                ReflectionUtils.setPropertyValue(target, propertyName, keyValues);
                continue;
            }

            final String propertyValue = extractPropertyValueAsString(propertyName, value);
            ReflectionUtils.setPropertyValue(target, propertyName, propertyValue);
        }
    }

    private String extractPropertyValueAsString(final String propertyName, final Object value) throws IOException {
        final String rawValue = StringUtils.isObjectSet(value) ? value.toString() : "";
        final String valueAsString = rawValue.trim();

        return propertyName.equalsIgnoreCase("file") ? loadContentFromFile(valueAsString) : valueAsString;
    }

    protected Map<String, String> encodeAuthorizationHeader(final Map<String, String> value) {
        if (!value.containsKey(StubRequest.AUTH_HEADER)) {
            return value;
        }
        final String rawHeader = value.get(StubRequest.AUTH_HEADER);
        final String authorizationHeader = StringUtils.isSet(rawHeader) ? rawHeader.trim() : rawHeader;
        final byte[] bytes = authorizationHeader.getBytes(StringUtils.utf8Charset());
        final String encodedAuthorizationHeader = String.format("%s %s", "Basic", Base64.encodeBase64String(bytes));
        value.put(StubRequest.AUTH_HEADER, encodedAuthorizationHeader);

        return value;
    }

    protected List<?> loadYamlData(final Reader io) throws IOException {
        final Yaml yaml = new Yaml(new Constructor(), new Representer(), new DumperOptions(),
                new YamlParserResolver());
        final Object loadedYaml = yaml.load(io);

        if (loadedYaml instanceof ArrayList) {
            return (ArrayList<?>) loadedYaml;
        }

        throw new IOException(String.format(
                "Loaded YAML data from %s must be an instance of ArrayList, otherwise something went wrong..",
                yamlConfigFilename));
    }

    public String getLoadedConfigYamlPath() {
        return loadedConfigAbsolutePath;
    }

    private static final class YamlParserResolver extends Resolver {

        public YamlParserResolver() {
            super();
        }

        @Override
        protected void addImplicitResolvers() {
            // no implicit resolvers - resolve everything to String
        }
    }
}