View Javadoc

1   /**
2    * Copyright (c) 2012-2013, JCabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.beanstalk.maven.plugin;
31  
32  import com.amazonaws.auth.AWSCredentials;
33  import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalk;
34  import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalkClient;
35  import com.amazonaws.services.s3.AmazonS3Client;
36  import com.jcabi.aspects.Tv;
37  import com.jcabi.log.Logger;
38  import java.io.File;
39  import java.util.concurrent.TimeUnit;
40  import org.apache.maven.plugin.AbstractMojo;
41  import org.apache.maven.plugin.MojoFailureException;
42  import org.apache.maven.settings.Settings;
43  import org.jfrog.maven.annomojo.annotations.MojoParameter;
44  import org.slf4j.impl.StaticLoggerBinder;
45  
46  /**
47   * Abstract MOJO for this plugin.
48   *
49   * @author Yegor Bugayenko (yegor@tpc2.com)
50   * @version $Id$
51   * @since 0.7.1
52   * @checkstyle ClassDataAbstractionCoupling (500 lines)
53   */
54  abstract class AbstractBeanstalkMojo extends AbstractMojo {
55  
56      /**
57       * Setting.xml.
58       */
59      @MojoParameter(
60          expression = "${settings}",
61          required = true,
62          readonly = true,
63          description = "Maven settings.xml reference"
64      )
65      private transient Settings settings;
66  
67      /**
68       * Shall we skip execution?
69       */
70      @MojoParameter(
71          defaultValue = "false",
72          required = false,
73          description = "Skips execution"
74      )
75      private transient boolean skip;
76  
77      /**
78       * Server ID to deploy to.
79       */
80      @MojoParameter(
81          defaultValue = "aws.amazon.com",
82          required = false,
83          description = "ID of the server to deploy to, from settings.xml"
84      )
85      private transient String server;
86  
87      /**
88       * Application name (also the name of environment and CNAME).
89       */
90      @MojoParameter(
91          required = true,
92          description = "EBT application name, environment name, and CNAME"
93      )
94      private transient String name;
95  
96      /**
97       * S3 bucket.
98       */
99      @MojoParameter(
100         required = true,
101         description = "Amazon S3 bucket name where to upload WAR file"
102     )
103     private transient String bucket;
104 
105     /**
106      * S3 key name.
107      */
108     @MojoParameter(
109         required = true,
110         description = "Amazon S3 bucket key where to upload WAR file"
111     )
112     private transient String key;
113 
114     /**
115      * Template name.
116      */
117     @MojoParameter(
118         required = true,
119         description = "Amazon Elastic Beanstalk configuration template name"
120     )
121     private transient String template;
122 
123     /**
124      * WAR file to deploy.
125      * @checkstyle LineLength (3 lines)
126      */
127     @MojoParameter(
128         defaultValue = "${project.build.directory}/${project.build.finalName}.war",
129         required = false,
130         description = "Location of .WAR file to deploy"
131     )
132     private transient File war;
133 
134     /**
135      * Set skip option.
136      * @param skp Shall we skip execution?
137      */
138     public void setSkip(final boolean skp) {
139         this.skip = skp;
140     }
141 
142     /**
143      * {@inheritDoc}
144      */
145     @Override
146     public void execute() throws MojoFailureException {
147         StaticLoggerBinder.getSingleton().setMavenLog(this.getLog());
148         if (this.skip) {
149             Logger.info(this, "execution skipped because of 'skip' option");
150             return;
151         }
152         if (!this.war.exists()) {
153             throw new MojoFailureException(
154                 String.format("WAR file '%s' doesn't exist", this.war)
155             );
156         }
157         final AWSCredentials creds = new ServerCredentials(
158             this.settings,
159             this.server
160         );
161         final AWSElasticBeanstalk ebt = new AWSElasticBeanstalkClient(creds);
162         try {
163             this.exec(
164                 new Application(ebt, this.name),
165                 new OverridingVersion(
166                     ebt,
167                     this.name,
168                     new Bundle.Safe(
169                         new OverridingBundle(
170                             new AmazonS3Client(creds),
171                             this.bucket,
172                             this.key,
173                             this.war
174                         )
175                     )
176                 ),
177                 this.template
178             );
179         } catch (DeploymentException ex) {
180             throw new MojoFailureException("failed to deploy", ex);
181         } finally {
182             ebt.shutdown();
183         }
184     }
185 
186     /**
187      * Deploy using this EBT client.
188      * @param app Application to deploy to
189      * @param version Version to deploy
190      * @param tmpl Template to use
191      */
192     protected abstract void exec(final Application app,
193         final Version version, final String tmpl);
194 
195     /**
196      * Report when environment is failed.
197      * @param env The environment
198      */
199     protected void postMortem(final Environment env) {
200         Logger.error(this, "Failed to deploy to '%s'", env);
201         if (!env.terminated()) {
202             Logger.error(
203                 this,
204                 "TAIL report should explain the cause of failure:"
205             );
206             this.log(env.tail().split("\n"));
207         }
208         Logger.error(this, "Latest EBT events (in reverse order):");
209         this.log(env.events());
210         env.terminate();
211     }
212 
213     /**
214      * Log all lines from the collection.
215      * @param lines All lines to log
216      */
217     private void log(final String[] lines) {
218         for (String line : lines) {
219             Logger.info(this, ">> %s", line);
220         }
221     }
222 
223     /**
224      * Wait for green status.
225      * @param env The environment
226      * @return TRUE if green
227      */
228     protected boolean isGreen(final Environment env) {
229         boolean green = env.green();
230         final long start = System.currentTimeMillis();
231         while (!green) {
232             final long age = System.currentTimeMillis() - start;
233             if (age > TimeUnit.MINUTES.toMillis(Tv.FIFTEEN)) {
234                 Logger.warn(this, "Waiting for %[ms]s, time to give up", age);
235                 break;
236             }
237             Logger.warn(
238                 this,
239                 "%s is not GREEN yet, let's wait another 15 second...", env
240             );
241             try {
242                 TimeUnit.SECONDS.sleep(Tv.FIFTEEN);
243             } catch (InterruptedException ex) {
244                 Thread.currentThread().interrupt();
245                 throw new DeploymentException(ex);
246             }
247             green = env.green();
248         }
249         return green;
250     }
251 
252 }