View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.release.exec;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.nio.file.Files;
29  import java.util.ArrayList;
30  import java.util.List;
31  
32  import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
33  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
34  import org.apache.maven.shared.invoker.DefaultInvoker;
35  import org.apache.maven.shared.invoker.InvocationRequest;
36  import org.apache.maven.shared.invoker.InvocationResult;
37  import org.apache.maven.shared.invoker.Invoker;
38  import org.apache.maven.shared.invoker.InvokerLogger;
39  import org.apache.maven.shared.invoker.MavenInvocationException;
40  import org.apache.maven.shared.release.ReleaseResult;
41  import org.apache.maven.shared.release.env.ReleaseEnvironment;
42  import org.apache.maven.shared.release.util.MavenCrypto;
43  import org.slf4j.Logger;
44  
45  /**
46   * Fork Maven using the maven-invoker shared library.
47   */
48  @Singleton
49  @Named("invoker")
50  public class InvokerMavenExecutor extends AbstractMavenExecutor {
51      @Inject
52      public InvokerMavenExecutor(MavenCrypto mavenCrypto) {
53          super(mavenCrypto);
54      }
55  
56      @Override
57      public void executeGoals(
58              File workingDirectory,
59              List<String> goals,
60              ReleaseEnvironment releaseEnvironment,
61              boolean interactive,
62              String additionalArguments,
63              String pomFileName,
64              ReleaseResult result)
65              throws MavenExecutorException {
66          InvokerLogger bridge = getInvokerLogger();
67  
68          Invoker invoker = new DefaultInvoker()
69                  .setMavenHome(releaseEnvironment.getMavenHome())
70                  .setLocalRepositoryDirectory(releaseEnvironment.getLocalRepositoryDirectory())
71                  .setLogger(bridge);
72  
73          InvocationRequest req = new DefaultInvocationRequest()
74                  .setDebug(getLogger().isDebugEnabled())
75                  .setBaseDirectory(workingDirectory)
76                  // fix for MRELEASE-1105
77                  // .addShellEnvironment( "MAVEN_DEBUG_OPTS", "" )
78                  .setBatchMode(!interactive)
79                  .setJavaHome(releaseEnvironment.getJavaHome())
80                  .setOutputHandler(getLogger()::info)
81                  .setErrorHandler(getLogger()::error);
82  
83          // for interactive mode we need some inputs stream
84          if (interactive) {
85              req.setInputStream(System.in);
86          }
87  
88          if (pomFileName != null) {
89              req.setPomFileName(pomFileName);
90          }
91  
92          File settingsFile = null;
93          if (releaseEnvironment.getSettings() != null) {
94              // Have to serialize to a file as if Maven is embedded, there may not actually be a settings.xml on disk
95              try {
96                  settingsFile = Files.createTempFile("release-settings", ".xml").toFile();
97                  SettingsXpp3Writer writer = getSettingsWriter();
98  
99                  try (FileWriter fileWriter = new FileWriter(settingsFile)) {
100                     writer.write(fileWriter, encryptSettings(releaseEnvironment.getSettings()));
101                 }
102                 req.setUserSettingsFile(settingsFile);
103             } catch (IOException e) {
104                 throw new MavenExecutorException("Could not create temporary file for release settings.xml", e);
105             }
106         }
107 
108         try {
109             List<String> targetGoals = new ArrayList<>(goals);
110 
111             if (additionalArguments != null && !additionalArguments.isEmpty()) {
112                 // additionalArguments will be parsed be MavenInvoker
113                 targetGoals.add(additionalArguments);
114             }
115 
116             req.setGoals(targetGoals);
117 
118             try {
119                 InvocationResult invocationResult = invoker.execute(req);
120 
121                 if (invocationResult.getExecutionException() != null) {
122                     throw new MavenExecutorException(
123                             "Error executing Maven.", invocationResult.getExecutionException());
124                 }
125 
126                 if (invocationResult.getExitCode() != 0) {
127                     throw new MavenExecutorException(
128                             "Maven execution failed, exit code: " + invocationResult.getExitCode(),
129                             invocationResult.getExitCode());
130                 }
131             } catch (MavenInvocationException e) {
132                 throw new MavenExecutorException("Failed to invoke Maven build.", e);
133             }
134         } finally {
135             if (settingsFile != null && settingsFile.exists() && !settingsFile.delete()) {
136                 settingsFile.deleteOnExit();
137             }
138         }
139     }
140 
141     /**
142      * <p>getInvokerLogger.</p>
143      *
144      * @return a {@link org.apache.maven.shared.invoker.InvokerLogger} object
145      */
146     protected InvokerLogger getInvokerLogger() {
147         return new LoggerBridge(getLogger());
148     }
149 
150     private static final class LoggerBridge implements InvokerLogger {
151 
152         private final Logger logger;
153 
154         LoggerBridge(Logger logger) {
155             this.logger = logger;
156         }
157 
158         @Override
159         public void debug(String message, Throwable error) {
160             logger.debug(message, error);
161         }
162 
163         @Override
164         public void debug(String message) {
165             logger.debug(message);
166         }
167 
168         @Override
169         public void error(String message, Throwable error) {
170             logger.error(message, error);
171         }
172 
173         @Override
174         public void error(String message) {
175             logger.error(message);
176         }
177 
178         @Override
179         public void fatalError(String message, Throwable error) {
180             logger.error(message, error);
181         }
182 
183         @Override
184         public void fatalError(String message) {
185             logger.error(message);
186         }
187 
188         @Override
189         public int getThreshold() {
190             return InvokerLogger.DEBUG;
191         }
192 
193         @Override
194         public void info(String message, Throwable error) {
195             logger.info(message, error);
196         }
197 
198         @Override
199         public void info(String message) {
200             logger.info(message);
201         }
202 
203         @Override
204         public boolean isDebugEnabled() {
205             return logger.isDebugEnabled();
206         }
207 
208         @Override
209         public boolean isErrorEnabled() {
210             return logger.isErrorEnabled();
211         }
212 
213         @Override
214         public boolean isFatalErrorEnabled() {
215             return logger.isErrorEnabled();
216         }
217 
218         @Override
219         public boolean isInfoEnabled() {
220             return logger.isInfoEnabled();
221         }
222 
223         @Override
224         public boolean isWarnEnabled() {
225             return logger.isWarnEnabled();
226         }
227 
228         @Override
229         public void setThreshold(int level) {
230             // NOTE:
231             // logger.setThreshold( level )
232             // is not supported in plexus-container-default:1.0-alpha-9 as used in Maven 2.x
233         }
234 
235         @Override
236         public void warn(String message, Throwable error) {
237             logger.warn(message, error);
238         }
239 
240         @Override
241         public void warn(String message) {
242             logger.warn(message);
243         }
244     }
245 }