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.booterclient.output.InPluginProcessDumpSingleton;
24  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
25  import org.apache.maven.surefire.booter.AbstractPathConfiguration;
26  import org.apache.maven.surefire.booter.Classpath;
27  import org.apache.maven.surefire.booter.ModularClasspath;
28  import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
29  import org.apache.maven.surefire.booter.StartupConfiguration;
30  import org.apache.maven.surefire.booter.SurefireBooterForkException;
31  import org.apache.maven.surefire.extensions.ForkNodeFactory;
32  
33  import javax.annotation.Nonnegative;
34  import javax.annotation.Nonnull;
35  import javax.annotation.Nullable;
36  import java.io.File;
37  import java.io.FileWriter;
38  import java.io.IOException;
39  import java.util.Collection;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Properties;
44  
45  import static java.io.File.createTempFile;
46  import static java.io.File.pathSeparatorChar;
47  import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
48  import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
49  import static org.apache.maven.surefire.shared.utils.StringUtils.replace;
50  
51  /**
52   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
53   * @since 2.21.0.Jigsaw
54   */
55  public class ModularClasspathForkConfiguration
56          extends DefaultForkConfiguration
57  {
58      @SuppressWarnings( "checkstyle:parameternumber" )
59      public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
60                                                @Nonnull File tempDirectory,
61                                                @Nullable String debugLine,
62                                                @Nonnull File workingDirectory,
63                                                @Nonnull Properties modelProperties,
64                                                @Nullable String argLine,
65                                                @Nonnull Map<String, String> environmentVariables,
66                                                @Nonnull String[] excludedEnvironmentVariables,
67                                                boolean debug,
68                                                @Nonnegative int forkCount,
69                                                boolean reuseForks,
70                                                @Nonnull Platform pluginPlatform,
71                                                @Nonnull ConsoleLogger log,
72                                                @Nonnull ForkNodeFactory forkNodeFactory )
73      {
74          super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
75              environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log,
76              forkNodeFactory );
77      }
78  
79      @Override
80      protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli, @Nonnull String startClass,
81                                       @Nonnull StartupConfiguration config, @Nonnull File dumpLogDirectory )
82              throws SurefireBooterForkException
83      {
84          try
85          {
86              AbstractPathConfiguration pathConfig = config.getClasspathConfiguration();
87  
88              ModularClasspathConfiguration modularClasspathConfiguration =
89                      pathConfig.toRealPath( ModularClasspathConfiguration.class );
90  
91              ModularClasspath modularClasspath = modularClasspathConfiguration.getModularClasspath();
92  
93              boolean isMainDescriptor = modularClasspath.isMainDescriptor();
94              String moduleName = modularClasspath.getModuleNameFromDescriptor();
95              List<String> modulePath = modularClasspath.getModulePath();
96              Collection<String> packages = modularClasspath.getPackages();
97              File patchFile = modularClasspath.getPatchFile();
98              List<String> classpath = toCompleteClasspath( config );
99  
100             File argsFile = createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass,
101                 isMainDescriptor, config.getJpmsArguments() );
102 
103             cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
104         }
105         catch ( IOException e )
106         {
107             String error = "Error creating args file";
108             InPluginProcessDumpSingleton.getSingleton()
109                     .dumpException( e, error, dumpLogDirectory );
110             throw new SurefireBooterForkException( error, e );
111         }
112     }
113 
114     @Nonnull
115     File createArgsFile( @Nonnull String moduleName, @Nonnull List<String> modulePath,
116                          @Nonnull List<String> classPath, @Nonnull Collection<String> packages,
117                          File patchFile, @Nonnull String startClassName, boolean isMainDescriptor,
118                          @Nonnull List<String[]> providerJpmsArguments )
119             throws IOException
120     {
121         File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
122         if ( isDebug() )
123         {
124             getLogger().debug( "Path to args file: " +  surefireArgs.getCanonicalPath() );
125         }
126         else
127         {
128             surefireArgs.deleteOnExit();
129         }
130 
131         try ( FileWriter io = new FileWriter( surefireArgs ) )
132         {
133             StringBuilder args = new StringBuilder( 64 * 1024 );
134             if ( !modulePath.isEmpty() )
135             {
136                 // https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-4856361B-8BFD-4964-AE84-121F5F6CF111
137                 args.append( "--module-path" )
138                         .append( NL )
139                         .append( '"' );
140 
141                 for ( Iterator<String> it = modulePath.iterator(); it.hasNext(); )
142                 {
143                     args.append( replace( it.next(), "\\", "\\\\" ) );
144                     if ( it.hasNext() )
145                     {
146                         args.append( pathSeparatorChar );
147                     }
148                 }
149 
150                 args.append( '"' )
151                         .append( NL );
152             }
153 
154             if ( !classPath.isEmpty() )
155             {
156                 args.append( "--class-path" )
157                         .append( NL )
158                         .append( '"' );
159 
160                 for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
161                 {
162                     args.append( replace( it.next(), "\\", "\\\\" ) );
163                     if ( it.hasNext() )
164                     {
165                         args.append( pathSeparatorChar );
166                     }
167                 }
168 
169                 args.append( '"' )
170                         .append( NL );
171             }
172 
173             if ( isMainDescriptor )
174             {
175                 args.append( "--patch-module" )
176                         .append( NL )
177                         .append( moduleName )
178                         .append( '=' )
179                         .append( '"' )
180                         .append( replace( patchFile.getPath(), "\\", "\\\\" ) )
181                         .append( '"' )
182                         .append( NL );
183 
184                 for ( String pkg : packages )
185                 {
186                     args.append( "--add-exports" )
187                             .append( NL )
188                             .append( moduleName )
189                             .append( '/' )
190                             .append( pkg )
191                             .append( '=' )
192                             .append( "ALL-UNNAMED" )
193                             .append( NL );
194                 }
195 
196                 args.append( "--add-modules" )
197                         .append( NL )
198                         .append( moduleName )
199                         .append( NL );
200 
201                 args.append( "--add-reads" )
202                         .append( NL )
203                         .append( moduleName )
204                         .append( '=' )
205                         .append( "ALL-UNNAMED" )
206                         .append( NL );
207             }
208             else
209             {
210                 args.append( "--add-modules" )
211                     .append( NL )
212                     .append( moduleName )
213                     .append( NL );
214             }
215 
216             for ( String[] entries : providerJpmsArguments )
217             {
218                 for ( String entry : entries )
219                 {
220                     args.append( entry )
221                         .append( NL );
222                 }
223             }
224 
225             args.append( startClassName );
226 
227             String argsFileContent = args.toString();
228 
229             if ( isDebug() )
230             {
231                 getLogger().debug( "args file content:" + NL + argsFileContent );
232             }
233 
234             io.write( argsFileContent );
235 
236             return surefireArgs;
237         }
238     }
239 }