Skip to content

Commit

Permalink
GEODE-2705: Jars undeployed from cluster configuration will not be lo…
Browse files Browse the repository at this point in the history
…aded from disk on member restart
  • Loading branch information
jaredjstewart committed Apr 16, 2017
1 parent 6fd2d12 commit ee11b0a
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ public DeployedJar(File versionedJarFile, String jarName) throws IOException {
this(versionedJarFile, jarName, Files.readAllBytes(versionedJarFile.toPath()));
}

/**
* Writes the given jarBytes to versionedJarFile
*/
public DeployedJar(File versionedJarFile, final String jarName, byte[] jarBytes)
throws IOException {
Assert.assertTrue(jarBytes != null, "jarBytes cannot be null");
Expand Down Expand Up @@ -377,6 +380,9 @@ private byte[] getJarContent() {
return new byte[0];
}

/**
* @return the unversioned name of this jar file, e.g. myJar.jar
*/
public String getJarName() {
return this.jarName;
}
Expand Down
148 changes: 90 additions & 58 deletions geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
import static java.util.stream.Collectors.toSet;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.geode.GemFireException;
import org.apache.geode.GemFireIOException;
import org.apache.geode.SystemFailure;
import org.apache.geode.internal.logging.LogService;
import org.apache.logging.log4j.Logger;

Expand All @@ -34,13 +30,9 @@
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -83,11 +75,23 @@ public File getDeployDirectory() {
return this.deployDirectory;
}

/**
* Writes the jarBytes for the given jarName to the next version of that jar file (if the bytes do
* not match the latest deployed version)
*
* @return the DeployedJar that was written from jarBytes, or null if those bytes matched the
* latest deployed version
*/
public DeployedJar deployWithoutRegistering(final String jarName, final byte[] jarBytes)
throws IOException {
lock.lock();

try {
boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, jarBytes);
if (!shouldDeployNewVersion) {
return null;
}

verifyWritableDeployDirectory();

File newVersionedJarFile = getNextVersionedJarFile(jarName);
Expand Down Expand Up @@ -129,6 +133,7 @@ public void resumeAll() {
lock.unlock();
}


protected File getNextVersionedJarFile(String unversionedJarName) {
File[] oldVersions = findSortedOldVersionsOfJar(unversionedJarName);

Expand Down Expand Up @@ -246,21 +251,17 @@ public static int extractVersionFromFilename(final String filename) {
}
}

protected Set<String> findDistinctDeployedJars() {
protected Set<String> findDistinctDeployedJarsOnDisk() {
// Find all deployed JAR files
final File[] oldFiles = this.deployDirectory.listFiles(new FilenameFilter() {
@Override
public boolean accept(final File file, final String name) {
return versionedPattern.matcher(name).matches();
}
});
final File[] oldFiles =
this.deployDirectory.listFiles((file, name) -> versionedPattern.matcher(name).matches());

// Now add just the original JAR name to the set
final Set<String> jarNames = new HashSet<String>();
final Set<String> jarNames = new HashSet<>();
for (File oldFile : oldFiles) {
Matcher matcher = versionedPattern.matcher(oldFile.getName());
matcher.find();
jarNames.add(matcher.group(1));
jarNames.add(matcher.group(1) + ".jar");
}
return jarNames;
}
Expand Down Expand Up @@ -368,67 +369,83 @@ private void renameJarWithOldNamingConvention(File oldJar) throws IOException {
String newJarName = unversionedJarNameWithoutExtension + ".v" + jarVersion + ".jar";

File newJar = new File(this.deployDirectory, newJarName);
logger.debug("Renaming deployed jar from " + oldJar.getCanonicalPath() + " to "
+ newJar.getCanonicalPath());
logger.debug("Renaming deployed jar from {} to {}", oldJar.getCanonicalPath(),
newJar.getCanonicalPath());

FileUtils.moveFile(oldJar, newJar);
FileUtils.deleteQuietly(oldJar);
}

/**
* Re-deploy all previously deployed JAR files.
* Re-deploy all previously deployed JAR files on disk.
*/
public void loadPreviouslyDeployedJars() {
public void loadPreviouslyDeployedJarsFromDisk() {
logger.info("Loading previously deployed jars");
lock.lock();
try {
verifyWritableDeployDirectory();
renameJarsWithOldNamingConvention();

final Set<String> jarNames = findDistinctDeployedJars();
final Set<String> jarNames = findDistinctDeployedJarsOnDisk();
if (jarNames.isEmpty()) {
return;
}

Map<String, DeployedJar> latestVersionOfEachJar = new LinkedHashMap<>();
List<DeployedJar> latestVersionOfEachJar = new ArrayList<>();

for (String jarName : jarNames) {
final File[] jarFiles = findSortedOldVersionsOfJar(jarName);

Optional<File> latestValidDeployedJarOptional =
Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
try {
return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
} catch (IOException e) {
return false;
}
}).findFirst();

if (!latestValidDeployedJarOptional.isPresent()) {
// No valid version of this jar
continue;
}
DeployedJar deployedJar = findLatestValidDeployedJarFromDisk(jarName);

File latestValidDeployedJar = latestValidDeployedJarOptional.get();
latestVersionOfEachJar.put(jarName, new DeployedJar(latestValidDeployedJar, jarName));

// Remove any old left-behind versions of this JAR file
for (File jarFile : jarFiles) {
if (!latestValidDeployedJar.equals(jarFile)) {
FileUtils.deleteQuietly(jarFile);
}
if (deployedJar != null) {
latestVersionOfEachJar.add(deployedJar);
deleteOtherVersionsOfJar(deployedJar);
}
}

registerNewVersions(latestVersionOfEachJar.values().stream().collect(toList()));
// ClassPathLoader.getLatest().deploy(latestVersionOfEachJar.keySet().toArray(),
// latestVersionOfEachJar.values().toArray())
registerNewVersions(latestVersionOfEachJar);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}

/**
* Deletes all versions of this jar on disk other than the given version
*/
public void deleteOtherVersionsOfJar(DeployedJar deployedJar) {
logger.info("Deleting all versions of " + deployedJar.getJarName() + " other than "
+ deployedJar.getFileName());
final File[] jarFiles = findSortedOldVersionsOfJar(deployedJar.getJarName());

Stream.of(jarFiles).filter(jarFile -> !jarFile.equals(deployedJar.getFile()))
.forEach(jarFile -> {
logger.info("Deleting old version of jar: " + jarFile.getAbsolutePath());
FileUtils.deleteQuietly(jarFile);
});
}

public DeployedJar findLatestValidDeployedJarFromDisk(String unversionedJarName)
throws IOException {
final File[] jarFiles = findSortedOldVersionsOfJar(unversionedJarName);

Optional<File> latestValidDeployedJarOptional =
Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
try {
return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
} catch (IOException e) {
return false;
}
}).findFirst();

if (!latestValidDeployedJarOptional.isPresent()) {
// No valid version of this jar
return null;
}

File latestValidDeployedJar = latestValidDeployedJarOptional.get();

return new DeployedJar(latestValidDeployedJar, unversionedJarName);
}

public URL[] getDeployedJarURLs() {
return this.deployedJars.values().stream().map(DeployedJar::getFileURL).toArray(URL[]::new);
Expand All @@ -441,6 +458,7 @@ public List<DeployedJar> registerNewVersions(List<DeployedJar> deployedJars)
try {
for (DeployedJar deployedJar : deployedJars) {
if (deployedJar != null) {
logger.info("Registering new version of jar: {}", deployedJar.toString());
DeployedJar oldJar = this.deployedJars.put(deployedJar.getJarName(), deployedJar);
if (oldJar != null) {
oldJar.cleanUp();
Expand Down Expand Up @@ -488,13 +506,7 @@ public List<DeployedJar> deploy(final String jarNames[], final byte[][] jarBytes
String jarName = jarNames[i];
byte[] newJarBytes = jarBytes[i];

boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, newJarBytes);

if (shouldDeployNewVersion) {
deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
} else {
deployedJars[i] = null;
}
deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
}

return registerNewVersions(Arrays.asList(deployedJars));
Expand All @@ -511,7 +523,7 @@ private boolean shouldDeployNewVersion(String jarName, byte[] newJarBytes) throw
}

if (oldDeployedJar.hasSameContentAs(newJarBytes)) {
logger.warn("Jar is identical to the latest deployed version: ",
logger.warn("Jar is identical to the latest deployed version: {}",
oldDeployedJar.getFileCanonicalPath());

return false;
Expand All @@ -520,6 +532,11 @@ private boolean shouldDeployNewVersion(String jarName, byte[] newJarBytes) throw
return true;
}

/**
* Returns the latest registered {@link DeployedJar} for the given JarName
*
* @param jarName - the unversioned jar name, e.g. myJar.jar
*/
public DeployedJar findDeployedJar(String jarName) {
return this.deployedJars.get(jarName);
}
Expand Down Expand Up @@ -565,9 +582,24 @@ public String undeploy(final String jarName) throws IOException {

deployedJar.cleanUp();

deleteAllVersionsOfJar(jarName);
return deployedJar.getFileCanonicalPath();
} finally {
lock.unlock();
}
}

public void deleteAllVersionsOfJar(String unversionedJarName) {
lock.lock();
try {
File[] jarFiles = findSortedOldVersionsOfJar(unversionedJarName);
for (File jarFile : jarFiles) {
logger.info("Deleting: {}", jarFile.getAbsolutePath());
FileUtils.deleteQuietly(jarFile);
}
} finally {
lock.unlock();
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
*/
package org.apache.geode.internal.cache;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -27,7 +30,9 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;

import org.apache.commons.lang.ArrayUtils;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -61,21 +66,36 @@ public class ClusterConfigurationLoader {
*/
public static void deployJarsReceivedFromClusterConfiguration(Cache cache,
ConfigurationResponse response) throws IOException, ClassNotFoundException {
logger.info("Requesting cluster configuration");
if (response == null) {
return;
}

String[] jarFileNames = response.getJarNames();
byte[][] jarBytes = response.getJars();
logger.info("Got response with jars: {}", Stream.of(jarFileNames).collect(joining(",")));

if (jarFileNames != null && jarBytes != null) {
List<DeployedJar> deployedJars =
ClassPathLoader.getLatest().getJarDeployer().deploy(jarFileNames, jarBytes);
JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
jarDeployer.suspendAll();
try {
List<String> extraJarsOnServer =
jarDeployer.findDeployedJars().stream().map(DeployedJar::getJarName)
.filter(jarName -> !ArrayUtils.contains(jarFileNames, jarName)).collect(toList());

for (String extraJar : extraJarsOnServer) {
logger.info("Removing jar not present in cluster configuration: {}", extraJar);
jarDeployer.deleteAllVersionsOfJar(extraJar);
}

deployedJars.stream().filter(Objects::nonNull)
.forEach((jar) -> logger.info("Deployed " + (jar.getFile().getAbsolutePath())));
List<DeployedJar> deployedJars = jarDeployer.deploy(jarFileNames, jarBytes);

deployedJars.stream().filter(Objects::nonNull)
.forEach((jar) -> logger.info("Deployed: {}", jar.getFile().getAbsolutePath()));
} finally {
jarDeployer.resumeAll();
}
}
// TODO: Jared - Does this need to actually undeploy extra jars like the javadoc says?
}

/***
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@
import org.apache.geode.i18n.LogWriterI18n;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.internal.JarDeployer;
import org.apache.geode.internal.SystemTimer;
import org.apache.geode.internal.cache.control.InternalResourceManager;
import org.apache.geode.internal.cache.control.InternalResourceManager.ResourceType;
Expand Down Expand Up @@ -1238,8 +1237,10 @@ private void initialize() {
initializeServices();

try {
// Deploy all the jars from the deploy working dir.
ClassPathLoader.getLatest().getJarDeployer().loadPreviouslyDeployedJars();
if (configurationResponse == null) {
// Deploy all the jars from the deploy working dir.
ClassPathLoader.getLatest().getJarDeployer().loadPreviouslyDeployedJarsFromDisk();
}
ClusterConfigurationLoader.applyClusterXmlConfiguration(this, configurationResponse,
system.getConfig());
initializeDeclarativeCache();
Expand Down
Loading

0 comments on commit ee11b0a

Please sign in to comment.