View Javadoc
1   package org.apache.maven.plugin.surefire.booterclient;
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 org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
23  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
24  import org.apache.maven.surefire.booter.Classpath;
25  import org.apache.maven.surefire.booter.StartupConfiguration;
26  import org.apache.maven.surefire.booter.SurefireBooterForkException;
27  
28  import javax.annotation.Nonnull;
29  import javax.annotation.Nullable;
30  import java.io.File;
31  import java.io.FileOutputStream;
32  import java.io.IOException;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Properties;
37  import java.util.jar.JarEntry;
38  import java.util.jar.JarOutputStream;
39  import java.util.jar.Manifest;
40  
41  import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
42  
43  /**
44   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
45   * @since 2.21.0.Jigsaw
46   */
47  public final class JarManifestForkConfiguration
48      extends AbstractClasspathForkConfiguration
49  {
50      @SuppressWarnings( "checkstyle:parameternumber" )
51      public JarManifestForkConfiguration( @Nonnull Classpath bootClasspath, @Nonnull File tempDirectory,
52                                           @Nullable String debugLine, @Nonnull File workingDirectory,
53                                           @Nonnull Properties modelProperties, @Nullable String argLine,
54                                           @Nonnull Map<String, String> environmentVariables, boolean debug,
55                                           int forkCount, boolean reuseForks, @Nonnull Platform pluginPlatform,
56                                           @Nonnull ConsoleLogger log )
57      {
58          super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
59                  environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
60      }
61  
62      @Override
63      protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
64                                       @Nonnull String booterThatHasMainMethod,
65                                       @Nonnull StartupConfiguration config )
66              throws SurefireBooterForkException
67      {
68          try
69          {
70              File jar = createJar( toCompleteClasspath( config ), booterThatHasMainMethod );
71              cli.createArg().setValue( "-jar" );
72              cli.createArg().setValue( escapeToPlatformPath( jar.getAbsolutePath() ) );
73          }
74          catch ( IOException e )
75          {
76              throw new SurefireBooterForkException( "Error creating archive file", e );
77          }
78      }
79  
80      /**
81       * Create a jar with just a manifest containing a Main-Class entry for BooterConfiguration and a Class-Path entry
82       * for all classpath elements.
83       *
84       * @param classPath      List&lt;String&gt; of all classpath elements.
85       * @param startClassName The class name to start (main-class)
86       * @return file of the jar
87       * @throws IOException When a file operation fails.
88       */
89      @Nonnull
90      private File createJar( @Nonnull List<String> classPath, @Nonnull String startClassName )
91              throws IOException
92      {
93          File file = File.createTempFile( "surefirebooter", ".jar", getTempDirectory() );
94          if ( !isDebug() )
95          {
96              file.deleteOnExit();
97          }
98          FileOutputStream fos = new FileOutputStream( file );
99          JarOutputStream jos = new JarOutputStream( fos );
100         try
101         {
102             jos.setLevel( JarOutputStream.STORED );
103             JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
104             jos.putNextEntry( je );
105 
106             Manifest man = new Manifest();
107 
108             // we can't use StringUtils.join here since we need to add a '/' to
109             // the end of directory entries - otherwise the jvm will ignore them.
110             StringBuilder cp = new StringBuilder();
111             for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
112             {
113                 File file1 = new File( it.next() );
114                 String uri = file1.toURI().toASCIIString();
115                 cp.append( uri );
116                 if ( file1.isDirectory() && !uri.endsWith( "/" ) )
117                 {
118                     cp.append( '/' );
119                 }
120 
121                 if ( it.hasNext() )
122                 {
123                     cp.append( ' ' );
124                 }
125             }
126 
127             man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
128             man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
129             man.getMainAttributes().putValue( "Main-Class", startClassName );
130 
131             man.write( jos );
132 
133             jos.closeEntry();
134             jos.flush();
135 
136             return file;
137         }
138         finally
139         {
140             jos.close();
141         }
142     }
143 }