Skip to content

Commit

Permalink
Initial working version
Browse files Browse the repository at this point in the history
  • Loading branch information
tresf committed Oct 17, 2023
1 parent 3536715 commit 5b5f342
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 88 deletions.
1 change: 1 addition & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<include name="**/**"/>
<exclude name="**/jlink/**"/>
<exclude name="**/javafx-*/**"/>
<exclude name="**/provision/**"/>
</fileset>
</delete>
</target>
Expand Down
4 changes: 2 additions & 2 deletions src/qz/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ public static void main(String ... args) {

// Invoke any provisioning steps that are phase=startup
try {
Provisioner provisioner = new Provisioner();
Provisioner provisioner = new Provisioner(SystemUtilities.getJarParentPath().resolve("provision"));
provisioner.invoke(Step.Phase.STARTUP);
} catch(Exception e) {
log.warn("An error occurred provisioning \"phase\": \"startup\" entries");
log.warn("An error occurred provisioning \"phase\": \"startup\" entries", e);
}

try {
Expand Down
146 changes: 109 additions & 37 deletions src/qz/build/provision/Provisionee.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package qz.build.provision;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import qz.installer.provision.Step;
import qz.utils.SystemUtilities;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static qz.installer.Installer.*;
import java.nio.file.Path;

public class Provisionee {
private JSONObject jsonStep;
protected static final Logger log = LogManager.getLogger(Provisionee.class);

public static final Path BUILD_PROVISION_FOLDER = SystemUtilities.getJarParentPath().resolve("provision");
public static final File BUILD_PROVISION_FILE = BUILD_PROVISION_FOLDER.resolve("provision.json").toFile();

private JSONArray jsonSteps;

/**
* Parses input to create a <code>Provisionee</code> object, a JSON formatted setting or bundled resource useful for
Expand All @@ -28,59 +35,111 @@ public class Provisionee {
* @param varArgs SCRIPT or INSTALL only, will honor spaces when invoked
*/
public Provisionee(String type, String phase, String os, String data, String args, String description, String ... varArgs) throws IOException, JSONException {
if(!PROVISION_FOLDER.toFile().isDirectory() && !PROVISION_FOLDER.toFile().mkdirs()) {
throw new IOException("Could not create provision destination:" + PROVISION_FOLDER);
}
createProvisionDirectory(false);
jsonSteps = new JSONArray();

// Wrap into JSON so that we can save it
jsonStep = new JSONObject();
put("description", description);
put("type", type);
put("phase", phase);
put("os", os);
put("data", data);
put("args", args);
put("arg%d", varArgs);

// Step will perform basic parsing/sanity checks
Step step = Step.parse(jsonStep);
JSONObject jsonStep = new JSONObject();
put(jsonStep, "description", description);
put(jsonStep, "type", type);
put(jsonStep, "phase", phase);
put(jsonStep, "os", os);
put(jsonStep, "data", data);
put(jsonStep, "args", args);
put(jsonStep, "arg%d", varArgs);

processStep(jsonStep);
}

public Provisionee(File jsonFile) throws IOException, JSONException {
createProvisionDirectory(true);
jsonSteps = new JSONArray();

// Copy resource to provision folder
if(step.getType() == Step.Type.INSTALLER || step.getType() == Step.Type.SCRIPT) {
File src = new File(data);
File dest = PROVISION_FOLDER.resolve(src.getName()).toFile();
FileUtils.copyFile(src, dest);
if(dest.exists()) {
jsonStep.remove("data");
jsonStep.put("data", PROVISION_FOLDER.relativize(dest.toPath()));
String jsonData = FileUtils.readFileToString(jsonFile, StandardCharsets.UTF_8);
JSONArray pendingSteps = new JSONArray(jsonData);

// Cycle through so that each Step can be individually processed
for(int i = 0; i < pendingSteps.length(); i++) {
JSONObject jsonStep = pendingSteps.getJSONObject(i);
try {
processStep(jsonStep);
} catch(Exception e) {
log.warn("Skipping step {}", jsonStep, e);
}
}

}

public JSONObject getJson() {
return jsonStep;
public JSONArray getJson() {
return jsonSteps;
}

/**
* Construct as a Step to perform basic parsing/sanity checks
* Copy resources (if needed) to provisioning directory
*/
private void processStep(JSONObject jsonStep) throws JSONException, IOException {
Step step = Step.parse(jsonStep);
if(saveResources(jsonStep, step)) {
jsonSteps.put(jsonStep);
} else {
log.warn("Skipping step. Resources could not be saved {}", step);
}
}

/**
* Save any resources files required for INSTALL and SCRIPT steps to provision folder
*/
public boolean saveResources(JSONObject jsonStep, Step step) throws IOException, JSONException {
switch(step.getType()) {
case CERT:
case SCRIPT:
case INSTALLER:
File src = new File(step.getData());
String fileName = src.getName();
if(fileName.equals(BUILD_PROVISION_FILE.getName())) {
throw new IOException("Skipping step. Resource name conflicts with provision file " + fileName);
}
File dest = BUILD_PROVISION_FOLDER.resolve(fileName).toFile();
FileUtils.copyFile(src, dest);
if(dest.exists()) {
jsonStep.remove("data");
jsonStep.put("data", BUILD_PROVISION_FOLDER.relativize(dest.toPath()));
} else {
return false;
}
break;
default:
}
return true;
}

/**
* Appends the JSONObject to the end of the provisionFile
*/
public boolean saveJson() throws IOException, JSONException {
JSONArray jsonSteps;
if(PROVISION_FILE.exists()) {
String jsonData = FileUtils.readFileToString(PROVISION_FILE, StandardCharsets.UTF_8);
jsonSteps = new JSONArray(jsonData);
public boolean saveJson(boolean overwrite) throws IOException, JSONException {
// Read existing JSON file if exists
JSONArray mergeSteps;
if(!overwrite && BUILD_PROVISION_FILE.exists()) {
String jsonData = FileUtils.readFileToString(BUILD_PROVISION_FILE, StandardCharsets.UTF_8);
mergeSteps = new JSONArray(jsonData);
} else {
jsonSteps = new JSONArray();
mergeSteps = new JSONArray();
}
jsonSteps.put(jsonStep);
FileUtils.writeStringToFile(PROVISION_FILE, jsonSteps.toString(3), StandardCharsets.UTF_8);

// Merge in new steps
for(int i = 0; i < jsonSteps.length(); i++) {
mergeSteps.put(jsonSteps.getJSONObject(i));
}

FileUtils.writeStringToFile(BUILD_PROVISION_FILE, mergeSteps.toString(3), StandardCharsets.UTF_8);
return true;
}

/**
* Convenience method for adding a name/value pair into the JSONObject
*/
private void put(String name, String val) throws JSONException {
private static void put(JSONObject jsonStep, String name, String val) throws JSONException {
if(val != null && !val.isEmpty()) {
jsonStep.put(name, val);
}
Expand All @@ -90,10 +149,23 @@ private void put(String name, String val) throws JSONException {
* Convenience method for adding consecutive patterned value pairs into the JSONObject
* e.g. --arg1 "foo" --arg2 "bar"
*/
private void put(String pattern, String ... varArgs) throws JSONException {
private static void put(JSONObject jsonStep, String pattern, String ... varArgs) throws JSONException {
int argCounter = 0;
for(String arg : varArgs) {
jsonStep.put(String.format(pattern, ++argCounter), arg);
}
}

private static void createProvisionDirectory(boolean cleanDirectory) throws IOException {
if(cleanDirectory) {
FileUtils.deleteDirectory(BUILD_PROVISION_FOLDER.toFile());
}
if(BUILD_PROVISION_FOLDER.toFile().isDirectory()) {
return;
}
if(BUILD_PROVISION_FOLDER.toFile().mkdirs()) {
return;
}
throw new IOException("Could not create provision destination:" + BUILD_PROVISION_FOLDER);
}
}
22 changes: 12 additions & 10 deletions src/qz/installer/Installer.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ public abstract class Installer {
public static boolean IS_SILENT = "1".equals(System.getenv(DATA_DIR + "_silent"));
public static String JRE_LOCATION = SystemUtilities.isMac() ? "Contents/PlugIns/Java.runtime/Contents/Home" : "runtime";

public static final Path PROVISION_FOLDER = SystemUtilities.getJarParentPath().resolve("provision");
public static final File PROVISION_FILE = PROVISION_FOLDER.resolve("provision.json").toFile();

public enum PrivilegeLevel {
USER,
SYSTEM
Expand Down Expand Up @@ -106,7 +103,7 @@ public static void install() throws Exception {
.addAppLauncher()
.addStartupEntry()
.addSystemSettings()
.addProvisioning();
.addProvisioning(Step.Phase.INSTALL);
}

public static void uninstall() {
Expand Down Expand Up @@ -285,6 +282,9 @@ public CertificateManager certGen(boolean forceNew, String... hostNames) throws
log.error("Something went wrong obtaining the certificate. HTTPS will fail.", e);
}

// Add provisioning steps that come after certgen
addProvisioning(Step.Phase.CERTGEN);

return certificateManager;
}

Expand Down Expand Up @@ -322,22 +322,24 @@ public Installer addUserSettings() {
return instance;
}

public Installer addProvisioning() {
public Installer addProvisioning(Step.Phase phase) {
try {
Provisioner provisioner = new Provisioner();
provisioner.invoke(Step.Phase.INSTALL);
Path provisionPath = Paths.get(getDestination()).resolve("provision");
Provisioner provisioner = new Provisioner(provisionPath);
provisioner.invoke(phase);
} catch(Exception e) {
log.warn("An error occurred provisioning \"phase\": \"install\" entries");
log.warn("An error occurred provisioning \"phase\": \"install\" entries", e);
}
return this;
}

public Installer removeProvisioning() {
try {
Provisioner provisioner = new Provisioner();
Path provisionPath = Paths.get(getDestination()).resolve("provision");
Provisioner provisioner = new Provisioner(provisionPath);
provisioner.invoke(Step.Phase.UNINSTALL);
} catch(Exception e) {
log.warn("An error occurred provisioning \"phase\": \"uninstall\" entries");
log.warn("An error occurred provisioning \"phase\": \"uninstall\" entries", e);
}
return this;
}
Expand Down
6 changes: 3 additions & 3 deletions src/qz/installer/provision/Provisioner.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import qz.installer.Installer;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class Provisioner {
protected static final Logger log = LogManager.getLogger(Provisioner.class);
private ArrayList<Step> steps;

public Provisioner() throws IOException, JSONException {
this(Installer.PROVISION_FOLDER, Installer.PROVISION_FILE);
public Provisioner(Path provisionFolder) throws IOException, JSONException {
this(provisionFolder, provisionFolder.resolve("provision.json").toFile());
}

public Provisioner(Class relativeClass, InputStream in) throws IOException, JSONException {
Expand Down
Loading

0 comments on commit 5b5f342

Please sign in to comment.