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