Skip to content

Commit

Permalink
Merge c1778a6 into dd30138
Browse files Browse the repository at this point in the history
  • Loading branch information
adamcin committed Nov 6, 2019
2 parents dd30138 + c1778a6 commit ceb643f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com)
### Fixed
- ExpectAces ACE criteria now correctly trims around parameter names
- #51 copied InstallHookPolicy details to scan goal doc
- #37 switched to jar checksums for opear cache folder names.

## [1.5.1] - 2019-10-03

Expand Down
58 changes: 35 additions & 23 deletions core/src/main/java/net/adamcin/oakpal/core/OpearFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.jar.Attributes;
Expand All @@ -35,25 +37,18 @@ public final class OpearFile implements Opear {
private static final Logger LOGGER = LoggerFactory.getLogger(OpearFile.class);

static final class OpearMetadata {
private final String cacheId;
private final String[] plans;
private final String[] planClassPath;
private final boolean defaultBasic;

OpearMetadata(final @NotNull String cacheId,
final @NotNull String[] plans,
OpearMetadata(final @NotNull String[] plans,
final @NotNull String[] planClassPath,
final boolean defaultBasic) {
this.cacheId = cacheId;
this.plans = plans;
this.planClassPath = planClassPath;
this.defaultBasic = defaultBasic;
}

@NotNull String getCacheId() {
return cacheId;
}

@NotNull String[] getPlans() {
return plans;
}
Expand Down Expand Up @@ -129,7 +124,7 @@ static OpearMetadata metaForSimpleDir(final @NotNull File directory) {
final String[] plans = new File(directory, Opear.SIMPLE_DIR_PLAN).exists()
? new String[]{Opear.SIMPLE_DIR_PLAN}
: new String[0];
return new OpearMetadata(directory.getName(), plans, new String[]{"."}, true);
return new OpearMetadata(plans, new String[]{"."}, true);
}

static Result<String[]> validateUriHeaderValuesWithDefaultForMissing(final @NotNull Manifest manifest,
Expand Down Expand Up @@ -181,7 +176,7 @@ static Result<OpearMetadata> validateOpearManifest(final @Nullable Manifest mani
validateUriHeaderValuesWithDefaultForMissing(man, NAME_CLASS_PATH, ".")
.flatMap(planClassPath ->
validateUriHeaderValues(man, NAME_OAKPAL_PLAN).flatMap(plans ->
Result.success(new OpearMetadata(bid, plans,
Result.success(new OpearMetadata(plans,
planClassPath, false))
)))));
}
Expand All @@ -201,22 +196,39 @@ public static Result<OpearFile> fromDirectory(final @NotNull File directory) {
.map(meta -> new OpearFile(directory, meta));
}

static Result<String> getHashCacheKey(final @NotNull String path) {
try (FileInputStream is = new FileInputStream(path)) {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] buf = new byte[1024];
int nread;
while ((nread = is.read(buf)) != -1) {
digest.update(buf, 0, nread);
}
final byte[] mdbytes = digest.digest();
return Result.success(Base64.getUrlEncoder().withoutPadding().encodeToString(mdbytes));
} catch (Exception e) {
return Result.failure(e);
}
}

public static Result<OpearFile> fromJar(final @NotNull JarFile jarFile, final @NotNull File cacheBaseDir) {
return result1(JarFile::getManifest).apply(jarFile)
.flatMap(OpearFile::validateOpearManifest)
.flatMap(metadata -> {
final File cacheDir = new File(cacheBaseDir, metadata.getCacheId());
if (cacheDir.isDirectory()) {
return fromDirectory(cacheDir);
} else {
if (!cacheDir.mkdirs()) {
return Result.failure(format("failed to create cache dir %s for specified opear file %s",
cacheDir.getPath(), jarFile.getName()));
return getHashCacheKey(jarFile.getName()).flatMap(cacheKey -> {
return result1(JarFile::getManifest).apply(jarFile)
.flatMap(OpearFile::validateOpearManifest)
.flatMap(metadata -> {
final File cacheDir = new File(cacheBaseDir, cacheKey);
if (cacheDir.isDirectory()) {
return fromDirectory(cacheDir);
} else {
return cacheJar(jarFile, cacheDir).flatMap(OpearFile::fromDirectory);
if (!cacheDir.mkdirs()) {
return Result.failure(format("failed to create cache dir %s for specified opear file %s",
cacheDir.getPath(), jarFile.getName()));
} else {
return cacheJar(jarFile, cacheDir).flatMap(OpearFile::fromDirectory);
}
}
}
});
});
});
}

static Result<File> cacheJar(final @NotNull JarFile jarFile, final @NotNull File cacheDir) {
Expand Down
52 changes: 39 additions & 13 deletions core/src/test/java/net/adamcin/oakpal/core/OpearFileTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package net.adamcin.oakpal.core;

import static net.adamcin.oakpal.core.Fun.result1;
import static net.adamcin.oakpal.core.OpearFile.NAME_CLASS_PATH;
import static org.junit.Assert.*;
import net.adamcin.oakpal.testing.TestPackageUtil;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
Expand All @@ -16,13 +25,14 @@
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import net.adamcin.oakpal.testing.TestPackageUtil;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static net.adamcin.oakpal.core.Fun.result1;
import static net.adamcin.oakpal.core.OpearFile.NAME_CLASS_PATH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

@RunWith(MockitoJUnitRunner.class)
public class OpearFileTest {
Expand Down Expand Up @@ -117,14 +127,30 @@ public void testGetPlanClassPath() {
opearFile.metadata.getPlanClassPath());
}

@Test
public void testGetHashCacheKey() throws Exception {
Result<String> cacheKeyDeletedResult = OpearFile.getHashCacheKey("/no/such/path");
assertTrue("cacheKey is failure", cacheKeyDeletedResult.isFailure());
assertTrue("cacheKey failure is FileNotFoundException",
cacheKeyDeletedResult.findCause(FileNotFoundException.class).isPresent());

buildDeepTestJar();
Result<String> cacheKeyResult = OpearFile.getHashCacheKey(deepTestTarget.getPath());
assertTrue("cacheKey is success", cacheKeyResult.isSuccess());
String cacheKey = cacheKeyResult.getOrDefault("");
assertEquals("cacheKey should be 43 characters long: " + cacheKey, 43, cacheKey.length());
final String pattern = "^[0-9A-Za-z_-]*$";
assertTrue(String.format("cacheKey %s matches regex %s", cacheKey, pattern), cacheKey.matches(pattern));
}

@Test
public void testFromJar_mkdirsFail() throws Exception {
buildDeepTestJar();
final File cacheDir = new File("target/test-output/OpearFileTest/testFromJar_mkdirsFail/cache");
if (cacheDir.exists()) {
FileUtils.deleteDirectory(cacheDir);
}
FileUtils.touch(new File(cacheDir, "deep_test"));
FileUtils.touch(new File(cacheDir, OpearFile.getHashCacheKey(deepTestTarget.getPath()).getOrDefault("failed_to_fail")));
assertTrue("fail with jar when nondirectory present at cache id",
OpearFile.fromJar(new JarFile(deepTestTarget), cacheDir).isFailure());
}
Expand Down Expand Up @@ -200,7 +226,7 @@ public void testGetPlanClassLoader_empty() throws Exception {
}

OpearFile opearFile = new OpearFile(cacheDir,
new OpearFile.OpearMetadata("foo", new String[0], new String[0], true));
new OpearFile.OpearMetadata(new String[0], new String[0], true));

final ClassLoader parent = new URLClassLoader(new URL[0], null);
assertSame("same classloader with empty classpath", parent, opearFile.getPlanClassLoader(parent));
Expand Down

0 comments on commit ceb643f

Please sign in to comment.