Skip to content

Commit 04d19cd

Browse files
committed
Fix execution of external commands using custom resolver
This PR also removes use of the finally deprecated class java.lang.SecurityManager: The MainClassProcessLauncher now spawns a new standalone JVM process, thus addressing the issue of capturing System.exit() calls
1 parent 99e587c commit 04d19cd

File tree

11 files changed

+188
-196
lines changed

11 files changed

+188
-196
lines changed

core/esmf-aspect-meta-model-java/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@
102102
</executions>
103103
</plugin>
104104

105+
<plugin>
106+
<groupId>pl.project13.maven</groupId>
107+
<artifactId>git-commit-id-plugin</artifactId>
108+
<executions>
109+
<execution>
110+
<id>get-the-git-infos</id>
111+
<phase>initialize</phase>
112+
<goals>
113+
<goal>revision</goal>
114+
</goals>
115+
</execution>
116+
</executions>
117+
<configuration>
118+
<dotGitDirectory>${project.basedir}/../.git</dotGitDirectory>
119+
<generateGitPropertiesFile>true</generateGitPropertiesFile>
120+
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties
121+
</generateGitPropertiesFilename>
122+
</configuration>
123+
</plugin>
124+
105125
<plugin>
106126
<groupId>org.codehaus.mojo</groupId>
107127
<artifactId>build-helper-maven-plugin</artifactId>

core/esmf-aspect-meta-model-java/src-buildtime/main/java/org/eclipse/esmf/buildtime/BuildtimeCodeGenerator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ private String generateClassContent() {
8282
final Matcher singleVariableMatcher = replaceSingleVariablePattern.matcher( line );
8383
if ( singleVariableMatcher.matches() ) {
8484
final String variableName = singleVariableMatcher.group( 1 );
85-
lineToAdd = line.replace( "${" + variableName + "}", interpolateVariable( variableName ) );
85+
final String targetValue = interpolateVariable( variableName );
86+
if ( targetValue == null ) {
87+
throw new RuntimeException( "Build-time code generation failed: Value for " + variableName + " could not be determined" );
88+
}
89+
lineToAdd = line.replace( "${" + variableName + "}", targetValue );
8690
} else {
8791
lineToAdd = line;
8892
}

core/esmf-aspect-meta-model-java/src-buildtime/main/java/org/eclipse/esmf/buildtime/GenerateVersionInfo.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,32 @@
1515

1616
import java.io.InputStream;
1717
import java.nio.file.Path;
18+
import java.text.SimpleDateFormat;
19+
import java.util.Date;
1820
import java.util.Properties;
1921

2022
/**
2123
* Generates the static version info class
2224
*/
2325
public class GenerateVersionInfo extends BuildtimeCodeGenerator {
24-
private final Properties properties;
26+
private final Properties appProperties;
27+
private final Properties gitProperties;
2528

2629
protected GenerateVersionInfo( final Path srcBuildTimePath, final Path srcGenPath ) {
2730
super( srcBuildTimePath, srcGenPath, "VersionInfo", "org.eclipse.esmf.aspectmodel" );
2831

29-
try ( final InputStream stream = GenerateVersionInfo.class.getClassLoader().getResourceAsStream( "app.properties" ) ) {
32+
appProperties = loadProperties( "app.properties" );
33+
gitProperties = loadProperties( "git.properties" );
34+
}
35+
36+
private Properties loadProperties( final String filename ) {
37+
try ( final InputStream stream = GenerateVersionInfo.class.getClassLoader().getResourceAsStream( filename ) ) {
3038
if ( stream == null ) {
3139
throw new RuntimeException();
3240
}
33-
properties = new Properties();
41+
final Properties properties = new Properties();
3442
properties.load( stream );
43+
return properties;
3544
} catch ( final Exception exception ) {
3645
throw new RuntimeException( "Could not load app.properties" );
3746
}
@@ -40,8 +49,10 @@ protected GenerateVersionInfo( final Path srcBuildTimePath, final Path srcGenPat
4049
@Override
4150
protected String interpolateVariable( final String variableName ) {
4251
return switch ( variableName ) {
43-
case "esmfSdkVersion" -> properties.getProperty( "project-version" );
44-
case "aspectMetaModelVersion" -> properties.getProperty( "aspect-meta-model-version" );
52+
case "esmfSdkVersion" -> appProperties.getProperty( "project-version" );
53+
case "aspectMetaModelVersion" -> appProperties.getProperty( "aspect-meta-model-version" );
54+
case "buildDate" -> new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( new Date() );
55+
case "commitId" -> gitProperties.getProperty( "git.commit.id" );
4556
case "generator" -> getClass().getCanonicalName();
4657
default -> throw new RuntimeException( "Unexpected variable in template: " + variableName );
4758
};

core/esmf-aspect-meta-model-java/src-buildtime/main/java/org/eclipse/esmf/buildtime/template/VersionInfo.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@
1919
public class VersionInfo {
2020
public static final String ESMF_SDK_VERSION = "${esmfSdkVersion}";
2121
public static final String ASPECT_META_MODEL_VERSION = "${aspectMetaModelVersion}";
22+
public static final String BUILD_DATE = "${buildDate}";
23+
public static final String COMMIT_ID = "${commitId}";
2224
}

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/CommandExecutor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ public class CommandExecutor {
3030
public static String executeCommand( final String command ) {
3131
final List<String> parts = Arrays.asList( command.split( " " ) );
3232
final String executableOrJar = parts.get( 0 );
33-
final ProcessLauncher processLauncher = executableOrJar.toLowerCase().endsWith( ".jar" )
33+
final ProcessLauncher<Process> processLauncher = executableOrJar.toLowerCase().endsWith( ".jar" )
3434
? new ExecutableJarLauncher( new File( executableOrJar ) )
3535
: new BinaryLauncher( new File( executableOrJar ) );
3636
final List<String> arguments = parts.size() == 1
3737
? List.of()
3838
: parts.subList( 1, parts.size() );
39-
final ProcessLauncher.ExecutionContext context = new ProcessLauncher.ExecutionContext(
39+
final ProcessLauncher<Process>.ExecutionContext context = processLauncher.new ExecutionContext(
4040
arguments, Optional.empty(), new File( System.getProperty( "user.dir" ) ) );
4141
final ProcessLauncher.ExecutionResult result = processLauncher.apply( context );
4242

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/process/OsProcessLauncher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
/**
3838
* A {@link ProcessLauncher} that spawns an external operating system process
3939
*/
40-
public class OsProcessLauncher extends ProcessLauncher {
40+
public class OsProcessLauncher extends ProcessLauncher<Process> {
4141
private static final Logger LOG = LoggerFactory.getLogger( OsProcessLauncher.class );
4242
private final List<String> commandWithArguments;
4343

@@ -74,6 +74,7 @@ public ExecutionResult apply( final ExecutionContext context ) {
7474
final Future<ByteArrayOutputStream> stderrFuture = executor.submit( new StreamAccumulator( process.getErrorStream() ) );
7575

7676
LOG.debug( "Waiting for process {} to finish", process.pid() );
77+
context.processConsumer().ifPresent( consumer -> consumer.accept( process ) );
7778
process.waitFor();
7879

7980
final byte[] stdoutRaw;

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/process/ProcessLauncher.java

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,22 @@
1616
import java.io.File;
1717
import java.util.Arrays;
1818
import java.util.List;
19+
import java.util.Objects;
1920
import java.util.Optional;
21+
import java.util.function.Consumer;
2022
import java.util.function.Function;
2123

2224
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ProcessExecutionException;
2325

2426
/**
2527
* This class abstracts running a "process", i.e. running a program by providing its arguments, optional stdin and its working directory,
2628
* and representing the output using exit status and stdout and stderr streams.
29+
*
30+
* @param <T> the type of "process" that is created (such as {@link Process} or {@link Thread})
2731
*/
28-
public abstract class ProcessLauncher implements Function<ProcessLauncher.ExecutionContext, ProcessLauncher.ExecutionResult> {
32+
public abstract class ProcessLauncher<T> implements Function<ProcessLauncher<T>.ExecutionContext, ProcessLauncher.ExecutionResult> {
2933
/**
30-
* Convenience constructor that to apply the launcher to a list of arguments with empty stdin and default working directory
34+
* Convenience method to apply the launcher to a list of arguments with empty stdin and default working directory
3135
*/
3236
public ExecutionResult apply( final String... arguments ) {
3337
return apply( new ExecutionContext( Arrays.asList( arguments ), Optional.empty(), new File( System.getProperty( "user.dir" ) ) ) );
@@ -37,20 +41,95 @@ public ExecutionResult runAndExpectSuccess( final String... arguments ) {
3741
final ExecutionResult result = apply( arguments );
3842
if ( result.exitStatus() != 0 ) {
3943
throw new ProcessExecutionException(
40-
"Execution failed (status " + result.exitStatus + "):\n"
41-
+ "stdout:"
42-
+ result.stdout()
43-
+ "\n"
44-
+ "stderr:"
45-
+ result.stderr()
46-
);
44+
"Execution failed (status %d):\nstdout:%s\nstderr:%s".formatted( result.exitStatus, result.stdout(), result.stderr() ) );
4745
}
4846
return result;
4947
}
5048

51-
public record ExecutionContext( List<String> arguments, Optional<byte[]> stdin, File workingDirectory ) {
49+
public ExecutionResult apply( final List<String> arguments, final Optional<byte[]> stdin, final File workingDirectory,
50+
final Consumer<T> processConsumer ) {
51+
return apply( new ExecutionContext( arguments, stdin, workingDirectory, Optional.of( processConsumer ) ) );
52+
}
53+
54+
public ExecutionResult apply( final List<String> arguments, final Optional<byte[]> stdin, final File workingDirectory ) {
55+
return apply( new ExecutionContext( arguments, stdin, workingDirectory ) );
5256
}
5357

54-
public record ExecutionResult( int exitStatus, String stdout, String stderr, byte[] stdoutRaw, byte[] stderrRaw ) {
58+
/**
59+
* The execution context determines inputs for the new process: arguments, stdin and working directory
60+
*/
61+
public final class ExecutionContext {
62+
private final List<String> arguments;
63+
private final Optional<byte[]> stdin;
64+
private final File workingDirectory;
65+
private final Optional<Consumer<T>> processConsumer;
66+
67+
public ExecutionContext( final List<String> arguments, final Optional<byte[]> stdin, final File workingDirectory,
68+
final Optional<Consumer<T>> processConsumer ) {
69+
this.arguments = arguments;
70+
this.stdin = stdin;
71+
this.workingDirectory = workingDirectory;
72+
this.processConsumer = processConsumer;
73+
}
74+
75+
public ExecutionContext( final List<String> arguments, final Optional<byte[]> stdin, final File workingDirectory ) {
76+
this( arguments, stdin, workingDirectory, Optional.empty() );
77+
}
78+
79+
public List<String> arguments() {
80+
return arguments;
81+
}
82+
83+
public Optional<byte[]> stdin() {
84+
return stdin;
85+
}
86+
87+
public File workingDirectory() {
88+
return workingDirectory;
89+
}
90+
91+
public Optional<Consumer<T>> processConsumer() {
92+
return processConsumer;
93+
}
94+
95+
@Override
96+
public boolean equals( final Object obj ) {
97+
if ( obj == this ) {
98+
return true;
99+
}
100+
if ( obj == null || obj.getClass() != getClass() ) {
101+
return false;
102+
}
103+
@SuppressWarnings( "unchecked" ) final ExecutionContext that = (ExecutionContext) obj;
104+
return Objects.equals( arguments, that.arguments ) &&
105+
Objects.equals( stdin, that.stdin ) &&
106+
Objects.equals( workingDirectory, that.workingDirectory ) &&
107+
Objects.equals( processConsumer, that.processConsumer );
108+
}
109+
110+
@Override
111+
public int hashCode() {
112+
return Objects.hash( arguments, stdin, workingDirectory, processConsumer );
113+
}
114+
115+
@Override
116+
public String toString() {
117+
return "ExecutionContext[" +
118+
"arguments=" + arguments + ", " +
119+
"stdin=" + stdin + ", " +
120+
"workingDirectory=" + workingDirectory + ", " +
121+
"processConsumer=" + processConsumer + ']';
122+
}
55123
}
124+
125+
/**
126+
* The execution result provides the process' status and stderr and stdout results
127+
*/
128+
public record ExecutionResult(
129+
int exitStatus,
130+
String stdout,
131+
String stderr,
132+
byte[] stdoutRaw,
133+
byte[] stderrRaw
134+
) {}
56135
}

tools/samm-cli/pom.xml

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@
169169
</systemPropertyVariables>
170170
<forkCount>1</forkCount>
171171
<reuseForks>false</reuseForks>
172-
<argLine>-Xmx6g -Djava.security.manager=allow</argLine>
172+
<argLine>-Xmx6g</argLine>
173173
<skip>${skip.maven.surefire}</skip>
174174
<reportsDirectory>${project.build.directory}/${testreports.surefire}</reportsDirectory>
175175
<!-- Do NOT wildcard include *Test.java, because then we would also run the SammCliIntegrationTest -->
@@ -292,26 +292,6 @@
292292
</executions>
293293
</plugin>
294294

295-
<plugin>
296-
<groupId>pl.project13.maven</groupId>
297-
<artifactId>git-commit-id-plugin</artifactId>
298-
<executions>
299-
<execution>
300-
<id>get-the-git-infos</id>
301-
<phase>compile</phase>
302-
<goals>
303-
<goal>revision</goal>
304-
</goals>
305-
</execution>
306-
</executions>
307-
<configuration>
308-
<dotGitDirectory>${project.basedir}/../.git</dotGitDirectory>
309-
<generateGitPropertiesFile>true</generateGitPropertiesFile>
310-
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties
311-
</generateGitPropertiesFilename>
312-
</configuration>
313-
</plugin>
314-
315295
<plugin>
316296
<groupId>org.apache.maven.plugins</groupId>
317297
<artifactId>maven-dependency-plugin</artifactId>
@@ -374,6 +354,7 @@
374354
<executableJar>${project.build.directory}/${project.artifactId}-${project.version}.jar
375355
</executableJar>
376356
<binary>${project.build.directory}/${binary-name}</binary>
357+
<nativeConfigPath>${native-config-path}</nativeConfigPath>
377358
<forkCount>1</forkCount>
378359
<reuseForks>true</reuseForks>
379360
<argLine>-Xmx4096m</argLine>

tools/samm-cli/src/main/java/org/eclipse/esmf/SammCli.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
1616

1717
import java.io.File;
18-
import java.io.IOException;
19-
import java.io.InputStream;
2018
import java.util.ArrayList;
2119
import java.util.Arrays;
2220
import java.util.Collections;
2321
import java.util.List;
24-
import java.util.Properties;
2522
import java.util.stream.IntStream;
2623

2724
import org.eclipse.esmf.aas.AasCommand;
@@ -32,6 +29,7 @@
3229
import org.eclipse.esmf.aspect.AspectPrettyPrintCommand;
3330
import org.eclipse.esmf.aspect.AspectToCommand;
3431
import org.eclipse.esmf.aspect.to.AspectToSvgCommand;
32+
import org.eclipse.esmf.aspectmodel.VersionInfo;
3533
import org.eclipse.esmf.exception.CommandException;
3634
import org.eclipse.esmf.exception.SubCommandException;
3735
import org.eclipse.esmf.namespacepackage.PackageCommand;
@@ -245,27 +243,14 @@ protected String format( final String string ) {
245243
return commandLine.getColorScheme().ansi().string( string );
246244
}
247245

248-
private Properties loadProperties( final String filename ) {
249-
final Properties properties = new Properties();
250-
final InputStream propertiesResource = SammCli.class.getClassLoader().getResourceAsStream( filename );
251-
try {
252-
properties.load( propertiesResource );
253-
} catch ( final IOException exception ) {
254-
throw new RuntimeException( "Failed to load Properties: " + filename );
255-
}
256-
return properties;
257-
}
258-
259246
@Override
260247
public void run() {
261248
if ( version ) {
262-
final Properties applicationProperties = loadProperties( "application.properties" );
263-
final Properties gitProperties = loadProperties( "git.properties" );
264-
System.out.printf( "samm-cli - %s%nVersion: %s%nBuild date: %s%nGit commit: %s%n",
265-
applicationProperties.get( "application.name" ),
266-
applicationProperties.get( "version" ),
267-
applicationProperties.get( "build.date" ),
268-
gitProperties.get( "git.commit.id" ) );
249+
System.out.println(
250+
"samm-cli - Semantic Aspect Meta Model Command Line Tool\n"
251+
+ "Version: " + VersionInfo.ESMF_SDK_VERSION + "\n"
252+
+ "Build date: " + VersionInfo.BUILD_DATE + "\n"
253+
+ "Git commit: " + VersionInfo.COMMIT_ID );
269254
System.exit( 0 );
270255
}
271256
System.out.println( commandLine.getHelp().fullSynopsis() );

0 commit comments

Comments
 (0)