Java tutorial
package br.com.ingenieux.mojo.aws; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; import com.amazonaws.internal.StaticCredentialsProvider; import org.apache.commons.beanutils.BeanMap; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.settings.Proxy; import org.apache.maven.settings.Server; import org.apache.maven.settings.Settings; import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.context.Context; import org.codehaus.plexus.context.ContextException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Collection; import java.util.Iterator; import java.util.Properties; import br.com.ingenieux.mojo.aws.util.AWSClientFactory; import br.com.ingenieux.mojo.aws.util.CredentialsUtil; import br.com.ingenieux.mojo.aws.util.TypeUtil; import static java.lang.String.format; /* * 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. */ /** * Represents a Mojo which keeps AWS passwords * * TODO: Refactor into tiny, delegated classes. Currently its a huge bloat, but it works, right? * * <p> <b>NOTE:</b> Settings in this class use properties based in "beanstalker", which is the * project. The beanstalk module, though, prefixes then as "beanstalk" instead </p> * * Parts of this class come from <a href="http://code.google.com/p/maven-gae-plugin">maven-gae-plugin</a>'s * source code. */ public abstract class AbstractAWSMojo<S extends AmazonWebServiceClient> extends AbstractMojo implements Contextualizable { private static final String SECURITY_DISPATCHER_CLASS_NAME = "org.sonatype.plexus.components.sec.dispatcher.SecDispatcher"; /** * Maven Settings Reference */ @Parameter(property = "settings", required = true, readonly = true) protected Settings settings; /** * AWS Credentials */ protected AWSCredentialsProvider awsCredentialsProvider; /** * The server id in maven settings.xml to use for AWS Services Credentials (accessKey / * secretKey) */ @Parameter(property = "beanstalker.serverId", defaultValue = "aws.amazon.com") protected String serverId; /** * Verbose Logging? */ @Parameter(property = "beanstalker.verbose", defaultValue = "false") protected boolean verbose; /** * Ignore Exceptions? */ @Parameter(property = "beanstalker.ignoreExceptions", defaultValue = "false") protected boolean ignoreExceptions; protected String version = "?"; protected Context context; /** * <p> Service region e.g. "us-east-1" </p> * * <p> <b>Note: Does not apply to all services.</b> </p> * * <p> <i>"-Cloudfront, I'm talking to you! Look at me when I do that!"</i> </p> * * <p> See <a href="http://docs.amazonwebservices.com/general/latest/gr/rande.html" >this list</a> * for reference. </p> */ @Parameter(property = "beanstalker.region") protected String region; protected AWSClientFactory clientFactory; /** * Plexus container, needed to manually lookup components. * * To be able to use Password Encryption http://maven.apache.org/guides/mini/guide-encryption.html */ private PlexusContainer container; private S service; protected AbstractAWSMojo() { setupVersion(); } public AWSCredentialsProvider getAWSCredentials() throws MojoFailureException { if (null != this.awsCredentialsProvider) { return this.awsCredentialsProvider; } /* * Are you using aws.accessKey and aws.secretKey? j'accuse! */ if (hasServerSettings()) { /* * This actually is the right way... */ Expose expose = exposeSettings(serverId); String awsAccessKey = expose.getAccessKey(); String awsSecretKey = expose.getSharedKey(); this.awsCredentialsProvider = new StaticCredentialsProvider( new BasicAWSCredentials(awsAccessKey, awsSecretKey)); } else if (null != (awsCredentialsProvider = getEnvironmentKeys())) { // Already assigned. \o/ } else { /* * Throws up. We have nowhere to get our credentials... */ String errorMessage = "Entries in settings.xml for server " + serverId + " not defined. See http://docs.ingenieux.com.br/project/beanstalker/aws-config.html for more information"; getLog().error(errorMessage); throw new MojoFailureException(errorMessage); } return this.awsCredentialsProvider; } /** * Attempts to use Environment-based Provider Variables * * @return AWS Credentials Provider if available. Null otherwise. */ private AWSCredentialsProvider getEnvironmentKeys() { final EnvironmentVariableCredentialsProvider provider = new EnvironmentVariableCredentialsProvider(); try { provider.getCredentials(); return provider; } catch (AmazonClientException exc) { return null; } } public AWSClientFactory getClientFactory() { return clientFactory; } protected Expose exposeSettings(String serverId) throws MojoFailureException { Server server = settings.getServer(serverId); Expose expose = new Expose(); if (null != server) { expose.setServerId(serverId); expose.setAccessKey(server.getUsername()); expose.setSharedKey(getDecryptedAwsKey(server.getPassword().trim())); } else { getLog().warn(format("serverId['%s'] not found. Using runtime defaults", serverId)); expose.setServerId("runtime"); expose.setAccessKey(getAWSCredentials().getCredentials().getAWSAccessKeyId()); expose.setSharedKey(getAWSCredentials().getCredentials().getAWSSecretKey()); } return expose; } /** * Decrypts (or warns) a supplied user password * * @param awsSecretKey Aws Secret Key * @return The same awsSecretKey - Decrypted */ private String getDecryptedAwsKey(final String awsSecretKey) { /* * Checks if we have a encrypted key. And warn if we don't. */ if (!(awsSecretKey.startsWith("{") && awsSecretKey.endsWith("}"))) { getLog().warn( "You should encrypt your passwords. See http://beanstalker.ingenieux.com.br/security.html for more information"); } else { /* * ... but we do have a valid key. Lets decrypt and return it. */ return decryptPassword(awsSecretKey); } return awsSecretKey; } public void contextualize(final Context context) throws ContextException { this.context = context; this.container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY); } private boolean hasServerSettings() { if (serverId == null) { return false; } else { final Server srv = settings.getServer(serverId); return srv != null; } } private String decryptPassword(final String password) { if (password != null) { try { final Class<?> securityDispatcherClass = container.getClass().getClassLoader() .loadClass(SECURITY_DISPATCHER_CLASS_NAME); final Object securityDispatcher = container.lookup(SECURITY_DISPATCHER_CLASS_NAME, "maven"); final Method decrypt = securityDispatcherClass.getMethod("decrypt", String.class); return (String) decrypt.invoke(securityDispatcher, password); } catch (final Exception e) { getLog().warn("security features are disabled. Cannot find plexus security dispatcher", e); } } getLog().debug("password could not be decrypted"); return password; } protected ClientConfiguration getClientConfiguration() { ClientConfiguration clientConfiguration = new ClientConfiguration().withUserAgent(getUserAgent()); if (null != this.settings && null != settings.getActiveProxy()) { Proxy proxy = settings.getActiveProxy(); clientConfiguration.setProxyHost(proxy.getHost()); clientConfiguration.setProxyUsername(proxy.getUsername()); clientConfiguration.setProxyPassword(proxy.getPassword()); clientConfiguration.setProxyPort(proxy.getPort()); } return clientConfiguration; } protected final String getUserAgent() { return format("Apache Maven/3.0 (ingenieux beanstalker/%s; http://beanstalker.ingenieux.com.br)", version); } protected void setupVersion() { InputStream is = null; try { Properties properties = new Properties(); is = getClass().getResourceAsStream("/beanstalker-core.properties"); if (null != is) { properties.load(is); this.version = properties.getProperty("beanstalker.version"); } } catch (Exception exc) { } finally { IOUtils.closeQuietly(is); } } protected void setupService() throws MojoExecutionException { @SuppressWarnings("unchecked") Class<S> serviceClass = (Class<S>) TypeUtil.getServiceClass(getClass()); try { clientFactory = new AWSClientFactory(getAWSCredentials(), getClientConfiguration(), region); this.service = clientFactory.getService(serviceClass); } catch (Exception exc) { throw new MojoExecutionException("Unable to create service", exc); } } public S getService() { if (null == service) { try { setupService(); } catch (Exception e) { throw new RuntimeException(e); } } return service; } @Override public final void execute() throws MojoExecutionException, MojoFailureException { Object result = null; try { configure(); result = executeInternal(); getLog().info("SUCCESS"); } catch (Exception e) { getLog().warn("FAILURE", e); handleException(e); return; } displayResults(result, 0); } /** * Extension Point - Meant for others to declare and redefine variables as needed. */ protected void configure() { } public void handleException(Exception e) throws MojoExecutionException, MojoFailureException { /* * This is actually the feature I really didn't want to have written, * ever. * * Thank you for reading this comment. */ if (ignoreExceptions) { getLog().warn("Ok. ignoreExceptions is set to true. No result for you!"); return; } else if (MojoExecutionException.class.isAssignableFrom(e.getClass())) { throw (MojoExecutionException) e; } else if (MojoFailureException.class.isAssignableFrom(e.getClass())) { throw (MojoFailureException) e; } else { throw new MojoFailureException("Failed", e); } } protected void displayResults(Object result, int level) { if (null == result) { return; } String prefix = StringUtils.repeat(" ", level * 2) + " * "; if (Collection.class.isAssignableFrom(result.getClass())) { @SuppressWarnings("unchecked") Collection<Object> coll = Collection.class.cast(result); for (Object o : coll) { displayResults(o, 1 + level); } return; } else if ("java.lang".equals(result.getClass().getPackage().getName())) { getLog().info(prefix + CredentialsUtil.redact("" + result) + " [class: " + result.getClass().getSimpleName() + "]"); return; } BeanMap beanMap = new BeanMap(result); for (Iterator<?> itProperty = beanMap.keyIterator(); itProperty.hasNext();) { String propertyName = "" + itProperty.next(); Object propertyValue = beanMap.get(propertyName); if ("class".equals(propertyName)) { continue; } if (null == propertyValue) { continue; } Class<?> propertyClass = null; try { propertyClass = beanMap.getType(propertyName); } catch (Exception e) { getLog().warn("Failure on property " + propertyName, e); } if (null == propertyClass) { getLog().info(prefix + propertyName + ": " + CredentialsUtil.redact("" + propertyValue)); } else { getLog().info(prefix + propertyName + ": " + CredentialsUtil.redact("" + propertyValue) + " [class: " + propertyClass.getSimpleName() + "]"); } } } protected abstract Object executeInternal() throws Exception; public boolean isVerbose() { return verbose; } }