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