Skip to content

Commit

Permalink
AppLoader: Make properties for app loader configurable in pom file
Browse files Browse the repository at this point in the history
Better design.
  • Loading branch information
damienbiggs committed May 14, 2021
1 parent 1e5785e commit 494f418
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 59 deletions.
16 changes: 8 additions & 8 deletions appLoader/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>appLoader</finalName>
<archive>
<manifest>
<mainClass>com.vmware.WorkflowAppLoader</mainClass>
</manifest>
<manifestEntries>
<appMainClass>com.vmware.WorkflowRunner</appMainClass>
<deleteOldReleaseJarPattern>workflow-[\w_]+.jar</deleteOldReleaseJarPattern>
<releaseJarName>workflow-May_14_2021.jar</releaseJarName>
<releaseUrl>https://github.com/vmware/workflowTools/releases/download/May_13_2021/workflow.jar</releaseUrl>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
Expand Down
16 changes: 16 additions & 0 deletions appLoader/src/main/java/com/vmware/AppLauncher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.vmware;

import java.util.List;

/**
* Main class for jar application
*/
public interface AppLauncher {
/**
* Launches application
*
* @param appClassLoader class loader to use for the application jar file.
* @param args command line arguments passed in
*/
void run(ClassLoader appClassLoader, List<String> args);
}
132 changes: 96 additions & 36 deletions appLoader/src/main/java/com/vmware/WorkflowAppLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,35 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;

public class WorkflowAppLoader {
private static final String RELEASE_DATE = "May_13_2021";
private static final String RELEASE_URL = "https://github.com/vmware/workflowTools/releases/download/" + RELEASE_DATE + "/workflow.jar";
private static final String RELEASE_NAME = "workflow-" + RELEASE_DATE + ".jar";

private final String tempDirectory;
private final String releaseDirectory;
private final List<String> argValues;
private final boolean debugLog;
private final File expectedReleaseJar;
private final boolean debugLog, reset;
private final File releaseJar;
private final Map<String, String> manifestAttributes;
private final File testReleaseJar;

public static void main(String[] args) {
WorkflowAppLoader loader = new WorkflowAppLoader(args);
Expand All @@ -32,71 +40,123 @@ public static void main(String[] args) {
}

public WorkflowAppLoader(String[] args) {
this.tempDirectory = getTempDirectory();
this.argValues = Arrays.asList(args);
this.argValues = new ArrayList<>(Arrays.asList(args));
this.debugLog = Stream.of("-d", "--debug", "-t", "--trace").anyMatch(argValues::contains);
this.expectedReleaseJar = new File(tempDirectory + File.separator + RELEASE_NAME);
this.reset = argValues.remove("--reset");
this.manifestAttributes = getManifestAttributes();
this.releaseDirectory = manifestAttributes.containsKey("releaseDirectory")
? manifestAttributes.get("releaseDirectory") : System.getProperty("java.io.tmpdir");
this.releaseJar = new File(this.releaseDirectory + File.separator + manifestAttributes.get("releaseJarName"));
Optional<String> testReleaseJarPath = getArgValue("--test-release-jar");
this.testReleaseJar = testReleaseJarPath.map(File::new).orElse(null);
}

public void executeWorkflowJar() {
if (debugLog) {
System.out.println("Launching workflow jar with args " + argValues);
}
debug("Launching workflow jar with args " + argValues);
try {
URLClassLoader urlClassLoader = new URLClassLoader(
new URL[] {expectedReleaseJar.toURI().toURL()},
this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.vmware.WorkflowRunner", true, urlClassLoader);
Method method = classToLoad.getDeclaredMethod("runWorkflow", ClassLoader.class, List.class);
Object instance = classToLoad.newInstance();
method.invoke(instance, urlClassLoader, argValues);

URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] { releaseJar.toURI().toURL()}, getClass().getClassLoader());
Class<? extends AppLauncher> classToLoad = (Class<? extends AppLauncher>) urlClassLoader.loadClass(manifestAttributes.get("appMainClass"));
AppLauncher launcher = classToLoad.newInstance();
launcher.run(urlClassLoader, argValues);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void downloadJarFileIfNeeded() {
if (debugLog) {
System.out.println("Expected release jar is " + expectedReleaseJar.getPath());
}
if (expectedReleaseJar.exists()) {
if (debugLog) {
System.out.println("Jar file " + expectedReleaseJar.getPath() + " already exists");
}
debug("Expected release jar is " + releaseJar.getPath());
if (releaseJar.exists() && !reset) {
debug("Jar file " + releaseJar.getPath() + " already exists");
return;
}
deleteOldReleasesIfNeeded();
URL releaseURL = createReleaseUrl();
System.out.println("Downloading workflow release jar " + releaseURL.toString() + " to temp directory " + tempDirectory);
info("Downloading workflow release jar " + releaseURL.toString() + " to " + releaseJar.getPath());

try {
ReadableByteChannel readableByteChannel = Channels.newChannel(releaseURL.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(expectedReleaseJar);
FileOutputStream fileOutputStream = new FileOutputStream(releaseJar);
fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
} catch (IOException e) {
throw new RuntimeException(e);
}

}

private void deleteOldReleasesIfNeeded() {
String deleteOldReleasesPattern = manifestAttributes.get("deleteOldReleaseJarPattern");
if (deleteOldReleasesPattern == null) {
debug("Delete old releases pattern not set, skipping deletion of old releases");
}
Pattern deleteJarPattern = Pattern.compile(deleteOldReleasesPattern);
File[] matchingReleases = new File(releaseDirectory).listFiles(file -> deleteJarPattern.matcher(file.getName()).matches());
Arrays.stream(matchingReleases).forEach(release -> {
info("Deleting old release " + release.getPath());
release.delete();
});
}

private URL createReleaseUrl() {
if (testReleaseJar != null) {
info("Using test release file " + testReleaseJar.getPath());
try {
return testReleaseJar.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
URL releaseURL;
try {
releaseURL = URI.create(RELEASE_URL).toURL();
releaseURL = URI.create(manifestAttributes.get("releaseUrl")).toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
return releaseURL;
}

private String getTempDirectory() {
private void debug(String message) {
if (debugLog) {
System.out.println(message);
}
}

private void info(String message) {
System.out.println(message);
}

private Map<String, String> getManifestAttributes() {
Optional<String> jarFilePath = getArgValue("--loader-jar-file");

InputStream manifestInputStream;
if (jarFilePath.isPresent()) {
info("Loading manifest from jar file " + jarFilePath.get());
try {
JarFile jarFile = new JarFile(jarFilePath.get());
ZipEntry manifestEntry = jarFile.getEntry(JarFile.MANIFEST_NAME);
manifestInputStream = jarFile.getInputStream(manifestEntry);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
manifestInputStream = getClass().getClassLoader().getResourceAsStream(JarFile.MANIFEST_NAME);
}

try {
File tempFile = File.createTempFile("sample", "txt");
String directory = tempFile.getParent();
tempFile.delete();
return directory;
Manifest manifest = new Manifest(manifestInputStream);
Attributes mainAttributes = manifest.getMainAttributes();
Set<Object> attributeKeys = mainAttributes.keySet();
Map<String, String> attributeValues = attributeKeys.stream().collect(Collectors.toMap(String::valueOf, key -> mainAttributes.getValue((Attributes.Name) key)));
debug("Manifest Attribute values " + attributeValues);
return attributeValues;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private Optional<String> getArgValue(String argName) {
Optional<String> argValue = argValues.stream().filter(arg -> arg.startsWith(argName + "=")).map(arg -> arg.split("=")[1]).findFirst();
argValues.removeIf(arg -> arg.startsWith(argName + "="));
return argValue;
}
}
2 changes: 1 addition & 1 deletion config/src/main/java/com/vmware/config/WorkflowConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class WorkflowConfig {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss");

public static ClassLoader realClassLoader;
public static ClassLoader appClassLoader;

@SectionConfig
public LoggingConfig loggingConfig;
Expand Down
7 changes: 6 additions & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
<artifactId>serviceApis</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.vmware</groupId>
<artifactId>appLoader</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
Expand Down Expand Up @@ -62,7 +67,7 @@
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>workflow</finalName>
<finalName>workflowTools</finalName>
<archive>
<manifest>
<mainClass>com.vmware.WorkflowRunner</mainClass>
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/java/com/vmware/Workflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ public class Workflow {
private String username = null;
private final List<String> args;

public Workflow(ClassLoader classLoader, List<String> args) {
WorkflowConfig.realClassLoader = classLoader;
public Workflow(ClassLoader appClassLoader, List<String> args) {
WorkflowConfig.appClassLoader = appClassLoader;
this.args = args;
}

Expand Down Expand Up @@ -198,7 +198,7 @@ private ArgumentCompleter createWorkflowCompleter() {
autocompleteList.add(workflow);
}
}
WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.realClassLoader);
WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.appClassLoader);
// ! means that it won't show up if nothing is entered
autocompleteList.addAll(workflowActions.getWorkflowActionClasses()
.stream().map(workflowAction -> "!" + workflowAction.getSimpleName()).collect(Collectors.toList()));
Expand Down Expand Up @@ -232,7 +232,7 @@ public void runWorkflow() {
return;
}

WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.realClassLoader);
WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.appClassLoader);
List<WorkflowAction> actions = workflowActions.determineActions(workflowToRun);
// update history file after all the workflow has been determined to be valid
updateWorkflowHistoryFile();
Expand Down Expand Up @@ -288,7 +288,7 @@ private void outputTotalExecutionTime(Date startingDate) {

private void checkAllActionsCanBeInstantiated(boolean runAllHelperMethods) {
log.info("Checking that each action value in the workflows is valid");
WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.realClassLoader);
WorkflowActions workflowActions = new WorkflowActions(config, WorkflowConfig.appClassLoader);
List<WorkflowAction> actions =
workflowActions.determineActions(StringUtils.join(config.workflows.keySet()));
WorkflowActionValues actionValues = new WorkflowActionValues();
Expand Down
9 changes: 5 additions & 4 deletions core/src/main/java/com/vmware/WorkflowRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@
/**
* Class that starts the workflow tools app.
*/
public class WorkflowRunner {
public class WorkflowRunner implements AppLauncher {

public static void main(String[] args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
new WorkflowRunner().runWorkflow(classLoader, Arrays.asList(args));
new WorkflowRunner().run(classLoader, Arrays.asList(args));
}

public void runWorkflow(ClassLoader classLoader, List<String> args) {
@Override
public void run(ClassLoader appClassLoader, List<String> args) {
LogManager.getLogManager().reset();
java.util.logging.Logger globalLogger = java.util.logging.Logger.getLogger("com.vmware");
globalLogger.addHandler(createHandler());

Workflow workflow = new Workflow(classLoader, args);
Workflow workflow = new Workflow(appClassLoader, args);
workflow.runWorkflow();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void process() {

private Map<String, List<Class<? extends BaseAction>>> generateClassMap() {
Map<String, List<Class<? extends BaseAction>>> classes = new TreeMap<String, List<Class<? extends BaseAction>>>();
List<Class<? extends BaseAction>> workflowActions = new WorkflowActions(config, WorkflowConfig.realClassLoader).getWorkflowActionClasses();
List<Class<? extends BaseAction>> workflowActions = new WorkflowActions(config, WorkflowConfig.appClassLoader).getWorkflowActionClasses();
for (Class<? extends BaseAction> action : workflowActions) {
String[] pieces = action.getName().split("\\.");
String packageName = pieces[pieces.length - 2];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.SortedSet;
import java.util.TreeSet;

import com.vmware.Workflow;
import com.vmware.action.BaseAction;
import com.vmware.config.ReplacementVariables;
import com.vmware.config.WorkflowAction;
Expand Down Expand Up @@ -41,7 +40,7 @@ public class ConfigValuesCompleter extends ImprovedStringsCompleter implements C
public ConfigValuesCompleter(WorkflowConfig config) {
this.configMappings = new ConfigMappings();
this.config = config;
this.workflowActions = new WorkflowActions(config, WorkflowConfig.realClassLoader).getWorkflowActionClasses();
this.workflowActions = new WorkflowActions(config, WorkflowConfig.appClassLoader).getWorkflowActionClasses();
super.values.addAll(configMappings.allConfigValues());
}

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<modelVersion>4.0.0</modelVersion>
<version>1.0</version>
<modules>
<module>appLoader</module>
<module>core</module>
<module>serviceApis</module>
<module>config</module>
<module>utils</module>
<module>appLoader</module>
</modules>

<build>
Expand Down
Binary file modified workflow.jar
Binary file not shown.

0 comments on commit 494f418

Please sign in to comment.