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.log.api.ConsoleLogger;
24 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
25 import org.apache.maven.surefire.booter.Classpath;
26 import org.apache.maven.surefire.booter.ModularClasspath;
27 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
28 import org.apache.maven.surefire.booter.StartupConfiguration;
29 import org.apache.maven.surefire.booter.SurefireBooterForkException;
30 import org.objectweb.asm.ClassReader;
31 import org.objectweb.asm.ClassVisitor;
32 import org.objectweb.asm.ModuleVisitor;
33
34 import javax.annotation.Nonnegative;
35 import javax.annotation.Nonnull;
36 import javax.annotation.Nullable;
37 import java.io.BufferedWriter;
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.objectweb.asm.Opcodes.ASM6;
52
53
54
55
56
57 public class ModularClasspathForkConfiguration
58 extends DefaultForkConfiguration
59 {
60 @SuppressWarnings( "checkstyle:parameternumber" )
61 public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
62 @Nonnull File tempDirectory,
63 @Nullable String debugLine,
64 @Nonnull File workingDirectory,
65 @Nonnull Properties modelProperties,
66 @Nullable String argLine,
67 @Nonnull Map<String, String> environmentVariables,
68 boolean debug,
69 @Nonnegative int forkCount,
70 boolean reuseForks,
71 @Nonnull Platform pluginPlatform,
72 @Nonnull ConsoleLogger log )
73 {
74 super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
75 environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
76 }
77
78 @Override
79 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli, @Nonnull String startClass,
80 @Nonnull StartupConfiguration config )
81 throws SurefireBooterForkException
82 {
83 try
84 {
85 AbstractPathConfiguration pathConfig = config.getClasspathConfiguration();
86
87 ModularClasspathConfiguration modularClasspathConfiguration =
88 pathConfig.toRealPath( ModularClasspathConfiguration.class );
89
90 ModularClasspath modularClasspath = modularClasspathConfiguration.getModularClasspath();
91
92 File descriptor = modularClasspath.getModuleDescriptor();
93 List<String> modulePath = modularClasspath.getModulePath();
94 Collection<String> packages = modularClasspath.getPackages();
95 File patchFile = modularClasspath.getPatchFile();
96 List<String> classpath = toCompleteClasspath( config );
97
98 File argsFile = createArgsFile( descriptor, modulePath, classpath, packages, patchFile, startClass );
99
100 cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
101 }
102 catch ( IOException e )
103 {
104 throw new SurefireBooterForkException( "Error creating args file", e );
105 }
106 }
107
108 @Nonnull
109 File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List<String> modulePath,
110 @Nonnull List<String> classPath, @Nonnull Collection<String> packages,
111 @Nonnull File patchFile, @Nonnull String startClassName )
112 throws IOException
113 {
114 File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
115 if ( !isDebug() )
116 {
117 surefireArgs.deleteOnExit();
118 }
119
120 BufferedWriter writer = null;
121 try
122 {
123 writer = new BufferedWriter( new FileWriter( surefireArgs ) );
124
125 if ( !modulePath.isEmpty() )
126 {
127 writer.write( "--module-path" );
128 writer.newLine();
129
130 for ( Iterator<String> it = modulePath.iterator(); it.hasNext(); )
131 {
132 writer.append( it.next() );
133 if ( it.hasNext() )
134 {
135 writer.append( pathSeparatorChar );
136 }
137 }
138
139 writer.newLine();
140 }
141
142 if ( !classPath.isEmpty() )
143 {
144 writer.write( "--class-path" );
145 writer.newLine();
146 for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
147 {
148 writer.append( it.next() );
149 if ( it.hasNext() )
150 {
151 writer.append( pathSeparatorChar );
152 }
153 }
154
155 writer.newLine();
156 }
157
158 final String moduleName = toModuleName( moduleDescriptor );
159
160 writer.write( "--patch-module" );
161 writer.newLine();
162 writer.append( moduleName )
163 .append( '=' )
164 .append( patchFile.getPath() );
165
166 writer.newLine();
167
168 for ( String pkg : packages )
169 {
170 writer.write( "--add-exports" );
171 writer.newLine();
172 writer.append( moduleName )
173 .append( '/' )
174 .append( pkg )
175 .append( '=' )
176 .append( "ALL-UNNAMED" );
177
178 writer.newLine();
179 }
180
181 writer.write( "--add-modules" );
182 writer.newLine();
183 writer.append( moduleName );
184
185 writer.newLine();
186
187 writer.write( "--add-reads" );
188 writer.newLine();
189 writer.append( moduleName )
190 .append( '=' )
191 .append( "ALL-UNNAMED" );
192
193 writer.newLine();
194
195 writer.write( startClassName );
196
197 writer.newLine();
198 }
199 finally
200 {
201 if ( writer != null )
202 {
203 writer.close();
204 }
205 }
206
207 return surefireArgs;
208 }
209
210 @Nonnull
211 String toModuleName( @Nonnull File moduleDescriptor ) throws IOException
212 {
213 if ( !moduleDescriptor.isFile() )
214 {
215 throw new IOException( "No such Jigsaw module-descriptor exists " + moduleDescriptor.getAbsolutePath() );
216 }
217
218 final StringBuilder sb = new StringBuilder();
219 new ClassReader( new FileInputStream( moduleDescriptor ) ).accept( new ClassVisitor( ASM6 )
220 {
221 @Override
222 public ModuleVisitor visitModule( String name, int access, String version )
223 {
224 sb.setLength( 0 );
225 sb.append( name );
226 return super.visitModule( name, access, version );
227 }
228 }, 0 );
229
230 return sb.toString();
231 }
232 }