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.Classpath;
26 import org.apache.maven.surefire.booter.StartupConfiguration;
27 import org.apache.maven.surefire.booter.SurefireBooterForkException;
28
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.jar.JarEntry;
43 import java.util.jar.JarOutputStream;
44 import java.util.jar.Manifest;
45
46 import static java.nio.file.Files.isDirectory;
47 import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
48
49
50
51
52
53 public final class JarManifestForkConfiguration
54 extends AbstractClasspathForkConfiguration
55 {
56 @SuppressWarnings( "checkstyle:parameternumber" )
57 public JarManifestForkConfiguration( @Nonnull Classpath bootClasspath, @Nonnull File tempDirectory,
58 @Nullable String debugLine, @Nonnull File workingDirectory,
59 @Nonnull Properties modelProperties, @Nullable String argLine,
60 @Nonnull Map<String, String> environmentVariables, boolean debug,
61 int forkCount, boolean reuseForks, @Nonnull Platform pluginPlatform,
62 @Nonnull ConsoleLogger log )
63 {
64 super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
65 environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
66 }
67
68 @Override
69 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
70 @Nonnull String booterThatHasMainMethod,
71 @Nonnull StartupConfiguration config,
72 @Nonnull File dumpLogDirectory )
73 throws SurefireBooterForkException
74 {
75 try
76 {
77 File jar = createJar( toCompleteClasspath( config ), booterThatHasMainMethod, dumpLogDirectory );
78 cli.createArg().setValue( "-jar" );
79 cli.createArg().setValue( escapeToPlatformPath( jar.getAbsolutePath() ) );
80 }
81 catch ( IOException e )
82 {
83 throw new SurefireBooterForkException( "Error creating archive file", e );
84 }
85 }
86
87
88
89
90
91
92
93
94
95
96 @Nonnull
97 private File createJar( @Nonnull List<String> classPath, @Nonnull String startClassName,
98 @Nonnull File dumpLogDirectory )
99 throws IOException
100 {
101 File file = File.createTempFile( "surefirebooter", ".jar", getTempDirectory() );
102 if ( !isDebug() )
103 {
104 file.deleteOnExit();
105 }
106 Path parent = file.getParentFile().toPath();
107 FileOutputStream fos = new FileOutputStream( file );
108 try ( JarOutputStream jos = new JarOutputStream( fos ) )
109 {
110 jos.setLevel( JarOutputStream.STORED );
111 JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
112 jos.putNextEntry( je );
113
114 Manifest man = new Manifest();
115
116
117
118 StringBuilder cp = new StringBuilder();
119 for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
120 {
121 Path classPathElement = Paths.get( it.next() );
122 String uri = toClasspathElementUri( parent, classPathElement, dumpLogDirectory );
123 cp.append( uri );
124 if ( isDirectory( classPathElement ) && !uri.endsWith( "/" ) )
125 {
126 cp.append( '/' );
127 }
128
129 if ( it.hasNext() )
130 {
131 cp.append( ' ' );
132 }
133 }
134
135 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
136 man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
137 man.getMainAttributes().putValue( "Main-Class", startClassName );
138
139 man.write( jos );
140
141 jos.closeEntry();
142 jos.flush();
143
144 return file;
145 }
146 }
147
148 static String relativize( @Nonnull Path parent, @Nonnull Path child )
149 throws IllegalArgumentException
150 {
151 return parent.relativize( child )
152 .toString();
153 }
154
155 static String toAbsoluteUri( @Nonnull Path absolutePath )
156 {
157 return absolutePath.toUri()
158 .toASCIIString();
159 }
160
161 static String toClasspathElementUri( @Nonnull Path parent,
162 @Nonnull Path classPathElement,
163 @Nonnull File dumpLogDirectory )
164 throws IOException
165 {
166 try
167 {
168 String relativeUriPath = relativize( parent, classPathElement )
169 .replace( '\\', '/' );
170
171 return new URI( null, relativeUriPath, null )
172 .toASCIIString();
173 }
174 catch ( IllegalArgumentException e )
175 {
176 String error = "Boot Manifest-JAR contains absolute paths in classpath " + classPathElement;
177 InPluginProcessDumpSingleton.getSingleton()
178 .dumpException( e, error, dumpLogDirectory );
179
180 return toAbsoluteUri( classPathElement );
181 }
182 catch ( URISyntaxException e )
183 {
184
185 throw new IOException( "Could not create a relative path "
186 + classPathElement
187 + " against "
188 + parent, e );
189 }
190 }
191 }