Skip to content

Commit

Permalink
Merge pull request payara#325 from Pandrex247/FISH-973
Browse files Browse the repository at this point in the history
FISH-973 FISH-1174 Allow Upgrade Tool To Use A Predownloaded Distribution and Fix NoSuchFileException When Upgrading Payara Web
  • Loading branch information
Pandrex247 authored Mar 18, 2021
2 parents 86a772a + b4deb94 commit d658fc8
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
Expand Down Expand Up @@ -202,6 +203,13 @@ public FileVisitResult visitFile(Path arg0, BasicFileAttributes arg1) throws IOE

@Override
public FileVisitResult visitFileFailed(Path arg0, IOException arg1) throws IOException {
// Don't fail out on NSFE, just try to delete all of them
if (arg1 instanceof NoSuchFileException) {
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for directory {0} and continuing cleanup.",
arg0);
return FileVisitResult.SKIP_SUBTREE;
}

LOGGER.log(Level.SEVERE, "File could not deleted: {0}", arg0.toString());
throw arg1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.logging.Level;
Expand Down Expand Up @@ -85,7 +86,20 @@ protected int executeCommand() {
LOGGER.log(Level.INFO, "Rolling back server...");
for (String file : MOVEFOLDERS) {
Files.walkFileTree(Paths.get(glassfishDir, file), visitor);
Files.move(Paths.get(glassfishDir, file + ".old"), Paths.get(glassfishDir, file), StandardCopyOption.REPLACE_EXISTING);
try {
Files.move(Paths.get(glassfishDir, file + ".old"), Paths.get(glassfishDir, file),
StandardCopyOption.REPLACE_EXISTING);
} catch (NoSuchFileException nsfe) {
// We can't nicely check if the current or old installation is a web distribution or not, so just
// attempt to move all and specifically catch a FNFE for the MQ directory
if (nsfe.getMessage().contains(
"payara5" + File.separator + "glassfish" + File.separator + ".." + File.separator + "mq")) {
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for mq directory under assumption " +
"this is a payara-web distribution. Continuing to move files...");
} else {
throw nsfe;
}
}
}

// Roll back the nodes for all domains
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,68 +40,174 @@
package fish.payara.extras.upgrade;

import com.sun.enterprise.admin.cli.CLICommand;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import com.sun.enterprise.util.OS;
import com.sun.enterprise.util.StringUtils;
import org.glassfish.api.ExecutionContext;
import org.glassfish.api.Param;
import org.glassfish.api.ParamDefaultCalculator;
import org.glassfish.api.admin.CommandException;
import org.glassfish.api.admin.CommandModel;
import org.glassfish.api.admin.CommandValidationException;
import org.glassfish.hk2.api.PerLookup;
import org.jvnet.hk2.annotations.Service;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.FileOutputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Arrays;
import java.util.Base64;
import java.util.Locale;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.sun.enterprise.util.OS;
import com.sun.enterprise.util.StringUtils;
import org.glassfish.api.ExecutionContext;
import org.glassfish.api.Param;
import org.glassfish.api.ParamDefaultCalculator;
import org.glassfish.api.admin.CommandException;
import org.glassfish.api.admin.CommandValidationException;
import org.glassfish.hk2.api.PerLookup;
import org.jvnet.hk2.annotations.Service;

/**
* Command to upgrade Payara server to a newer version
* @author Jonathan Coustick
*/
@Service(name = "upgrade-server")
@PerLookup
public class UpgradeServerCommand extends BaseUpgradeCommand {

@Param

private static final String USE_DOWNLOADED_PARAM_NAME = "useDownloaded";
private static final String USERNAME_PARAM_NAME = "username";
private static final String NEXUS_PASSWORD_PARAM_NAME = "nexusPassword";
private static final String VERSION_PARAM_NAME = "version";

@Param(name = USERNAME_PARAM_NAME, optional = true)
private String username;

@Param(password = true)
@Param(name = NEXUS_PASSWORD_PARAM_NAME, password = true, optional = true, alias = "nexuspassword")
private String nexusPassword;

@Param(defaultValue="payara", acceptableValues = "payara, payara-ml, payara-web, payara-web-ml")
@Param(defaultValue="payara", acceptableValues = "payara, payara-ml, payara-web, payara-web-ml", optional = true)
private String distribution;

@Param
@Param(name = VERSION_PARAM_NAME, optional = true)
private String version;

@Param(name = "stage", optional = true, defaultCalculator = DefaultStageParamCalculator.class)
private boolean stage;

@Param(name = USE_DOWNLOADED_PARAM_NAME, optional = true, alias = "usedownloaded")
private File useDownloadedFile;

private static final String NEXUS_URL = System.getProperty("fish.payara.upgrade.repo.url",
"https://nexus.payara.fish/repository/payara-enterprise/fish/payara/distributions/");
private static final String ZIP = ".zip";

private static final LocalStringsImpl strings = new LocalStringsImpl(CLICommand.class);

@Override
protected void prevalidate() throws CommandException {
// Perform usual pre-validation; we don't want to skip it or alter it in anyway, we just want to add to it
super.prevalidate();

// If useDownloaded is present, check it's present. If it isn't, we need to pre-validate the download parameters
// again with optional set to false so as to mimic a "conditional optional".
// Note that we can't use the parameter variables here since CLICommand#inject() hasn't been called yet
if (getOption(USE_DOWNLOADED_PARAM_NAME) != null) {
if (!Paths.get(getOption(USE_DOWNLOADED_PARAM_NAME)).toFile().exists()) {
throw new CommandValidationException("File specified does not exist: " + useDownloadedFile);
}
} else {
if (getOption(USERNAME_PARAM_NAME) == null) {
prevalidateParameter(USERNAME_PARAM_NAME);
}

if (getOption(VERSION_PARAM_NAME) == null) {
prevalidateParameter(VERSION_PARAM_NAME);
}

if (getOption(NEXUS_PASSWORD_PARAM_NAME) == null) {
prevalidatePasswordParameter(NEXUS_PASSWORD_PARAM_NAME);
}
}
}

/**
* Adapted from method in parent class, namely {@link CLICommand#prevalidate()}
* @param parameterName
* @throws CommandValidationException
*/
private void prevalidateParameter(String parameterName) throws CommandValidationException {
// if option isn't set, prompt for it (if interactive), otherwise throw an error
if (programOpts.isInteractive()) {
try {
// Build the terminal if it isn't present
buildTerminal();
buildLineReader();

// Prompt for it
if (getOption(parameterName) == null && lineReader != null) {
String val = lineReader.readLine(strings.get("optionPrompt", parameterName.toLowerCase(Locale.ENGLISH)));
if (ok(val)) {
options.set(parameterName, val);
}
}
// if it's still not set, that's an error
if (getOption(parameterName) == null) {
logger.log(Level.INFO, strings.get("missingOption", "--" + parameterName));
throw new CommandValidationException(strings.get("missingOptions", parameterName));
}
} finally {
closeTerminal();
}
} else {
throw new CommandValidationException(strings.get("missingOptions", parameterName));
}
}

/**
* Adapted from method in parent class, namely {@link CLICommand#initializeCommandPassword()}
* @param passwordParameterName
* @throws CommandValidationException
*/
private void prevalidatePasswordParameter(String passwordParameterName) throws CommandValidationException {
// Get the ParamModel
CommandModel.ParamModel passwordParam = commandModel.getParameters()
.stream().filter(paramModel -> paramModel.getName().equalsIgnoreCase(passwordParameterName))
.findFirst().orElse(null);

// Get the password
char[] passwordChars = null;
if (passwordParam != null) {
passwordChars = getPassword(passwordParam.getName(), passwordParam.getLocalizedPrompt(),
passwordParam.getLocalizedPromptAgain(), true);
}

if (passwordChars == null) {
// if not terse, provide more advice about what to do
String msg;
if (programOpts.isTerse()){
msg = strings.get("missingPassword", name, passwordParameterName);
} else {
msg = strings.get("missingPasswordAdvice", name, passwordParameterName);
}

throw new CommandValidationException(msg);
}

options.set(passwordParameterName, new String(passwordChars));
}

@Override
protected void validate() throws CommandException {
// Perform usual validation; we don't want to skip it or alter it in anyway, we just want to add to it
Expand Down Expand Up @@ -197,22 +303,27 @@ public int executeCommand() {
String url = NEXUS_URL + distribution + "/" + version + "/" + distribution + "-" + version + ZIP;
String basicAuthString = username + ":" + nexusPassword;
String authBytes = "Basic " + Base64.getEncoder().encodeToString(basicAuthString.getBytes());

try {
LOGGER.log(Level.INFO, "Downloading new Payara version...");
URL nexusUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) nexusUrl.openConnection();
connection.setRequestProperty("Authorization", authBytes);

int code = connection.getResponseCode();
if (code != 200) {
LOGGER.log(Level.SEVERE, "Error connecting to server: {0}", code);
return ERROR;
}

Path tempFile = Files.createTempFile("payara", ".zip");
Files.copy(connection.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);


if (useDownloadedFile != null) {
Files.copy(useDownloadedFile.toPath(), tempFile, StandardCopyOption.REPLACE_EXISTING);
} else {
LOGGER.log(Level.INFO, "Downloading new Payara version...");
URL nexusUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) nexusUrl.openConnection();
connection.setRequestProperty("Authorization", authBytes);

int code = connection.getResponseCode();
if (code != 200) {
LOGGER.log(Level.SEVERE, "Error connecting to server: {0}", code);
return ERROR;
}

Files.copy(connection.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
}

FileInputStream unzipFileStream = new FileInputStream(tempFile.toFile());
Path unzippedDirectory = extractZipFile(unzipFileStream);

Expand Down Expand Up @@ -323,8 +434,20 @@ private void moveFiles(Path newVersion) throws IOException {
if (!stage) {
LOGGER.log(Level.FINE, "Moving files to old");
for (String folder : MOVEFOLDERS) {
Files.move(Paths.get(glassfishDir, folder), Paths.get(glassfishDir, folder + ".old"),
StandardCopyOption.REPLACE_EXISTING);
try {
Files.move(Paths.get(glassfishDir, folder), Paths.get(glassfishDir, folder + ".old"),
StandardCopyOption.REPLACE_EXISTING);
} catch (NoSuchFileException nsfe) {
// We can't nicely check if the "current" installation is a web distribution or not, so just attempt
// to move all and specifically catch a NSFE for the MQ directory
if (nsfe.getMessage().contains(
"payara5" + File.separator + "glassfish" + File.separator + ".." + File.separator + "mq")) {
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for mq directory under assumption " +
"this is a payara-web distribution. Continuing to move files...");
} else {
throw nsfe;
}
}
}
}

Expand All @@ -333,6 +456,7 @@ private void moveFiles(Path newVersion) throws IOException {

private void moveExtracted(Path newVersion) throws IOException {
LOGGER.log(Level.FINE, "Copying extracted files");

for (String folder : MOVEFOLDERS) {
Path sourcePath = newVersion.resolve("payara5" + File.separator + "glassfish" + File.separator + folder);
Path targetPath = Paths.get(glassfishDir, folder);
Expand All @@ -354,7 +478,14 @@ private void moveExtracted(Path newVersion) throws IOException {
private void undoMoveFiles() throws IOException {
LOGGER.log(Level.FINE, "Moving old back");
for (String folder : MOVEFOLDERS) {
Files.move(Paths.get(glassfishDir, folder + ".old"), Paths.get(glassfishDir, folder), StandardCopyOption.REPLACE_EXISTING);
try {
Files.move(Paths.get(glassfishDir, folder + ".old"), Paths.get(glassfishDir, folder), StandardCopyOption.REPLACE_EXISTING);
} catch (NoSuchFileException nsfe) {
// Don't exit out on NoSuchFileExceptions, just keep going - any NoSuchFileException is likely
// just a case of the file not having been moved yet.
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for directory {0} under assumption " +
"it hasn't been moved yet. Continuing rollback...", folder + ".old");
}
}
}

Expand Down Expand Up @@ -431,6 +562,15 @@ public FileVisitResult visitFile(Path arg0, BasicFileAttributes arg1) throws IOE

@Override
public FileVisitResult visitFileFailed(Path arg0, IOException arg1) throws IOException {
// We can't nicely check if the "new" installation is a web distribution or not, so specifically catch a
// NSFE for the MQ directory.
if (arg1 instanceof NoSuchFileException && arg1.getMessage().contains(
"payara5" + File.separator + "glassfish" + File.separator + ".." + File.separator + "mq")) {
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for mq directory under assumption " +
"this is a payara-web distribution. Continuing copy...");
return FileVisitResult.SKIP_SUBTREE;
}

LOGGER.log(Level.SEVERE, "File could not visited: {0}", arg0.toString());
throw arg1;
}
Expand Down Expand Up @@ -470,6 +610,15 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// We can't nicely check if the "new" installation is a web distribution or not, so specifically catch a
// NSFE for the MQ directory.
if (exc instanceof NoSuchFileException && exc.getMessage().contains(
"payara5" + File.separator + "glassfish" + File.separator + ".." + File.separator + "mq")) {
LOGGER.log(Level.FINE, "Ignoring NoSuchFileException for mq directory under assumption " +
"this is a payara-web distribution. Continuing fixing of permissions...");
return FileVisitResult.SKIP_SUBTREE;
}

LOGGER.log(Level.SEVERE, "File could not visited: {0}", file.toString());
throw exc;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SYNOPSIS
[--distribution={payara|payara-web|payara-ml|payara-web-ml}]
[--stage={true|false}]
[--domaindir domain-dir]
[--usedownloaded distribution.zip]

DESCRIPTION
The upgrade-server subcommand upgrades Payara Server to the specified
Expand Down Expand Up @@ -45,6 +46,15 @@ OPTIONS

The default value is as-install/domains.

--usedownloaded
Specifies a pre-downloaded Payara Server distribution zip file
that you'd like to upgrade your existing installation to use,
instead of downloading one from the Payara Nexus.

When this option is specified, the username, password,
distribution, and version parameters are all ignored and are
no longer mandatory.

EXAMPLES
Example 1, Upgrading Payara Server
In this example, a Payara Server Web domain is upgraded to version
Expand Down

0 comments on commit d658fc8

Please sign in to comment.