1 package org.apache.maven.plugin.surefire.booterclient;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.objectweb.asm.ClassReader;
32 import org.objectweb.asm.ClassVisitor;
33 import org.objectweb.asm.ModuleVisitor;
34
35 import javax.annotation.Nonnegative;
36 import javax.annotation.Nonnull;
37 import javax.annotation.Nullable;
38 import java.io.File;
39 import java.io.FileInputStream;
40 import java.io.FileWriter;
41 import java.io.IOException;
42 import java.util.Collection;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Properties;
47
48 import static java.io.File.createTempFile;
49 import static java.io.File.pathSeparatorChar;
50 import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
51 import static org.apache.maven.shared.utils.StringUtils.replace;
52 import static org.apache.maven.surefire.util.internal.StringUtils.NL;
53 import static org.objectweb.asm.Opcodes.ASM7;
54
55
56
57
58
59 public class ModularClasspathForkConfiguration
60 extends DefaultForkConfiguration
61 {
62 @SuppressWarnings( "checkstyle:parameternumber" )
63 public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
64 @Nonnull File tempDirectory,
65 @Nullable String debugLine,
66 @Nonnull File workingDirectory,
67 @Nonnull Properties modelProperties,
68 @Nullable String argLine,
69 @Nonnull Map<String, String> environmentVariables,
70 boolean debug,
71 @Nonnegative int forkCount,
72 boolean reuseForks,
73 @Nonnull Platform pluginPlatform,
74 @Nonnull ConsoleLogger log )
75 {
76 super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
77 environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
78 }
79
80 @Override
81 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli, @Nonnull String startClass,
82 @Nonnull StartupConfiguration config, @Nonnull File dumpLogDirectory )
83 throws SurefireBooterForkException
84 {
85 try
86 {
87 AbstractPathConfiguration pathConfig = config.getClasspathConfiguration();
88
89 ModularClasspathConfiguration modularClasspathConfiguration =
90 pathConfig.toRealPath( ModularClasspathConfiguration.class );
91
92 ModularClasspath modularClasspath = modularClasspathConfiguration.getModularClasspath();
93
94 File descriptor = modularClasspath.getModuleDescriptor();
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( descriptor, modulePath, classpath, packages, patchFile, startClass );
101
102 cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
103 }
104 catch ( IOException e )
105 {
106 String error = "Error creating args file";
107 InPluginProcessDumpSingleton.getSingleton()
108 .dumpException( e, error, dumpLogDirectory );
109 throw new SurefireBooterForkException( error, e );
110 }
111 }
112
113 @Nonnull
114 File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List<String> modulePath,
115 @Nonnull List<String> classPath, @Nonnull Collection<String> packages,
116 @Nonnull File patchFile, @Nonnull String startClassName )
117 throws IOException
118 {
119 File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
120 if ( isDebug() )
121 {
122 getLogger().debug( "Path to args file: " + surefireArgs.getCanonicalPath() );
123 }
124 else
125 {
126 surefireArgs.deleteOnExit();
127 }
128
129 try ( FileWriter io = new FileWriter( surefireArgs ) )
130 {
131 StringBuilder args = new StringBuilder( 64 * 1024 );
132 if ( !modulePath.isEmpty() )
133 {
134
135 args.append( "--module-path" )
136 .append( NL )
137 .append( '"' );
138
139 for ( Iterator<String> it = modulePath.iterator(); it.hasNext(); )
140 {
141 args.append( replace( it.next(), "\\", "\\\\" ) );
142 if ( it.hasNext() )
143 {
144 args.append( pathSeparatorChar );
145 }
146 }
147
148 args.append( '"' )
149 .append( NL );
150 }
151
152 if ( !classPath.isEmpty() )
153 {
154 args.append( "--class-path" )
155 .append( NL )
156 .append( '"' );
157
158 for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
159 {
160 args.append( replace( it.next(), "\\", "\\\\" ) );
161 if ( it.hasNext() )
162 {
163 args.append( pathSeparatorChar );
164 }
165 }
166
167 args.append( '"' )
168 .append( NL );
169 }
170
171 final String moduleName = toModuleName( moduleDescriptor );
172
173 args.append( "--patch-module" )
174 .append( NL )
175 .append( moduleName )
176 .append( '=' )
177 .append( '"' )
178 .append( replace( patchFile.getPath(), "\\", "\\\\" ) )
179 .append( '"' )
180 .append( NL );
181
182 for ( String pkg : packages )
183 {
184 args.append( "--add-exports" )
185 .append( NL )
186 .append( moduleName )
187 .append( '/' )
188 .append( pkg )
189 .append( '=' )
190 .append( "ALL-UNNAMED" )
191 .append( NL );
192 }
193
194 args.append( "--add-modules" )
195 .append( NL )
196 .append( moduleName )
197 .append( NL );
198
199 args.append( "--add-reads" )
200 .append( NL )
201 .append( moduleName )
202 .append( '=' )
203 .append( "ALL-UNNAMED" )
204 .append( NL );
205
206 args.append( startClassName );
207
208 String argsFileContent = args.toString();
209
210 if ( isDebug() )
211 {
212 getLogger().debug( "args file content:" + NL + argsFileContent );
213 }
214
215 io.write( argsFileContent );
216
217 return surefireArgs;
218 }
219 }
220
221 @Nonnull
222 String toModuleName( @Nonnull File moduleDescriptor ) throws IOException
223 {
224 if ( !moduleDescriptor.isFile() )
225 {
226 throw new IOException( "No such Jigsaw module-descriptor exists " + moduleDescriptor.getAbsolutePath() );
227 }
228
229 final StringBuilder sb = new StringBuilder();
230 new ClassReader( new FileInputStream( moduleDescriptor ) ).accept( new ClassVisitor( ASM7 )
231 {
232 @Override
233 public ModuleVisitor visitModule( String name, int access, String version )
234 {
235 sb.setLength( 0 );
236 sb.append( name );
237 return super.visitModule( name, access, version );
238 }
239 }, 0 );
240
241 return sb.toString();
242 }
243 }