PageRenderTime 61ms CodeModel.GetById 13ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 1ms

/maven-amps-plugin/src/main/java/com/atlassian/maven/plugins/amps/RunMojo.java

https://bitbucket.org/mmeinhold/amps
Java | 349 lines | 260 code | 45 blank | 44 comment | 32 complexity | 495e0eda63b24079610ac3a895d94b7d MD5 | raw file
  1package com.atlassian.maven.plugins.amps;
  2
  3import java.io.File;
  4import java.io.FileOutputStream;
  5import java.io.IOException;
  6import java.io.OutputStream;
  7import java.util.*;
  8import java.util.concurrent.TimeUnit;
  9
 10import com.atlassian.maven.plugins.amps.product.ProductHandler;
 11import com.atlassian.maven.plugins.amps.util.GoogleAmpsTracker;
 12
 13import com.google.common.base.Predicate;
 14import com.google.common.collect.Iterables;
 15import com.google.common.collect.Lists;
 16
 17import org.apache.commons.io.IOUtils;
 18import org.apache.maven.artifact.Artifact;
 19import org.apache.maven.plugin.MojoExecutionException;
 20import org.apache.maven.plugin.MojoFailureException;
 21import org.apache.maven.plugins.annotations.Execute;
 22import org.apache.maven.plugins.annotations.LifecyclePhase;
 23import org.apache.maven.plugins.annotations.Mojo;
 24import org.apache.maven.plugins.annotations.Parameter;
 25import org.apache.maven.plugins.annotations.ResolutionScope;
 26import org.apache.maven.surefire.shade.org.apache.commons.lang.StringUtils;
 27
 28import static org.apache.commons.lang.StringUtils.isBlank;
 29
 30/**
 31 * Run the webapp
 32 */
 33@Mojo(name = "run", requiresDependencyResolution = ResolutionScope.RUNTIME)
 34@Execute(phase = LifecyclePhase.PACKAGE)
 35public class RunMojo extends AbstractTestGroupsHandlerMojo
 36{
 37   
 38    @Parameter(property = "wait", defaultValue = "true")
 39    private boolean wait;
 40
 41    /**
 42     * Whether or not to write properties used by the plugin to amps.properties.
 43     */
 44    @Parameter(property = "amps.properties", required = true, defaultValue = "false")
 45    protected boolean writePropertiesToFile;
 46
 47    /**
 48     * Test group to run. If provided, used to determine the products to run.
 49     */
 50    @Parameter(property = "testGroup")
 51    protected String testGroup;
 52
 53    /**
 54     * Excluded instances from the execution. Only useful when Studio brings in all instances and you want to run only one.
 55     * List of comma separated instanceIds, or {@literal *}/instanceId to exclude all but one product.
 56     * <p>
 57     * Examples:
 58     * <ul>
 59     * <li>mvn amps:run -DexcludeInstances=studio-crowd</li>
 60     * <li>mvn amps:run -DexcludeInstances={@literal *}/studio-crowd to run only StudioCrowd</li>
 61     * </ul>
 62     */
 63    @Parameter(property = "excludeInstances")
 64    protected String excludeInstances;
 65
 66    /**
 67     * The properties actually used by the mojo when running
 68     */
 69    protected final Map<String, String> properties = new HashMap<String, String>();
 70
 71    
 72
 73    protected void doExecute() throws MojoExecutionException, MojoFailureException
 74    {
 75        getUpdateChecker().check();
 76
 77        getAmpsPluginVersionChecker().checkAmpsVersionInPom(getSdkVersion(),getMavenContext().getProject());
 78
 79        promptForEmailSubscriptionIfNeeded();
 80        
 81        trackFirstRunIfNeeded();
 82        
 83        getGoogleTracker().track(GoogleAmpsTracker.RUN);
 84
 85        final List<ProductExecution> productExecutions = getProductExecutions();
 86
 87        startProducts(productExecutions);
 88    }
 89
 90    protected void startProducts(List<ProductExecution> productExecutions) throws MojoExecutionException
 91    {
 92        long globalStartTime = System.nanoTime();
 93        setParallelMode(productExecutions);
 94        List<StartupInformation> successMessages = Lists.newArrayList();
 95        for (ProductExecution productExecution : productExecutions)
 96        {
 97            final ProductHandler productHandler = productExecution.getProductHandler();
 98            final Product product = productExecution.getProduct();
 99            if (product.isInstallPlugin() == null)
100            {
101                product.setInstallPlugin(shouldInstallPlugin());
102            }
103
104            //add artifacts for test console
105            if(shouldBuildTestPlugin())
106            {
107                List<ProductArtifact> plugins = product.getBundledArtifacts();
108                plugins.addAll(testFrameworkPlugins);
109            }
110
111            // Leave a blank line and say what it's doing
112            getLog().info("");
113            if (StringUtils.isNotBlank(product.getOutput()))
114            {
115                getLog().info(String.format("Starting %s... (see log at %s)", product.getInstanceId(), product.getOutput()));
116            }
117            else
118            {
119                getLog().info(String.format("Starting %s...", product.getInstanceId()));
120            }
121
122            // Actually start the product
123            long startTime = System.nanoTime();
124            int actualHttpPort = productHandler.start(product);
125            long durationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
126
127            // Log the success message
128            StartupInformation message = new StartupInformation(product, "started successfully", actualHttpPort, durationSeconds);
129            if (!parallel)
130            {
131                getLog().info(message.toString());
132            }
133            successMessages.add(message);
134
135            if (writePropertiesToFile)
136            {
137                if (productExecutions.size() == 1)
138                {
139                    properties.put("http.port", String.valueOf(actualHttpPort));
140                    properties.put("context.path", product.getContextPath());
141                }
142
143                properties.put("http." + product.getInstanceId() + ".port", String.valueOf(actualHttpPort));
144                properties.put("context." + product.getInstanceId() + ".path", product.getContextPath());
145                String baseUrl = MavenGoals.getBaseUrl(product, actualHttpPort);
146                properties.put("baseurl." + product.getInstanceId(), baseUrl);
147            }
148        }
149
150        if (writePropertiesToFile)
151        {
152            writePropertiesFile();
153        }
154
155        if (parallel)
156        {
157            waitForProducts(productExecutions, true);
158        }
159        long globalDurationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - globalStartTime);
160
161        // Give the messages once all applications are started
162        if (successMessages.size() > 1 || parallel)
163        {
164            getLog().info("");
165            getLog().info("=== Summary (total time " + globalDurationSeconds + "s):");
166            // First show the log files
167            for (StartupInformation message : successMessages)
168            {
169                if (StringUtils.isNotBlank(message.getOutput()))
170                {
171                    getLog().info("Log available at: " + message.getOutput());
172                }
173            }
174            // Then show the applications
175            for (StartupInformation message : successMessages)
176            {
177                getLog().info(message.toString());
178            }
179        }
180
181        if (wait)
182        {
183            getLog().info("Type Ctrl-D to shutdown gracefully");
184            getLog().info("Type Ctrl-C to exit");
185            try
186            {
187                while (System.in.read() != -1)
188                {
189                }
190            }
191            catch (final IOException e)
192            {
193                // ignore
194            }
195
196            // We don't stop products when -Dwait=false, because some projects rely on the
197            // application running after the end of the RunMojo goal. The SHITTY tests
198            // check this behaviour.
199            stopProducts(productExecutions);
200        }
201    }
202
203    protected List<ProductExecution> getProductExecutions() throws MojoExecutionException
204    {
205        final List<ProductExecution> productExecutions;
206        final MavenGoals goals = getMavenGoals();
207        if (!isBlank(testGroup))
208        {
209            productExecutions = getTestGroupProductExecutions(testGroup);
210        }
211        else if (!isBlank(instanceId))
212        {
213            Product ctx = getProductContexts().get(instanceId);
214            if (ctx == null)
215            {
216                throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
217            }
218            ProductHandler product = createProductHandler(ctx.getId());
219            productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
220        }
221        else
222        {
223            Product ctx = getProductContexts().get(getProductId());
224            ProductHandler product = createProductHandler(ctx.getId());
225            productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
226        }
227        return filterExcludedInstances(includeStudioDependentProducts(productExecutions, goals));
228    }
229
230    private List<ProductExecution> filterExcludedInstances(List<ProductExecution> executions) throws MojoExecutionException
231    {
232        if (StringUtils.isBlank(excludeInstances))
233        {
234            return executions;
235        }
236        boolean inverted = excludeInstances.startsWith("*/");
237        String instanceIdList = inverted ? excludeInstances.substring(2) : excludeInstances;
238
239        // Parse the list given by the user and find ProductExecutions
240        List<String> excludedInstanceIds = Lists.newArrayList(instanceIdList.split(","));
241        List<ProductExecution> excludedExecutions = Lists.newArrayList();
242        for (final String instanceId : excludedInstanceIds)
243        {
244            try
245            {
246                excludedExecutions.add(Iterables.find(executions, new Predicate<ProductExecution>()
247                {
248                    @Override
249                    public boolean apply(ProductExecution input)
250                    {
251                        return input.getProduct().getInstanceId().equals(instanceId);
252                    }
253                }));
254            }
255            catch (NoSuchElementException nsee)
256            {
257                throw new MojoExecutionException("You specified -Dexclude=" + excludeInstances + " but " + instanceId + " is not an existing instance id.");
258            }
259        }
260
261        if (inverted)
262        {
263            return excludedExecutions;
264        }
265        else
266        {
267            executions.removeAll(excludedExecutions);
268            return executions;
269        }
270    }
271
272    /**
273     * Only install a plugin if the installPlugin flag is true and the project is a jar. If the test plugin was built,
274     * it will be installed as well.
275     */
276    private boolean shouldInstallPlugin()
277    {
278        Artifact artifact = getMavenContext().getProject().getArtifact();
279        return installPlugin &&
280                (artifact != null && !"pom".equalsIgnoreCase(artifact.getType()));
281    }
282
283    private void writePropertiesFile() throws MojoExecutionException
284    {
285        final Properties props = new Properties();
286
287        for (Map.Entry<String, String> entry : properties.entrySet())
288        {
289            props.setProperty(entry.getKey(), entry.getValue());
290        }
291
292        final File ampsProperties = new File(getMavenContext().getProject().getBuild().getDirectory(), "amps.properties");
293        OutputStream out = null;
294        try
295        {
296            out = new FileOutputStream(ampsProperties);
297            props.store(out, "");
298        }
299        catch (IOException e)
300        {
301            throw new MojoExecutionException("Error writing " + ampsProperties.getAbsolutePath(), e);
302        }
303        finally
304        {
305            IOUtils.closeQuietly(out);
306        }
307    }
308
309    /**
310     * Wraps information about the startup of a product
311     */
312    private static class StartupInformation
313    {
314        int actualHttpPort;
315        long durationSeconds;
316        Product product;
317        String event;
318
319        public StartupInformation(Product product, String event, int actualHttpPort, long durationSeconds)
320        {
321            super();
322            this.actualHttpPort = actualHttpPort;
323            this.product = product;
324            this.event = event;
325            this.durationSeconds = durationSeconds;
326        }
327
328        @Override
329        public String toString()
330        {
331            String message = String.format("%s %s in %ds", product.getInstanceId(), event
332                    + (Boolean.FALSE.equals(product.getSynchronousStartup()) ? " (asynchronously)" : ""), durationSeconds);
333            if (actualHttpPort != 0)
334            {
335                message += " at http://" + product.getServer() + ":" + actualHttpPort + (product.getContextPath().equals("ROOT") ? "" : product.getContextPath());
336            }
337            return message;
338        }
339
340        /**
341         * @return the output of the product
342         */
343        public String getOutput()
344        {
345            return product.getOutput();
346        }
347
348    }
349}