View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.maven.plugins.cq;
21  
22  import java.io.File;
23  import java.util.Arrays;
24  
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.maven.execution.MavenSession;
27  import org.apache.maven.model.Plugin;
28  import org.apache.maven.plugin.AbstractMojo;
29  import org.apache.maven.plugin.BuildPluginManager;
30  import org.apache.maven.plugin.MavenPluginManager;
31  import org.apache.maven.plugin.MojoExecution;
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.MojoFailureException;
34  import org.apache.maven.plugin.descriptor.MojoDescriptor;
35  import org.apache.maven.plugin.descriptor.PluginDescriptor;
36  import org.apache.maven.plugin.logging.Log;
37  import org.apache.maven.plugins.annotations.Component;
38  import org.apache.maven.plugins.annotations.Execute;
39  import org.apache.maven.plugins.annotations.LifecyclePhase;
40  import org.apache.maven.plugins.annotations.Mojo;
41  import org.apache.maven.plugins.annotations.Parameter;
42  import org.apache.maven.plugins.annotations.ResolutionScope;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.settings.Settings;
45  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
46  import org.apache.maven.shared.invoker.DefaultInvoker;
47  import org.apache.maven.shared.invoker.InvocationOutputHandler;
48  import org.apache.maven.shared.invoker.InvocationRequest;
49  import org.apache.maven.shared.invoker.InvocationResult;
50  import org.apache.maven.shared.invoker.Invoker;
51  import org.apache.maven.shared.invoker.MavenInvocationException;
52  import org.apache.maven.shared.utils.cli.CommandLineException;
53  import org.codehaus.plexus.configuration.PlexusConfiguration;
54  import org.codehaus.plexus.configuration.PlexusConfigurationException;
55  import org.codehaus.plexus.util.xml.Xpp3Dom;
56  
57  /**
58   * Executes install phase and installs an OSGi bundle jar to a running Sling instance
59   * (combines goals "install" and "sling:install").
60   */
61  @Mojo(name = "install",
62      requiresDependencyResolution = ResolutionScope.COMPILE,
63      requiresProject = true,
64      threadSafe = true)
65  @Execute(phase = LifecyclePhase.INSTALL)
66  public class InstallMojo extends AbstractMojo {
67  
68    /**
69     * Version of sling plugin
70     */
71    @Parameter(property = "sling.plugin.version", required = true, defaultValue = "2.4.2")
72    private String slingPluginVersion;
73  
74    /**
75     * The URL of osgi console
76     */
77    @Parameter(property = "sling.console.url", required = true, defaultValue = "http://localhost:8080/system/console")
78    private String slingConsoleUrl;
79  
80    /**
81     * The user name to authenticate at osgi console
82     */
83    @Parameter(property = "sling.console.user", required = true, defaultValue = "admin")
84    private String slingConsoleUser;
85  
86    /**
87     * The password to authenticate at osgi console
88     */
89    @Parameter(property = "sling.console.password", required = true, defaultValue = "admin")
90    private String slingConsolePassword;
91  
92  
93    @Parameter(defaultValue = "${project}", readonly = true)
94    private MavenProject project;
95    @Parameter(defaultValue = "${settings}", readonly = true)
96    private Settings settings;
97    @Parameter(defaultValue = "${session}", readonly = true)
98    private MavenSession session;
99  
100   @Component(role = MavenPluginManager.class)
101   private MavenPluginManager pluginManager;
102   @Component(role = BuildPluginManager.class)
103   private BuildPluginManager buildPluginManager;
104 
105   @Override
106   public void execute() throws MojoExecutionException, MojoFailureException {
107 
108     // detect goal to deploy current project based on packaging
109     if (isBundleProject()) {
110       executeSlingPluginDirectly();
111     }
112     else if (isContentPackageProject()) {
113       getLog().info("Install content package to instance...");
114       executeWithMavenInvoker("wcmio-content-package:install");
115     }
116     else {
117       // no supported packaging - skip processing
118       getLog().info("No bundle or content-package project, skip deployment.");
119     }
120   }
121 
122   private boolean isBundleProject() {
123     // check for "bundle" packaging as used by maven-bundle-plugin
124     String packaging = project.getPackaging();
125     if (StringUtils.equals(packaging, "bundle")) {
126       return true;
127     }
128 
129     // check for active bnd-maven-plugin in current project
130     return project.getBuildPlugins().stream()
131         .anyMatch(this::isBndMavenPlugin);
132   }
133 
134   private boolean isBndMavenPlugin(Plugin plugin) {
135     return StringUtils.equals(plugin.getGroupId(), "biz.aQute.bnd")
136         && StringUtils.equals(plugin.getArtifactId(), "bnd-maven-plugin");
137   }
138 
139   private boolean isContentPackageProject() {
140     String packaging = project.getPackaging();
141     return StringUtils.equals(packaging, "content-package");
142   }
143 
144   /**
145    * Executes the sling-maven-plugin directly from the current project.
146    */
147   private void executeSlingPluginDirectly() throws MojoExecutionException {
148 
149     Plugin plugin = new Plugin();
150     plugin.setGroupId("org.apache.sling");
151     plugin.setArtifactId("sling-maven-plugin");
152     plugin.setVersion(this.slingPluginVersion);
153 
154     try {
155       PluginDescriptor pluginDescriptor = pluginManager.getPluginDescriptor(plugin,
156           project.getRemotePluginRepositories(), session.getRepositorySession());
157       MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo("install-file");
158       MojoExecution mojoExecution = new MojoExecution(pluginDescriptor.getMojo("install-file"));
159 
160       Xpp3Dom config = convertConfiguration(mojoDescriptor.getMojoConfiguration());
161       config.getChild("slingUrl").setValue(this.slingConsoleUrl);
162       config.getChild("user").setValue(this.slingConsoleUser);
163       config.getChild("password").setValue(this.slingConsolePassword);
164       config.getChild("mountByFS").setValue("false");
165       mojoExecution.setConfiguration(config);
166 
167       buildPluginManager.executeMojo(session, mojoExecution);
168     } /*CHECKSTYLE:OFF*/
169     catch (Throwable ex) { /*CHECKSTYLE_ON*/
170       throw new MojoExecutionException("Faild to execute plugin: " + plugin, ex);
171     }
172 
173   }
174 
175   private Xpp3Dom convertConfiguration(PlexusConfiguration plexusConfig) throws PlexusConfigurationException {
176     Xpp3Dom config = new Xpp3Dom(plexusConfig.getName());
177     config.setValue(plexusConfig.getValue());
178     for (String attribute : plexusConfig.getAttributeNames()) {
179       config.setAttribute(attribute, plexusConfig.getAttribute(attribute));
180     }
181     for (PlexusConfiguration child : plexusConfig.getChildren()) {
182       config.addChild(convertConfiguration(child));
183     }
184     return config;
185   }
186 
187   /**
188    * Invoke maven for the current project with all it's setting and the given goal.
189    * @param goal Goal
190    * @throws MojoExecutionException
191    */
192   private void executeWithMavenInvoker(String goal) throws MojoExecutionException {
193     InvocationRequest invocationRequest = new DefaultInvocationRequest();
194     invocationRequest.setPomFile(project.getFile());
195     invocationRequest.setGoals(Arrays.asList(goal));
196     invocationRequest.setBatchMode(true);
197 
198     // take over all systems properties and profile settings from current maven execution
199     invocationRequest.setShellEnvironmentInherited(true);
200     invocationRequest.setLocalRepositoryDirectory(new File(settings.getLocalRepository()));
201     invocationRequest.setProperties(session.getUserProperties());
202     invocationRequest.setProfiles(settings.getActiveProfiles());
203 
204     setupInvokerLogger(invocationRequest);
205     Invoker invoker = new DefaultInvoker();
206 
207     try {
208       InvocationResult invocationResult = invoker.execute(invocationRequest);
209       if (invocationResult.getExitCode() != 0) {
210         String msg = "Execution of cq:install failed, see above.";
211         if (invocationResult.getExecutionException() != null) {
212           msg = invocationResult.getExecutionException().getMessage();
213         }
214         throw new CommandLineException(msg);
215       }
216     }
217     catch (MavenInvocationException | CommandLineException ex) {
218       throw new MojoExecutionException("Failed to execute goals", ex);
219     }
220   }
221 
222   /**
223    * Mirror maven execution log output to current maven logger.
224    * @param request Invocation request
225    */
226   private void setupInvokerLogger(InvocationRequest request) {
227     Log log = getLog();
228     request.setOutputHandler(new InvocationOutputHandler() {
229       @Override
230       public void consumeLine(String line) {
231         if (StringUtils.startsWith(line, "[ERROR] ")) {
232           log.error(StringUtils.substringAfter(line, "[ERROR] "));
233         }
234         else if (StringUtils.startsWith(line, "[WARNING] ")) {
235           log.warn(StringUtils.substringAfter(line, "[WARNING] "));
236         }
237         else if (StringUtils.startsWith(line, "[INFO] ")) {
238           log.info(StringUtils.substringAfter(line, "[INFO] "));
239         }
240         else if (StringUtils.startsWith(line, "[DEBUG] ")) {
241           log.debug(StringUtils.substringAfter(line, "[DEBUG] "));
242         }
243         else {
244           log.info(line);
245         }
246       }
247     });
248   }
249 
250 }