mirror of
https://github.com/JHUAPL/DistMaker.git
synced 2026-01-07 21:54:07 -05:00
Initial changes needed to allow DistMaker to support Java 9.
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
lib/commons-compress-1.15-sources.jar
Normal file
BIN
lib/commons-compress-1.15-sources.jar
Normal file
Binary file not shown.
BIN
lib/commons-compress-1.15.jar
Normal file
BIN
lib/commons-compress-1.15.jar
Normal file
Binary file not shown.
BIN
lib/glum-src.jar
BIN
lib/glum-src.jar
Binary file not shown.
BIN
lib/glum.jar
BIN
lib/glum.jar
Binary file not shown.
Binary file not shown.
25
src/distMaker/DistApp.java
Normal file
25
src/distMaker/DistApp.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package distMaker;
|
||||
|
||||
import distMaker.utils.PlainVersion;
|
||||
import distMaker.utils.Version;
|
||||
|
||||
/**
|
||||
* Provides main entry point.
|
||||
* <P>
|
||||
* Currently this will just print the library name and the version. This is used during the build process for making
|
||||
* Distmaker releases.
|
||||
*/
|
||||
public class DistApp
|
||||
{
|
||||
/** The DistMaker version is defined here. */
|
||||
public static final Version version = new PlainVersion(0, 48, 0);
|
||||
|
||||
/**
|
||||
* Main entry point that will print out the version of DistMaker to stdout.
|
||||
*/
|
||||
public static void main(String[] aArgArr)
|
||||
{
|
||||
System.out.println("DistMaker " + DistApp.version);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,12 @@
|
||||
package distMaker;
|
||||
|
||||
import glum.gui.panel.generic.MessagePanel;
|
||||
import glum.gui.panel.generic.PromptPanel;
|
||||
import glum.gui.panel.task.FullTaskPanel;
|
||||
import glum.io.IoUtil;
|
||||
import glum.net.Credential;
|
||||
import glum.reflect.FunctionRunnable;
|
||||
import glum.task.*;
|
||||
import glum.unit.DateUnit;
|
||||
import glum.util.ThreadUtil;
|
||||
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
|
||||
@@ -30,12 +21,19 @@ import distMaker.gui.PickReleasePanel;
|
||||
import distMaker.jre.*;
|
||||
import distMaker.node.*;
|
||||
import distMaker.platform.PlatformUtils;
|
||||
import glum.gui.panel.generic.MessagePanel;
|
||||
import glum.gui.panel.generic.PromptPanel;
|
||||
import glum.gui.panel.task.FullTaskPanel;
|
||||
import glum.io.IoUtil;
|
||||
import glum.net.Credential;
|
||||
import glum.task.*;
|
||||
import glum.unit.DateUnit;
|
||||
import glum.util.ThreadUtil;
|
||||
|
||||
public class DistMakerEngine
|
||||
{
|
||||
// Constants
|
||||
private final String NonDistmakerAppMsg = "This application does not appear to be a properly configured DistMaker application.\n\n"
|
||||
+ "Please check installation configuration.";
|
||||
private final String NonDistmakerAppMsg = "This application does not appear to be a properly configured DistMaker application.\n\n" + "Please check installation configuration.";
|
||||
|
||||
// State vars
|
||||
private URL updateSiteUrl;
|
||||
@@ -100,7 +98,8 @@ public class DistMakerEngine
|
||||
taskPanel.setVisible(true);
|
||||
|
||||
// Launch the actual checking of updates in a separate worker thread
|
||||
ThreadUtil.launchRunnable(new FunctionRunnable(this, "checkForUpdatesWorker", taskPanel, listener), "thread-checkForUpdates");
|
||||
Runnable tmpRunnable = () -> checkForUpdatesWorker(taskPanel, listener);
|
||||
ThreadUtil.launchRunnable(tmpRunnable, "thread-checkForUpdates");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +149,8 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets in the credentials used to access the update site. If either argument is null, then the credentials will be cleared out.
|
||||
* Sets in the credentials used to access the update site. If either argument is null, then the credentials will be
|
||||
* cleared out.
|
||||
*/
|
||||
public void setCredentials(String aUsername, char[] aPassword)
|
||||
{
|
||||
@@ -243,7 +243,7 @@ public class DistMakerEngine
|
||||
|
||||
// Form the PickReleasePanel
|
||||
pickVersionPanel = new PickReleasePanel(parentFrame, currRelease);
|
||||
pickVersionPanel.setSize(550, 500); // 320, 350);
|
||||
pickVersionPanel.setSize(550, 500);
|
||||
|
||||
// Notify the user of (any) update results
|
||||
showUpdateResults();
|
||||
@@ -254,7 +254,6 @@ public class DistMakerEngine
|
||||
* <P>
|
||||
* This method will be called via reflection.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void checkForUpdatesWorker(FullTaskPanel aTask, UpdateCheckListener listener)
|
||||
{
|
||||
List<AppRelease> fullList;
|
||||
@@ -308,10 +307,8 @@ public class DistMakerEngine
|
||||
aTask.infoAppendln("Please select the release to install...");
|
||||
try
|
||||
{
|
||||
FunctionRunnable aFuncRunnable;
|
||||
|
||||
aFuncRunnable = new FunctionRunnable(this, "queryUserForInput", aTask, deltaPath, fullList);
|
||||
SwingUtilities.invokeAndWait(aFuncRunnable);
|
||||
Runnable tmpRunnable = () -> queryUserForInput(aTask, deltaPath, fullList);
|
||||
SwingUtilities.invokeAndWait(tmpRunnable);
|
||||
}
|
||||
catch(Exception aExp)
|
||||
{
|
||||
@@ -348,7 +345,19 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
// Download the release
|
||||
isPass = downloadAppRelease(aTask, chosenItem, deltaPath);
|
||||
try
|
||||
{
|
||||
isPass = downloadAppRelease(aTask, chosenItem, deltaPath);
|
||||
}
|
||||
catch(Throwable aThrowable)
|
||||
{
|
||||
IoUtil.deleteDirectory(deltaPath);
|
||||
aTask.infoAppendln("An error occurred while trying to perform an update.");
|
||||
aTask.infoAppendln("Application update aborted.");
|
||||
aTask.infoAppendln("\nStackTrace:\n" + ThreadUtil.getStackTraceClassic(aThrowable));
|
||||
aTask.abort();
|
||||
return;
|
||||
}
|
||||
if (isPass == false || aTask.isActive() == false)
|
||||
{
|
||||
IoUtil.deleteDirectory(deltaPath);
|
||||
@@ -368,12 +377,9 @@ public class DistMakerEngine
|
||||
*/
|
||||
private void displayNotice(String aMsg)
|
||||
{
|
||||
Runnable silentRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
; // Nothing to do
|
||||
}
|
||||
Runnable silentRunnable = () ->
|
||||
{
|
||||
; // Nothing to do
|
||||
};
|
||||
|
||||
// Delegate to displayNoticeAndExecute
|
||||
@@ -395,7 +401,8 @@ public class DistMakerEngine
|
||||
// If the parentFrame is not visible then execute the code once it is made visible
|
||||
if (parentFrame.isVisible() == false)
|
||||
{
|
||||
parentFrame.addComponentListener(new ComponentAdapter() {
|
||||
parentFrame.addComponentListener(new ComponentAdapter()
|
||||
{
|
||||
@Override
|
||||
public void componentShown(ComponentEvent aEvent)
|
||||
{
|
||||
@@ -574,7 +581,8 @@ public class DistMakerEngine
|
||||
// Retrieve the reference to the appCfgFile
|
||||
File appCfgFile = PlatformUtils.getConfigurationFile();
|
||||
|
||||
// Create the delta.cmd file which provides the Updater with the clean activities to perform (based on fail / pass conditions)
|
||||
// Create the delta.cmd file which provides the Updater with the clean activities to perform
|
||||
// (based on fail / pass conditions)
|
||||
File deltaCmdFile = new File(destPath, "delta.cmd");
|
||||
try (FileWriter tmpFW = new FileWriter(deltaCmdFile))
|
||||
{
|
||||
@@ -586,12 +594,12 @@ public class DistMakerEngine
|
||||
tmpFW.write("# Define the fail section (clean up for failure)\n");
|
||||
tmpFW.write("sect,fail\n");
|
||||
tmpFW.write("copy," + "delta/" + appCfgFile.getName() + ".old," + MiscUtils.getRelativePath(rootPath, appCfgFile) + "\n");
|
||||
tmpFW.write("reboot,trash,jre" + targJreVer.getLabel() + "\n");
|
||||
tmpFW.write("reboot,trash," + JreUtils.getExpandJrePath(targJreVer) + "\n");
|
||||
tmpFW.write("exit\n\n");
|
||||
|
||||
tmpFW.write("# Define the pass section (clean up for success)\n");
|
||||
tmpFW.write("sect,pass\n");
|
||||
tmpFW.write("trash,jre" + currJreVer.getLabel() + "\n");
|
||||
tmpFW.write("trash," + JreUtils.getExpandJrePath(currJreVer) + "\n");
|
||||
tmpFW.write("exit\n\n");
|
||||
}
|
||||
else
|
||||
@@ -626,7 +634,7 @@ public class DistMakerEngine
|
||||
|
||||
// Since an updated JRE was needed...
|
||||
// Moved the JRE (unpacked folder) from its drop path to the proper location
|
||||
File jreDropPath = new File(destPath, "jre" + targJre.getVersion().getLabel());
|
||||
File jreDropPath = new File(destPath, JreUtils.getExpandJrePath(targJre.getVersion()));
|
||||
File jreTargPath = PlatformUtils.getJreLocation(targJre);
|
||||
jreTargPath.getParentFile().setWritable(true);
|
||||
if (jreDropPath.renameTo(jreTargPath) == false)
|
||||
@@ -688,10 +696,10 @@ public class DistMakerEngine
|
||||
updnStr = "upgraded";
|
||||
aTask.infoAppendln("Your current JRE is not compatible with this release. It will need to be " + updnStr + "!");
|
||||
aTask.infoAppendln("\tCurrent JRE: " + currJreVer.getLabel());
|
||||
aTask.infoAppendln("\tMinimun JRE: " + aUpdateCat.getMinJreVersion().getLabel());
|
||||
aTask.infoAppendln("\tMinimum JRE: " + aUpdateCat.getMinJreVersion().getLabel());
|
||||
JreVersion tmpJreVer = aUpdateCat.getMaxJreVersion();
|
||||
if (tmpJreVer != null)
|
||||
aTask.infoAppendln("\tMaximun JRE: " + tmpJreVer.getLabel());
|
||||
aTask.infoAppendln("\tMaximum JRE: " + tmpJreVer.getLabel());
|
||||
aTask.infoAppendln("");
|
||||
|
||||
// Bail if we are running a bundled JRE
|
||||
@@ -738,6 +746,15 @@ public class DistMakerEngine
|
||||
}
|
||||
JreVersion pickJreVer = pickJre.getVersion();
|
||||
|
||||
// Update the AppLauncher if required
|
||||
AppLauncherRelease pickAppLauncher = null;
|
||||
if (AppLauncherUtils.isAppLauncherUpdateNeeded(aTask, pickJre) == true)
|
||||
{
|
||||
pickAppLauncher = AppLauncherUtils.updateAppLauncher(aTask, pickJre, aDestPath, updateSiteUrl, refCredential);
|
||||
if (pickAppLauncher == null)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update the number of bytes to be retrieved to take into account the JRE which we will be downloading
|
||||
long tmpFileLen = pickJre.getFileLen();
|
||||
releaseSizeFull += tmpFileLen;
|
||||
@@ -761,21 +778,21 @@ public class DistMakerEngine
|
||||
aTask.infoAppendln("The download of the JRE appears to be corrupted.");
|
||||
aTask.infoAppendln("\tFile: " + dstFile);
|
||||
aTask.infoAppendln("\t\tExpected " + targDigest.getDescr());
|
||||
aTask.infoAppendln("\t\tRecieved " + testDigest.getDescr() + "\n");
|
||||
aTask.infoAppendln("\t\tReceived " + testDigest.getDescr() + "\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Unpack the JRE at the unpack location
|
||||
aTask.infoAppendln("Finshed downloading JRE. Unpacking JRE...");
|
||||
File jreRootPath = null;
|
||||
File jreTargPath = new File(aDestPath, "jre" + pickJreVer.getLabel());
|
||||
File jreTargPath = new File(aDestPath, JreUtils.getExpandJrePath(pickJreVer));
|
||||
try
|
||||
{
|
||||
// Create the working unpack folder where the JRE will be initially unpacked to.
|
||||
File unpackPath = new File(aDestPath, "unpack");
|
||||
unpackPath.mkdirs();
|
||||
|
||||
// Unpack the JRE to the working unpack folder and ensure that the unpacked JRE results in a 1 top level root folder.
|
||||
// Unpack the JRE to the working unpack folder. Ensure that the unpacked JRE results in 1 top level folder.
|
||||
tmpTask = new PartialTask(aTask, aTask.getProgress(), (tmpFileLen * 0.25) / (releaseSizeFull + 0.00));
|
||||
MiscUtils.unTar(tmpTask, dstFile, unpackPath);
|
||||
File[] fileArr = unpackPath.listFiles();
|
||||
@@ -793,6 +810,9 @@ public class DistMakerEngine
|
||||
aTask.infoAppendln("Failed to properly untar archive. The update has been aborted.");
|
||||
aTask.infoAppendln("\tTar File: " + dstFile);
|
||||
aTask.infoAppendln("\tDestination: " + jreTargPath);
|
||||
|
||||
String errMsg = ThreadUtil.getStackTrace(aExp);
|
||||
aTask.infoAppend("\nStack Trace:\n" + errMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -800,16 +820,17 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that "reverts" an update. After this method is called the DistMaker application's configuration should be in the same state as before an
|
||||
* update was applied. Reverting consists of the following:
|
||||
* Helper method that "reverts" an update. After this method is called the DistMaker application's configuration
|
||||
* should be in the same state as before an update was applied. Reverting consists of the following:
|
||||
* <UL>
|
||||
* <LI>Removal of any downloaded and installed JRE
|
||||
* <LI>Removing the delta directory
|
||||
* <LI>Removing the delta.cfg file
|
||||
* </UL>
|
||||
* <P>
|
||||
* There should not be any issues with this roll back process. However if there are a best effort will be made to continue rolling back the updates - note
|
||||
* that the application might be in an unstable state - and may not be able to be restarted.
|
||||
* There should not be any issues with this roll back process. However if there are, a best effort will be made to
|
||||
* continue rolling back the updates - note that the application might be in an unstable state - and may not be able
|
||||
* to be restarted.
|
||||
*/
|
||||
private void revertUpdate(Task aTask)
|
||||
{
|
||||
@@ -883,8 +904,9 @@ public class DistMakerEngine
|
||||
if (strArr.length == 1 && cmdStr.equals("exit") == true)
|
||||
break;
|
||||
|
||||
// We are interested only in trash (or reboot,trash) commands. Execute the individual trash (or reboot,trash) commands.
|
||||
// It is safe to execute reboot,trash commands now since the actual update is not running yet.
|
||||
// We are interested only in trash (or reboot,trash) commands. Execute the individual trash (or
|
||||
// reboot,trash) commands. It is safe to execute reboot,trash commands now since the actual update is not
|
||||
// running yet.
|
||||
String delTargStr = null;
|
||||
if (inputStr.startsWith("trash,") == true)
|
||||
delTargStr = inputStr.substring(6);
|
||||
@@ -935,13 +957,12 @@ public class DistMakerEngine
|
||||
* <P>
|
||||
* This method will be called via reflection.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void queryUserForInput(Task aTask, File deltaPath, List<AppRelease> fullList)
|
||||
private void queryUserForInput(Task aTask, File aDeltaPath, List<AppRelease> aFullList)
|
||||
{
|
||||
AppRelease chosenItem;
|
||||
|
||||
// Query the user, if the wish to destroy the old update
|
||||
if (deltaPath.isDirectory() == true)
|
||||
if (aDeltaPath.isDirectory() == true)
|
||||
{
|
||||
promptPanel.setTitle("Overwrite recent update?");
|
||||
promptPanel.setInfo("An update has already been downloaded... If you proceed this update will be removed. Proceed?");
|
||||
@@ -958,7 +979,7 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
// Query the user of the version to update to
|
||||
pickVersionPanel.setConfiguration(fullList);
|
||||
pickVersionPanel.setConfiguration(aFullList);
|
||||
pickVersionPanel.setVisibleAsModal();
|
||||
chosenItem = pickVersionPanel.getChosenItem();
|
||||
if (chosenItem == null)
|
||||
@@ -970,7 +991,8 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the corresponding application has been fully initialized. This helper method will notify the user on the status of any update.
|
||||
* Notification that the corresponding application has been fully initialized. This helper method will notify the
|
||||
* user on the status of any update.
|
||||
*/
|
||||
private void showUpdateResults()
|
||||
{
|
||||
@@ -1000,7 +1022,8 @@ public class DistMakerEngine
|
||||
}
|
||||
|
||||
// Setup the runnable that will clean up our delta folder
|
||||
Runnable cleanDeltaRunnable = new Runnable() {
|
||||
Runnable cleanDeltaRunnable = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
|
||||
@@ -22,18 +22,56 @@ import distMaker.digest.Digest;
|
||||
import distMaker.digest.DigestType;
|
||||
import distMaker.jre.JreVersion;
|
||||
import distMaker.node.*;
|
||||
import distMaker.utils.ParseUtils;
|
||||
import distMaker.utils.PlainVersion;
|
||||
import distMaker.utils.Version;
|
||||
|
||||
public class DistUtils
|
||||
{
|
||||
// Static members that may be automatically updated by the AppLauncher class loader.
|
||||
// Do not rename or change these variables, without changing the AppLauncher class loader.
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Start of AppLauncher class loader related vars.
|
||||
// Static members that will be automatically configured by the AppLauncher class loader.
|
||||
// Do not rename or change the variables below, without changing the AppLauncher class loader.
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
private static String appLauncherVersion = null;
|
||||
private static boolean isDevelopersEnvironment = true;
|
||||
private static int updateCode = 0; // Static member to declare the update status, 0: None, 1: Pass, 2: Fail
|
||||
private static String updateMsg = null;
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Do not rename or change the variables above, without changing the AppLauncher class loader.
|
||||
// End of AppLauncher class loader related vars.
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Static field used only when developing DistMaker. Otherwise this field should always be null.
|
||||
/** Cached value of the AppLauncherVersion. Note the cached value is of type Version rather than String */
|
||||
private static Version cAppLauncherVersion;
|
||||
|
||||
/* Static field used only when developing DistMaker. Otherwise this field should always be null. */
|
||||
private static File developAppPath = null;
|
||||
|
||||
/**
|
||||
* Utility method to return the version of the AppLauncher that started this process.
|
||||
* <P>
|
||||
* If we are running in a developers environment then this value will be null.
|
||||
*/
|
||||
public static Version getAppLauncherVersion()
|
||||
{
|
||||
// Return the cached value
|
||||
if (cAppLauncherVersion != null)
|
||||
return cAppLauncherVersion;
|
||||
|
||||
// Return null if we are in a developers environment
|
||||
if (isDevelopersEnvironment == true)
|
||||
return null;
|
||||
|
||||
// Legacy AppLaunchers do not configure this field. All legacy AppLaunchers will be defined as version: 0.0
|
||||
if (appLauncherVersion == null && isDevelopersEnvironment == false)
|
||||
cAppLauncherVersion = PlainVersion.Zero;
|
||||
else
|
||||
cAppLauncherVersion = PlainVersion.parse(appLauncherVersion);
|
||||
|
||||
return cAppLauncherVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to determine the path where the application is installed.
|
||||
* <P>
|
||||
@@ -66,6 +104,14 @@ public class DistUtils
|
||||
return jarPath.getParentFile().getParentFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of DistMaker which is running.
|
||||
*/
|
||||
public static Version getDistMakerVersion()
|
||||
{
|
||||
return DistApp.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JreVersion for the JRE which we are running on.
|
||||
*/
|
||||
@@ -124,10 +170,10 @@ public class DistUtils
|
||||
/**
|
||||
* Downloads the specified file from srcUrl to destFile. Returns true on success
|
||||
* <P>
|
||||
* Note the passed in task's progress will be updated from 0% to 100% at file download completion, If the specified file size is invalid (aFileSize <= 0) or
|
||||
* the download turns out to be bigger than the specified size then there will be no progress update while the file is being downloaded - only at completion.
|
||||
* Note the passed in task's progress will be updated from 0% to 100% at file download completion, If the specified
|
||||
* file size is invalid (aFileSize <= 0) or the download turns out to be bigger than the specified size then there
|
||||
* will be no progress update while the file is being downloaded - only at completion.
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static boolean downloadFile(Task aTask, URL aUrl, File aFile, Credential aCredential, long aFileSize, MessageDigest aDigest)
|
||||
{
|
||||
URLConnection connection;
|
||||
@@ -222,26 +268,23 @@ public class DistUtils
|
||||
public static List<AppRelease> getAvailableAppReleases(Task aTask, URL aUpdateUrl, String appName, Credential aCredential)
|
||||
{
|
||||
List<AppRelease> fullList;
|
||||
AppRelease workAR;
|
||||
URL catUrl;
|
||||
URLConnection connection;
|
||||
InputStream inStream;
|
||||
BufferedReader bufReader;
|
||||
DateUnit dateUnit;
|
||||
String errMsg;
|
||||
|
||||
errMsg = null;
|
||||
fullList = new ArrayList<>();
|
||||
catUrl = IoUtil.createURL(aUpdateUrl.toString() + "/" + appName + "/" + "releaseInfo.txt");
|
||||
catUrl = IoUtil.createURL(aUpdateUrl.toString() + "/" + appName + "/" + "appCatalog.txt");
|
||||
|
||||
workAR = null;
|
||||
connection = null;
|
||||
inStream = null;
|
||||
bufReader = null;
|
||||
try
|
||||
{
|
||||
String[] tokens;
|
||||
String strLine, verName;
|
||||
long buildTime;
|
||||
|
||||
// Read the contents of the file
|
||||
connection = catUrl.openConnection();
|
||||
inStream = NetUtil.getInputStream(connection, aCredential);
|
||||
@@ -250,30 +293,84 @@ public class DistUtils
|
||||
// Read the lines
|
||||
while (true)
|
||||
{
|
||||
String strLine;
|
||||
strLine = bufReader.readLine();
|
||||
|
||||
// Bail once we are done
|
||||
if (strLine == null)
|
||||
break;
|
||||
|
||||
tokens = strLine.split(",");
|
||||
// Ignore comments and empty lines
|
||||
if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
|
||||
continue;
|
||||
|
||||
String[] tokenArr;
|
||||
tokenArr = strLine.split(",");
|
||||
if (tokenArr.length == 2 && tokenArr[0].equals("name") == true)
|
||||
; // Nothing to do
|
||||
else if (tokens.length == 2 && tokens[0].equals("name") == true)
|
||||
; // Nothing to do
|
||||
else if (tokens.length != 2)
|
||||
aTask.infoAppendln("Unreconized line: " + strLine);
|
||||
else
|
||||
// if (tokens.length == 2)
|
||||
// Logic to handle the 'exit' command
|
||||
else if (tokenArr.length >= 1 && tokenArr[0].equals("exit") == true)
|
||||
{
|
||||
verName = tokens[0];
|
||||
// We support exit commands with 3 tokens. All others
|
||||
// we will just exit.
|
||||
if (tokenArr.length != 3)
|
||||
break;
|
||||
|
||||
String targName = tokenArr[1];
|
||||
String needVer = tokenArr[2];
|
||||
if (ParseUtils.shouldExitLogic(targName, needVer) == true)
|
||||
break;
|
||||
}
|
||||
// Logic to handle a distribution release
|
||||
else if (tokenArr.length == 3 && tokenArr[0].equals("R") == true)
|
||||
{
|
||||
DateUnit dateUnit;
|
||||
String verName;
|
||||
long buildTime;
|
||||
|
||||
verName = tokenArr[1];
|
||||
|
||||
dateUnit = new DateUnit("", "yyyyMMMdd HH:mm:ss");
|
||||
buildTime = dateUnit.parseString(tokens[1], 0);
|
||||
buildTime = dateUnit.parseString(tokenArr[2], 0);
|
||||
|
||||
fullList.add(new AppRelease(appName, verName, buildTime));
|
||||
// Record the prior AppRelease
|
||||
if (workAR != null)
|
||||
fullList.add(workAR);
|
||||
|
||||
workAR = new AppRelease(appName, verName, buildTime);
|
||||
}
|
||||
// Record any comments and associate with the current AppRelease
|
||||
else if (tokenArr.length >= 2 && tokenArr[0].equals("info") == true)
|
||||
{
|
||||
// We support the 'info,msg' instruction. Otherwise we just ignore info instructions silently
|
||||
if (tokenArr[1].equals("msg") == true && workAR != null)
|
||||
{
|
||||
// Retokenize to ensure at most we get only 3 tokens
|
||||
tokenArr = strLine.split(",", 3);
|
||||
|
||||
String infoMsg;
|
||||
infoMsg = workAR.getInfoMsg();
|
||||
if (infoMsg != null)
|
||||
infoMsg += "\n";
|
||||
else
|
||||
infoMsg = "";
|
||||
if (tokenArr.length > 2)
|
||||
infoMsg += tokenArr[2];
|
||||
|
||||
// Form an updated AppRelease with the updated infoMsg
|
||||
workAR = new AppRelease(workAR.getName(), workAR.getVersion(), workAR.getBuildTime(), infoMsg);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
aTask.infoAppendln("Unreconized line: " + strLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last AppRelease
|
||||
if (workAR != null)
|
||||
fullList.add(workAR);
|
||||
}
|
||||
catch(IOException aExp)
|
||||
{
|
||||
@@ -310,40 +407,39 @@ public class DistUtils
|
||||
*/
|
||||
private static String getErrorCodeMessage(URL aUpdateUrl, URLConnection aConnection, IOException aExp)
|
||||
{
|
||||
URL fetchUrl;
|
||||
Result result;
|
||||
String errMsg;
|
||||
|
||||
// Form a user friendly exception
|
||||
String errMsg;
|
||||
errMsg = "The update site, " + aUpdateUrl + ", is not available.\n\t";
|
||||
|
||||
Result result;
|
||||
result = NetUtil.getResult(aExp, aConnection);
|
||||
switch (result)
|
||||
{
|
||||
case BadCredentials:
|
||||
errMsg += "The update site is password protected and bad credentials were provided.\n";
|
||||
break;
|
||||
errMsg += "The update site is password protected and bad credentials were provided.\n";
|
||||
break;
|
||||
|
||||
case ConnectFailure:
|
||||
case UnreachableHost:
|
||||
case UnsupportedConnection:
|
||||
errMsg += "The update site appears to be unreachable.\n";
|
||||
break;
|
||||
errMsg += "The update site appears to be unreachable.\n";
|
||||
break;
|
||||
|
||||
case Interrupted:
|
||||
errMsg += "The retrival of the remote file has been interrupted.\n";
|
||||
break;
|
||||
errMsg += "The retrival of the remote file has been interrupted.\n";
|
||||
break;
|
||||
|
||||
case InvalidResource:
|
||||
errMsg += "The remote file does not appear to be valid.\n";
|
||||
break;
|
||||
errMsg += "The remote file does not appear to be valid.\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
errMsg += "An undefined error occurred while retrieving the remote file.\n";
|
||||
break;
|
||||
errMsg += "An undefined error occurred while retrieving the remote file.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Log the URL which we failed on
|
||||
URL fetchUrl;
|
||||
fetchUrl = aConnection.getURL();
|
||||
errMsg += "\tURL: " + fetchUrl + "\n";
|
||||
|
||||
@@ -351,8 +447,9 @@ public class DistUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to determine if the specified path is fully writable by this process. This is done by making sure that all folders and child folders are
|
||||
* writable by the current process. Note after this method is called, all folders will have the write permission bit set.
|
||||
* Utility method to determine if the specified path is fully writable by this process. This is done by making sure
|
||||
* that all folders and child folders are writable by the current process. Note after this method is called, all
|
||||
* folders will have the write permission bit set.
|
||||
*/
|
||||
public static boolean isFullyWriteable(File aPath)
|
||||
{
|
||||
@@ -388,7 +485,6 @@ public class DistUtils
|
||||
{
|
||||
List<Node> nodeList;
|
||||
JreVersion minJreVersion, maxJreVersion;
|
||||
DigestType digestType;
|
||||
String errMsg, strLine;
|
||||
|
||||
errMsg = null;
|
||||
@@ -397,16 +493,17 @@ public class DistUtils
|
||||
maxJreVersion = null;
|
||||
|
||||
// Default to DigestType of MD5
|
||||
DigestType digestType;
|
||||
digestType = DigestType.MD5;
|
||||
|
||||
try (BufferedReader bufReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(aCatalogFile))));)
|
||||
try (BufferedReader tmpBR = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(aCatalogFile))));)
|
||||
{
|
||||
String[] tokens;
|
||||
|
||||
// Read the contents of the file
|
||||
while (true)
|
||||
{
|
||||
strLine = bufReader.readLine();
|
||||
strLine = tmpBR.readLine();
|
||||
|
||||
// Bail once we are done
|
||||
if (strLine == null)
|
||||
@@ -450,8 +547,7 @@ public class DistUtils
|
||||
{
|
||||
if (minJreVersion != null)
|
||||
{
|
||||
aTask.infoAppendln("JRE version has already been specified. Current ver: " + minJreVersion.getLabel() + " Requested ver: " + tokens[1]
|
||||
+ ". Skipping...");
|
||||
aTask.infoAppendln("JRE version has already been specified. Current ver: " + minJreVersion.getLabel() + " Requested ver: " + tokens[1] + ". Skipping...");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -487,7 +583,8 @@ public class DistUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to switch the DistMaker library into debug mode. You should never call this method unless you are modifying the DistMaker library.
|
||||
* Utility method to switch the DistMaker library into debug mode. You should never call this method unless you are
|
||||
* modifying the DistMaker library.
|
||||
* <P>
|
||||
* This functionality only exists to allow rapid development of DistMaker
|
||||
*/
|
||||
|
||||
@@ -7,31 +7,31 @@ import java.util.List;
|
||||
|
||||
public class LoggingTask extends SilentTask
|
||||
{
|
||||
private final List<String> messages = new ArrayList<>();
|
||||
private final List<String> messages = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void infoAppend(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoAppend(aMsg);
|
||||
}
|
||||
@Override
|
||||
public void infoAppend(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoAppend(aMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void infoAppendln(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoAppendln(aMsg);
|
||||
}
|
||||
@Override
|
||||
public void infoAppendln(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoAppendln(aMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void infoUpdate(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoUpdate(aMsg);
|
||||
}
|
||||
@Override
|
||||
public void infoUpdate(String aMsg)
|
||||
{
|
||||
messages.add(aMsg);
|
||||
super.infoUpdate(aMsg);
|
||||
}
|
||||
|
||||
List<String> getMessages()
|
||||
{
|
||||
return messages;
|
||||
}
|
||||
List<String> getMessages()
|
||||
{
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class MiscUtils
|
||||
*/
|
||||
public static String convertUnixModeToStr(int aMode)
|
||||
{
|
||||
char permArr[] = { 'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x' };
|
||||
char permArr[] = {'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x'};
|
||||
|
||||
for (int c1 = 8; c1 >= 0; c1--)
|
||||
{
|
||||
@@ -87,7 +87,8 @@ public class MiscUtils
|
||||
/**
|
||||
* Helper method that prints the exception of ErrorDM in an intelligent fashion to the specified task.
|
||||
* <P>
|
||||
* All ErrorDM exceptions (and their causes) will be printed. If the cause is not of type ErrorDM then the stack trace will be printed as well.
|
||||
* All ErrorDM exceptions (and their causes) will be printed. If the cause is not of type ErrorDM then the stack
|
||||
* trace will be printed as well.
|
||||
*/
|
||||
public static void printErrorDM(Task aTask, ErrorDM aErrorDM, int numTabs)
|
||||
{
|
||||
@@ -123,16 +124,15 @@ public class MiscUtils
|
||||
* Source based off of:<BR>
|
||||
* http://stackoverflow.com/questions/315618/how-do-i-extract-a-tar-file-in-java/7556307#7556307
|
||||
* <P>
|
||||
*
|
||||
* The output file is created in the output folder, having the same name as the input file, minus the '.tar' extension.
|
||||
* The output file is created in the output folder, having the same name as the input file, minus the '.tar'
|
||||
* extension.
|
||||
*
|
||||
* @param inputFile
|
||||
* the input .tar file
|
||||
* the input .tar file
|
||||
* @param aDestPath
|
||||
* The destination folder where the content will be dumped.
|
||||
* The destination folder where the content will be dumped.
|
||||
* @throws IOException
|
||||
* @throws FileNotFoundException
|
||||
*
|
||||
* @return The {@link List} of {@link File}s with the untared content.
|
||||
* @throws ArchiveException
|
||||
*/
|
||||
@@ -176,6 +176,9 @@ public class MiscUtils
|
||||
}
|
||||
else if (entry.isSymbolicLink() == true)
|
||||
{
|
||||
// Ensure the parent folders exist
|
||||
outputFile.getParentFile().mkdirs();
|
||||
|
||||
File tmpFile = new File(entry.getLinkName());
|
||||
Files.createSymbolicLink(outputFile.toPath(), tmpFile.toPath());
|
||||
|
||||
@@ -194,7 +197,11 @@ public class MiscUtils
|
||||
}
|
||||
else if (entry.isFile() == true)
|
||||
{
|
||||
final OutputStream outputFileStream = new FileOutputStream(outputFile);
|
||||
// Ensure the parent folders exist
|
||||
outputFile.getParentFile().mkdirs();
|
||||
|
||||
// Copy over the file
|
||||
OutputStream outputFileStream = new FileOutputStream(outputFile);
|
||||
IOUtils.copy(debInputStream, outputFileStream);
|
||||
outputFileStream.close();
|
||||
|
||||
|
||||
@@ -80,4 +80,4 @@ public class Digest
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package distMaker.digest;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
|
||||
/**
|
||||
* Collection of utility methods to ease working with the MessageDigest and associated classes.
|
||||
@@ -44,24 +44,24 @@ public class DigestUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns the hex string corresponding to the byte array.
|
||||
* Utility method that returns the (lower case) hex string corresponding to the byte array.
|
||||
* <P>
|
||||
* Delegates to {@link BaseEncoding.base16().lowerCase().encode(CharSequence)}
|
||||
*/
|
||||
public static String byteArr2HexStr(byte[] aByteArr)
|
||||
{
|
||||
String retStr;
|
||||
|
||||
retStr = (new HexBinaryAdapter()).marshal(aByteArr).toLowerCase();
|
||||
String retStr = BaseEncoding.base16().lowerCase().encode(aByteArr);
|
||||
return retStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns a byte array corresponding to the hex string.
|
||||
* <P>
|
||||
* Delegates to {@link BaseEncoding.base16().lowerCase().decode(CharSequence)}
|
||||
*/
|
||||
public static byte[] hexStr2ByteArr(String aHexStr)
|
||||
{
|
||||
byte[] retArr;
|
||||
|
||||
retArr = (new HexBinaryAdapter()).unmarshal(aHexStr);
|
||||
byte[] retArr = BaseEncoding.base16().lowerCase().decode(aHexStr);
|
||||
return retArr;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,17 @@ import distMaker.node.AppRelease;
|
||||
|
||||
public class PickReleasePanel extends GlassPanel implements ActionListener, ListSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
// Constants
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Color ColorFail = Color.red.darker().darker();
|
||||
|
||||
// GUI vars
|
||||
// GUI vars
|
||||
private JLabel titleL;
|
||||
private JRadioButton newestRB, olderRB;
|
||||
private ItemListPanel<AppRelease> listPanel;
|
||||
private QueryTableCellRenderer col0Renderer, col1Renderer;
|
||||
private JButton abortB, proceedB;
|
||||
private JTextArea infoTA, warnTA;
|
||||
private JTextArea headTA, infoTA;
|
||||
private Font smallFont;
|
||||
|
||||
// State vars
|
||||
@@ -57,7 +59,7 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
// Build the actual GUI
|
||||
smallFont = (new JTextField()).getFont();
|
||||
buildGuiArea();
|
||||
setPreferredSize(new Dimension(250, getPreferredSize().height));
|
||||
setPreferredSize(new Dimension(350, getPreferredSize().height));
|
||||
|
||||
// Set up some keyboard shortcuts
|
||||
FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(abortB));
|
||||
@@ -74,17 +76,17 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
/**
|
||||
* Sets in the configuration of available versions
|
||||
*/
|
||||
public void setConfiguration(List<AppRelease> itemList)
|
||||
public void setConfiguration(List<AppRelease> aItemList)
|
||||
{
|
||||
DateUnit dateUnit;
|
||||
// String currBuildStr;
|
||||
String lastBuildStr;
|
||||
String currVerStr, lastVerStr;
|
||||
String appName, infoMsg;
|
||||
String appName, headMsg;
|
||||
|
||||
// Sort the items, and isolate the newest item
|
||||
LinkedList<AppRelease> linkedList;
|
||||
linkedList = new LinkedList<>(itemList);
|
||||
linkedList = new LinkedList<>(aItemList);
|
||||
Collections.sort(linkedList);
|
||||
Collections.reverse(linkedList); // reverse the list to show most recent versions on top
|
||||
newestItem = linkedList.removeFirst();
|
||||
@@ -104,20 +106,27 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
myItemProcessor.setItems(linkedList);
|
||||
|
||||
// Update the infoTA
|
||||
if (newestItem.equals(installedItem) == true) {
|
||||
titleL.setText(appName + " is up to date.");
|
||||
infoMsg = "You are running the latest release "
|
||||
+ " (" + lastVerStr + ") that was built on " + lastBuildStr + ". ";
|
||||
infoMsg += "You may switch to an older release by choosing one of the versions below.";
|
||||
} else {
|
||||
titleL.setText(appName + " needs to be updated.");
|
||||
infoMsg = "You are running version " + currVerStr + ". ";
|
||||
infoMsg += "You may update to the latest release. You may also switch to an "
|
||||
+ "older release by choosing another version below. ";
|
||||
if (newestItem.equals(installedItem) == true)
|
||||
{
|
||||
titleL.setText(appName + " is up to date.");
|
||||
headMsg = "You are running the latest release " + " (" + lastVerStr + ") that was built on " + lastBuildStr + ". ";
|
||||
headMsg += "You may switch to an older release by choosing one of the versions below.";
|
||||
}
|
||||
infoMsg += "\n";
|
||||
else if (installedItem.getBuildTime() > newestItem.getBuildTime())
|
||||
{
|
||||
titleL.setText(appName + " (Out-Of-Band Release)");
|
||||
headMsg = "You are running version " + currVerStr + ". This version has never been released! ";
|
||||
headMsg += "You may update to the latest release or switch to an older release by choosing another version below. ";
|
||||
}
|
||||
else
|
||||
{
|
||||
titleL.setText(appName + " needs to be updated.");
|
||||
headMsg = "You are running version " + currVerStr + ". ";
|
||||
headMsg += "You may update to the latest release. You may also switch to an " + "older release by choosing another version below. ";
|
||||
}
|
||||
headMsg += "\n";
|
||||
|
||||
infoTA.setText(infoMsg);
|
||||
headTA.setText(headMsg);
|
||||
updateGui();
|
||||
}
|
||||
|
||||
@@ -161,38 +170,50 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
*/
|
||||
private void buildGuiArea()
|
||||
{
|
||||
JPanel tmpPanel;
|
||||
JPanel listPanel, mainPanel;
|
||||
|
||||
// Form the layout
|
||||
setLayout(new MigLayout("", "[left][grow][]", "[]25[][][]3[grow]10[]"));
|
||||
setLayout(new MigLayout("", "[left][grow][]", "[]"));
|
||||
|
||||
// Title Area
|
||||
titleL = new JLabel("Please select an update", JLabel.CENTER); // this text gets replaced once the curent version status is known
|
||||
// Title Area: Note that the default text gets replaced once the current version status is known
|
||||
titleL = new JLabel("Please select an update", JLabel.CENTER);
|
||||
add(titleL, "growx,span 2,wrap");
|
||||
|
||||
// Info area
|
||||
infoTA = GuiUtil.createUneditableTextArea(2, 0);
|
||||
add(infoTA, "w 0::,growx,span,wrap");
|
||||
// Header area
|
||||
headTA = GuiUtil.createUneditableTextArea(2, 0);
|
||||
add(headTA, "w 0::,growx,span,wrap");
|
||||
|
||||
// Latest version area
|
||||
newestRB = GuiUtil.createJRadioButton("Unspecified", this, smallFont);
|
||||
newestRB.setSelected(true);
|
||||
add(newestRB, "span,wrap");
|
||||
|
||||
// Older version area
|
||||
olderRB = GuiUtil.createJRadioButton("Select an older release:", this, smallFont);
|
||||
add(olderRB, "span,wrap");
|
||||
|
||||
tmpPanel = buildItemListTablePanel();
|
||||
tmpPanel.setBorder(new EmptyBorder(0, 15, 0, 0));
|
||||
add(tmpPanel, "growx,growy,span,wrap");
|
||||
listPanel = buildItemListTablePanel();
|
||||
listPanel.setBorder(new EmptyBorder(0, 15, 0, 0));
|
||||
|
||||
mainPanel = new JPanel(new MigLayout("", "0[left,grow]", "0[][]3[grow]0"));
|
||||
mainPanel.add(newestRB, "wrap");
|
||||
mainPanel.add(olderRB, "wrap");
|
||||
mainPanel.add(listPanel, "growx,growy");
|
||||
|
||||
// Link the radio buttons
|
||||
GuiUtil.linkRadioButtons(newestRB, olderRB);
|
||||
|
||||
// Warn Area
|
||||
warnTA = GuiUtil.createUneditableTextArea(0, 0);
|
||||
add(warnTA, "w 0::,growx,span,wrap");
|
||||
// Info Area
|
||||
JScrollPane tmpSP;
|
||||
infoTA = GuiUtil.createUneditableTextArea(0, 0);
|
||||
infoTA.setTabSize(3);
|
||||
tmpSP = new JScrollPane(infoTA);
|
||||
tmpSP.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
JSplitPane tmpPane;
|
||||
tmpPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, mainPanel, tmpSP);
|
||||
tmpPane.setBorder(null);
|
||||
tmpPane.setResizeWeight(0.15);
|
||||
// tmpPane.setDividerLocation(0.50);
|
||||
add(tmpPane, "growx,growy,pushy,span,wrap");
|
||||
|
||||
// Action area
|
||||
abortB = GuiUtil.createJButton("Abort", this, smallFont);
|
||||
@@ -208,26 +229,26 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
*/
|
||||
private JPanel buildItemListTablePanel()
|
||||
{
|
||||
QueryComposer<LookUp> aComposer;
|
||||
QueryItemHandler<AppRelease> aItemHandler;
|
||||
QueryComposer<LookUp> tmpComposer;
|
||||
QueryItemHandler<AppRelease> tmpIH;
|
||||
DateUnit dateUnit;
|
||||
|
||||
dateUnit = new DateUnit("", "yyyyMMMdd HH:mm");
|
||||
|
||||
aComposer = new QueryComposer<LookUp>();
|
||||
aComposer.addAttribute(LookUp.Version, String.class, "Version", null);
|
||||
aComposer.addAttribute(LookUp.BuildTime, new ConstUnitProvider(dateUnit), "Build Date", null);
|
||||
tmpComposer = new QueryComposer<LookUp>();
|
||||
tmpComposer.addAttribute(LookUp.Version, String.class, "Version", null);
|
||||
tmpComposer.addAttribute(LookUp.BuildTime, new ConstUnitProvider(dateUnit), "Build Date", null);
|
||||
|
||||
col0Renderer = new QueryTableCellRenderer();
|
||||
col1Renderer = new QueryTableCellRenderer();
|
||||
col1Renderer.setUnit(dateUnit);
|
||||
aComposer.setRenderer(LookUp.Version, col0Renderer);
|
||||
aComposer.setRenderer(LookUp.BuildTime, col1Renderer);
|
||||
tmpComposer.setRenderer(LookUp.Version, col0Renderer);
|
||||
tmpComposer.setRenderer(LookUp.BuildTime, col1Renderer);
|
||||
|
||||
aItemHandler = new QueryItemHandler<AppRelease>(aComposer);
|
||||
myItemProcessor = new StaticItemProcessor<AppRelease>();
|
||||
tmpIH = new QueryItemHandler<AppRelease>(tmpComposer);
|
||||
myItemProcessor = new StaticItemProcessor<>();
|
||||
|
||||
listPanel = new ItemListPanel<AppRelease>(aItemHandler, myItemProcessor, false, false);
|
||||
listPanel = new ItemListPanel<>(tmpIH, myItemProcessor, false, false);
|
||||
listPanel.setSortingEnabled(false);
|
||||
listPanel.addListSelectionListener(this);
|
||||
return listPanel;
|
||||
@@ -239,7 +260,7 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
private void updateGui()
|
||||
{
|
||||
AppRelease pickItem;
|
||||
String warnMsg;
|
||||
String failMsg, infoMsg;
|
||||
boolean isEnabled;
|
||||
|
||||
// Determine the selected version
|
||||
@@ -258,15 +279,39 @@ public class PickReleasePanel extends GlassPanel implements ActionListener, List
|
||||
col1Renderer.setEnabled(isEnabled);
|
||||
|
||||
// Determine the warnMsg
|
||||
warnMsg = null;
|
||||
failMsg = null;
|
||||
infoMsg = null;
|
||||
if (pickItem == null)
|
||||
warnMsg = "Select the version you want to switch to.";
|
||||
failMsg = "Select the version you want to switch to.";
|
||||
else if (pickItem.equals(installedItem) == true)
|
||||
warnMsg = "You cannot update to the same version you are running. You can switch to a different version.";
|
||||
failMsg = "You cannot update to the same version you are running. You can switch to a different version.";
|
||||
else if (pickItem.getBuildTime() < installedItem.getBuildTime())
|
||||
warnMsg = "Please note that your current selection will revert back to an older version than the currently installed version.";
|
||||
infoMsg = "Please note that your current selection will revert back to an older version than the currently installed version.";
|
||||
|
||||
warnTA.setText(warnMsg);
|
||||
// Add the app release notes
|
||||
if (pickItem != null)
|
||||
{
|
||||
String noteMsg;
|
||||
noteMsg = pickItem.getInfoMsg();
|
||||
if (noteMsg != null)
|
||||
{
|
||||
if (infoMsg != null)
|
||||
infoMsg += "\n\n" + noteMsg;
|
||||
else
|
||||
infoMsg = noteMsg;
|
||||
}
|
||||
}
|
||||
|
||||
Color tmpColor = Color.BLACK;
|
||||
if (failMsg != null)
|
||||
tmpColor = ColorFail;
|
||||
|
||||
String tmpMsg = failMsg;
|
||||
if (tmpMsg == null)
|
||||
tmpMsg = infoMsg;
|
||||
|
||||
infoTA.setText(tmpMsg);
|
||||
infoTA.setForeground(tmpColor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
80
src/distMaker/jre/AppLauncherRelease.java
Normal file
80
src/distMaker/jre/AppLauncherRelease.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package distMaker.jre;
|
||||
|
||||
import distMaker.digest.Digest;
|
||||
import distMaker.utils.PlainVersion;
|
||||
import distMaker.utils.Version;
|
||||
import distMaker.utils.VersionUtils;
|
||||
|
||||
/**
|
||||
* Immutable class that describes an AppLauncher release.
|
||||
* <P>
|
||||
* The reference fileName should be a jar file.
|
||||
*/
|
||||
public class AppLauncherRelease implements Comparable<AppLauncherRelease>
|
||||
{
|
||||
private final Version version;
|
||||
private final Digest digest;
|
||||
private final String fileName;
|
||||
private final long fileLen;
|
||||
|
||||
public AppLauncherRelease(String aVersion, String aFileName, Digest aDigest, long aFileLen)
|
||||
{
|
||||
version = PlainVersion.parse(aVersion);
|
||||
fileName = aFileName;
|
||||
digest = aDigest;
|
||||
fileLen = aFileLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Digest associated with the AppLauncher (jar) file.
|
||||
*/
|
||||
public Digest getDigest()
|
||||
{
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the AppLauncher corresponding to this release.
|
||||
*/
|
||||
public Version getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the associated file
|
||||
*/
|
||||
public long getFileLen()
|
||||
{
|
||||
return fileLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename of this AppLauncher release.
|
||||
*/
|
||||
public String getFileName()
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AppLauncherRelease aItem)
|
||||
{
|
||||
int cmpVal;
|
||||
|
||||
cmpVal = VersionUtils.compare(version, aItem.version);
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
|
||||
cmpVal = fileName.compareTo(aItem.fileName);
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
|
||||
cmpVal = Long.compare(fileLen, aItem.fileLen);
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
339
src/distMaker/jre/AppLauncherUtils.java
Normal file
339
src/distMaker/jre/AppLauncherUtils.java
Normal file
@@ -0,0 +1,339 @@
|
||||
package distMaker.jre;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import distMaker.DistUtils;
|
||||
import distMaker.digest.Digest;
|
||||
import distMaker.digest.DigestType;
|
||||
import distMaker.digest.DigestUtils;
|
||||
import distMaker.platform.PlatformUtils;
|
||||
import distMaker.utils.ParseUtils;
|
||||
import distMaker.utils.PlainVersion;
|
||||
import distMaker.utils.Version;
|
||||
import distMaker.utils.VersionUtils;
|
||||
import glum.gui.GuiUtil;
|
||||
import glum.io.IoUtil;
|
||||
import glum.net.Credential;
|
||||
import glum.net.NetUtil;
|
||||
import glum.task.PartialTask;
|
||||
import glum.task.Task;
|
||||
import glum.util.ThreadUtil;
|
||||
|
||||
public class AppLauncherUtils
|
||||
{
|
||||
/**
|
||||
* Returns a list of all the available AppLauncher releases specified at: <BR>
|
||||
* {@literal <aUpdateSiteUrl>/launcher/appCatalog.txt}
|
||||
*/
|
||||
public static List<AppLauncherRelease> getAvailableAppLauncherReleases(Task aTask, URL aUpdateSiteUrl, Credential aCredential)
|
||||
{
|
||||
List<AppLauncherRelease> retList;
|
||||
URL catUrl;
|
||||
URLConnection connection;
|
||||
InputStream inStream;
|
||||
BufferedReader bufReader;
|
||||
DigestType digestType;
|
||||
String errMsg, strLine;
|
||||
|
||||
errMsg = null;
|
||||
retList = new ArrayList<>();
|
||||
catUrl = IoUtil.createURL(aUpdateSiteUrl.toString() + "/launcher/appCatalog.txt");
|
||||
|
||||
// Default to DigestType of MD5
|
||||
digestType = DigestType.MD5;
|
||||
|
||||
inStream = null;
|
||||
bufReader = null;
|
||||
try
|
||||
{
|
||||
// Read the contents of the file
|
||||
connection = catUrl.openConnection();
|
||||
inStream = NetUtil.getInputStream(connection, aCredential);
|
||||
bufReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(inStream)));
|
||||
|
||||
// Read the lines
|
||||
while (true)
|
||||
{
|
||||
strLine = bufReader.readLine();
|
||||
|
||||
// Bail once we are done
|
||||
if (strLine == null)
|
||||
break;
|
||||
|
||||
// Ignore comments and empty lines
|
||||
if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
|
||||
continue;
|
||||
|
||||
String[] tokens;
|
||||
tokens = strLine.split(",", 5);
|
||||
if (tokens.length == 2 && tokens[0].equals("name") == true && tokens[1].equals("AppLauncher") == true)
|
||||
; // Nothing to do - we just entered the "JRE" section
|
||||
// Logic to handle the 'exit' command
|
||||
else if (tokens.length >= 1 && tokens[0].equals("exit") == true)
|
||||
{
|
||||
// We support exit commands with 3 tokens. All others
|
||||
// we will just exit.
|
||||
if (tokens.length != 3)
|
||||
break;
|
||||
|
||||
String targName = tokens[1];
|
||||
String needVer = tokens[2];
|
||||
if (ParseUtils.shouldExitLogic(targName, needVer) == true)
|
||||
break;
|
||||
}
|
||||
// Logic to handle the 'digest' command
|
||||
else if (tokens.length == 2 && tokens[0].equals("digest") == true)
|
||||
{
|
||||
DigestType tmpDigestType;
|
||||
|
||||
tmpDigestType = DigestType.parse(tokens[1]);
|
||||
if (tmpDigestType == null)
|
||||
aTask.infoAppendln("Failed to locate DigestType for: " + tokens[1]);
|
||||
else
|
||||
digestType = tmpDigestType;
|
||||
}
|
||||
// Logic to handle the 'F' command: AppLauncher File
|
||||
else if (tokens.length == 5 && tokens[0].equals("F") == true)
|
||||
{
|
||||
String filename, digestStr, version;
|
||||
long fileLen;
|
||||
|
||||
// Form the JreRelease
|
||||
digestStr = tokens[1];
|
||||
fileLen = GuiUtil.readLong(tokens[2], -1);
|
||||
filename = tokens[3];
|
||||
version = tokens[4];
|
||||
|
||||
Digest tmpDigest = new Digest(digestType, digestStr);
|
||||
retList.add(new AppLauncherRelease(version, filename, tmpDigest, fileLen));
|
||||
}
|
||||
else
|
||||
{
|
||||
aTask.infoAppendln("Unreconized line: " + strLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(FileNotFoundException aExp)
|
||||
{
|
||||
errMsg = "Failed to locate resource: " + catUrl;
|
||||
}
|
||||
catch(IOException aExp)
|
||||
{
|
||||
errMsg = ThreadUtil.getStackTrace(aExp);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IoUtil.forceClose(inStream);
|
||||
IoUtil.forceClose(bufReader);
|
||||
}
|
||||
|
||||
// See if we are in a valid state
|
||||
if (errMsg != null)
|
||||
; // Nothing to do, as an earlier error has occurred
|
||||
else if (retList.size() == 0)
|
||||
errMsg = "The catalog appears to be invalid.";
|
||||
|
||||
// Bail if there were issues
|
||||
if (errMsg != null)
|
||||
{
|
||||
aTask.infoAppendln(errMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return retList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that checks to see if the AppLauncher will need be updated in order to support the specified JRE.
|
||||
* <P>
|
||||
* Returns false if the AppLauncher does NOT need be updated. Otherwise true will be returned and the version info
|
||||
* will be logged to the specified aTask.
|
||||
*/
|
||||
public static boolean isAppLauncherUpdateNeeded(Task aTask, JreRelease aJreRelease)
|
||||
{
|
||||
Version currVer, nMinVer, nMaxVer;
|
||||
|
||||
// Bail if the installed version of AppLauncher is equal to or later than the required version
|
||||
currVer = DistUtils.getAppLauncherVersion();
|
||||
nMinVer = aJreRelease.getAppLauncherMinVersion();
|
||||
nMaxVer = aJreRelease.getAppLauncherMaxVersion();
|
||||
if (VersionUtils.isInRange(currVer, nMinVer, nMaxVer) == true)
|
||||
return false;
|
||||
|
||||
// Log the fact that we need to update our AppLauncher
|
||||
aTask.infoAppendln("Your current AppLauncher is not compatible with this release. It will need to be upgraded!");
|
||||
aTask.infoAppendln("\tCurrent ver: " + currVer);
|
||||
if (nMinVer != PlainVersion.AbsMin)
|
||||
aTask.infoAppendln("\tMinimum ver: " + nMinVer);
|
||||
if (nMaxVer != PlainVersion.AbsMax)
|
||||
aTask.infoAppendln("\tMaximum ver: " + nMaxVer);
|
||||
aTask.infoAppendln("");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that will return the AppLauncherRelease that satisfies the requirements as specified by the
|
||||
* JreRelease.
|
||||
* <P>
|
||||
* Returns null if no AppLauncherRelease is located that can satisfy the specified JreRelease.
|
||||
* <P>
|
||||
* Any issues that cropped up while searching for a valid AppLauncherRelease will be logged to the specified task,
|
||||
* aTask.
|
||||
* <P>
|
||||
* TODO: Remove the comments below
|
||||
* <P>
|
||||
* corresponding to the AppLauncher that we to the specified version required by this JRE.
|
||||
* <P>
|
||||
* Returns false on any failure.
|
||||
*/
|
||||
public static AppLauncherRelease updateAppLauncher(Task aTask, JreRelease aJreRelease, File aDestPath, URL updateSiteUrl, Credential aCredential)
|
||||
{
|
||||
// Locate the list of available AppLaunchers
|
||||
List<AppLauncherRelease> availList;
|
||||
availList = AppLauncherUtils.getAvailableAppLauncherReleases(aTask, updateSiteUrl, aCredential);
|
||||
if (availList == null)
|
||||
{
|
||||
aTask.infoAppendln("The update site does not have any deployed AppLaunchers.");
|
||||
aTask.infoAppendln("Please contact the update site adminstartor.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (availList.size() == 0)
|
||||
{
|
||||
aTask.infoAppendln("No AppLauncher releases found!");
|
||||
aTask.infoAppendln("Please contact the update site adminstartor.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Retrieve the AppLauncher that is compatible from the list
|
||||
Version nMinVer = aJreRelease.getAppLauncherMinVersion();
|
||||
Version nMaxVer = aJreRelease.getAppLauncherMaxVersion();
|
||||
|
||||
AppLauncherRelease pickRelease = null;
|
||||
for (AppLauncherRelease aRelease : availList)
|
||||
{
|
||||
Version evalVer = aRelease.getVersion();
|
||||
if (VersionUtils.isInRange(evalVer, nMinVer, nMaxVer) == false)
|
||||
continue;
|
||||
|
||||
pickRelease = aRelease;
|
||||
break;
|
||||
}
|
||||
|
||||
// Bail if no compatible release could be found
|
||||
if (pickRelease == null)
|
||||
{
|
||||
aTask.infoAppendln("No compatible AppLauncher releases have been deployed!");
|
||||
aTask.infoAppendln("Please contact the update site adminstartor.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get stats on the release
|
||||
Version pickVer = pickRelease.getVersion();
|
||||
long fileLen = pickRelease.getFileLen();
|
||||
|
||||
// Define the path to the launcher
|
||||
File launcherPath;
|
||||
if (PlatformUtils.getPlatform().equals("Apple") == true)
|
||||
launcherPath = new File(aDestPath.getParentFile(), "Java");
|
||||
else
|
||||
launcherPath = new File(aDestPath.getParentFile(), "launcher");
|
||||
|
||||
// Download the AppLauncher
|
||||
Digest targDigest, testDigest;
|
||||
MessageDigest msgDigest;
|
||||
targDigest = pickRelease.getDigest();
|
||||
msgDigest = DigestUtils.getDigest(targDigest.getType());
|
||||
aTask.infoAppendln("Downloading AppLauncher... Version: " + pickVer);
|
||||
URL srcUrl = IoUtil.createURL(updateSiteUrl.toString() + "/launcher/appLauncher-" + pickVer + ".jar");
|
||||
File dstFile = new File(launcherPath, pickRelease.getFileName());
|
||||
int zios_2018Feb15; // TODO: Consider refactoring this code and making it cleaner...
|
||||
Task tmpTask = new PartialTask(aTask, aTask.getProgress(), 0.01);
|
||||
// Task tmpTask = new PartialTask(aTask, aTask.getProgress(), (tmpFileLen * 0.75) / (releaseSizeFull + 0.00));
|
||||
// Task tmpTask = new SilentTask();
|
||||
if (DistUtils.downloadFile(tmpTask, srcUrl, dstFile, aCredential, fileLen, msgDigest) == false)
|
||||
{
|
||||
aTask.infoAppendln("Failed to download updated AppLauncher.");
|
||||
aTask.infoAppendln("\tSource: " + srcUrl);
|
||||
aTask.infoAppendln("\tFile: " + dstFile + "\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate that the AppLauncher was downloaded successfully
|
||||
testDigest = new Digest(targDigest.getType(), msgDigest.digest());
|
||||
if (targDigest.equals(testDigest) == false)
|
||||
{
|
||||
aTask.infoAppendln("The download of the AppLauncher appears to be corrupted.");
|
||||
aTask.infoAppendln("\tFile: " + dstFile);
|
||||
aTask.infoAppendln("\t\tExpected " + targDigest.getDescr());
|
||||
aTask.infoAppendln("\t\tReceived " + testDigest.getDescr() + "\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update the appLauncher.jar file
|
||||
File targFile = new File(launcherPath, "appLauncher.jar");
|
||||
boolean isPass;
|
||||
|
||||
// Back up the "original" targFile (only if a backup has not already already been made). In case
|
||||
// there are multiple updates we do not want to overwrite the "real" original
|
||||
boolean isOriginalBackedUp = false;
|
||||
File origFile = new File(launcherPath, "appLauncher.jar.orig");
|
||||
if (origFile.exists() == false)
|
||||
{
|
||||
isPass = targFile.renameTo(origFile);
|
||||
if (isPass == false)
|
||||
{
|
||||
aTask.infoAppendln("Failed to rename: " + targFile + " to " + origFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOriginalBackedUp = true;
|
||||
aTask.infoAppendln("Original file has been backed up.");
|
||||
aTask.infoAppendln("\t" + targFile + " ---> " + origFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the targFile with the newly downloaded file
|
||||
isPass = dstFile.renameTo(targFile);
|
||||
// targFile.renameTo(origFile);
|
||||
if (isPass == false)
|
||||
{
|
||||
aTask.infoAppendln("Failed to rename: " + dstFile + " to " + targFile);
|
||||
aTask.infoAppendln("Update is being aborted");
|
||||
|
||||
// We need to revert to the original backup
|
||||
if (isOriginalBackedUp == true)
|
||||
{
|
||||
isPass = origFile.renameTo(targFile);
|
||||
if (isPass == false)
|
||||
{
|
||||
aTask.infoAppendln("Failed to revert to the orginal AppLauncher.");
|
||||
aTask.infoAppendln("\t" + targFile);
|
||||
aTask.infoAppendln("Update has FAILED and left application in an invalid state.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
aTask.infoAppendln("Success updating AppLauncher... ");
|
||||
aTask.infoAppendln("\t" + dstFile + " ---> " + targFile + "\n");
|
||||
}
|
||||
|
||||
return pickRelease;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package distMaker.jre;
|
||||
|
||||
import distMaker.digest.Digest;
|
||||
import distMaker.utils.Version;
|
||||
|
||||
/**
|
||||
* Immutable class that describes a JRE Release.
|
||||
@@ -9,19 +10,39 @@ import distMaker.digest.Digest;
|
||||
*/
|
||||
public class JreRelease implements Comparable<JreRelease>
|
||||
{
|
||||
private String platform;
|
||||
private JreVersion version;
|
||||
private Digest digest;
|
||||
private String fileName;
|
||||
private long fileLen;
|
||||
private final Version alMinVer;
|
||||
private final Version alMaxVer;
|
||||
private final String platform;
|
||||
private final JreVersion version;
|
||||
private final Digest digest;
|
||||
private final String fileName;
|
||||
private final long fileLen;
|
||||
|
||||
public JreRelease(String aPlatform, String aVersion, String aFileName, Digest aDigest, long aFileLen)
|
||||
public JreRelease(String aPlatform, String aVersion, String aFileName, Digest aDigest, long aFileLen, Version aAlMinVer, Version aAlMaxVer)
|
||||
{
|
||||
platform = aPlatform;
|
||||
version = new JreVersion(aVersion);
|
||||
fileName = aFileName;
|
||||
digest = aDigest;
|
||||
fileLen = aFileLen;
|
||||
alMinVer = aAlMinVer;
|
||||
alMaxVer = aAlMaxVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum AppLauncher version compatible with this JRE release.
|
||||
*/
|
||||
public Version getAppLauncherMinVersion()
|
||||
{
|
||||
return alMinVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum AppLauncher version compatible with this JRE release.
|
||||
*/
|
||||
public Version getAppLauncherMaxVersion()
|
||||
{
|
||||
return alMaxVer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
package distMaker.jre;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import distMaker.digest.Digest;
|
||||
import distMaker.digest.DigestType;
|
||||
import distMaker.utils.ParseUtils;
|
||||
import distMaker.utils.PlainVersion;
|
||||
import glum.gui.GuiUtil;
|
||||
import glum.io.IoUtil;
|
||||
import glum.net.Credential;
|
||||
@@ -7,19 +23,30 @@ import glum.net.NetUtil;
|
||||
import glum.task.Task;
|
||||
import glum.util.ThreadUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.*;
|
||||
|
||||
import distMaker.digest.Digest;
|
||||
import distMaker.digest.DigestType;
|
||||
import distMaker.platform.PlatformUtils;
|
||||
|
||||
public class JreUtils
|
||||
{
|
||||
/**
|
||||
* Returns a list of all the available JRE releases specified in <aUpdateSiteUrl>/jre/jreCatalog.txt
|
||||
* Returns the relative path a JRE should be expanded to.
|
||||
* <P>
|
||||
* Namely legacy JRE versions (versions prior to Java 9) will be expanded to:<BR>
|
||||
* <B>{@code <jre><version>}</B><BR>
|
||||
* while non legacy versions will be expanded to something like:<BR>
|
||||
* <B>{@code <jre>-<version>}</B>
|
||||
*/
|
||||
public static String getExpandJrePath(JreVersion aJreVersion)
|
||||
{
|
||||
String version;
|
||||
|
||||
version = aJreVersion.getLabel();
|
||||
if (version.startsWith("1.") == true)
|
||||
return "jre" + version;
|
||||
else
|
||||
return "jre-" + version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the available JRE releases specified at: <BR>
|
||||
* {@literal <aUpdateSiteUrl>/jre/jreCatalog.txt}
|
||||
*/
|
||||
public static List<JreRelease> getAvailableJreReleases(Task aTask, URL aUpdateSiteUrl, Credential aCredential)
|
||||
{
|
||||
@@ -28,24 +55,22 @@ public class JreUtils
|
||||
URLConnection connection;
|
||||
InputStream inStream;
|
||||
BufferedReader bufReader;
|
||||
DigestType digestType;
|
||||
String errMsg, strLine;
|
||||
String version;
|
||||
|
||||
errMsg = null;
|
||||
retList = new ArrayList<>();
|
||||
catUrl = IoUtil.createURL(aUpdateSiteUrl.toString() + "/jre/jreCatalog.txt");
|
||||
|
||||
// Default to DigestType of MD5
|
||||
digestType = DigestType.MD5;
|
||||
version = null;
|
||||
PlainVersion alMinVer = PlainVersion.Zero;
|
||||
PlainVersion alMaxVer = PlainVersion.AbsMax;
|
||||
DigestType digestType = DigestType.MD5;
|
||||
String version = null;
|
||||
|
||||
inStream = null;
|
||||
bufReader = null;
|
||||
try
|
||||
{
|
||||
String[] tokens;
|
||||
|
||||
// Read the contents of the file
|
||||
connection = catUrl.openConnection();
|
||||
inStream = NetUtil.getInputStream(connection, aCredential);
|
||||
@@ -60,13 +85,28 @@ public class JreUtils
|
||||
if (strLine == null)
|
||||
break;
|
||||
|
||||
tokens = strLine.split(",", 4);
|
||||
// Ignore comments and empty lines
|
||||
if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
|
||||
; // Nothing to do
|
||||
else if (tokens.length >= 1 && tokens[0].equals("exit") == true)
|
||||
break; // Bail once we get the 'exit' command
|
||||
else if (tokens.length == 2 && tokens[0].equals("name") == true && tokens[1].equals("JRE") == true)
|
||||
continue;
|
||||
|
||||
String[] tokens;
|
||||
tokens = strLine.split(",", 5);
|
||||
if (tokens.length == 2 && tokens[0].equals("name") == true && tokens[1].equals("JRE") == true)
|
||||
; // Nothing to do - we just entered the "JRE" section
|
||||
// Logic to handle the 'exit' command
|
||||
else if (tokens.length >= 1 && tokens[0].equals("exit") == true)
|
||||
{
|
||||
// We support exit commands with 3 tokens. All others
|
||||
// we will just exit.
|
||||
if (tokens.length != 3)
|
||||
break;
|
||||
|
||||
String targName = tokens[1];
|
||||
String needVer = tokens[2];
|
||||
if (ParseUtils.shouldExitLogic(targName, needVer) == true)
|
||||
break;
|
||||
}
|
||||
// Logic to handle the 'digest' command
|
||||
else if (tokens.length == 2 && tokens[0].equals("digest") == true)
|
||||
{
|
||||
DigestType tmpDigestType;
|
||||
@@ -77,10 +117,57 @@ public class JreUtils
|
||||
else
|
||||
digestType = tmpDigestType;
|
||||
}
|
||||
// Logic to handle the 'jre' command
|
||||
else if (tokens.length == 2 && tokens[0].equals("jre") == true)
|
||||
{
|
||||
version = tokens[1];
|
||||
|
||||
// On any new JRE version reset the default required AppLauncher versions
|
||||
alMinVer = PlainVersion.Zero;
|
||||
alMaxVer = new PlainVersion(0, 99, 0);
|
||||
}
|
||||
// Logic to handle the 'require' command: JRE File
|
||||
else if (tokens.length >= 3 && tokens[0].equals("require") == true)
|
||||
{
|
||||
String target;
|
||||
|
||||
// Process the require,AppLauncher instruction
|
||||
target = tokens[1];
|
||||
if (target.equals("AppLauncher") == true && (tokens.length == 3 || tokens.length == 4))
|
||||
{
|
||||
alMinVer = PlainVersion.parse(tokens[2]);
|
||||
alMaxVer = new PlainVersion(0, 99, 0);
|
||||
if (tokens.length == 4)
|
||||
alMaxVer = PlainVersion.parse(tokens[3]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
aTask.infoAppendln("Unreconized line: " + strLine);
|
||||
}
|
||||
// Logic to handle the 'F' command: JRE File
|
||||
else if (tokens.length == 5 && tokens[0].equals("F") == true)
|
||||
{
|
||||
String platform, filename, digestStr;
|
||||
long fileLen;
|
||||
|
||||
if (version == null)
|
||||
{
|
||||
aTask.infoAppendln("Skipping input: " + strLine);
|
||||
aTask.infoAppendln("\tJRE version has not been specifed. Missing input line: jre,<jreVersion>");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Form the JreRelease
|
||||
digestStr = tokens[1];
|
||||
fileLen = GuiUtil.readLong(tokens[2], -1);
|
||||
platform = tokens[3];
|
||||
filename = tokens[4];
|
||||
|
||||
Digest tmpDigest = new Digest(digestType, digestStr);
|
||||
retList.add(new JreRelease(platform, version, filename, tmpDigest, fileLen, alMinVer, alMaxVer));
|
||||
}
|
||||
// Legacy Logic to handle the 'F' command: JRE File (pre DistMaker 0.50)
|
||||
else if (tokens.length == 4 && tokens[0].equals("F") == true)
|
||||
{
|
||||
String platform, filename, digestStr;
|
||||
@@ -98,7 +185,7 @@ public class JreUtils
|
||||
fileLen = GuiUtil.readLong(tokens[2], -1);
|
||||
filename = tokens[3];
|
||||
|
||||
platform = PlatformUtils.getPlatformOfJreTarGz(filename);
|
||||
platform = JreUtils.getPlatformOfJreTarGz(filename);
|
||||
if (platform == null)
|
||||
{
|
||||
aTask.infoAppendln("Skipping input: " + strLine);
|
||||
@@ -106,7 +193,7 @@ public class JreUtils
|
||||
continue;
|
||||
}
|
||||
|
||||
retList.add(new JreRelease(platform, version, filename, new Digest(digestType, digestStr), fileLen));
|
||||
retList.add(new JreRelease(platform, version, filename, new Digest(digestType, digestStr), fileLen, alMinVer, alMaxVer));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,8 +232,8 @@ public class JreUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns a list of matching JREs. The list will be sorted in order from newest to oldest. All returned JREs will have a platform that
|
||||
* matches aPlatform.
|
||||
* Utility method that returns a list of matching JREs. The list will be sorted in order from newest to oldest. All
|
||||
* returned JREs will have a platform that matches aPlatform.
|
||||
*/
|
||||
public static List<JreRelease> getMatchingPlatforms(List<JreRelease> aJreList, String aPlatform)
|
||||
{
|
||||
@@ -169,4 +256,25 @@ public class JreUtils
|
||||
return retList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns the platform of the JRE file.
|
||||
* <P>
|
||||
* This only examines the filename to determine the platform.
|
||||
* <P>
|
||||
* This method should be considered deprecated as of DistMaker 0.48
|
||||
*/
|
||||
@Deprecated
|
||||
private static String getPlatformOfJreTarGz(String aFileName)
|
||||
{
|
||||
aFileName = aFileName.toUpperCase();
|
||||
if (aFileName.contains("LINUX") == true)
|
||||
return "Linux";
|
||||
if (aFileName.contains("MACOSX") == true)
|
||||
return "Apple";
|
||||
if (aFileName.contains("WINDOWS") == true)
|
||||
return "Windows";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,46 @@
|
||||
package distMaker.jre;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import distMaker.utils.Version;
|
||||
import glum.gui.GuiUtil;
|
||||
|
||||
public class JreVersion implements Comparable<JreVersion>
|
||||
/**
|
||||
* Immutable class which defines a Java version.
|
||||
*/
|
||||
public class JreVersion implements Comparable<JreVersion>, Version
|
||||
{
|
||||
private String version;
|
||||
/** String used to construct this JreVersion */
|
||||
private final String label;
|
||||
|
||||
public JreVersion(String aVersion)
|
||||
/** List of integers corresponding to the various components of the JRE version. */
|
||||
private final ImmutableList<Integer> compL;
|
||||
|
||||
/** Flag for legacy JRE versions. JRE versions prior to 9.0 are considered legacy. */
|
||||
private final boolean isLegacy;
|
||||
|
||||
public JreVersion(String aLabel)
|
||||
{
|
||||
version = aVersion;
|
||||
label = aLabel;
|
||||
|
||||
String[] tokenArr = label.split("[._]");
|
||||
ArrayList<Integer> workL = new ArrayList<>();
|
||||
for (String aStr : tokenArr)
|
||||
{
|
||||
int tmpVal = GuiUtil.readInt(aStr, Integer.MIN_VALUE);
|
||||
if (tmpVal == Integer.MIN_VALUE)
|
||||
break;
|
||||
|
||||
workL.add(tmpVal);
|
||||
}
|
||||
compL = ImmutableList.copyOf(workL);
|
||||
|
||||
if (compL.size() >= 2 && compL.get(0) == 1)
|
||||
isLegacy = true;
|
||||
else
|
||||
isLegacy = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,7 +48,7 @@ public class JreVersion implements Comparable<JreVersion>
|
||||
*/
|
||||
public String getLabel()
|
||||
{
|
||||
return version;
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,43 +61,78 @@ public class JreVersion implements Comparable<JreVersion>
|
||||
public static JreVersion getBetterVersion(JreVersion verA, JreVersion verB)
|
||||
{
|
||||
JreVersion defaultVer;
|
||||
String[] tokenA, tokenB;
|
||||
int valA, valB, idxCnt;
|
||||
|
||||
tokenA = verA.getLabel().split("[._]");
|
||||
tokenB = verB.getLabel().split("[._]");
|
||||
|
||||
// Default JreVersion is the version that is more specific
|
||||
defaultVer = null;
|
||||
if (tokenA.length < tokenB.length)
|
||||
if (verA.compL.size() < verB.compL.size())
|
||||
defaultVer = verB;
|
||||
else if (tokenB.length < tokenA.length)
|
||||
else if (verB.compL.size() < verA.compL.size())
|
||||
defaultVer = verA;
|
||||
|
||||
// Set the idxCnt to the less specific JreVersion
|
||||
idxCnt = tokenA.length;
|
||||
if (tokenB.length < tokenA.length)
|
||||
idxCnt = tokenB.length;
|
||||
idxCnt = Math.min(verA.compL.size(), verB.compL.size());
|
||||
|
||||
// Compare each component of the version string. Each component should be separated by '.'
|
||||
// Assume each component is an integer where larger values correspond to later versions
|
||||
// Compare each integral component (which originated from the label)
|
||||
// Assume higher values correspond to later versions
|
||||
for (int c1 = 0; c1 < idxCnt; c1++)
|
||||
{
|
||||
valA = GuiUtil.readInt(tokenA[c1], -1);
|
||||
valB = GuiUtil.readInt(tokenB[c1], -1);
|
||||
if (valA == -1 && valB == -1)
|
||||
return null;
|
||||
|
||||
if (valB == -1 || valA > valB)
|
||||
valA = verA.compL.get(c1);
|
||||
valB = verB.compL.get(c1);
|
||||
if (valA > valB)
|
||||
return verA;
|
||||
if (valA == -1 || valB > valA)
|
||||
if (valB > valA)
|
||||
return verB;
|
||||
}
|
||||
|
||||
// Defaults to verA
|
||||
// Defaults to the defaultVer
|
||||
return defaultVer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion()
|
||||
{
|
||||
if (isLegacy == true)
|
||||
return compL.get(1);
|
||||
|
||||
if (compL.size() >= 1)
|
||||
return compL.get(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinorVersion()
|
||||
{
|
||||
if (isLegacy == true)
|
||||
{
|
||||
if (compL.size() >= 3)
|
||||
return compL.get(2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compL.size() >= 2)
|
||||
return compL.get(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPatchVersion()
|
||||
{
|
||||
if (isLegacy == true)
|
||||
{
|
||||
if (compL.size() >= 4)
|
||||
return compL.get(3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compL.size() >= 3)
|
||||
return compL.get(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(JreVersion aItem)
|
||||
{
|
||||
|
||||
@@ -88,7 +88,7 @@ public class AppCatalog
|
||||
*/
|
||||
public boolean isJreVersionTooNew(JreVersion aJreVer)
|
||||
{
|
||||
// Check to make sure aJreVer is not too old
|
||||
// Check to make sure aJreVer is not too new
|
||||
if (maxJreVer != null && JreVersion.getBetterVersion(maxJreVer, aJreVer) == aJreVer)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -11,12 +11,19 @@ public class AppRelease implements Comparable<AppRelease>, QueryItem<LookUp>
|
||||
private final String appName;
|
||||
private final String version;
|
||||
private final long buildTime;
|
||||
private final String infoMsg;
|
||||
|
||||
public AppRelease(String aAppName, String aVersion, long aBuildTime)
|
||||
public AppRelease(String aAppName, String aVersion, long aBuildTime, String aInfoMsg)
|
||||
{
|
||||
appName = aAppName;
|
||||
version = aVersion;
|
||||
buildTime = aBuildTime;
|
||||
infoMsg = aInfoMsg;
|
||||
}
|
||||
|
||||
public AppRelease(String aAppName, String aVersion, long aBuildTime)
|
||||
{
|
||||
this(aAppName, aVersion, aBuildTime, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +49,16 @@ public class AppRelease implements Comparable<AppRelease>, QueryItem<LookUp>
|
||||
{
|
||||
return buildTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the info message associated with this release.
|
||||
* <P>
|
||||
* Returns null if no infoMsg has been specified.
|
||||
*/
|
||||
public String getInfoMsg()
|
||||
{
|
||||
return infoMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AppRelease o)
|
||||
|
||||
@@ -13,6 +13,7 @@ import javax.xml.transform.stream.StreamResult;
|
||||
import org.w3c.dom.*;
|
||||
|
||||
import distMaker.*;
|
||||
import distMaker.jre.JreUtils;
|
||||
import distMaker.jre.JreVersion;
|
||||
|
||||
/**
|
||||
@@ -54,8 +55,8 @@ public class AppleUtils
|
||||
/**
|
||||
* Utility method to update the specified version in the plist file (pFile) to the new version.
|
||||
* <P>
|
||||
* Note this method is very brittle, and assumes that the version will occur in the sibling node which immediately follows the node with a value of
|
||||
* CFBundleVersion. TODO: Consider reducing brittleness.
|
||||
* Note this method is very brittle, and assumes that the version will occur in the sibling node which immediately
|
||||
* follows the node with a value of CFBundleVersion. TODO: Consider reducing brittleness.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
@@ -68,8 +69,8 @@ public class AppleUtils
|
||||
/**
|
||||
* Utility method to update the specified version in the plist file (pFile) to the new version.
|
||||
* <P>
|
||||
* Note this method is very brittle, and assumes that the version will occur in the sibling node which immediately follows the node with a value of
|
||||
* CFBundleVersion. TODO: Consider reducing brittleness.
|
||||
* Note this method is very brittle, and assumes that the version will occur in the sibling node which immediately
|
||||
* follows the node with a value of CFBundleVersion. TODO: Consider reducing brittleness.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
@@ -170,7 +171,7 @@ public class AppleUtils
|
||||
|
||||
// Update the pFile
|
||||
String regex = "<string>(.*?)</string>";
|
||||
String repStr = "<string>jre" + aJreVersion.getLabel() + "</string>";
|
||||
String repStr = "<string>" + JreUtils.getExpandJrePath(aJreVersion) + "</string>";
|
||||
if (targLineNum == -1)
|
||||
throw new ErrorDM("[" + pFile + "] The pFile does not specify a 'JVMRuntime' section.");
|
||||
else if (targLineNum >= inputList.size())
|
||||
@@ -183,10 +184,11 @@ public class AppleUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to update the specified max memory (-Xmx) value in the system plist file to the specified maxMemVal.
|
||||
* Utility method to update the specified max memory (-Xmx) value in the system plist file to the specified
|
||||
* maxMemVal.
|
||||
* <P>
|
||||
* In order for this method to succeed there must be a valid JVMOptions section followed by an array of string elements of JVM arguments. The array element
|
||||
* may be empty but must be specified.
|
||||
* In order for this method to succeed there must be a valid JVMOptions section followed by an array of string
|
||||
* elements of JVM arguments. The array element may be empty but must be specified.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
@@ -197,10 +199,11 @@ public class AppleUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to update the specified max memory (-Xmx) value in the plist file (pFile) to the specified maxMemVal.
|
||||
* Utility method to update the specified max memory (-Xmx) value in the plist file (pFile) to the specified
|
||||
* maxMemVal.
|
||||
* <P>
|
||||
* In order for this method to succeed there must be a valid JVMOptions section followed by an array of string elements of JVM arguments. The array element
|
||||
* may be empty but must be specified.
|
||||
* In order for this method to succeed there must be a valid JVMOptions section followed by an array of string
|
||||
* elements of JVM arguments. The array element may be empty but must be specified.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import distMaker.*;
|
||||
import distMaker.jre.JreUtils;
|
||||
import distMaker.jre.JreVersion;
|
||||
|
||||
public class LinuxUtils
|
||||
@@ -15,8 +16,8 @@ public class LinuxUtils
|
||||
* <P>
|
||||
* If there are multiple launch scripts then this method may grab the wrong file and fail.
|
||||
* <P>
|
||||
* TODO: In the future the launch script should pass itself as an argument to the JVM and DistMaker should keep track of that. If the script is significantly
|
||||
* manipulated from the original the launch file may be improperly detected.
|
||||
* TODO: In the future the launch script should pass itself as an argument to the JVM and DistMaker should keep track
|
||||
* of that. If the script is significantly manipulated from the original the launch file may be improperly detected.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
@@ -45,7 +46,7 @@ public class LinuxUtils
|
||||
if (retFile.isFile() == false)
|
||||
throw new ErrorDM("The script file is NOT a regular file.");
|
||||
|
||||
// Ensure the file is executable. If this is really the script file used to launch us then it should be executable!
|
||||
// Ensure the file is executable. If this is the script file used to launch us then it should be executable!
|
||||
if (Files.isExecutable(retFile.toPath()) == false)
|
||||
throw new ErrorDM("The script file is NOT executable.");
|
||||
|
||||
@@ -107,7 +108,7 @@ public class LinuxUtils
|
||||
|
||||
// Update the script
|
||||
if (targLineNum != -1)
|
||||
inputList.set(targLineNum, "javaExe=../jre" + aJreVersion.getLabel() + "/bin/java");
|
||||
inputList.set(targLineNum, "javaExe=../" + JreUtils.getExpandJrePath(aJreVersion) + "/bin/java");
|
||||
else
|
||||
throw new ErrorDM("[" + aScriptFile + "] The script does not specify 'javaExe'.");
|
||||
|
||||
@@ -118,11 +119,11 @@ public class LinuxUtils
|
||||
/**
|
||||
* Utility method to update the specified maxMem var in the script (aFile) to the requested number of bytes.
|
||||
* <P>
|
||||
* Note this method assumes the specified file is a shell script built by DistMaker where the var maxMem holds the proper (right side) specification for the
|
||||
* JVM's -Xmx value.
|
||||
* Note this method assumes the specified file is a shell script built by DistMaker where the var maxMem holds the
|
||||
* proper (right side) specification for the JVM's -Xmx value.
|
||||
* <P>
|
||||
* If the maxMem var definition is moved in the script file to after the launch of the application then this method will (silently) fail to configure the
|
||||
* value needed to launch the JVM.
|
||||
* If the maxMem var definition is moved in the script file to after the launch of the application then this method
|
||||
* will (silently) fail to configure the value needed to launch the JVM.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
@@ -135,11 +136,11 @@ public class LinuxUtils
|
||||
/**
|
||||
* Utility method to update the specified maxMem var in the script (aFile) to the requested number of bytes.
|
||||
* <P>
|
||||
* Note this method assumes the specified file is a shell script built by DistMaker where the var maxMem holds the proper (right side) specification for the
|
||||
* JVM's -Xmx value.
|
||||
* Note this method assumes the specified file is a shell script built by DistMaker where the var maxMem holds the
|
||||
* proper (right side) specification for the JVM's -Xmx value.
|
||||
* <P>
|
||||
* If the maxMem var definition is moved in the script file to after the launch of the application then this method will (silently) fail to configure the
|
||||
* value needed to launch the JVM.
|
||||
* If the maxMem var definition is moved in the script file to after the launch of the application then this method
|
||||
* will (silently) fail to configure the value needed to launch the JVM.
|
||||
* <P>
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,8 @@ public class MemUtils
|
||||
public static final long GB_SIZE = 1024 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Utility method that attempts to compute the installed system memory (ram). If the installed system ram can not be computed, then the system is assumed to
|
||||
* have 4 GB.
|
||||
* Utility method that attempts to compute the installed system memory (ram). If the installed system ram can not be
|
||||
* computed, then the system is assumed to have 4 GB.
|
||||
*/
|
||||
public static long getInstalledSystemMemory()
|
||||
{
|
||||
@@ -57,7 +57,8 @@ public class MemUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that takes an inputStr, locates the fragment -Xmx*, and replaces the fragment with the appropriate -Xmx with respect to numBytes.
|
||||
* Utility method that takes an inputStr, locates the fragment -Xmx*, and replaces the fragment with the appropriate
|
||||
* -Xmx with respect to numBytes.
|
||||
* <P>
|
||||
* This method is a bit brittle in that it assumes the -Xmx string is surrounded with 1 white space character.
|
||||
* <P>
|
||||
|
||||
@@ -4,8 +4,7 @@ import java.io.File;
|
||||
|
||||
import distMaker.DistUtils;
|
||||
import distMaker.ErrorDM;
|
||||
import distMaker.jre.JreRelease;
|
||||
import distMaker.jre.JreVersion;
|
||||
import distMaker.jre.*;
|
||||
import distMaker.node.AppRelease;
|
||||
|
||||
public class PlatformUtils
|
||||
@@ -45,22 +44,22 @@ public class PlatformUtils
|
||||
*/
|
||||
public static File getJreLocation(JreVersion aJreVersion)
|
||||
{
|
||||
String platform, versionStr;
|
||||
File jrePath, installPath;
|
||||
String platform, relJrePathStr;
|
||||
File installPath, retJrePath;
|
||||
|
||||
jrePath = null;
|
||||
retJrePath = null;
|
||||
installPath = DistUtils.getAppPath();
|
||||
versionStr = aJreVersion.getLabel();
|
||||
relJrePathStr = JreUtils.getExpandJrePath(aJreVersion);
|
||||
|
||||
platform = PlatformUtils.getPlatform().toUpperCase();
|
||||
if (platform.equals("APPLE") == true)
|
||||
jrePath = new File(installPath.getParentFile(), "PlugIns/jre" + versionStr);
|
||||
retJrePath = new File(installPath.getParentFile(), "PlugIns/" + relJrePathStr);
|
||||
else if (platform.equals("LINUX") == true)
|
||||
jrePath = new File(installPath.getParentFile(), "jre" + versionStr);
|
||||
retJrePath = new File(installPath.getParentFile(), relJrePathStr);
|
||||
else if (platform.equals("WINDOWS") == true)
|
||||
jrePath = new File(installPath.getParentFile(), "jre" + versionStr);
|
||||
retJrePath = new File(installPath.getParentFile(), relJrePathStr);
|
||||
|
||||
return jrePath;
|
||||
return retJrePath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,24 +80,6 @@ public class PlatformUtils
|
||||
return System.getProperty("os.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the platform of the JRE file.
|
||||
* <P>
|
||||
* This only examines the filename to determine the platform.
|
||||
*/
|
||||
public static String getPlatformOfJreTarGz(String aFileName)
|
||||
{
|
||||
aFileName = aFileName.toUpperCase();
|
||||
if (aFileName.contains("LINUX") == true)
|
||||
return "Linux";
|
||||
if (aFileName.contains("MACOSX") == true)
|
||||
return "Apple";
|
||||
if (aFileName.contains("WINDOWS") == true)
|
||||
return "Windows";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to configure the JRE version used by the (active) DistMaker distribution.
|
||||
* <P>
|
||||
@@ -107,7 +88,7 @@ public class PlatformUtils
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*
|
||||
* @param aJrePath
|
||||
* Path to top of the JRE.
|
||||
* Path to top of the JRE.
|
||||
*/
|
||||
public static void setJreVersion(JreVersion aJreVersion)
|
||||
{
|
||||
@@ -133,7 +114,7 @@ public class PlatformUtils
|
||||
* On failure this method will throw an exception of type ErrorDM.
|
||||
*
|
||||
* @param maxMemSize
|
||||
* Maximum heap memory in bytes.
|
||||
* Maximum heap memory in bytes.
|
||||
*/
|
||||
public static void setMaxHeapMem(long maxMemSize)
|
||||
{
|
||||
|
||||
@@ -84,7 +84,7 @@ public final class DeployUtils {
|
||||
|
||||
updateButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(@SuppressWarnings("unused") ActionEvent e)
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
UpdateCheckListener listener = new UpdateCheckListener() {
|
||||
@Override
|
||||
@@ -105,7 +105,7 @@ public final class DeployUtils {
|
||||
|
||||
configureMemoryButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(@SuppressWarnings("unused") ActionEvent e)
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
if(DistUtils.isDevelopersEnvironment()) {
|
||||
JOptionPane.showMessageDialog(parentFrame, "Cannot configure memory in a developer environment.");
|
||||
|
||||
64
src/distMaker/utils/ParseUtils.java
Normal file
64
src/distMaker/utils/ParseUtils.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package distMaker.utils;
|
||||
|
||||
import distMaker.DistUtils;
|
||||
|
||||
public class ParseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Utility method that processes the 'exit' instruction.
|
||||
* <P>
|
||||
* Returns true if the processing of the configuration file should exit.
|
||||
* <P>
|
||||
* Processing of the configuration file should exit if the specified needed version is not met or the version string
|
||||
* could not be parsed into major minor components.
|
||||
*
|
||||
* @param aTargName
|
||||
* The target component whose version will be evaluated. Current supported values are one of the following:
|
||||
* [AppLauncher, DistMaker]
|
||||
* @param aNeededVer
|
||||
* A string describing the minimum version that is required in order for this exit instruction to be ignored.
|
||||
* @return Returns true if the needed version requirements are not met.
|
||||
*/
|
||||
public static boolean shouldExitLogic(String aTargName, String aNeededVer)
|
||||
{
|
||||
// We handle logic for the following targets: [AppLauncher, DistMaker]
|
||||
// If not one of the specified targets then further parsing should stop
|
||||
Version evalVer;
|
||||
if (aTargName.equals("DistMaker") == true)
|
||||
evalVer = DistUtils.getDistMakerVersion();
|
||||
else if (aTargName.equals("AppLauncher") == true)
|
||||
evalVer = DistUtils.getAppLauncherVersion();
|
||||
else
|
||||
return true;
|
||||
|
||||
// Determine the needed version
|
||||
int needMajorVer = Integer.MAX_VALUE;
|
||||
int needMinorVer = Integer.MAX_VALUE;
|
||||
try
|
||||
{
|
||||
String[] versionArr;
|
||||
|
||||
versionArr = aNeededVer.split("\\.");
|
||||
if (versionArr.length >= 1)
|
||||
needMajorVer = Integer.parseInt(versionArr[0]);
|
||||
if (versionArr.length >= 2)
|
||||
needMinorVer = Integer.parseInt(versionArr[1]);
|
||||
}
|
||||
catch(Throwable aExp)
|
||||
{
|
||||
// Ignore just assume version components are whatever we managed to parse
|
||||
}
|
||||
Version needVer;
|
||||
needVer = new PlainVersion(needMajorVer, needMinorVer, 0);
|
||||
|
||||
// Exit the logic if the needVer > evalVer
|
||||
if (needVer.getMajorVersion() > evalVer.getMajorVersion())
|
||||
return true;
|
||||
if (needVer.getMajorVersion() == needVer.getMajorVersion() && needVer.getMinorVersion() > evalVer.getMinorVersion())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
72
src/distMaker/utils/PlainVersion.java
Normal file
72
src/distMaker/utils/PlainVersion.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package distMaker.utils;
|
||||
|
||||
/**
|
||||
* Provides the standard implementation of the Version interface.
|
||||
*/
|
||||
public class PlainVersion implements Version
|
||||
{
|
||||
// Constants
|
||||
public static PlainVersion AbsMin = new PlainVersion(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
|
||||
public static PlainVersion AbsMax = new PlainVersion(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
public static PlainVersion Zero = new PlainVersion(0, 0, 0);
|
||||
|
||||
// Attributes
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int patch;
|
||||
|
||||
public PlainVersion(int aMajor, int aMinor, int aPatch)
|
||||
{
|
||||
major = aMajor;
|
||||
minor = aMinor;
|
||||
patch = aPatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms a PlainVersion from the specified string. The version should have have at most 3 integer components
|
||||
* separated by the char: '.'. Any extra components after the first 3 will be ignored. A NumberFormatException will
|
||||
* be thrown if the type of any of the first 3 are not integers.
|
||||
*/
|
||||
public static PlainVersion parse(String aStr)
|
||||
{
|
||||
String[] tokenArr = aStr.split("\\.");
|
||||
|
||||
int major = 0, minor = 0, patch = 0;
|
||||
major = Integer.parseInt(tokenArr[0]);
|
||||
if (tokenArr.length >= 2)
|
||||
minor = Integer.parseInt(tokenArr[1]);
|
||||
if (tokenArr.length >= 3)
|
||||
patch = Integer.parseInt(tokenArr[2]);
|
||||
|
||||
return new PlainVersion(major, minor, patch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion()
|
||||
{
|
||||
return major;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinorVersion()
|
||||
{
|
||||
return minor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPatchVersion()
|
||||
{
|
||||
return patch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String retStr = "" + major + "." + minor;
|
||||
if (patch != 0)
|
||||
retStr += "." + patch;
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/distMaker/utils/Version.java
Normal file
29
src/distMaker/utils/Version.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package distMaker.utils;
|
||||
|
||||
/**
|
||||
* Interface which provides access to version components (major, minor, patch).
|
||||
* <P>
|
||||
* Each component is modeled as an integer and it is assumed that higher values correspond to more developed software.
|
||||
* <P>
|
||||
* Reference: https://semver.org/
|
||||
* <P>
|
||||
* Implementors of this interface should be immutable.
|
||||
*/
|
||||
public interface Version
|
||||
{
|
||||
/**
|
||||
* Returns the major version component.
|
||||
*/
|
||||
public int getMajorVersion();
|
||||
|
||||
/**
|
||||
* Returns the minor version component.
|
||||
*/
|
||||
public int getMinorVersion();
|
||||
|
||||
/**
|
||||
* Returns the patch version component.
|
||||
*/
|
||||
public int getPatchVersion();
|
||||
|
||||
}
|
||||
94
src/distMaker/utils/VersionUtils.java
Normal file
94
src/distMaker/utils/VersionUtils.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package distMaker.utils;
|
||||
|
||||
/**
|
||||
* Utility class that allows for comparing Versions.
|
||||
* <P>
|
||||
* Eventually when Java allows operator overloading then this class can go away since the standard mathematical
|
||||
* comparison symbols would be much clearer.
|
||||
*/
|
||||
public class VersionUtils
|
||||
{
|
||||
/**
|
||||
* Utility method that returns true if aVerA occurs after aVerB
|
||||
*/
|
||||
public static boolean isAfter(Version aVerA, Version aVerB)
|
||||
{
|
||||
int majorA = aVerA.getMajorVersion();
|
||||
int minorA = aVerA.getMinorVersion();
|
||||
int patchA = aVerA.getPatchVersion();
|
||||
int majorB = aVerB.getMajorVersion();
|
||||
int minorB = aVerB.getMinorVersion();
|
||||
int patchB = aVerB.getPatchVersion();
|
||||
|
||||
if (majorA > majorB)
|
||||
return true;
|
||||
if (majorA == majorB && minorA > minorB)
|
||||
return true;
|
||||
if (majorA == majorB && minorA == minorB && patchA > patchB)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns true if aVerA occurs after aVerB
|
||||
*/
|
||||
public static boolean isAfterOrEquar(Version aVerA, Version aVerB)
|
||||
{
|
||||
// Delegate to isAfter
|
||||
return isAfter(aVerB, aVerA) == false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns true if the following statement is true:
|
||||
* <P>
|
||||
* aVerEval >= aVerMin && aVerEval <= aVerMax
|
||||
* <P>
|
||||
* A LogicError will be thrown if the aVerMin and aVerMax are inverted (aVerMin > aVerMax)
|
||||
*/
|
||||
public static boolean isInRange(Version aVerEval, Version aVerMin, Version aVerMax)
|
||||
{
|
||||
// Ensure the endpoints are not inverted
|
||||
if (isAfter(aVerMin, aVerMax) == true)
|
||||
throw new RuntimeException("Min/Max versions appear to be swapped. min: " + aVerMin + " max: " + aVerMax);
|
||||
|
||||
// Decompose and delegate
|
||||
if (isAfter(aVerMin, aVerEval) == true)
|
||||
return false;
|
||||
if (isAfter(aVerEval, aVerMax) == true)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to allow the comparison of two versions.
|
||||
*
|
||||
* @param aVerA
|
||||
* @param aVerB
|
||||
* @return
|
||||
*/
|
||||
public static int compare(Version aVerA, Version aVerB)
|
||||
{
|
||||
|
||||
int majorA = aVerA.getMajorVersion();
|
||||
int minorA = aVerA.getMinorVersion();
|
||||
int patchA = aVerA.getPatchVersion();
|
||||
int majorB = aVerB.getMajorVersion();
|
||||
int minorB = aVerB.getMinorVersion();
|
||||
int patchB = aVerB.getPatchVersion();
|
||||
|
||||
int cmpVal;
|
||||
cmpVal = majorA - majorB;
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
cmpVal = minorA - minorB;
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
cmpVal = patchA - patchB;
|
||||
if (cmpVal != 0)
|
||||
return cmpVal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,30 +1,14 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import fnmatch
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Globals
|
||||
# The default version of DistMaker
|
||||
version = '0.47'
|
||||
|
||||
|
||||
def logAndPrint(message="", indent=0, showTime=False):
|
||||
while indent > 0:
|
||||
indent -= 1
|
||||
message = ' ' + message
|
||||
if showTime == True:
|
||||
message = '[' + getCurrTimeStr() + '] ' + message;
|
||||
# logging.info(message)
|
||||
print(message)
|
||||
|
||||
|
||||
def buildRelease(version, doNotClean=False):
|
||||
def getDistMakerVersion():
|
||||
# Retrieve the install path
|
||||
installPath = getInstallRoot()
|
||||
installPath = os.path.dirname(installPath)
|
||||
@@ -32,15 +16,38 @@ def buildRelease(version, doNotClean=False):
|
||||
# Check for distMaker.jar library prerequisite
|
||||
testPath = os.path.join(installPath, 'lib', 'distMaker.jar')
|
||||
if os.path.exists(testPath) == False:
|
||||
logAndPrint('Aborting DistMaker release build. The file ' + testPath + ' does not exist.', indent=1)
|
||||
logAndPrint('Please run the buildDistMakerBin.jardesc from your workspace.', indent=1)
|
||||
errPrintln('Aborting DistMaker release build. The file ' + testPath + ' does not exist.', indent=1)
|
||||
errPrintln('Please run the buildDistMakerBin.jardesc from your workspace.', indent=1)
|
||||
exit(-1)
|
||||
|
||||
try:
|
||||
exeCmd = ['java', '-cp', 'lib/distMaker.jar', 'distMaker.DistApp', '--version']
|
||||
output = subprocess.check_output(exeCmd).decode('utf-8')
|
||||
version = output.split()[1]
|
||||
return version
|
||||
except:
|
||||
errPrintln('Please run the buildDistMakerBin.jardesc from your workspace.', indent=1)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def errPrintln(message="", indent=0):
|
||||
"""Print the specified string with a trailing newline to stderr."""
|
||||
while indent > 0:
|
||||
indent -= 1
|
||||
message = ' ' + message
|
||||
sys.stderr.write(message + '\n')
|
||||
|
||||
|
||||
def buildRelease(version, doNotClean=False):
|
||||
# Retrieve the install path
|
||||
installPath = getInstallRoot()
|
||||
installPath = os.path.dirname(installPath)
|
||||
|
||||
# Check for static jre prerequisites
|
||||
isPass = os.path.exists(os.path.join(installPath, 'jre'))
|
||||
if isPass == False:
|
||||
logAndPrint('Aborting DistMaker release build. The jre path is not properly configured.', indent=1)
|
||||
logAndPrint('Please setup the jre path properly. A quick fix is to copy the jre tree from a previous release of DistMaker.', indent=1)
|
||||
errPrintln('Aborting DistMaker release build. The jre path is not properly configured.', indent=1)
|
||||
errPrintln('Please setup the jre path properly. A quick fix is to copy the jre tree from a previous release of DistMaker.', indent=1)
|
||||
exit(-1)
|
||||
|
||||
# Determine the workPath
|
||||
@@ -49,12 +56,12 @@ def buildRelease(version, doNotClean=False):
|
||||
|
||||
# Bail if the work folder for which we compose the release already exists
|
||||
if os.path.exists(workPath) == True:
|
||||
logAndPrint('Aborting DistMaker release build. Release folder already exists: ' + workPath, indent=1)
|
||||
errPrintln('Aborting DistMaker release build. Release folder already exists: ' + workPath, indent=1)
|
||||
exit(-1)
|
||||
|
||||
# Bail if the release already exists
|
||||
if os.path.exists(destFileGZ) == True:
|
||||
logAndPrint('Aborting DistMaker release build. Release already exists. File: ' + destFileGZ, indent=1)
|
||||
errPrintln('Aborting DistMaker release build. Release already exists. File: ' + destFileGZ, indent=1)
|
||||
exit(-1)
|
||||
|
||||
# Laydown the structure, and let the user know of the version we are building
|
||||
@@ -113,16 +120,6 @@ def getInstallRoot():
|
||||
return installRoot
|
||||
|
||||
|
||||
def printUsage():
|
||||
scriptName = os.path.split(sys.argv[0])[1]
|
||||
print(scriptName + ' [-help,-doNotClean,-full],[-version <aVersion>]')
|
||||
print(' -help: Show this help message and exit.')
|
||||
print(' -version: Version to build. Default is: ' + version)
|
||||
print(' -doNotClean: Do NOT remove temporary work folder created while generating release.')
|
||||
print(' -full: Force a full build of the main jar file. (Unsupported action)')
|
||||
exit(-1)
|
||||
|
||||
|
||||
def handleSignal(signal, frame):
|
||||
"""Signal handler, typically used to capture ctrl-c."""
|
||||
print('User aborted processing!')
|
||||
@@ -130,43 +127,31 @@ def handleSignal(signal, frame):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv = sys.argv
|
||||
argc = len(argv)
|
||||
|
||||
# Intercept any request for a help message and bail
|
||||
for aArg in argv:
|
||||
if aArg == '-h' or aArg == '-help':
|
||||
printUsage()
|
||||
exit()
|
||||
|
||||
doFullBuild = False
|
||||
doNotClean = False
|
||||
skipNext = False;
|
||||
for currIdx in xrange(1, len(argv)):
|
||||
aArg = argv[currIdx]
|
||||
if skipNext == True:
|
||||
skipNext = False
|
||||
continue
|
||||
if aArg == '-full':
|
||||
doFullBuild = True
|
||||
elif aArg == '-doNotClean':
|
||||
doNotClean = True
|
||||
elif aArg == '-version':
|
||||
if currIdx == argc - 1:
|
||||
print('\tPlease specify an actual version when using -version')
|
||||
exit(-1)
|
||||
else:
|
||||
version = argv[currIdx + 1]
|
||||
skipNext = True
|
||||
else:
|
||||
print(' Unrecognized argument: ' + aArg)
|
||||
printUsage()
|
||||
|
||||
# Logic to capture Ctrl-C and bail
|
||||
signal.signal(signal.SIGINT, handleSignal)
|
||||
|
||||
# TODO: Finish this functionality
|
||||
if doFullBuild == True:
|
||||
print("Unsupported action: [-full]. Skipping...")
|
||||
tmpDescr = 'Utility to build a DistMaker release\n'
|
||||
parser = argparse.ArgumentParser(prefix_chars='-', description=tmpDescr, add_help=False, fromfile_prefix_chars='@')
|
||||
parser.add_argument('-help', '-h', help='Show this help message and exit.', action='help')
|
||||
parser.add_argument('-doNotClean', default=False, action='store_true', help='Do NOT remove temporary work folder created while generating release.')
|
||||
parser.add_argument('-doFullBuild', default=False, action='store_true', help='Force a full build of the main jar file. (Unsupported action)')
|
||||
|
||||
buildRelease(version, doNotClean)
|
||||
# Intercept any request for a help message and bail
|
||||
argv = sys.argv;
|
||||
if '-h' in argv or '-help' in argv:
|
||||
parser.print_help()
|
||||
exit()
|
||||
|
||||
# Parse the args
|
||||
parser.formatter_class.max_help_position = 50
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get the version of DistMaker we are building
|
||||
version = getDistMakerVersion()
|
||||
print('DistMaker version: ' + version)
|
||||
|
||||
# TODO: Finish this functionality
|
||||
if args.doFullBuild == True:
|
||||
print("Unsupported action: [-doFullBuild]. Skipping...")
|
||||
|
||||
buildRelease(version, args.doNotClean)
|
||||
|
||||
Reference in New Issue
Block a user