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", null );
97              Object classWorld = getWorld.invoke( launcher, null );
98  
99              Method getMainClass = launcherClass.getMethod( "getMainClass", null );
100             Class<?> cliClass = (Class<?>) getMainClass.invoke( launcher, 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 ( ClassNotFoundException e )
111         {
112             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
113         }
114         catch ( InstantiationException e )
115         {
116             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
117         }
118         catch ( IllegalAccessException e )
119         {
120             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
121         }
122         catch ( NoSuchMethodException e )
123         {
124             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
125         }
126         catch ( InvocationTargetException e )
127         {
128             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
129         }
130         catch ( IOException e )
131         {
132             throw new LauncherException( "Invalid Maven home directory " + mavenHome, e );
133         }
134         finally
135         {
136             Thread.currentThread().setContextClassLoader( oldClassLoader );
137         }
138     }
139 
140     /**
141      * Launches an embedded Maven 3.x instance from the current class path, i.e. the Maven 3.x dependencies are assumed
142      * to be present on the class path.
143      */
144     public static Embedded3xLauncher createFromClasspath()
145         throws LauncherException
146     {
147         ClassLoader coreLoader = Thread.currentThread().getContextClassLoader();
148 
149         try
150         {
151             Class<?> cliClass = coreLoader.loadClass( "org.apache.maven.cli.MavenCli" );
152 
153             Object mavenCli = cliClass.newInstance();
154 
155             Class<?>[] parameterTypes = { String[].class, String.class, PrintStream.class, PrintStream.class };
156             Method doMain = cliClass.getMethod( "doMain", parameterTypes );
157 
158             return new Embedded3xLauncher( mavenCli, doMain );
159         }
160         catch ( ClassNotFoundException e )
161         {
162             throw new LauncherException( e.getMessage(), e );
163         }
164         catch ( NoSuchMethodException e )
165         {
166             throw new LauncherException( e.getMessage(), e );
167         }
168         catch ( InstantiationException e )
169         {
170             throw new LauncherException( e.getMessage(), e );
171         }
172         catch ( IllegalAccessException e )
173         {
174             throw new LauncherException( e.getMessage(), e );
175         }
176     }
177 
178     private static ClassLoader getBootLoader( String mavenHome, List<URL> classpath )
179     {
180         List<URL> urls = classpath;
181 
182         if ( urls == null )
183         {
184             urls = new ArrayList<URL>();
185 
186             File bootDir = new File( mavenHome, "boot" );
187             addUrls( urls, bootDir );
188         }
189 
190         if ( urls.isEmpty() )
191         {
192             throw new IllegalArgumentException( "Invalid Maven home directory " + mavenHome );
193         }
194 
195         URL[] ucp = (URL[]) urls.toArray( new URL[urls.size()] );
196 
197         return new URLClassLoader( ucp, ClassLoader.getSystemClassLoader().getParent() );
198     }
199 
200     private static void addUrls( List<URL> urls, File directory )
201     {
202         File[] jars = directory.listFiles();
203 
204         if ( jars != null )
205         {
206             for ( int i = 0; i < jars.length; i++ )
207             {
208                 File jar = jars[i];
209 
210                 if ( jar.getName().endsWith( ".jar" ) )
211                 {
212                     try
213                     {
214                         urls.add( jar.toURI().toURL() );
215                     }
216                     catch ( MalformedURLException e )
217                     {
218                         throw (RuntimeException) new IllegalStateException().initCause( e );
219                     }
220                 }
221             }
222         }
223     }
224 
225     public int run( String[] cliArgs, String workingDirectory, File logFile )
226         throws IOException, LauncherException
227     {
228         PrintStream out = ( logFile != null ) ? new PrintStream( new FileOutputStream( logFile ) ) : System.out;
229         try
230         {
231             Properties originalProperties = System.getProperties();
232             System.setProperties( null );
233             System.setProperty( "maven.home", originalProperties.getProperty( "maven.home", "" ) );
234             System.setProperty( "user.dir", new File( workingDirectory ).getAbsolutePath() );
235 
236             ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
237             Thread.currentThread().setContextClassLoader( mavenCli.getClass().getClassLoader() );
238             try
239             {
240                 Object result = doMain.invoke( mavenCli, new Object[] { cliArgs, workingDirectory, out, out } );
241 
242                 return ( (Number) result ).intValue();
243             }
244             finally
245             {
246                 Thread.currentThread().setContextClassLoader( originalClassLoader );
247 
248                 System.setProperties( originalProperties );
249             }
250         }
251         catch ( IllegalAccessException e )
252         {
253             throw new LauncherException( "Failed to run Maven: " + e.getMessage(), e );
254         }
255         catch ( InvocationTargetException e )
256         {
257             throw new LauncherException( "Failed to run Maven: " + e.getMessage(), e );
258         }
259         finally
260         {
261             if ( logFile != null )
262             {
263                 out.close();
264             }
265         }
266     }
267 
268     public String getMavenVersion()
269         throws LauncherException
270     {
271         Properties props = new Properties();
272 
273         InputStream is =
274             mavenCli.getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" );
275         if ( is != null )
276         {
277             try
278             {
279                 props.load( is );
280             }
281             catch ( IOException e )
282             {
283                 throw new LauncherException( "Failed to read Maven version", e );
284             }
285             finally
286             {
287                 IOUtil.close( is );
288             }
289         }
290 
291         String version = props.getProperty( "version" );
292         if ( version != null )
293         {
294             return version;
295         }
296 
297         throw new LauncherException( "Could not determine embedded Maven version" );
298     }
299 
300 }