Package com.dtolabs.rundeck.core.resources

Source Code of com.dtolabs.rundeck.core.resources.URLResourceModelSource$Configuration

/*
* Copyright 2011 DTO Solutions, Inc. (http://dtosolutions.com)
*
*  Licensed 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.
*/

/*
* URLResourceModelSource.java
*
* User: Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
* Created: 7/21/11 4:33 PM
*
*/
package com.dtolabs.rundeck.core.resources;

import com.dtolabs.rundeck.core.common.*;
import com.dtolabs.rundeck.core.common.impl.URLFileUpdater;
import com.dtolabs.rundeck.core.common.impl.URLFileUpdaterBuilder;
import com.dtolabs.rundeck.core.plugins.configuration.*;
import com.dtolabs.rundeck.core.resources.format.ResourceFormatParser;
import com.dtolabs.rundeck.core.resources.format.ResourceFormatParserException;
import com.dtolabs.rundeck.core.resources.format.UnsupportedFormatException;
import com.dtolabs.rundeck.plugins.util.DescriptionBuilder;
import com.dtolabs.rundeck.plugins.util.PropertyBuilder;
import org.apache.commons.codec.binary.Hex;
import org.apache.log4j.Logger;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
* URLResourceModelSource produces nodes from a URL
*
* @author Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
*/
public class URLResourceModelSource implements ResourceModelSource, Configurable {
    static final Logger logger = Logger.getLogger(URLResourceModelSource.class.getName());
    public static final int DEFAULT_TIMEOUT = 30;
    final private Framework framework;
    Configuration configuration;
    private File destinationTempFile;
    private File destinationCacheData;
    private String tempFileName;
    URLFileUpdater.httpClientInteraction interaction;

    public URLResourceModelSource(final Framework framework) {
        this.framework = framework;
    }

    final static HashSet<String> allowedProtocols = new HashSet<String>(Arrays.asList("http", "https", "file"));
    public static final class URLValidator implements PropertyValidator {
        public boolean isValid (String value)throws ValidationException {
            final URL url;

            try {
                url = new URL(value);
            } catch (MalformedURLException e) {
                throw new ValidationException(e.getMessage());
            }
            if (null != url && !allowedProtocols.contains(url.getProtocol().toLowerCase())) {
                throw new ValidationException("url protocol not supported: " + url.getProtocol());
            }
            return true;
        }
    }

    public static final Description DESCRIPTION = DescriptionBuilder.builder()
        .name("url")
        .title("URL Source")
        .description("Retrieves a URL containing node definitions in a supported format")

        .property(PropertyBuilder.builder()
                      .string(Configuration.URL)
                      .title("URL")
                      .description("URL for the remote resource model document")
                      .required(true)
                      .validator(new URLValidator())
                      .build()
        )

        .property(PropertyBuilder.builder()
                      .integer(Configuration.TIMEOUT)
                      .title("Timeout")
                      .description("Timeout (in seconds) before requests fail. 0 means no timeout.")
                      .defaultValue("30")
                      .build()
        )
        .property(PropertyBuilder.builder()
                      .booleanType(Configuration.CACHE)
                      .title("Cache results")
                      .description("Refresh results only if modified?")
                      .required(true)
                      .defaultValue("true")
                      .build()
        )
        .build();


    public static class Configuration {
        public static final String URL = "url";
        public static final String PROJECT = "project";
        public static final String CACHE = "cache";
        public static final String TIMEOUT = "timeout";
        URL nodesUrl;
        String project;
        boolean useCache = true;
        int timeout = DEFAULT_TIMEOUT;

        private final Properties properties;

        Configuration() {
            properties = new Properties();
        }

        Configuration(final Properties configuration) {
            if (null == configuration) {
                throw new NullPointerException("configuration");
            }
            this.properties = configuration;
            configure();
        }

        private void configure() {
            if (properties.containsKey(URL)) {
                try {
                    nodesUrl = new URL(properties.getProperty(URL));
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
            if (properties.containsKey(PROJECT)) {
                project = properties.getProperty(PROJECT);
            }
            if (properties.containsKey(CACHE)) {
                useCache = Boolean.parseBoolean(properties.getProperty(CACHE));
            }
            if (properties.containsKey(TIMEOUT)) {
                try {
                    timeout = Integer.parseInt(properties.getProperty(TIMEOUT));
                } catch (NumberFormatException e) {
                }
            }
        }

        void validate() throws ConfigurationException {
            if (null == project) {
                throw new ConfigurationException("project is required");
            }
            if (null == nodesUrl && properties.containsKey(URL)) {
                try {
                    nodesUrl = new URL(properties.getProperty(URL));
                } catch (MalformedURLException e) {
                    throw new ConfigurationException("url is malformed: " + e.getMessage(), e);
                }
            } else if (null == nodesUrl) {
                throw new ConfigurationException("url is required");
            }
            if (null != nodesUrl && !allowedProtocols.contains(nodesUrl.getProtocol().toLowerCase())) {
                throw new ConfigurationException("url protocol not allowed: " + nodesUrl.getProtocol());
            }
            if (properties.containsKey(TIMEOUT)) {
                try {
                    timeout = Integer.parseInt(properties.getProperty(TIMEOUT));
                } catch (NumberFormatException e) {
                    throw new ConfigurationException("timeout is invalid: " + e.getMessage(), e);
                }
            }
        }

        Configuration(final Configuration configuration) {
            this(configuration.getProperties());
        }

        public Configuration url(final String url) {
            try {
                this.nodesUrl = new URL(url);
            } catch (MalformedURLException e) {
            }
            properties.setProperty("url", url);
            return this;
        }

        public Configuration project(final String project) {
            this.project = project;
            properties.setProperty(PROJECT, project);
            return this;
        }

        public Configuration cache(final boolean cache) {
            this.useCache = cache;
            properties.setProperty(CACHE, Boolean.toString(cache));
            return this;
        }

        public Configuration timeout(final int timeout) {
            this.timeout = timeout;
            properties.setProperty(TIMEOUT, Integer.toString(timeout));
            return this;
        }

        public static Configuration fromProperties(final Properties configuration) {
            return new Configuration(configuration);
        }

        public static Configuration clone(final Configuration configuration) {
            return fromProperties(configuration.getProperties());
        }

        public static Configuration build() {
            return new Configuration();
        }

        public Properties getProperties() {
            return properties;
        }
    }

    public void configure(final Properties configuration) throws ConfigurationException {
        this.configuration = new Configuration(configuration);
        this.configuration.validate();
        //set destination temp file
        final FrameworkProject frameworkProject = framework.getFrameworkProjectMgr().getFrameworkProject(
            this.configuration.project);

        tempFileName = hashURL(this.configuration.nodesUrl.toExternalForm()) + ".temp";
        destinationTempFile = new File(frameworkProject.getBaseDir(),
            "var/urlResourceModelSourceCache/" + tempFileName);
        destinationCacheData = new File(frameworkProject.getBaseDir(),
            "var/urlResourceModelSourceCache/" + tempFileName + ".cache.properties");
        if (!destinationTempFile.getParentFile().isDirectory() && !destinationTempFile.getParentFile().mkdirs()) {
            logger.warn(
                "Unable to create destination directory: " + destinationTempFile.getParentFile().getAbsolutePath());
        }
    }

    private String hashURL(final String url) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.reset();
            digest.update(url.getBytes(Charset.forName("UTF-8")));
            return new String(Hex.encodeHex(digest.digest()));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return Integer.toString(url.hashCode());
    }

    public INodeSet getNodes() throws ResourceModelSourceException {
        //update from URL if necessary
        final URLFileUpdaterBuilder urlFileUpdaterBuilder = new URLFileUpdaterBuilder()
            .setUrl(configuration.nodesUrl)
            .setAcceptHeader("*/*")
            .setTimeout(configuration.timeout);
        if (configuration.useCache) {
            urlFileUpdaterBuilder
                .setCacheMetadataFile(destinationCacheData)
                .setCachedContent(destinationTempFile)
                .setUseCaching(true);
        }
        final URLFileUpdater updater = urlFileUpdaterBuilder.createURLFileUpdater();
        try {
            if (null != interaction) {
                //allow mock
                updater.setInteraction(interaction);
            }
            UpdateUtils.update(updater, destinationTempFile);

            logger.debug("Updated nodes resources file: " + destinationTempFile);
        } catch (UpdateUtils.UpdateException e) {
            if (!destinationTempFile.isFile() || destinationTempFile.length() < 1) {
                throw new ResourceModelSourceException(
                    "Error requesting URL Resource Model Source: " + configuration.nodesUrl + ": " + e.getMessage(), e);
            } else {
                logger.error("Error requesting URL Resource Model Source: " + configuration.nodesUrl + ": " + e.getMessage(), e);
            }
        }
        final ResourceFormatParser parser;
        if ("file".equalsIgnoreCase(configuration.nodesUrl.getProtocol())) {
            try {
                parser = framework.getResourceFormatParserService().getParserForFileExtension(new File(
                    configuration.nodesUrl.toURI()));
            } catch (UnsupportedFormatException e) {
                throw new ResourceModelSourceException(
                    "Error requesting URL Resource Model Source: " + configuration.nodesUrl + ": No supported format available for file extension", e);
            } catch (URISyntaxException e) {
                throw new ResourceModelSourceException(
                    "Error requesting URL Resource Model Source: " + configuration.nodesUrl + ": " + e.getMessage(), e);
            }
            logger.debug("Determined URL content format from file name: " + configuration.nodesUrl);
        } else {
            final String mimetype = updater.getContentType();
            try {
                parser = framework.getResourceFormatParserService().getParserForMIMEType(mimetype);
            } catch (UnsupportedFormatException e) {
                throw new ResourceModelSourceException(
                    "Error requesting URL Resource Model Source: " + configuration.nodesUrl
                    + ": Response content type is not supported: " + mimetype, e);
            }
            logger.debug("Determined URL content format from MIME type: " + mimetype);
        }
        if (destinationTempFile.isFile() && destinationTempFile.length() > 0) {
            try {
                return parser.parseDocument(destinationTempFile);
            } catch (ResourceFormatParserException e) {
                throw new ResourceModelSourceException(
                    "Error requesting URL Resource Model Source: " + configuration.nodesUrl
                    + ": Content could not be parsed: "+e.getMessage(),e);
            }
        } else {
            return new NodeSetImpl();
        }
    }

    @Override
    public String toString() {
        return "URLResourceModelSource{" +
               "URL='" + configuration.nodesUrl + '\'' +
               '}';
    }
}
TOP

Related Classes of com.dtolabs.rundeck.core.resources.URLResourceModelSource$Configuration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.