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.BufferedWriter;
39 import java.io.File;
40 import java.io.FileInputStream;
41 import java.io.FileWriter;
42 import java.io.IOException;
43 import java.util.Collection;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Properties;
48
49 import static java.io.File.createTempFile;
50 import static java.io.File.pathSeparatorChar;
51 import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
52 import static org.objectweb.asm.Opcodes.ASM7;
53
54
55
56
57
58 public class ModularClasspathForkConfiguration
59 extends DefaultForkConfiguration
60 {
61 @SuppressWarnings( "checkstyle:parameternumber" )
62 public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
63 @Nonnull File tempDirectory,
64 @Nullable String debugLine,
65 @Nonnull File workingDirectory,
66 @Nonnull Properties modelProperties,
67 @Nullable String argLine,
68 @Nonnull Map<String, String> environmentVariables,
69 boolean debug,
70 @Nonnegative int forkCount,
71 boolean reuseForks,
72 @Nonnull Platform pluginPlatform,
73 @Nonnull ConsoleLogger log )
74 {
75 super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
76 environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
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 File descriptor = modularClasspath.getModuleDescriptor();
94 List<String> modulePath = modularClasspath.getModulePath();
95 Collection<String> packages = modularClasspath.getPackages();
96 File patchFile = modularClasspath.getPatchFile();
97 List<String> classpath = toCompleteClasspath( config );
98
99 File argsFile = createArgsFile( descriptor, modulePath, classpath, packages, patchFile, startClass );
100
101 cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
102 }
103 catch ( IOException e )
104 {
105 String error = "Error creating args file";
106 InPluginProcessDumpSingleton.getSingleton()
107 .dumpException( e, error, dumpLogDirectory );
108 throw new SurefireBooterForkException( error, e );
109 }
110 }
111
112 @Nonnull
113 File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List<String> modulePath,
114 @Nonnull List<String> classPath, @Nonnull Collection<String> packages,
115 @Nonnull File patchFile, @Nonnull String startClassName )
116 throws IOException
117 {
118 File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
119 if ( !isDebug() )
120 {
121 surefireArgs.deleteOnExit();
122 }
123
124 try ( BufferedWriter writer = new BufferedWriter( new FileWriter( surefireArgs ), 64 * 1024 ) )
125 {
126 if ( !modulePath.isEmpty() )
127 {
128 writer.write( "--module-path" );
129 writer.newLine();
130
131 for ( Iterator<String> it = modulePath.iterator(); it.hasNext(); )
132 {
133 writer.append( it.next() );
134 if ( it.hasNext() )
135 {
136 writer.append( pathSeparatorChar );
137 }
138 }
139
140 writer.newLine();
141 }
142
143 if ( !classPath.isEmpty() )
144 {
145 writer.write( "--class-path" );
146 writer.newLine();
147 for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
148 {
149 writer.append( it.next() );
150 if ( it.hasNext() )
151 {
152 writer.append( pathSeparatorChar );
153 }
154 }
155
156 writer.newLine();
157 }
158
159 final String moduleName = toModuleName( moduleDescriptor );
160
161 writer.write( "--patch-module" );
162 writer.newLine();
163 writer.append( moduleName )
164 .append( '=' )
165 .append( patchFile.getPath() );
166
167 writer.newLine();
168
169 for ( String pkg : packages )
170 {
171 writer.write( "--add-exports" );
172 writer.newLine();
173 writer.append( moduleName )
174 .append( '/' )
175 .append( pkg )
176 .append( '=' )
177 .append( "ALL-UNNAMED" );
178
179 writer.newLine();
180 }
181
182 writer.write( "--add-modules" );
183 writer.newLine();
184 writer.append( moduleName );
185
186 writer.newLine();
187
188 writer.write( "--add-reads" );
189 writer.newLine();
190 writer.append( moduleName )
191 .append( '=' )
192 .append( "ALL-UNNAMED" );
193
194 writer.newLine();
195
196 writer.write( startClassName );
197
198 writer.newLine();
199
200 return surefireArgs;
201 }
202 }
203
204 @Nonnull
205 String toModuleName( @Nonnull File moduleDescriptor ) throws IOException
206 {
207 if ( !moduleDescriptor.isFile() )
208 {
209 throw new IOException( "No such Jigsaw module-descriptor exists " + moduleDescriptor.getAbsolutePath() );
210 }
211
212 final StringBuilder sb = new StringBuilder();
213 new ClassReader( new FileInputStream( moduleDescriptor ) ).accept( new ClassVisitor( ASM7 )
214 {
215 @Override
216 public ModuleVisitor visitModule( String name, int access, String version )
217 {
218 sb.setLength( 0 );
219 sb.append( name );
220 return super.visitModule( name, access, version );
221 }
222 }, 0 );
223
224 return sb.toString();
225 }
226 }