1   package com.atlassian.maven.plugins.amps;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.lang.reflect.InvocationTargetException;
6   import java.lang.reflect.Method;
7   import java.util.List;
8   
9   import org.apache.maven.artifact.Artifact;
10  import org.apache.maven.execution.MavenSession;
11  import org.apache.maven.execution.ReactorManager;
12  import org.apache.maven.model.Plugin;
13  import org.apache.maven.model.PluginManagement;
14  import org.apache.maven.plugin.MojoExecutionException;
15  import org.apache.maven.plugin.MojoFailureException;
16  import org.apache.maven.project.MavenProject;
17  import org.apache.maven.project.MavenProjectBuilder;
18  import org.apache.maven.project.ProjectBuilder;
19  import org.apache.maven.project.ProjectBuilderConfiguration;
20  import org.apache.maven.project.ProjectBuildingException;
21  import org.apache.maven.project.ProjectBuildingRequest;
22  import org.apache.maven.project.ProjectBuildingResult;
23  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
24  import org.codehaus.plexus.util.xml.Xpp3Dom;
25  import org.jfrog.maven.annomojo.annotations.MojoGoal;
26  import org.jfrog.maven.annomojo.annotations.MojoParameter;
27  import org.jfrog.maven.annomojo.annotations.MojoRequiresProject;
28  
29  import static java.util.Collections.singletonList;
30  
31  /**
32   * Run the webapp without a plugin project
33   */
34  @MojoGoal ("run-standalone")
35  @MojoRequiresProject (false)
36  public class RunStandaloneMojo extends AbstractProductHandlerMojo
37  {
38      private final String
39          GROUP_ID = "com.atlassian.amps",
40          ARTIFACT_ID = "standalone";
41  
42      @MojoParameter (expression = "${component.org.apache.maven.project.MavenProjectBuilder}", required = true, readonly = true)
43      private MavenProjectBuilder projectBuilder;
44  
45      private Artifact getStandaloneArtifact()
46      {
47          final String version = getPluginInformation().getVersion();
48          return artifactFactory.createProjectArtifact(GROUP_ID, ARTIFACT_ID, version);
49      }
50  
51      protected void doExecute() throws MojoExecutionException, MojoFailureException
52      {
53          try
54          {
55              MavenGoals goals;
56              Xpp3Dom configuration;
57  
58              try
59              {
60                  /* Maven 3 */
61                  Object o = getMavenContext().getSession().lookup("org.apache.maven.project.ProjectBuilder");
62                  goals = createMavenGoals((ProjectBuilder) o);
63  
64                  /* When we run with Maven 3 the configuration from the pom isn't automatically picked up
65                   * by the mojo executor. Grab it manually from pluginManagement.
66                   */
67                  PluginManagement mgmt = goals.getContextProject().getBuild().getPluginManagement();
68                  Plugin plugin = (Plugin) mgmt.getPluginsAsMap().get("com.atlassian.maven.plugins:maven-amps-plugin");
69  
70                  configuration = (Xpp3Dom) plugin.getConfiguration();
71              }
72              catch (ComponentLookupException e)
73              {
74                  /* Maven 2 */
75                  goals = createMavenGoals(projectBuilder);
76                  configuration = new Xpp3Dom("configuration");
77              }
78  
79              goals.executeAmpsRecursively(getPluginInformation().getVersion(), "run", configuration);
80          }
81          catch (Exception e)
82          {
83              throw new MojoExecutionException(e.getMessage(), e);
84          }
85      }
86  
87      protected MavenGoals createMavenGoals(MavenProjectBuilder projectBuilder) throws MojoExecutionException, MojoFailureException
88      {
89          final String version = getPluginInformation().getVersion();
90          final Artifact artifact = artifactFactory.createProjectArtifact(GROUP_ID, ARTIFACT_ID, version);
91          try
92          {
93              // overall goal here is to create a new MavenContext / MavenGoals for the standalone project
94              final MavenContext oldContext = getMavenContext();
95  
96              // construct new project for new context
97              final MavenProject
98                  oldProject = oldContext.getProject(),
99                  newProject = projectBuilder.buildFromRepository(artifact, repositories, localRepository);
100 
101             // horrible hack #1: buildFromRepository() doesn't actually set the project's remote repositories
102             newProject.setRemoteArtifactRepositories(oldProject.getRemoteArtifactRepositories());
103             newProject.setPluginArtifactRepositories(oldProject.getPluginArtifactRepositories());
104 
105             // horrible hack #2: we need to modify the session to use the new project as its reactor
106             final List<MavenProject> newReactor = singletonList(newProject);
107             final MavenSession
108                 oldSession = oldContext.getSession(),
109                 newSession = new MavenSession(
110                     oldSession.getContainer(),
111                     oldSession.getSettings(),
112                     oldSession.getLocalRepository(),
113                     oldSession.getEventDispatcher(),
114                     new ReactorManager(newReactor),
115                     oldSession.getGoals(),
116                     oldSession.getExecutionRootDirectory(),
117                     oldSession.getExecutionProperties(),
118                     oldSession.getUserProperties(),
119                     oldSession.getStartTime()
120                 );
121 
122             // horrible hack #3: we need to create a base directory from scratch, and convince the project to like it
123             final String baseDir = System.getProperty("user.dir") + "/amps-standalone/";
124             newProject.setFile(new File(baseDir, "pom.xml"));
125 
126             ProjectBuilderConfiguration projectBuilderConfiguration =
127                     getProjectBuilderConfigurationFromMavenSession(newSession);
128 
129             projectBuilder.calculateConcreteState(newProject, projectBuilderConfiguration);
130 
131             // finally, execute run goal against standalone project
132             final MavenContext newContext = oldContext.with(
133                 newProject,
134                 newReactor,
135                 newSession);
136             return new MavenGoals(newContext);
137         }
138         catch (Exception e)
139         {
140             throw new MojoExecutionException(e.getMessage(), e);
141         }
142     }
143 
144     /**
145      * MavenSession.getProjectBuilderConfiguration() works at runtime with Maven 2 but isn't
146      * available at compile time when we build with Maven 3 artifacts.
147      */
148     private static ProjectBuilderConfiguration getProjectBuilderConfigurationFromMavenSession(MavenSession session)
149         throws MojoExecutionException, InvocationTargetException, IllegalAccessException
150     {
151         try
152         {
153             Method m = MavenSession.class.getMethod("getProjectBuilderConfiguration");
154             return (ProjectBuilderConfiguration) m.invoke(session);
155         }
156         catch (NoSuchMethodException e)
157         {
158             throw new MojoExecutionException("Maven 3 is not supported for run-standalone", e);
159         }
160     }
161 
162     protected MavenGoals createMavenGoals(ProjectBuilder projectBuilder)
163             throws MojoExecutionException, MojoFailureException, ProjectBuildingException,
164             IOException
165     {
166         // overall goal here is to create a new MavenContext / MavenGoals for the standalone project
167         final MavenContext oldContext = getMavenContext();
168 
169         MavenSession oldSession = oldContext.getSession();
170 
171         ProjectBuildingRequest pbr = oldSession.getProjectBuildingRequest();
172         pbr.getSystemProperties().setProperty("project.basedir", "amps-standalone");
173 
174         ProjectBuildingResult result = projectBuilder.build(getStandaloneArtifact(), false, pbr);
175 
176         final List<MavenProject> newReactor = singletonList(result.getProject());
177 
178         MavenSession newSession = oldSession.clone();
179         newSession.setProjects(newReactor);
180 
181         final MavenContext newContext = oldContext.with(
182             result.getProject(),
183             newReactor,
184             newSession);
185 
186         return new MavenGoals(newContext);
187     }
188 }