Java tutorial
/* * 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 + '\'' + '}'; } }