View Javadoc
1   package org.apache.maven.it;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.PrintStream;
28  import java.lang.reflect.Constructor;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.net.MalformedURLException;
32  import java.net.URL;
33  import java.net.URLClassLoader;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Properties;
37  
38  import org.apache.maven.shared.utils.io.IOUtil;
39  
40  /**
41   * Launches an embedded Maven 3.x instance from some Maven installation directory.
42   * 
43   * @author Benjamin Bentmann
44   */
45  class Embedded3xLauncher
46      implements MavenLauncher
47  {
48  
49      private final Object mavenCli;
50  
51      private final Method doMain;
52  
53      private Embedded3xLauncher( Object mavenCli, Method doMain )
54      {
55          this.mavenCli = mavenCli;
56          this.doMain = doMain;
57      }
58  
59      /**
60       * Launches an embedded Maven 3.x instance from some Maven installation directory.
61       */
62      public static Embedded3xLauncher createFromMavenHome( String mavenHome, String classworldConf, List<URL> classpath )
63          throws LauncherException
64      {
65          if ( mavenHome == null || mavenHome.length() <= 0 )
66          {
67              throw new LauncherException( "Invalid Maven home directory " + mavenHome );
68          }
69  
70          System.setProperty( "maven.home", mavenHome );
71  
72          File config;
73          if ( classworldConf != null )
74          {
75              config = new File( classworldConf );
76          }
77          else
78          {
79              config = new File( mavenHome, "bin/m2.conf" );
80          }
81  
82          ClassLoader bootLoader = getBootLoader( mavenHome, classpath );
83  
84          ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
85          Thread.currentThread().setContextClassLoader( bootLoader );
86          try
87          {
88              Class<?> launcherClass = bootLoader.loadClass( "org.codehaus.plexus.classworlds.launcher.Launcher" );
89  
90              Object launcher = launcherClass.newInstance();
91  
92              Method configure = launcherClass.getMethod( "configure", new Class[] { InputStream.class } );
93  
94              configure.invoke( launcher, new Object[] { new FileInputStream( config ) } );
95  
96              Method getWorld = launcherClass.getMethod( "getWorld", (Class<?>) null );
97              Object classWorld = getWorld.invoke( launcher, (Class<?>) null );
98  
99              Method getMainClass = launcherClass.getMethod( "getMainClass", (Class<?>) null );
100             Class<?> cliClass = (Class<?>) getMainClass.invoke( launcher, (Class<?>) null );
101 
102             Constructor<?> newMavenCli = cliClass.getConstructor( new Class[] { classWorld.getClass() } );
103             Object mavenCli = newMavenCli.newInstance( new Object[] { classWorld } );
104 
105             Class<?>[] parameterTypes = { String[].class, String.class, PrintStream.class, PrintStream.class };
106             Method doMain = cliClass.getMethod( "doMain", parameterTypes );
107 
108             return new Embedded3xLauncher( mavenCli, doMain );
109         }
110         catch ( ReflectiveOperationException | IOException e )
111         {
112             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
113         }
114         finally
115         {
116             Thread.currentThread().setContextClassLoader( oldClassLoader );
117         }
118     }
119 
120     /**
121      * Launches an embedded Maven 3.x instance from the current class path, i.e. the Maven 3.x dependencies are assumed
122      * to be present on the class path.
123      */
124     public static Embedded3xLauncher createFromClasspath()
125         throws LauncherException
126     {
127         ClassLoader coreLoader = Thread.currentThread().getContextClassLoader();
128 
129         try
130         {
131             Class<?> cliClass = coreLoader.loadClass( "org.apache.maven.cli.MavenCli" );
132 
133             Object mavenCli = cliClass.newInstance();
134 
135             Class<?>[] parameterTypes = { String[].class, String.class, PrintStream.class, PrintStream.class };
136             Method doMain = cliClass.getMethod( "doMain", parameterTypes );
137 
138             return new Embedded3xLauncher( mavenCli, doMain );
139         }
140         catch ( ReflectiveOperationException e )
141         {
142             throw new LauncherException( e.getMessage(), e );
143         }
144     }
145 
146     private static ClassLoader getBootLoader( String mavenHome, List<URL> classpath )
147     {
148         List<URL> urls = classpath;
149 
150         if ( urls == null )
151         {
152             urls = new ArrayList<URL>();
153 
154             File bootDir = new File( mavenHome, "boot" );
155             addUrls( urls, bootDir );
156         }
157 
158         if ( urls.isEmpty() )
159         {
160             throw new IllegalArgumentException( "Invalid Maven home directory " + mavenHome );
161         }
162 
163         URL[] ucp = (URL[]) urls.toArray( new URL[urls.size()] );
164 
165         return new URLClassLoader( ucp, ClassLoader.getSystemClassLoader().getParent() );
166     }
167 
168     private static void addUrls( List<URL> urls, File directory )
169     {
170         File[] jars = directory.listFiles();
171 
172         if ( jars != null )
173         {
174             for ( int i = 0; i < jars.length; i++ )
175             {
176                 File jar = jars[i];
177 
178                 if ( jar.getName().endsWith( ".jar" ) )
179                 {
180                     try
181                     {
182                         urls.add( jar.toURI().toURL() );
183                     }
184                     catch ( MalformedURLException e )
185                     {
186                         throw (RuntimeException) new IllegalStateException().initCause( e );
187                     }
188                 }
189             }
190         }
191     }
192 
193     public int run( String[] cliArgs, Properties systemProperties, String workingDirectory, File logFile )
194         throws IOException, LauncherException
195     {
196         PrintStream out = ( logFile != null ) ? new PrintStream( new FileOutputStream( logFile ) ) : System.out;
197         try
198         {
199             Properties originalProperties = System.getProperties();
200             System.setProperties( null );
201             System.setProperty( "maven.home", originalProperties.getProperty( "maven.home", "" ) );
202             System.setProperty( "user.dir", new File( workingDirectory ).getAbsolutePath() );
203 
204             for ( Object o : systemProperties.keySet() )
205             {
206                 String key = (String) o;
207                 String value = systemProperties.getProperty( key );
208                 System.setProperty( key, value );
209             }
210 
211             ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
212             Thread.currentThread().setContextClassLoader( mavenCli.getClass().getClassLoader() );
213             try
214             {
215                 Object result = doMain.invoke( mavenCli, new Object[] { cliArgs, workingDirectory, out, out } );
216 
217                 return ( (Number) result ).intValue();
218             }
219             finally
220             {
221                 Thread.currentThread().setContextClassLoader( originalClassLoader );
222 
223                 System.setProperties( originalProperties );
224             }
225         }
226         catch ( IllegalAccessException | InvocationTargetException e )
227         {
228             throw new LauncherException( "Failed to run Maven: " + e.getMessage(), e );
229         }
230         finally
231         {
232             if ( logFile != null )
233             {
234                 out.close();
235             }
236         }
237     }
238 
239     public String getMavenVersion()
240         throws LauncherException
241     {
242         Properties props = new Properties();
243 
244         InputStream is =
245             mavenCli.getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" );
246         if ( is != null )
247         {
248             try
249             {
250                 props.load( is );
251             }
252             catch ( IOException e )
253             {
254                 throw new LauncherException( "Failed to read Maven version", e );
255             }
256             finally
257             {
258                 IOUtil.close( is );
259             }
260         }
261 
262         String version = props.getProperty( "version" );
263         if ( version != null )
264         {
265             return version;
266         }
267 
268         throw new LauncherException( "Could not determine embedded Maven version" );
269     }
270 
271 }