/* * Copyright (C) 2012 Red Hat, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.redhat.rcm.version.util; import static org.apache.commons.io.FileUtils.readFileToString; import static org.apache.commons.io.IOUtils.closeQuietly; import static org.apache.commons.io.IOUtils.copy; import static org.apache.commons.lang.StringUtils.isEmpty; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.params.ConnRouteParams; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.codehaus.plexus.util.DirectoryScanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.redhat.rcm.version.VManException; import com.redhat.rcm.version.mgr.session.VersionManagerSession; public final class InputUtils { private static final Logger logger = LoggerFactory.getLogger( InputUtils.class ); private static final int MAX_RETRIES = 5; private InputUtils() { } public static String[] getIncludedSubpaths( final File basedir, final String includes, final String excludes, final VersionManagerSession session ) { final DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir( basedir ); final String[] initExcludes = new String[] { session.getWorkspace() .getName() + "/**", session.getReports() .getName() + "/**" }; final String[] excludePattern = excludes == null ? new String[] {} : excludes.split( "\\s*,\\s*" ); final String[] excluded = Arrays.copyOf( initExcludes, initExcludes.length + excludePattern.length ); System.arraycopy( excludePattern, 0, excluded, initExcludes.length, excludePattern.length ); scanner.setExcludes( excluded ); scanner.addDefaultExcludes(); final String[] included = includes == null ? new String[] { "**/pom.xml", "**/*.pom" } : includes.split( "\\s*,\\s*" ); scanner.setIncludes( included ); scanner.scan(); final String[] includedSubpaths = scanner.getIncludedFiles(); logger.info( "Scanning from " + basedir + " and got included files " + Arrays.toString( includedSubpaths ) + " and got excluded files " + Arrays.toString( scanner.getExcludedFiles() ) ); return includedSubpaths; } public static List<String> readListProperty( final Properties props, final String property ) { final String val = props.getProperty( property ); if ( val != null ) { final String[] rm = val.split( "(\\s)*,(\\s)*" ); return Arrays.asList( rm ); } return null; } public static File readFileProperty( final Properties props, final String property, final File downloadsDir ) throws VManException { return readFileProperty( props, property, downloadsDir, false ); } public static File readFileProperty( final Properties props, final String property, final File downloadsDir, final boolean deleteExisting ) throws VManException { final String val = props.getProperty( property ); if ( val != null ) { return getFile( val, downloadsDir, deleteExisting ); } return null; } public static Map<String, String> readPropertiesList( final List<String> propertiesLocations, final File downloadsDir, final boolean deleteExisting ) throws VManException { if ( propertiesLocations == null ) { return null; } final Map<String, String> result = new HashMap<String, String>(); for ( final String propertiesLocation : propertiesLocations ) { final File properties = getFile( propertiesLocation, downloadsDir, deleteExisting ); String content; try { content = readFileToString( properties ); } catch ( final IOException e ) { throw new VManException( "Failed to load properties file: %s. Error: %s", e, properties, e.getMessage() ); } final Map<String, String> props = parseProperties( content ); for ( final Map.Entry<String, String> entry : props.entrySet() ) { if ( !result.containsKey( entry.getKey() ) ) { result.put( entry.getKey(), entry.getValue() ); } } } return result; } public static Map<String, String> readProperties( final String propertiesLocation, final File downloadsDir, final boolean deleteExisting ) throws VManException { final File properties = getFile( propertiesLocation, downloadsDir, deleteExisting ); String content; try { content = readFileToString( properties ); } catch ( final IOException e ) { throw new VManException( "Failed to load properties file: %s. Error: %s", e, properties, e.getMessage() ); } return parseProperties( content ); } public static Map<String, String> readProperties( final File properties ) throws VManException { String content; try { content = readFileToString( properties ); } catch ( final IOException e ) { throw new VManException( "Failed to load properties file: %s. Error: %s", e, properties, e.getMessage() ); } return parseProperties( content ); } public static URL getClasspathResource( final String resource ) { return Thread.currentThread() .getContextClassLoader() .getResource( resource ); } public static Map<String, String> readClasspathProperties( final String resource ) throws VManException { final InputStream is = Thread.currentThread() .getContextClassLoader() .getResourceAsStream( resource ); if ( is == null ) { return null; } String content; try { content = IOUtils.toString( is ); } catch ( final IOException e ) { throw new VManException( "Failed to load properties resource: %s. Error: %s", e, resource, e.getMessage() ); } finally { closeQuietly( is ); } return parseProperties( content ); } public static Map<String, String> parseProperties( final String content ) { if ( content == null ) { return null; } final Map<String, String> properties = new LinkedHashMap<String, String>(); final String[] lines = content.split( "\\s*[,\\n]\\s*" ); if ( lines != null && lines.length > 0 ) { // int count = 0; for ( final String line : lines ) { // logger.info( "processing: '" + line + "'" ); String ln = line.trim(); if ( ln.startsWith( "#" ) ) { continue; } int idx = ln.indexOf( '#' ); if ( idx > -1 ) { ln = line.substring( 0, idx ); } idx = ln.indexOf( "=" ); if ( idx < 0 ) { logger.warn( "Invalid property; key and value must not be empty! (line: '" + line + "')" ); continue; } final String k = propCleanup( ln.substring( 0, idx ) .trim() ); final String v = propCleanup( ln.substring( idx + 1 ) .trim() ); properties.put( k, v ); // count++; } // logger.info( "Found " + count + " properties..." ); } return properties; } private static String propCleanup( final String value ) { return value.replace( "\\:", ":" ) .replace( "\\=", "=" ) .trim(); } public static File[] getFiles( final List<String> locations, final File downloadsDir ) throws VManException { return getFiles( locations, downloadsDir, false ); } public static File[] getFiles( final List<String> locations, final File downloadsDir, final boolean deleteExisting ) throws VManException { final List<File> result = new ArrayList<File>( locations.size() ); for ( final String bom : locations ) { final File bomFile = getFile( bom, downloadsDir, deleteExisting ); if ( bomFile != null ) { result.add( bomFile ); } } return result.toArray( new File[] {} ); } private static DefaultHttpClient client; private static KeyStore trustKs; public static File getFile( final String location, final File downloadsDir ) throws VManException { return getFile( location, downloadsDir, false ); } public static File getFile( final String location, final File downloadsDir, final boolean deleteExisting ) throws VManException { if ( client == null ) { setupClient(); } File result = null; if ( location.startsWith( "http" ) ) { logger.info( "Downloading: '" + location + "'..." ); try { final URL url = new URL( location ); final String userpass = url.getUserInfo(); if ( !isEmpty( userpass ) ) { final AuthScope scope = new AuthScope( url.getHost(), url.getPort() ); final Credentials creds = new UsernamePasswordCredentials( userpass ); client.getCredentialsProvider() .setCredentials( scope, creds ); } } catch ( final MalformedURLException e ) { logger.error( "Malformed URL: '" + location + "'", e ); throw new VManException( "Failed to download: %s. Reason: %s", e, location, e.getMessage() ); } final File downloaded = new File( downloadsDir, new File( location ).getName() ); if ( deleteExisting && downloaded.exists() ) { downloaded.delete(); } if ( !downloaded.exists() ) { HttpGet get = new HttpGet( location ); OutputStream out = null; try { HttpResponse response = null; // Work around for scenario where we are loading from a server // that does a refresh e.g. gitweb final int tries = 0; do { get = new HttpGet( location ); response = client.execute( get ); if ( response.containsHeader( "Cache-control" ) ) { logger.info( "Waiting for server to generate cache..." ); get.abort(); try { Thread.sleep( 3000 ); } catch ( final InterruptedException e ) { } } else { break; } } while ( tries < MAX_RETRIES ); if ( response.containsHeader( "Cache-control" ) ) { throw new VManException( "Failed to read: %s. Cache-control header was present in final attempt.", location ); } final int code = response.getStatusLine() .getStatusCode(); if ( code == 200 ) { final InputStream in = response.getEntity() .getContent(); out = new FileOutputStream( downloaded ); copy( in, out ); } else { logger.info( "Received status: '{}' while downloading: {}", response.getStatusLine(), location ); throw new VManException( "Received status: '%s' while downloading: %s", response.getStatusLine(), location ); } } catch ( final ClientProtocolException e ) { throw new VManException( "Failed to download: '%s'. Error: %s", e, location, e.getMessage() ); } catch ( final IOException e ) { throw new VManException( "Failed to download: '%s'. Error: %s", e, location, e.getMessage() ); } finally { closeQuietly( out ); get.abort(); } } result = downloaded; } else { logger.info( "Using local file: '" + location + "'..." ); result = new File( location ); } return result; } private static void setupClient() throws VManException { if ( client == null ) { SSLSocketFactory sslSocketFactory; try { sslSocketFactory = new SSLSocketFactory( SSLSocketFactory.TLS, null, null, trustKs, null, new TrustSelfSignedStrategy(), SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); // sslSocketFactory = // new SSLSocketFactory( SSLSocketFactory.TLS, null, null, trustKs, null, null, // SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); } catch ( final KeyManagementException e ) { logger.error( "Failed to setup SSL socket factory: {}", e, e.getMessage() ); throw new VManException( "Failed to setup SSL socket factory: %s", e, e.getMessage() ); } catch ( final UnrecoverableKeyException e ) { logger.error( "Failed to setup SSL socket factory: {}", e, e.getMessage() ); throw new VManException( "Failed to setup SSL socket factory: %s", e, e.getMessage() ); } catch ( final NoSuchAlgorithmException e ) { logger.error( "Failed to setup SSL socket factory: {}", e, e.getMessage() ); throw new VManException( "Failed to setup SSL socket factory: %s", e, e.getMessage() ); } catch ( final KeyStoreException e ) { logger.error( "Failed to setup SSL socket factory: {}", e, e.getMessage() ); throw new VManException( "Failed to setup SSL socket factory: %s", e, e.getMessage() ); } final ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(); ccm.getSchemeRegistry() .register( new Scheme( "https", 443, sslSocketFactory ) ); final DefaultHttpClient hc = new DefaultHttpClient( ccm ); hc.setRedirectStrategy( new DefaultRedirectStrategy() ); final String proxyHost = System.getProperty( "http.proxyHost" ); final int proxyPort = Integer.parseInt( System.getProperty( "http.proxyPort", "-1" ) ); if ( proxyHost != null && proxyPort > 0 ) { final HttpHost proxy = new HttpHost( proxyHost, proxyPort ); hc.getParams() .setParameter( ConnRouteParams.DEFAULT_PROXY, proxy ); } client = hc; } } public static void setTrustKeyStore( final KeyStore trustKs ) { InputUtils.trustKs = trustKs; } }