diff --git a/.travis.yml b/.travis.yml index 9f05b726e..99d38d659 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ matrix: dist: trusty language: java jdk: oraclejdk8 - script: ./mvnw -B -e verify site site:stage + script: ./mvnw -B -e -fae verify site site:stage after_success: - - ./mvnw -B -Pcoverage clean verify coveralls:report + - ./mvnw -B -fae -Pcoverage clean verify coveralls:report - os: linux dist: trusty language: java @@ -21,6 +21,14 @@ matrix: dist: trusty language: java jdk: openjdk11 + - os: linux + dist: trusty + language: java + jdk: oraclejdk14 + - os: linux + dist: trusty + language: java + jdk: openjdk14 - os: windows language: bash before_script: @@ -31,7 +39,7 @@ matrix: # for jdk 11.0.1+13 osx_image: xcode10.1 script: - - ./mvnw -B -e verify + - ./mvnw -B -e -fae verify cache: directories: - $HOME/.m2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a59e05e..d1231e973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,30 @@ The format is based on [Keep a Changelog](http://keepachangelog.com) ## [Unreleased] +### Added + +- API extracted to net.adamcin.oakpal.api package in new oakpal-api module for tighter dependency control, requiring a major version bump. +- @ProviderType and @ConsumerType annotations added to interfaces in exported packages +- Added setResourceBundle() method to ViolationReporter interface and default implementations to support future i18n enhancements. +- Added SimpleViolation.builder() with easy support for formatting violation description with MessageFormat.format(), using withArguments() builder method. +- Migrated CompositeStoreAlignment check from ACS AEM Commons because of unavoidable tight-coupling to oak-core-spi classes. + +### Changed + +- Added overload with default implementation for ProgressCheck.importedPath() that accepts new PathAction enumerator type. Other signature now deprecated. +- Moved Violation.Severity enumerator to top-level type because 1) I wanted to and 2) the API extraction made this a convenient time to do it. +- JSON Config keys are now managed in child interface types with @ProviderType annotations to avoid major version bumps when adding config constants. + +## [1.5.2] - 2020-04-15 + ### Added - #38 modified pom to upload cli dist binaries to releases. - #39 add --no-hooks cli option ### Fixed +- #50 Provide CLI dist with Windows binary launcher - ExpectAces ACE criteria now correctly trims around parameter names -- #51 copied InstallHookPolicy details to scan goal doc +- #35 copied InstallHookPolicy details to scan goal doc - #37 switched to jar checksums for opear cache folder names. ## [1.5.1] - 2019-10-03 diff --git a/Dockerfile b/Dockerfile index 65a3cca13..3f9c2f49f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ADD . /app WORKDIR /app RUN mvn clean install -pl testing,core,cli -FROM adoptopenjdk/openjdk11-openj9:alpine-slim +FROM adoptopenjdk/openjdk14-openj9:alpine-slim RUN mkdir -p /app/oakpal-cli COPY --from=build /app/cli/target/oakpal-cli-*-dist.tar.gz /app RUN tar --strip-components 1 -C /app/oakpal-cli -zxf /app/oakpal-cli-*-dist.tar.gz \ diff --git a/README.md b/README.md index 0d819118b..f297a9a64 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ OakPAL was designed to fill this gap, by providing: 2. An OakMachine class with a fluent Builder API that encapsulates the creation of a fresh Oak repository, state initialization, and package installation for every set of package files. -3. A pluggable listener API with classpath discovery of third-party [Checklists](https://github.com/adamcin/oakpal/blob/master/core/src/main/resources/OAKPAL-INF/checklists/basic.json), -[ProgressChecks](oakpal-core/apidocs/net/adamcin/oakpal/core/ProgressCheck.html), and [ScriptProgressChecks](oakpal-maven-plugin/writing-a-script-check.html), +3. A pluggable listener API with classpath discovery of third-party [Checklists](https://github.com/adamcin/oakpal/blob/master/checks/src/main/resources/OAKPAL-INF/checklists/basic.json), +[ProgressChecks](net.adamcin.oakpal.api/apidocs/net/adamcin/oakpal/api/ProgressCheck.html), and [ScriptProgressChecks](oakpal-maven-plugin/writing-a-script-check.html), which receive progress tracker events along with read-only access to incremental repository state, and which can report Violations at the end of a scan. diff --git a/api/README.md b/api/README.md new file mode 100644 index 000000000..a0393f38a --- /dev/null +++ b/api/README.md @@ -0,0 +1,4 @@ +# net.adamcin.oakpal.api + +This is the API dependency for use by progress check implementers. + diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 000000000..d28ed1c6d --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,184 @@ + + + + + 4.0.0 + + + net.adamcin.oakpal + oakpal + 2.0.0-SNAPSHOT + .. + + + oakpal-api + jar + + OakPAL - API bundle + OakPAL API bundle + + 2017 + + + https://github.com/adamcin/oakpal + scm:git:git@github.com:adamcin/oakpal.git + scm:git://github.com/adamcin/oakpal.git + HEAD + + + + + + src/test/resources + false + + + src/test/filtered-resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-test-packages + + + + + org.codehaus.mojo + templating-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + biz.aQute.bnd + bnd-maven-plugin + + + + bnd-process + + + + + + + + + biz.aQute.bnd + bnd-baseline-maven-plugin + + + baseline + + baseline + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + + org.jetbrains + annotations + + + org.osgi + org.osgi.annotation.versioning + + + org.apache.jackrabbit.vault + org.apache.jackrabbit.vault + + + org.apache.jackrabbit + jackrabbit-jcr-commons + + + org.apache.jackrabbit + oak-jackrabbit-api + + + org.apache.sling + org.apache.sling.commons.johnzon + + + javax.jcr + jcr + + + org.slf4j + slf4j-api + + + + + junit + junit + test + + + net.adamcin.oakpal + oakpal-testing + test + + + ch.qos.logback + logback-classic + test + + + org.mockito + mockito-core + test + + + commons-io + commons-io + 2.5 + test + + + junit-addons + junit-addons + 1.4 + test + + + diff --git a/api/src/main/java/net/adamcin/oakpal/api/ApiConstants.java b/api/src/main/java/net/adamcin/oakpal/api/ApiConstants.java new file mode 100644 index 000000000..3ba179b68 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/ApiConstants.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; + +/** + * Hosts constants as static singleton getter methods defined by interfaces. This reduces the impact on semantic + * versioning rules of adding or modifying interface constants. + */ +public final class ApiConstants { + private ApiConstants() { + /* no construction */ + } + + private static final ViolationKeys VIOLATION_KEYS = new ViolationKeys() { + @Override + public String description() { + return "description"; + } + + @Override + public String severity() { + return "severity"; + } + + @Override + public String packages() { + return "packages"; + } + }; + + /** + * Json key constant accessors for violations. + */ + @ProviderType + public interface ViolationKeys { + String description(); + + String severity(); + + String packages(); + } + + @NotNull + public static ViolationKeys violationKeys() { + return VIOLATION_KEYS; + } +} diff --git a/core/src/main/java/net/adamcin/oakpal/core/Fun.java b/api/src/main/java/net/adamcin/oakpal/api/Fun.java similarity index 99% rename from core/src/main/java/net/adamcin/oakpal/core/Fun.java rename to api/src/main/java/net/adamcin/oakpal/api/Fun.java index b1fa6e190..d0bae5ad6 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Fun.java +++ b/api/src/main/java/net/adamcin/oakpal/api/Fun.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -855,8 +855,7 @@ private FunRuntimeException(final @NotNull Throwable cause) { final @Nullable BiConsumer onError) { final BiConsumer consumeError = onError != null ? onError - : (e, t) -> { - }; + : (e, t) -> { /* do nothing */ }; return element -> { try { diff --git a/core/src/main/java/net/adamcin/oakpal/core/JavaxJson.java b/api/src/main/java/net/adamcin/oakpal/api/JavaxJson.java similarity index 95% rename from core/src/main/java/net/adamcin/oakpal/core/JavaxJson.java rename to api/src/main/java/net/adamcin/oakpal/api/JavaxJson.java index 24f895d29..3c1e9006c 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/JavaxJson.java +++ b/api/src/main/java/net/adamcin/oakpal/api/JavaxJson.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,13 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.apache.jackrabbit.util.ISO8601; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import javax.json.Json; import javax.json.JsonArray; @@ -49,7 +50,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; +import static java.util.Optional.ofNullable; +import static net.adamcin.oakpal.api.Fun.compose; /** * Simple DSL for constructing javax.json objects for {@link ProgressCheckFactory} configs using only three-letter identifiers. @@ -63,13 +65,13 @@ private JavaxJson() { /** * To keep things simple and concise, the JSON CND format does not need to serialize null values, empty arrays, or - * empty objects. This method should be used as a filtering predicate when mapping {@link JsonCnd.DefinitionToken} values to + * empty objects. This method should be used as a filtering predicate when mapping {@code JsonCnd.DefinitionToken} values to * the JSON stream. * * @param value the JsonValue to test * @return true if not null or empty */ - static boolean nonEmptyValue(final @Nullable JsonValue value) { + public static boolean nonEmptyValue(final @Nullable JsonValue value) { return !(value == null || value.getValueType() == JsonValue.ValueType.NULL || (value.getValueType() == JsonValue.ValueType.ARRAY && value.asJsonArray().isEmpty()) @@ -77,34 +79,21 @@ static boolean nonEmptyValue(final @Nullable JsonValue value) { } /** + * Return true if the value is not equal to the provided default value. + * * @param value the JsonValue to test * @param defaultValue the default JsonValue to test against * @return true if provided value is not equal to the provided default value */ - static boolean nonDefaultValue(final @Nullable JsonValue value, final @NotNull JsonValue defaultValue) { + public static boolean nonDefaultValue(final @Nullable JsonValue value, final @NotNull JsonValue defaultValue) { return !defaultValue.equals(value); } - /** - * Custom pojo types which should be usable within this DSL should implement this method to provide a - * {@link JsonObject}, which can be wrapped quickly by {@link #val(Object)}. - */ - public interface ObjectConvertible { - JsonObject toJson(); - } - - /** - * Custom pojo types which should be usable within this DSL should implement this method to provide a - * {@link JsonArray}, which can be wrapped quickly by {@link #val(Object)}. - */ - public interface ArrayConvertible { - JsonArray toJson(); - } - /** * Defines a method toValue which coalesces the underlying value to prevent over-wrapping by the * {@link #val(Object)} method. */ + @ProviderType public interface HasValue { Value toValue(); } @@ -114,6 +103,7 @@ public interface HasValue { * * @param the return type of the get method */ + @ProviderType public interface As { TYPE get(); } @@ -121,6 +111,7 @@ public interface As { /** * Type which allows a different fluent style for building keys, i.e. {@code key(String).val(obj)}. */ + @ProviderType public interface Cursor { String getKey(); } @@ -139,10 +130,10 @@ public static JsonValue wrap(final Object object) { return (JsonValue) object; } else if (object instanceof HasValue) { return ((HasValue) object).toValue().get(); - } else if (object instanceof ObjectConvertible) { - return ((ObjectConvertible) object).toJson(); - } else if (object instanceof ArrayConvertible) { - return ((ArrayConvertible) object).toJson(); + } else if (object instanceof JsonObjectConvertible) { + return ((JsonObjectConvertible) object).toJson(); + } else if (object instanceof JsonArrayConvertible) { + return ((JsonArrayConvertible) object).toJson(); } else if (object instanceof String) { return Json.createArrayBuilder().add((String) object).build().get(0); } else if (object instanceof Calendar) { @@ -276,6 +267,22 @@ public static List unwrapArray(final JsonArray jsonArray) { return jsonArray.getValuesAs(JavaxJson::unwrap); } + /** + * Merge an overlay json object's entries into a base json object, replacing values + * for duplicate keys. + * + * @param base the base json object + * @param overlay the overlay json object + * @return a merged json object + */ + public static @NotNull JsonObject shallowMergeObjects(final @Nullable JsonObject base, + final @Nullable JsonObject overlay) { + JsonObjectBuilder init = Json.createObjectBuilder(); + ofNullable(base).ifPresent(json -> json.forEach(init::add)); + ofNullable(overlay).ifPresent(json -> json.forEach(init::add)); + return init.build(); + } + /** * Discrete value wrapper. Delegates to {@link #wrap(Object)} for null-handling, etc. */ diff --git a/api/src/main/java/net/adamcin/oakpal/api/JsonArrayConvertible.java b/api/src/main/java/net/adamcin/oakpal/api/JsonArrayConvertible.java new file mode 100644 index 000000000..420a18a64 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/JsonArrayConvertible.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import javax.json.JsonArray; + +/** + * Custom pojo types which should be usable within the JavaxJson DSL should implement this method to provide a + * {@link JsonArray}, which can be wrapped quickly by {@code JavaxJson.val(Object)}. + */ +public interface JsonArrayConvertible { + JsonArray toJson(); +} diff --git a/core/src/test/java/net/adamcin/oakpal/core/SimpleViolationTest.java b/api/src/main/java/net/adamcin/oakpal/api/JsonObjectConvertible.java similarity index 61% rename from core/src/test/java/net/adamcin/oakpal/core/SimpleViolationTest.java rename to api/src/main/java/net/adamcin/oakpal/api/JsonObjectConvertible.java index 3594c0c2b..359b882a5 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/SimpleViolationTest.java +++ b/api/src/main/java/net/adamcin/oakpal/api/JsonObjectConvertible.java @@ -14,18 +14,14 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; -import static org.junit.Assert.assertNotNull; +import javax.json.JsonObject; -import org.apache.jackrabbit.vault.packaging.PackageId; -import org.junit.Test; - -public class SimpleViolationTest { - - @Test - public void testConstruct() { - SimpleViolation nulls = new SimpleViolation(null, null, (PackageId[]) null); - assertNotNull("toString not null", nulls); - } +/** + * Custom pojo types which should be usable within the JavaxJson DSL should implement this method to provide a + * {@link JsonObject}, which can be wrapped quickly by {@code JavaxJson.val(Object)}. + */ +public interface JsonObjectConvertible { + JsonObject toJson(); } diff --git a/core/src/main/java/net/adamcin/oakpal/core/Nothing.java b/api/src/main/java/net/adamcin/oakpal/api/Nothing.java similarity index 66% rename from core/src/main/java/net/adamcin/oakpal/core/Nothing.java rename to api/src/main/java/net/adamcin/oakpal/api/Nothing.java index 14742272c..9b972ef1c 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Nothing.java +++ b/api/src/main/java/net/adamcin/oakpal/api/Nothing.java @@ -1,4 +1,20 @@ -package net.adamcin.oakpal.core; +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/api/src/main/java/net/adamcin/oakpal/api/PathAction.java b/api/src/main/java/net/adamcin/oakpal/api/PathAction.java new file mode 100644 index 000000000..02daa96af --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/PathAction.java @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import java.util.EnumSet; + +/** + * Enumeration of progress tracker path import action types. + */ +public enum PathAction { + NOOP("-"), MODIFIED("U"), REPLACED("R"), + ERROR("E"), ADDED("A"), DELETED("D"), + MISSING("!"), UNKNOWN("?"); + + private final String shortCode; + + PathAction(final String shortCode) { + this.shortCode = shortCode; + } + + /** + * Get the short code reported by filevault. + * + * @return the action type short code + */ + public String getShortCode() { + return shortCode; + } + + /** + * Returns true if this is the NOOP action type. + * + * @return true if noop + */ + public boolean isNoop() { + return this == NOOP; + } + + /** + * Returns true if this is the {@link #UNKNOWN} fallback action type. This type may be reported in the future if a + * new filevault action code is introduced that isn't recognized by oakpal. + * + * @return true if unknown action type + */ + public boolean isUnknown() { + return this == UNKNOWN; + } + + /** + * Returns true if it is safe to get the item at the referenced path based on this action type. + * + * @return true if safe to get item from session + */ + public boolean canGetItem() { + return EnumSet.of(NOOP, MODIFIED, REPLACED, ADDED).contains(this); + } + + @Override + public String toString() { + return shortCode; + } + + /** + * Lookup an action type by the short code. + * + * @param shortCode the short code + * @return the representative action type or {@link #UNKNOWN} + */ + public static PathAction fromShortCode(final String shortCode) { + for (PathAction value : values()) { + if (value.getShortCode().equals(shortCode)) { + return value; + } + } + return UNKNOWN; + } +} diff --git a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheck.java b/api/src/main/java/net/adamcin/oakpal/api/ProgressCheck.java similarity index 83% rename from core/src/main/java/net/adamcin/oakpal/core/ProgressCheck.java rename to api/src/main/java/net/adamcin/oakpal/api/ProgressCheck.java index 9aa2ca733..71c8dda91 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheck.java +++ b/api/src/main/java/net/adamcin/oakpal/api/ProgressCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,12 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.osgi.annotation.versioning.ConsumerType; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -34,7 +35,7 @@ * Some constraints for behavior: *
    *
  1. Once constructed, instances of this class should be immutable from the perspective of any other public API.
  2. - *
  3. The {@link OakMachine} will call these methods synchronously and in a single-threaded fashion.
  4. + *
  5. The {@code OakMachine} will call these methods synchronously and in a single-threaded fashion.
  6. *
  7. Mutation of the repository state exposed by {@link Session} is not allowed.
  8. *
  9. Implementations of this which are referenced directly by a checklist must expose a zero-argument default * constructor.
  10. @@ -42,6 +43,7 @@ * {@link ProgressCheckFactory} in order to be loaded successfully. *
*/ +@ConsumerType public interface ProgressCheck extends ScanListener, ViolationReporter { /** @@ -56,7 +58,7 @@ default String getCheckName() { /** * Called after the package is uploaded to the package manager at the beginning of the scan. Track subsequent * events using the package ID provided to this method. This method will only be called once for each package - * provided to {@link OakMachine#scanPackage(File...)}. + * provided to {@code OakMachine.scanPackage(File...)}. * * @param packageId the package ID of the newly opened package * @param file the package file that will be opened @@ -103,17 +105,33 @@ default void beforeExtract(PackageId packageId, Session inspectSession, PackageP } /** - * Notified when package importer adds, modifies, or leaves a node untouched. + * Notified when package importer adds, modifies, or leaves a node untouched. This method is not called if + * {@link #importedPath(PackageId, String, Node, PathAction)} is overridden. * * @param packageId the current package * @param path the imported path * @param node the imported node * @throws RepositoryException because of access to a {@link Node} + * @deprecated 2.0.0 implement {@link #importedPath(PackageId, String, Node, PathAction)} instead */ + @Deprecated default void importedPath(PackageId packageId, String path, Node node) throws RepositoryException { } + /** + * Notified when package importer adds, modifies, or leaves a node untouched. + * + * @param packageId the current package + * @param path the imported path + * @param node the imported node + * @param action the reported path action type + * @throws RepositoryException because of access to a {@link Node} + */ + default void importedPath(PackageId packageId, String path, Node node, PathAction action) throws RepositoryException { + importedPath(packageId, path, node); + } + /** * Notified when package importer deletes an existing node. * diff --git a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckFactory.java b/api/src/main/java/net/adamcin/oakpal/api/ProgressCheckFactory.java similarity index 90% rename from core/src/main/java/net/adamcin/oakpal/core/ProgressCheckFactory.java rename to api/src/main/java/net/adamcin/oakpal/api/ProgressCheckFactory.java index 6afadc1fd..ed5feb063 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckFactory.java +++ b/api/src/main/java/net/adamcin/oakpal/api/ProgressCheckFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,9 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; + +import org.osgi.annotation.versioning.ConsumerType; import javax.json.JsonObject; @@ -23,6 +25,7 @@ * * @since 0.5.0 */ +@ConsumerType public interface ProgressCheckFactory { /** diff --git a/core/src/main/java/net/adamcin/oakpal/core/ReportCollector.java b/api/src/main/java/net/adamcin/oakpal/api/ReportCollector.java similarity index 95% rename from core/src/main/java/net/adamcin/oakpal/core/ReportCollector.java rename to api/src/main/java/net/adamcin/oakpal/api/ReportCollector.java index 91ab10e70..b37c0dc72 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ReportCollector.java +++ b/api/src/main/java/net/adamcin/oakpal/api/ReportCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import java.util.ArrayList; import java.util.Collection; diff --git a/core/src/main/java/net/adamcin/oakpal/core/Result.java b/api/src/main/java/net/adamcin/oakpal/api/Result.java similarity index 99% rename from core/src/main/java/net/adamcin/oakpal/core/Result.java rename to api/src/main/java/net/adamcin/oakpal/api/Result.java index e869201da..f255c680a 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Result.java +++ b/api/src/main/java/net/adamcin/oakpal/api/Result.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,11 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +42,7 @@ * * @param The result type. */ +@ProviderType public abstract class Result implements Serializable { private static final Logger LOGGER = LoggerFactory.getLogger(Result.class); diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/Rule.java b/api/src/main/java/net/adamcin/oakpal/api/Rule.java similarity index 90% rename from core/src/main/java/net/adamcin/oakpal/core/checks/Rule.java rename to api/src/main/java/net/adamcin/oakpal/api/Rule.java index ff9498661..7f0d169e9 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/Rule.java +++ b/api/src/main/java/net/adamcin/oakpal/api/Rule.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ * limitations under the License. */ -package net.adamcin.oakpal.core.checks; +package net.adamcin.oakpal.api; -import net.adamcin.oakpal.core.JavaxJson; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.json.JsonArray; import javax.json.JsonObject; @@ -39,7 +40,31 @@ * are assumed). * */ -public final class Rule implements JavaxJson.ObjectConvertible { +public final class Rule implements JsonObjectConvertible { + @ProviderType + public interface JsonKeys { + String type(); + + String pattern(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String type() { + return "type"; + } + + @Override + public String pattern() { + return "pattern"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + static final Pattern PATTERN_MATCH_ALL = Pattern.compile(".*"); /** @@ -62,8 +87,11 @@ public final class Rule implements JavaxJson.ObjectConvertible { */ public static final Rule DEFAULT_DENY = new Rule(RuleType.DENY, PATTERN_MATCH_ALL); - public static final String CONFIG_TYPE = "type"; - public static final String CONFIG_PATTERN = "pattern"; + @Deprecated + public static final String CONFIG_TYPE = keys().type(); + @Deprecated + public static final String CONFIG_PATTERN = keys().pattern(); + private final RuleType type; private final Pattern pattern; @@ -151,7 +179,7 @@ public boolean matches(String value) { */ @Override public JsonObject toJson() { - return JavaxJson.key(CONFIG_TYPE, getType().name()).key(CONFIG_PATTERN, getPattern().pattern()).get(); + return JavaxJson.key(keys().type(), getType().name()).key(keys().pattern(), getPattern().pattern()).get(); } @Override @@ -205,8 +233,8 @@ public static List fromJsonArray(final JsonArray rulesArray) { * @return a new rule */ public static Rule fromJson(final JsonObject ruleJson) { - return new Rule(Rule.RuleType.fromName(ruleJson.getString(CONFIG_TYPE)), - Pattern.compile(ruleJson.getString(CONFIG_PATTERN))); + return new Rule(Rule.RuleType.fromName(ruleJson.getString(keys().type())), + Pattern.compile(ruleJson.getString(keys().pattern()))); } /** diff --git a/core/src/main/java/net/adamcin/oakpal/core/ScanListener.java b/api/src/main/java/net/adamcin/oakpal/api/ScanListener.java similarity index 83% rename from core/src/main/java/net/adamcin/oakpal/core/ScanListener.java rename to api/src/main/java/net/adamcin/oakpal/api/ScanListener.java index fb173f611..24b0f53d3 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ScanListener.java +++ b/api/src/main/java/net/adamcin/oakpal/api/ScanListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,15 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; + +import org.osgi.annotation.versioning.ConsumerType; /** - * Defines listener notifications for beginning and end of scan common to {@link ErrorListener} and - * {@link ProgressCheck}. + * Defines listener notifications for beginning and end of scan common to {@link ProgressCheck} and + * {@code ErrorListener}. */ +@ConsumerType public interface ScanListener { /** diff --git a/api/src/main/java/net/adamcin/oakpal/api/Severity.java b/api/src/main/java/net/adamcin/oakpal/api/Severity.java new file mode 100644 index 000000000..22bc1dc3b --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/Severity.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Predicate; + +/** + * Levels of severity for violations detected during package scans. + */ +public enum Severity { + /** + * Unlikely to disrupt application functionality. Appropriate for reporting violations of + * code or style conventions, or inconsistency between modes of installation. + */ + MINOR(2), + + /** + * Likely to be the source of component instability. Appropriate for importer errors, mistaken + * assumptions about root path dependencies or namespaces, or failures related to unit testing of + * application packages. + */ + MAJOR(1), + + /** + * Likely to be the source of platform instability. Appropriate for reporting cross-package filter + * overlap, destructive ACL handling modes, destruction of authorable content, or security violations. + */ + SEVERE(0); + + private final int ordinal; + + Severity(int ordinal) { + this.ordinal = ordinal; + } + + /** + * Runtime throwing function to lookup severity codes by name. + * + * @param name the severity level name + * @return the associated severity level + */ + public static Severity byName(final @NotNull String name) { + for (Severity value : values()) { + if (value.name().equalsIgnoreCase(name)) { + return value; + } + } + throw new IllegalArgumentException("Unknown severity level: " + name); + } + + public boolean isLessSevereThan(Severity other) { + return this.ordinal > other.ordinal; + } + + public Predicate meetsMinimumSeverity() { + return other -> !other.isLessSevereThan(this); + } + + public Severity maxSeverity(final @NotNull Severity other) { + return this.isLessSevereThan(other) ? other : this; + } +} diff --git a/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheck.java b/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheck.java new file mode 100644 index 000000000..c36a8ea49 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheck.java @@ -0,0 +1,133 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ConsumerType; + +import java.util.Collection; +import java.util.MissingResourceException; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.function.Consumer; + +/** + * Simple implementation of a {@link ProgressCheck} with convenient methods for reporting and collecting violations. + */ +@ConsumerType +public class SimpleProgressCheck implements ProgressCheck { + protected final ReportCollector collector = new ReportCollector(); + private ResourceBundle resourceBundle; + + @Override + public void setResourceBundle(final ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + /** + * Used by {@link #getString(String)} to retrieve localized messages. + * NOTE: If this method is called before a non-null ResourceBundle has been injected via + * {@link ViolationReporter#setResourceBundle(ResourceBundle)}, it will try to get a fallback ResourceBundle + * by calling {@link ResourceBundle#getBundle(String)}, which invokes the default classloader and locale behavior, + * using {@link ViolationReporter#getResourceBundleBaseName()} as the resource bundle base name. + * + * @return the resource bundle + * @see ResourceBundle#getBundle(String) + * @see ViolationReporter#getResourceBundleBaseName() + */ + @Nullable + protected ResourceBundle getResourceBundle() throws MissingResourceException { + if (this.resourceBundle == null) { + if (getResourceBundleBaseName() != null) { + this.resourceBundle = ResourceBundle.getBundle(getResourceBundleBaseName()); + } + } + return this.resourceBundle; + } + + /** + * Lookup a localized string from the resource bundle. + * + * @param key the i18n key + * @return the localized string + * @throws MissingResourceException if an attempt is made to load a missing ResourceBundle + */ + @NotNull + protected String getString(@NotNull final String key) { + return Optional.ofNullable(getResourceBundle()) + .filter(bundle -> bundle.containsKey(key)) + .map(bundle -> bundle.getString(key)) + .orElse(key); + } + + protected void reportViolation(final Violation violation) { + collector.reportViolation(violation); + } + + /** + * Report a violation with a customizing consumer function. + * + * @param customizer the customizing consumer function + */ + protected final void reporting(@NotNull final Consumer customizer) { + SimpleViolation.Builder builder = SimpleViolation.builder(getResourceBundle()); + customizer.accept(builder); + this.reportViolation(builder.build()); + } + + protected final void reportViolation(final Severity severity, + final String description, + final PackageId... packages) { + this.reporting(builder -> builder + .withSeverity(severity) + .withDescription(description) + .withPackage(packages)); + } + + protected final void minorViolation(final String description, final PackageId... packages) { + this.reporting(builder -> builder + .withSeverity(Severity.MINOR) + .withDescription(description) + .withPackage(packages)); + } + + protected final void majorViolation(final String description, final PackageId... packages) { + this.reporting(builder -> builder + .withSeverity(Severity.MAJOR) + .withDescription(description) + .withPackage(packages)); + } + + protected final void severeViolation(final String description, final PackageId... packages) { + this.reporting(builder -> builder + .withSeverity(Severity.SEVERE) + .withDescription(description) + .withPackage(packages)); + } + + @Override + public void startedScan() { + collector.clearViolations(); + } + + @Override + public Collection getReportedViolations() { + return collector.getReportedViolations(); + } +} diff --git a/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheck.java b/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheck.java new file mode 100644 index 000000000..d1f2e4821 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheck.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ConsumerType; + +/** + * Convenience type for simple progress checks that are identified by their factory. + * + * @param the factory type parameter + */ +@ConsumerType +public class SimpleProgressCheckFactoryCheck extends SimpleProgressCheck { + + private final Class factoryClass; + + /** + * Need to keep a handle on the concrete class. + * + * @param factoryClass the factory class + */ + public SimpleProgressCheckFactoryCheck(final Class factoryClass) { + this.factoryClass = factoryClass; + } + + @Override + public String getCheckName() { + return factoryClass.getSimpleName(); + } + + @Override + public @Nullable String getResourceBundleBaseName() { + return factoryClass.getName(); + } +} diff --git a/api/src/main/java/net/adamcin/oakpal/api/SimpleViolation.java b/api/src/main/java/net/adamcin/oakpal/api/SimpleViolation.java new file mode 100644 index 000000000..4e83e612a --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/SimpleViolation.java @@ -0,0 +1,245 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.Nullable; + +import javax.json.JsonObject; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.ResourceBundle; + +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.JavaxJson.optArray; + +/** + * Simple implementation of a {@link Violation}. + */ +public final class SimpleViolation implements Violation { + private final Severity severity; + private final String description; + private final List packages; + + /** + * Constructor. + * + * @param severity the severity + * @param description the description + * @param packages the package ids + */ + public SimpleViolation(final Severity severity, final String description, final PackageId... packages) { + this(severity, description, packages != null ? Arrays.asList(packages) : null); + } + + /** + * Constructor. + * + * @param severity the severity + * @param description the description + * @param packages the package ids + */ + public SimpleViolation(final Severity severity, final String description, final List packages) { + this.severity = severity != null ? severity : Severity.MAJOR; + this.description = description; + this.packages = packages == null || packages.isEmpty() + ? Collections.emptyList() + : Collections.unmodifiableList(new ArrayList<>(packages)); + } + + /** + * Use this builder method to more easily construct a violation with MessageFormat arguments. + * + * @return a new SimpleViolation Builder + */ + public static Builder builder() { + return new Builder(null); + } + + /** + * Use this builder method to more easily construct a violation with MessageFormat arguments. + * + * @param resourceBundle a ResourceBundle to lookup a localized message format string if available + * @return a new SimpleViolation Builder + */ + public static Builder builder(final ResourceBundle resourceBundle) { + return new Builder(resourceBundle); + } + + public static final class Builder { + @Nullable + private final ResourceBundle resourceBundle; + private Severity severity; + private String description; + private List arguments = new ArrayList<>(); + private List packages = new ArrayList<>(); + + private Builder(@Nullable final ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + /** + * Set severity. + * + * @param severity severity value + * @return this builder + */ + public Builder withSeverity(final Severity severity) { + this.severity = severity; + return this; + } + + /** + * Set description. + * + * @param description description + * @return this builder + */ + public Builder withDescription(final String description) { + this.description = description; + return this; + } + + /** + * Set arguments. + * + * @param arguments arguments + * @return this builder + */ + public Builder withArguments(final List arguments) { + this.arguments = arguments != null ? new ArrayList<>(arguments) : new ArrayList<>(); + return this; + } + + /** + * Add one or more arguments. + * + * @param argument vararg argument values + * @return this builder + */ + public Builder withArgument(final Object... argument) { + if (argument != null) { + this.arguments.addAll(Arrays.asList(argument)); + } + return this; + } + + /** + * Set package ids. + * + * @param packages package ids + * @return this builder + */ + public Builder withPackages(final List packages) { + this.packages = packages != null ? new ArrayList<>(packages) : new ArrayList<>(); + return this; + } + + /** + * Add one or more packageIds. + * + * @param packageId vararg packageId values + * @return this builder + */ + public Builder withPackage(final PackageId... packageId) { + if (packageId != null) { + this.packages.addAll(Arrays.asList(packageId)); + } + return this; + } + + /** + * Build a simple violation. + * + * @return a new violation + */ + public SimpleViolation build() { + final String localDescription = + description != null && resourceBundle != null && resourceBundle.containsKey(description) + ? resourceBundle.getString(description) + : description; + if (localDescription == null || arguments == null || arguments.isEmpty()) { + return new SimpleViolation(severity, localDescription, packages); + } else { + return new SimpleViolation(severity, MessageFormat.format(localDescription, + arguments.toArray(new Object[arguments.size()])), packages); + } + } + } + + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public Collection getPackages() { + return packages; + } + + @Override + public String getDescription() { + return description; + } + + public static SimpleViolation fromReported(final Violation violation) { + Severity severity = violation.getSeverity(); + String description = violation.getDescription(); + List packages = new ArrayList<>(violation.getPackages()); + return new SimpleViolation(severity, description, packages); + } + + public static SimpleViolation fromJson(final JsonObject jsonViolation) { + String vSeverity = jsonViolation.getString(ApiConstants.violationKeys().severity(), Severity.MINOR.name()); + Severity severity = Severity.valueOf(vSeverity); + String description = jsonViolation.getString(ApiConstants.violationKeys().description(), ""); + List packages = optArray(jsonViolation, ApiConstants.violationKeys().packages()) + .map(array -> mapArrayOfStrings(array, PackageId::fromString, true)) + .orElseGet(Collections::emptyList); + + return new SimpleViolation(severity, description, packages); + } + + @Override + public String toString() { + return "SimpleViolation{" + + "severity=" + severity + + ", description='" + description + '\'' + + ", packages=" + packages + + '}'; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SimpleViolation that = (SimpleViolation) o; + return severity == that.severity && + Objects.equals(description, that.description) && + packages.equals(that.packages); + } + + @Override + public int hashCode() { + return Objects.hash(severity, description, packages); + } +} diff --git a/api/src/main/java/net/adamcin/oakpal/api/Violation.java b/api/src/main/java/net/adamcin/oakpal/api/Violation.java new file mode 100644 index 000000000..f3a7d3e66 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/Violation.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.osgi.annotation.versioning.ProviderType; + +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import java.util.Collection; + +/** + * Report type for validations. + */ +@ProviderType +public interface Violation extends JsonObjectConvertible { + + /** + * Describe the severity of the violation. + * + * @return the severity of the violation + */ + Severity getSeverity(); + + /** + * Provides a list of one or more Packages responsible for the violation. + * + * @return a list of package IDs responsible for the violation. + */ + Collection getPackages(); + + /** + * Describes the nature of the violation. + * + * @return the description + */ + String getDescription(); + + /** + * Serializes the Violation to a JsonObject. + * + * @return the json representation of the violation + */ + @Override + default JsonObject toJson() { + JsonObjectBuilder json = Json.createObjectBuilder(); + if (this.getSeverity() != null) { + json.add(ApiConstants.violationKeys().severity(), this.getSeverity().toString()); + } + if (this.getDescription() != null) { + json.add(ApiConstants.violationKeys().description(), this.getDescription()); + } + if (this.getPackages() != null && !this.getPackages().isEmpty()) { + JsonArrayBuilder array = Json.createArrayBuilder(); + for (PackageId packageId : this.getPackages()) { + array.add(packageId.toString()); + } + json.add(ApiConstants.violationKeys().packages(), array.build()); + } + return json.build(); + } +} diff --git a/api/src/main/java/net/adamcin/oakpal/api/ViolationReporter.java b/api/src/main/java/net/adamcin/oakpal/api/ViolationReporter.java new file mode 100644 index 000000000..b6acfcac7 --- /dev/null +++ b/api/src/main/java/net/adamcin/oakpal/api/ViolationReporter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ConsumerType; + +import java.util.Collection; +import java.util.ResourceBundle; + +/** + * Base interface for violation reporters. + */ +@ConsumerType +public interface ViolationReporter { + + /** + * Get the resource bundle base name for loading the default resource bundle for this violation reporter. Returns + * {@code getClass().getName()} by default. If this method is overridden to return null, the oakpal framework will + * not attempt to load a parent resource bundle specific to this violation reporter when creating a resource bundle + * during initialization. + * + * @return the resource bundle base name + */ + @Nullable + default String getResourceBundleBaseName() { + return getClass().getName(); + } + + /** + * Called by the framework before a scan to provide a resource bundle for immediate localization of strings. + * + * @param resourceBundle the resource bundle + */ + default void setResourceBundle(ResourceBundle resourceBundle) { + + } + + /** + * Called at the end of execution to collect any detected violations. + * + * @return any reported violations. + */ + Collection getReportedViolations(); +} diff --git a/api/src/main/resources/net/adamcin/oakpal/api/SimpleProgressCheck.properties b/api/src/main/resources/net/adamcin/oakpal/api/SimpleProgressCheck.properties new file mode 100644 index 000000000..b8553e3c2 --- /dev/null +++ b/api/src/main/resources/net/adamcin/oakpal/api/SimpleProgressCheck.properties @@ -0,0 +1,16 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTest.properties b/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTest.properties new file mode 100644 index 000000000..071600c48 --- /dev/null +++ b/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTest.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +first\ {0}\ then\ {1}=first {0} then {1} \ No newline at end of file diff --git a/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTestReversed.properties b/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTestReversed.properties new file mode 100644 index 000000000..0d0e07680 --- /dev/null +++ b/api/src/main/resources/net/adamcin/oakpal/api/SimpleViolationTestReversed.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +first\ {0}\ then\ {1}=first {1} then {0} \ No newline at end of file diff --git a/api/src/site/site.xml b/api/src/site/site.xml new file mode 100644 index 000000000..a1409caaf --- /dev/null +++ b/api/src/site/site.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/api/src/test/filtered-resources/test-packages.properties b/api/src/test/filtered-resources/test-packages.properties new file mode 100644 index 000000000..eae2b1bad --- /dev/null +++ b/api/src/test/filtered-resources/test-packages.properties @@ -0,0 +1,18 @@ +# +# Copyright 2018 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +test-packages.root=${project.build.directory} +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/core/src/test/java/net/adamcin/oakpal/core/FunTest.java b/api/src/test/java/net/adamcin/oakpal/api/FunTest.java similarity index 92% rename from core/src/test/java/net/adamcin/oakpal/core/FunTest.java rename to api/src/test/java/net/adamcin/oakpal/api/FunTest.java index cce581ac5..454eb9705 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/FunTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/FunTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,68 +14,68 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; - -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.compose0; -import static net.adamcin.oakpal.core.Fun.compose2; -import static net.adamcin.oakpal.core.Fun.composeTest; -import static net.adamcin.oakpal.core.Fun.composeTest2; -import static net.adamcin.oakpal.core.Fun.composeTry; -import static net.adamcin.oakpal.core.Fun.composeTry0; -import static net.adamcin.oakpal.core.Fun.composeTry2; -import static net.adamcin.oakpal.core.Fun.entriesToMap; -import static net.adamcin.oakpal.core.Fun.entriesToMapOfType; -import static net.adamcin.oakpal.core.Fun.entryTee; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.infer0; -import static net.adamcin.oakpal.core.Fun.infer1; -import static net.adamcin.oakpal.core.Fun.infer2; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.inferTest2; -import static net.adamcin.oakpal.core.Fun.isKeyIn; -import static net.adamcin.oakpal.core.Fun.isValueIn; -import static net.adamcin.oakpal.core.Fun.keepFirstMerger; -import static net.adamcin.oakpal.core.Fun.keepLastMerger; -import static net.adamcin.oakpal.core.Fun.mapEntry; -import static net.adamcin.oakpal.core.Fun.mapKey; -import static net.adamcin.oakpal.core.Fun.mapValue; -import static net.adamcin.oakpal.core.Fun.onEntry; -import static net.adamcin.oakpal.core.Fun.onKey; -import static net.adamcin.oakpal.core.Fun.onValue; -import static net.adamcin.oakpal.core.Fun.result0; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.result2; -import static net.adamcin.oakpal.core.Fun.resultNothing1; -import static net.adamcin.oakpal.core.Fun.resultNothing2; -import static net.adamcin.oakpal.core.Fun.testEntry; -import static net.adamcin.oakpal.core.Fun.testKey; -import static net.adamcin.oakpal.core.Fun.testOrDefault1; -import static net.adamcin.oakpal.core.Fun.testOrDefault2; -import static net.adamcin.oakpal.core.Fun.testValue; -import static net.adamcin.oakpal.core.Fun.throwingMerger; -import static net.adamcin.oakpal.core.Fun.throwingVoidToNothing1; -import static net.adamcin.oakpal.core.Fun.throwingVoidToNothing2; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.Fun.toVoid1; -import static net.adamcin.oakpal.core.Fun.toVoid2; -import static net.adamcin.oakpal.core.Fun.tryOrDefault0; -import static net.adamcin.oakpal.core.Fun.tryOrDefault1; -import static net.adamcin.oakpal.core.Fun.tryOrDefault2; -import static net.adamcin.oakpal.core.Fun.tryOrOptional0; -import static net.adamcin.oakpal.core.Fun.tryOrOptional1; -import static net.adamcin.oakpal.core.Fun.tryOrOptional2; -import static net.adamcin.oakpal.core.Fun.tryOrVoid1; -import static net.adamcin.oakpal.core.Fun.tryOrVoid2; -import static net.adamcin.oakpal.core.Fun.uncheck0; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheck2; -import static net.adamcin.oakpal.core.Fun.uncheckTest1; -import static net.adamcin.oakpal.core.Fun.uncheckTest2; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid2; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; -import static net.adamcin.oakpal.core.Fun.zipValuesWithKeyFunc; +package net.adamcin.oakpal.api; + +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.compose0; +import static net.adamcin.oakpal.api.Fun.compose2; +import static net.adamcin.oakpal.api.Fun.composeTest; +import static net.adamcin.oakpal.api.Fun.composeTest2; +import static net.adamcin.oakpal.api.Fun.composeTry; +import static net.adamcin.oakpal.api.Fun.composeTry0; +import static net.adamcin.oakpal.api.Fun.composeTry2; +import static net.adamcin.oakpal.api.Fun.entriesToMap; +import static net.adamcin.oakpal.api.Fun.entriesToMapOfType; +import static net.adamcin.oakpal.api.Fun.entryTee; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.infer0; +import static net.adamcin.oakpal.api.Fun.infer1; +import static net.adamcin.oakpal.api.Fun.infer2; +import static net.adamcin.oakpal.api.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.inferTest2; +import static net.adamcin.oakpal.api.Fun.isKeyIn; +import static net.adamcin.oakpal.api.Fun.isValueIn; +import static net.adamcin.oakpal.api.Fun.keepFirstMerger; +import static net.adamcin.oakpal.api.Fun.keepLastMerger; +import static net.adamcin.oakpal.api.Fun.mapEntry; +import static net.adamcin.oakpal.api.Fun.mapKey; +import static net.adamcin.oakpal.api.Fun.mapValue; +import static net.adamcin.oakpal.api.Fun.onEntry; +import static net.adamcin.oakpal.api.Fun.onKey; +import static net.adamcin.oakpal.api.Fun.onValue; +import static net.adamcin.oakpal.api.Fun.result0; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.result2; +import static net.adamcin.oakpal.api.Fun.resultNothing1; +import static net.adamcin.oakpal.api.Fun.resultNothing2; +import static net.adamcin.oakpal.api.Fun.testEntry; +import static net.adamcin.oakpal.api.Fun.testKey; +import static net.adamcin.oakpal.api.Fun.testOrDefault1; +import static net.adamcin.oakpal.api.Fun.testOrDefault2; +import static net.adamcin.oakpal.api.Fun.testValue; +import static net.adamcin.oakpal.api.Fun.throwingMerger; +import static net.adamcin.oakpal.api.Fun.throwingVoidToNothing1; +import static net.adamcin.oakpal.api.Fun.throwingVoidToNothing2; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.toVoid1; +import static net.adamcin.oakpal.api.Fun.toVoid2; +import static net.adamcin.oakpal.api.Fun.tryOrDefault0; +import static net.adamcin.oakpal.api.Fun.tryOrDefault1; +import static net.adamcin.oakpal.api.Fun.tryOrDefault2; +import static net.adamcin.oakpal.api.Fun.tryOrOptional0; +import static net.adamcin.oakpal.api.Fun.tryOrOptional1; +import static net.adamcin.oakpal.api.Fun.tryOrOptional2; +import static net.adamcin.oakpal.api.Fun.tryOrVoid1; +import static net.adamcin.oakpal.api.Fun.tryOrVoid2; +import static net.adamcin.oakpal.api.Fun.uncheck0; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheck2; +import static net.adamcin.oakpal.api.Fun.uncheckTest1; +import static net.adamcin.oakpal.api.Fun.uncheckTest2; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid2; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.Fun.zipValuesWithKeyFunc; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -594,6 +594,18 @@ public void testIsValueIn() { assertArrayEquals("filtered should contain 5 and 4", new Integer[]{5, 4}, filtered); } + @Test + public void testComposeTry_noOnError() { + final Fun.ThrowingFunction> classLoader = this.getClass().getClassLoader()::loadClass; + final Function>> func = + composeTry(Stream::of, Stream::empty, classLoader, null); + final String notARealClassName = "net.adamcin.oakpal.core.NotARealClass"; + final Class[] loadedClasses = Stream.of("java.lang.String", notARealClassName, "java.util.Map") + .flatMap(func).toArray(Class[]::new); + assertArrayEquals("loadedClasses should contain String and Map", + new Class[]{String.class, Map.class}, loadedClasses); + } + @Test public void testComposeTry() { final Fun.ThrowingFunction> classLoader = this.getClass().getClassLoader()::loadClass; diff --git a/core/src/test/java/net/adamcin/oakpal/core/JavaxJsonTest.java b/api/src/test/java/net/adamcin/oakpal/api/JavaxJsonTest.java similarity index 83% rename from core/src/test/java/net/adamcin/oakpal/core/JavaxJsonTest.java rename to api/src/test/java/net/adamcin/oakpal/api/JavaxJsonTest.java index 7937e29f4..02f9a2e44 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/JavaxJsonTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/JavaxJsonTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,32 +14,19 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; - -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.mapValue; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfObjects; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; -import static net.adamcin.oakpal.core.JavaxJson.mapObjectValues; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.JavaxJson.objectOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.optArray; -import static net.adamcin.oakpal.core.JavaxJson.optObject; -import static net.adamcin.oakpal.core.JavaxJson.unwrap; -import static net.adamcin.oakpal.core.JavaxJson.val; -import static net.adamcin.oakpal.core.JavaxJson.wrap; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +package net.adamcin.oakpal.api; + +import org.apache.jackrabbit.util.ISO8601; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -52,17 +39,78 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; -import org.apache.jackrabbit.util.ISO8601; -import org.junit.Test; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.mapValue; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfObjects; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.JavaxJson.mapObjectValues; +import static net.adamcin.oakpal.api.JavaxJson.shallowMergeObjects; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.objectOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.optArray; +import static net.adamcin.oakpal.api.JavaxJson.optObject; +import static net.adamcin.oakpal.api.JavaxJson.unwrap; +import static net.adamcin.oakpal.api.JavaxJson.val; +import static net.adamcin.oakpal.api.JavaxJson.wrap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class JavaxJsonTest { + static final String KEY_PREFIX = "prefix"; + static final String KEY_URI = "uri"; + + private static class JsonNamespace implements JsonObjectConvertible { + private String prefix; + private String uri; + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public static @Nullable JavaxJsonTest.JsonNamespace fromJson(final @NotNull JsonObject json) { + if (!json.containsKey(KEY_PREFIX) || !json.containsKey(KEY_URI)) { + return null; + } + JsonNamespace jsonNamespace = new JsonNamespace(); + jsonNamespace.setPrefix(json.getString(KEY_PREFIX, "")); + jsonNamespace.setUri(json.getString(KEY_URI, "")); + return jsonNamespace; + } + + @Override + public JsonObject toJson() { + return key(KEY_PREFIX, getPrefix()).key(KEY_URI, getUri()).get(); + } + + public static @NotNull JavaxJsonTest.JsonNamespace create(final @NotNull String prefix, final @NotNull String uri) { + final JsonNamespace ns = new JsonNamespace(); + ns.setPrefix(prefix); + ns.setUri(uri); + return ns; + } + } @Test public void testNonEmptyValue() { @@ -232,10 +280,10 @@ public void testWrap() { assertSame("same value", value, wrap(value)); assertEquals("equal string value", value, wrap("foo")); final JsonObject object = key("foo", "bar").get(); - final JavaxJson.ObjectConvertible objectible = () -> object; + final JsonObjectConvertible objectible = () -> object; assertSame("same value", object, wrap(objectible)); final JsonArray array = arr().val("foo1").val("foo2").val("foo3").get(); - final JavaxJson.ArrayConvertible arrayable = () -> array; + final JsonArrayConvertible arrayable = () -> array; assertSame("same value", array, wrap(arrayable)); final Calendar now = Calendar.getInstance(); assertEquals("same date", wrap(ISO8601.format(now)), wrap(now)); @@ -284,10 +332,10 @@ public void testWrap() { final String prefix = "foo"; final String uri = "http://foo.com/1.0"; - final Map convMap = Collections - .singletonMap(5, JcrNs.create(prefix, uri)); + final Map convMap = Collections + .singletonMap(5, JsonNamespace.create(prefix, uri)); assertEquals("convert map to json obj", - key("5", key(JcrNs.KEY_PREFIX, prefix).key(JcrNs.KEY_URI, uri)).get(), wrap(convMap)); + key("5", key(KEY_PREFIX, prefix).key(KEY_URI, uri)).get(), wrap(convMap)); final List ordinals = Arrays.asList("one", "two", "three"); assertEquals("collection to json array of wrapped elements", @@ -393,7 +441,7 @@ public void testCursorVal() { @Test public void testObjectConvertible() { - JavaxJson.ObjectConvertible conv = () -> key("apple", "pie").get(); + JsonObjectConvertible conv = () -> key("apple", "pie").get(); assertEquals("ObjectConvertible val is JsonObject", "pie", ((JsonObject) val(conv).get()).getString("apple")); @@ -401,7 +449,7 @@ public void testObjectConvertible() { @Test public void testArrayConvertible() { - JavaxJson.ArrayConvertible conv = () -> arr("one", "two").get(); + JsonArrayConvertible conv = () -> arr("one", "two").get(); assertEquals("ArrayConvertible val is JsonArray", "two", ((JsonArray) val(conv).get()).getString(1)); @@ -581,4 +629,51 @@ public void testHasNonNull() { assertTrue("hasNonNull for key with non-null json value", hasNonNull(key("foo", "bar").get(), "foo")); } + + @Test + public void testShallowMergeObjects() { + assertEquals("expect empty object from nulls", JsonObject.EMPTY_JSON_OBJECT, + JavaxJson.shallowMergeObjects(null, null)); + + assertEquals("expect empty object from null and empty", JsonObject.EMPTY_JSON_OBJECT, + JavaxJson.shallowMergeObjects(null, JsonValue.EMPTY_JSON_OBJECT)); + + assertEquals("expect empty object from empty and null", JsonObject.EMPTY_JSON_OBJECT, + JavaxJson.shallowMergeObjects(JsonValue.EMPTY_JSON_OBJECT, null)); + + final JsonObject base = obj() + .key("baseKey", "unmodified") + .key("keyString", "base") + .key("keyObject", key("baseKey", "unmodified").key("keyString", "base")) + .get(); + + assertEquals("expect equal base for null overlay", base, shallowMergeObjects(base, null)); + assertEquals("expect equal base for empty overlay", base, shallowMergeObjects(base, + JsonValue.EMPTY_JSON_OBJECT)); + assertEquals("expect equal base as overlay with null base", base, shallowMergeObjects(null, base)); + assertEquals("expect equal base as overlay with empty base", base, + shallowMergeObjects(JsonValue.EMPTY_JSON_OBJECT, base)); + + final JsonObject overlay = obj() + .key("overlayKey", "unmodified") + .key("keyString", "overlay") + .key("keyObject", key("overlayKey", "unmodified").key("keyString", "overlay")) + .get(); + + assertEquals("expect merged base <- overlay", obj() + .key("baseKey", "unmodified") + .key("overlayKey", "unmodified") + .key("keyString", "overlay") + .key("keyObject", key("overlayKey", "unmodified").key("keyString", "overlay")) + .get(), + shallowMergeObjects(base, overlay)); + + assertEquals("expect merged overlay <- base", obj() + .key("baseKey", "unmodified") + .key("overlayKey", "unmodified") + .key("keyString", "base") + .key("keyObject", key("baseKey", "unmodified").key("keyString", "base")) + .get(), + shallowMergeObjects(overlay, base)); + } } diff --git a/core/src/test/java/net/adamcin/oakpal/core/NothingTest.java b/api/src/test/java/net/adamcin/oakpal/api/NothingTest.java similarity index 97% rename from core/src/test/java/net/adamcin/oakpal/core/NothingTest.java rename to api/src/test/java/net/adamcin/oakpal/api/NothingTest.java index e498ba572..9400a8dd0 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/NothingTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/NothingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import static org.junit.Assert.*; diff --git a/api/src/test/java/net/adamcin/oakpal/api/PathActionTest.java b/api/src/test/java/net/adamcin/oakpal/api/PathActionTest.java new file mode 100644 index 000000000..4cdf034c3 --- /dev/null +++ b/api/src/test/java/net/adamcin/oakpal/api/PathActionTest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.junit.Test; + +import java.util.EnumSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PathActionTest { + + @Test + public void testGetShortCode() { + assertEquals("short code for NOOP is -", "-", PathAction.NOOP.getShortCode()); + assertEquals("short code for UNKNOWN is ?", "?", PathAction.UNKNOWN.getShortCode()); + assertEquals("short code for ADDED is A", "A", PathAction.ADDED.getShortCode()); + assertEquals("short code for DELETED is D", "D", PathAction.DELETED.getShortCode()); + assertEquals("short code for REPLACED is R", "R", PathAction.REPLACED.getShortCode()); + assertEquals("short code for MODIFIED is U", "U", PathAction.MODIFIED.getShortCode()); + assertEquals("short code for ERROR is E", "E", PathAction.ERROR.getShortCode()); + assertEquals("short code for MISSING is !", "!", PathAction.MISSING.getShortCode()); + + + } + + @Test + public void fromShortCode() { + assertEquals("expect NOOP for -", PathAction.NOOP, PathAction.fromShortCode("-")); + assertEquals("expect ADDED for A", PathAction.ADDED, PathAction.fromShortCode("A")); + assertEquals("expect DELETED for D", PathAction.DELETED, PathAction.fromShortCode("D")); + assertEquals("expect REPLACED for R", PathAction.REPLACED, PathAction.fromShortCode("R")); + assertEquals("expect MODIFIED for U", PathAction.MODIFIED, PathAction.fromShortCode("U")); + assertEquals("expect ERROR for E", PathAction.ERROR, PathAction.fromShortCode("E")); + assertEquals("expect MISSING for !", PathAction.MISSING, PathAction.fromShortCode("!")); + assertEquals("expect UNKNOWN for ?", PathAction.UNKNOWN, PathAction.fromShortCode("?")); + assertEquals("expect UNKNOWN for Z", PathAction.UNKNOWN, PathAction.fromShortCode("Z")); + assertEquals("expect UNKNOWN for ''", PathAction.UNKNOWN, PathAction.fromShortCode("")); + assertEquals("expect UNKNOWN for null", PathAction.UNKNOWN, PathAction.fromShortCode(null)); + } + + @Test + public void testToString() { + for (PathAction action : PathAction.values()) { + assertEquals(action + " toString is shortCode", action.getShortCode(), action.toString()); + } + } + + @Test + public void testIsNoop() { + for (PathAction action : PathAction.values()) { + if (action == PathAction.NOOP) { + assertTrue("NOOP isNoop true", action.isNoop()); + } else { + assertFalse(action.name() + " isNoop false", action.isNoop()); + } + } + } + + @Test + public void testIsUnknown() { + for (PathAction action : PathAction.values()) { + if (action == PathAction.UNKNOWN) { + assertTrue("NOOP isUnknown true", action.isUnknown()); + } else { + assertFalse(action.name() + " isUnknown false", action.isUnknown()); + } + } + } + + @Test + public void testCanGetItem() { + for (PathAction action : PathAction.values()) { + if (EnumSet.of(PathAction.NOOP, PathAction.MODIFIED, + PathAction.REPLACED, PathAction.ADDED).contains(action)) { + assertTrue(action.name() + " canGetItem true", action.canGetItem()); + } else { + assertFalse(action.name() + " canGetItem false", action.canGetItem()); + } + } + } +} \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckTest.java b/api/src/test/java/net/adamcin/oakpal/api/ProgressCheckTest.java similarity index 86% rename from core/src/test/java/net/adamcin/oakpal/core/ProgressCheckTest.java rename to api/src/test/java/net/adamcin/oakpal/api/ProgressCheckTest.java index fb7b38ad3..cd200e94f 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/ProgressCheckTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,14 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; + +import org.junit.Assert; +import org.junit.Test; import java.util.Collection; import java.util.Collections; -import org.junit.Test; - public class ProgressCheckTest { @Test @@ -32,12 +33,14 @@ public Collection getReportedViolations() { } }; + Assert.assertNotNull("expect nonnull checkName", mock.getCheckName()); mock.startedScan(); mock.identifyPackage(null, null); mock.identifySubpackage(null, null); mock.readManifest(null, null); mock.beforeExtract(null, null, null, null, null); mock.importedPath(null, null, null); + mock.importedPath(null, null, null, null); mock.deletedPath(null, null, null); mock.afterExtract(null, null); mock.finishedScan(); diff --git a/core/src/test/java/net/adamcin/oakpal/core/ResultTest.java b/api/src/test/java/net/adamcin/oakpal/api/ResultTest.java similarity index 92% rename from core/src/test/java/net/adamcin/oakpal/core/ResultTest.java rename to api/src/test/java/net/adamcin/oakpal/api/ResultTest.java index 60151f15e..f6a4b6ad8 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ResultTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/ResultTest.java @@ -1,6 +1,22 @@ -package net.adamcin.oakpal.core; - -import static net.adamcin.oakpal.core.Fun.result1; +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import static net.adamcin.oakpal.api.Fun.result1; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -9,21 +25,15 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.Spliterator; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import org.junit.Rule; import org.junit.Test; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ResultTest { diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/RuleTest.java b/api/src/test/java/net/adamcin/oakpal/api/RuleTest.java similarity index 61% rename from core/src/test/java/net/adamcin/oakpal/core/checks/RuleTest.java rename to api/src/test/java/net/adamcin/oakpal/api/RuleTest.java index 1d5765818..8a53937e1 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/RuleTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/RuleTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,56 +14,54 @@ * limitations under the License. */ -package net.adamcin.oakpal.core.checks; +package net.adamcin.oakpal.api; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.regex.Pattern; -import org.junit.Test; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class RuleTest { - @Test - public void testConstruct() { - boolean npeForRuleType = false; - try { - Rule rule = new Rule(null, Pattern.compile(".*")); - } catch (final NullPointerException npe) { - npeForRuleType = true; - } - - assertTrue("null ruleType should throw NPE immediately", npeForRuleType); - - boolean npeForPattern = false; - try { - Rule rule = new Rule(Rule.RuleType.DENY, null); - } catch (final NullPointerException npe) { - npeForPattern = true; - } + @Test(expected = NullPointerException.class) + public void testConstruct_nullType() { + new Rule(null, Pattern.compile(".*")); + } - assertTrue("null pattern should throw NPE immediately", npeForPattern); + @Test(expected = NullPointerException.class) + public void testConstruct_nullPattern() { + new Rule(Rule.RuleType.DENY, null); } @Test public void testRuleType() { - boolean illegalForUnknown = false; - try { - Rule.RuleType.fromName("not a thing"); - } catch (IllegalArgumentException iae) { - illegalForUnknown = true; - } + assertSame("expect allow", Rule.RuleType.ALLOW, Rule.RuleType.fromName("allow")); + assertSame("expect allow", Rule.RuleType.ALLOW, Rule.RuleType.fromName("ALLOW")); + assertSame("expect deny", Rule.RuleType.DENY, Rule.RuleType.fromName("deny")); + assertSame("expect deny", Rule.RuleType.DENY, Rule.RuleType.fromName("DENY")); + assertSame("expect include", Rule.RuleType.INCLUDE, Rule.RuleType.fromName("include")); + assertSame("expect include", Rule.RuleType.INCLUDE, Rule.RuleType.fromName("INCLUDE")); + assertSame("expect exclude", Rule.RuleType.EXCLUDE, Rule.RuleType.fromName("exclude")); + assertSame("expect exclude", Rule.RuleType.EXCLUDE, Rule.RuleType.fromName("EXCLUDE")); + } - assertTrue("unknown ruletype name should throw IllegalArgumentException immediately", - illegalForUnknown); + @Test(expected = IllegalArgumentException.class) + public void testRuleType_throws() { + Rule.RuleType.fromName("not a thing"); } @Test @@ -92,6 +90,43 @@ public void testHashCode() { Rule.DEFAULT_INCLUDE.hashCode(), newAllow.hashCode()); } + @Test + public void testMatches() { + assertTrue("default matches all", Rule.DEFAULT_ALLOW.matches("/foo")); + final Rule allowsDigits = new Rule(Rule.RuleType.ALLOW, Pattern.compile("[0-9]*")); + assertTrue("allows digits matches digits", allowsDigits.matches("123")); + assertFalse("allows digits doesn't match letters", allowsDigits.matches("abc")); + } + + @Test + public void testToJson() { + final Rule allowsDigits = new Rule(Rule.RuleType.ALLOW, Pattern.compile("[0-9]*")); + assertEquals("expect json", key(Rule.keys().type(), "ALLOW") + .key(Rule.keys().pattern(), "[0-9]*").get(), allowsDigits.toJson()); + } + + @Test + public void testFromJson() { + final Rule allowsDigits = Rule.fromJson(key(Rule.keys().type(), "ALLOW") + .key(Rule.keys().pattern(), "[0-9]*").get()); + assertEquals("expect type", Rule.RuleType.ALLOW, allowsDigits.getType()); + assertEquals("expect pattern", "[0-9]*", allowsDigits.getPattern().pattern()); + } + + @Test + public void testFromJsonArray() { + final Rule allowsDigits = Rule.fromJson(key(Rule.keys().type(), "ALLOW") + .key(Rule.keys().pattern(), "[0-9]*").get()); + final Rule allowsLetters = Rule.fromJson(key(Rule.keys().type(), "ALLOW") + .key(Rule.keys().pattern(), "[a-z]*").get()); + + final List expectRules = Arrays.asList(allowsDigits, allowsLetters); + assertEquals("exect same rules", expectRules, Rule.fromJsonArray(arr() + .val(key(Rule.keys().type(), "allow").key(Rule.keys().pattern(), "[0-9]*")) + .val(key(Rule.keys().type(), "allow").key(Rule.keys().pattern(), "[a-z]*")) + .get())); + } + @Test public void testRuleDefaultAllow() { final Rule rule = Rule.DEFAULT_ALLOW; @@ -168,8 +203,8 @@ public void testFuzzyDefaults() { }); fuzzyDefaultExcFns.entrySet().forEach(fuzzyFnPair -> { - final String fuzzyFnName = fuzzyFnPair.getKey(); - final Function, Rule> fuzzyFn = fuzzyFnPair.getValue(); + final String fuzzyFnName = fuzzyFnPair.getKey(); + final Function, Rule> fuzzyFn = fuzzyFnPair.getValue(); defaultExcRules.forEach(rule -> { final Rule defaultRule = fuzzyFn.apply(singletonList(rule)); assertEquals(fuzzyFnName + ": invert default exclude when first rule is exclude-like: " + rule, @@ -182,4 +217,27 @@ public void testFuzzyDefaults() { }); }); } + + @Test + public void testLastMatch() { + final Rule allowsDigits = Rule.fromJson(key(Rule.keys().type(), "ALLOW") + .key(Rule.keys().pattern(), "[0-9]*").get()); + final Rule deniesLetters = Rule.fromJson(key(Rule.keys().type(), "DENY") + .key(Rule.keys().pattern(), "[a-z]*").get()); + + final List expectRules = Arrays.asList(allowsDigits, deniesLetters); + final Rule adHocDefault = new Rule(Rule.RuleType.ALLOW, Pattern.compile("\\.\\.")); + final Rule matched0 = Rule.lastMatch(expectRules, "...", rules -> adHocDefault); + assertSame("expect specific default", adHocDefault, matched0); + + final Rule matched1 = Rule.lastMatch(expectRules, "...", null); + assertSame("expect globbal default", Rule.DEFAULT_INCLUDE, matched1); + + final Rule matchedDigits = Rule.lastMatch(expectRules, "123"); + assertSame("expect digits rule", allowsDigits, matchedDigits); + + final Rule matchedAlpha = Rule.lastMatch(expectRules, "abc"); + assertSame("expect alpha rule", deniesLetters, matchedAlpha); + + } } diff --git a/api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheckTest.java b/api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheckTest.java new file mode 100644 index 000000000..4c4260524 --- /dev/null +++ b/api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckFactoryCheckTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.junit.Test; + +import javax.json.JsonObject; +import javax.json.JsonValue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SimpleProgressCheckFactoryCheckTest { + + static class TestProgressCheckFactory implements ProgressCheckFactory { + @Override + public ProgressCheck newInstance(final JsonObject config) throws Exception { + return new TestProgressCheckFactoryCheck(); + } + + } + + static class TestProgressCheckFactoryCheck extends SimpleProgressCheckFactoryCheck { + public TestProgressCheckFactoryCheck() { + super(TestProgressCheckFactory.class); + } + } + + @Test + public void testBaseMethods() throws Exception { + ProgressCheck progressCheck = new TestProgressCheckFactory().newInstance(JsonValue.EMPTY_JSON_OBJECT); + assertTrue("expect instance of TestProgressCheckFactoryCheck", + progressCheck instanceof TestProgressCheckFactoryCheck); + + assertEquals("expect checkName", TestProgressCheckFactory.class.getSimpleName(), + progressCheck.getCheckName()); + assertEquals("expect resourceBundleName", TestProgressCheckFactory.class.getName(), + progressCheck.getResourceBundleBaseName()); + + } +} \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/SimpleProgressCheckTest.java b/api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckTest.java similarity index 68% rename from core/src/test/java/net/adamcin/oakpal/core/SimpleProgressCheckTest.java rename to api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckTest.java index 0cbbfd82a..48eb8ef7a 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/SimpleProgressCheckTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/SimpleProgressCheckTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,50 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.apache.jackrabbit.vault.packaging.PackageId; import org.junit.Test; -import static org.junit.Assert.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Locale; +import java.util.ResourceBundle; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class SimpleProgressCheckTest { + @Test + public void testSetResourceBundle() { + final SimpleProgressCheck check = new SimpleProgressCheck(); + + ResourceBundle originalBundle = check.getResourceBundle(); + assertSame("same object returned twice", originalBundle, check.getResourceBundle()); + ResourceBundle newBundle = ResourceBundle.getBundle(check.getResourceBundleBaseName(), + Locale.getDefault(), new URLClassLoader(new URL[0], getClass().getClassLoader())); + assertNotSame("not same object as created externally", newBundle, check.getResourceBundle()); + check.setResourceBundle(newBundle); + assertSame("same object as set", newBundle, check.getResourceBundle()); + assertSame("same object as set, again", newBundle, check.getResourceBundle()); + } + + @Test + public void testGetString() { + final SimpleProgressCheck check = new SimpleProgressCheck(); + assertEquals("expect passthrough", "testKey", check.getString("testKey")); + ResourceBundle newBundle = ResourceBundle.getBundle(getClass().getName()); + check.setResourceBundle(newBundle); + assertEquals("expect from bundle", "yeKtset", check.getString("testKey")); + } + @Test public void testReportViolation() { SimpleProgressCheck check = new SimpleProgressCheck(); - SimpleViolation violation = new SimpleViolation(Violation.Severity.SEVERE, "description"); + SimpleViolation violation = new SimpleViolation(Severity.SEVERE, "description"); check.reportViolation(violation); assertTrue("contains violation", check.getReportedViolations().contains(violation)); } @@ -36,9 +67,9 @@ public void testReportViolation_args() { SimpleProgressCheck check = new SimpleProgressCheck(); PackageId id0 = PackageId.fromString("my_packages:test_0:1.0"); PackageId id1 = PackageId.fromString("my_packages:test_1:1.0"); - check.reportViolation(Violation.Severity.MINOR, "description", id0, id1); + check.reportViolation(Severity.MINOR, "description", id0, id1); assertTrue("violation reported", check.getReportedViolations().stream() - .anyMatch(violation -> violation.getSeverity() == Violation.Severity.MINOR + .anyMatch(violation -> violation.getSeverity() == Severity.MINOR && "description".equals(violation.getDescription()) && violation.getPackages().contains(id0) && violation.getPackages().contains(id1) @@ -50,7 +81,7 @@ public void testStartedScan() { SimpleProgressCheck check = new SimpleProgressCheck(); PackageId id0 = PackageId.fromString("my_packages:test_0:1.0"); PackageId id1 = PackageId.fromString("my_packages:test_1:1.0"); - check.reportViolation(Violation.Severity.MINOR, "description", id0, id1); + check.reportViolation(Severity.MINOR, "description", id0, id1); assertEquals("violation reported", 1, check.getReportedViolations().size()); check.startedScan(); assertTrue("violations are now empty", check.getReportedViolations().isEmpty()); @@ -63,7 +94,7 @@ public void testMinorViolation() { PackageId id1 = PackageId.fromString("my_packages:test_1:1.0"); check.minorViolation("description", id0, id1); assertTrue("violation reported", check.getReportedViolations().stream() - .anyMatch(violation -> violation.getSeverity() == Violation.Severity.MINOR + .anyMatch(violation -> violation.getSeverity() == Severity.MINOR && "description".equals(violation.getDescription()) && violation.getPackages().contains(id0) && violation.getPackages().contains(id1) @@ -77,7 +108,7 @@ public void testMajorViolation() { PackageId id1 = PackageId.fromString("my_packages:test_1:1.0"); check.majorViolation("description", id0, id1); assertTrue("violation reported", check.getReportedViolations().stream() - .anyMatch(violation -> violation.getSeverity() == Violation.Severity.MAJOR + .anyMatch(violation -> violation.getSeverity() == Severity.MAJOR && "description".equals(violation.getDescription()) && violation.getPackages().contains(id0) && violation.getPackages().contains(id1) @@ -91,7 +122,7 @@ public void testSevereViolation() { PackageId id1 = PackageId.fromString("my_packages:test_1:1.0"); check.severeViolation("description", id0, id1); assertTrue("violation reported", check.getReportedViolations().stream() - .anyMatch(violation -> violation.getSeverity() == Violation.Severity.SEVERE + .anyMatch(violation -> violation.getSeverity() == Severity.SEVERE && "description".equals(violation.getDescription()) && violation.getPackages().contains(id0) && violation.getPackages().contains(id1) diff --git a/api/src/test/java/net/adamcin/oakpal/api/SimpleViolationTest.java b/api/src/test/java/net/adamcin/oakpal/api/SimpleViolationTest.java new file mode 100644 index 000000000..bb6c6b843 --- /dev/null +++ b/api/src/test/java/net/adamcin/oakpal/api/SimpleViolationTest.java @@ -0,0 +1,234 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.junit.Test; + +import javax.json.JsonObject; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.ResourceBundle; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class SimpleViolationTest { + + @Test + public void testConstruct() { + SimpleViolation nulls = new SimpleViolation(null, null, (PackageId[]) null); + assertNotNull("toString not null", nulls); + } + + @Test + public void testFromReported() { + final Severity severity = Severity.MINOR; + final PackageId packId = PackageId.fromString("my_pack"); + final String description = "some description"; + final Violation violation = new Violation() { + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public Collection getPackages() { + return Collections.singletonList(packId); + } + + @Override + public String getDescription() { + return description; + } + }; + + final SimpleViolation simpleViolation = SimpleViolation.fromReported(violation); + assertSame("expect same severity", severity, simpleViolation.getSeverity()); + assertEquals("expect equal packageIds", Collections.singletonList(packId), + simpleViolation.getPackages()); + assertSame("expect same description", description, simpleViolation.getDescription()); + } + + @Test + public void testFromJson() { + final Severity severity = Severity.MINOR; + final PackageId packId = PackageId.fromString("my_pack"); + final String description = "some description"; + final Violation violation = new Violation() { + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public Collection getPackages() { + return Collections.singletonList(packId); + } + + @Override + public String getDescription() { + return description; + } + }; + + final JsonObject json = violation.toJson(); + + final SimpleViolation simpleViolation = SimpleViolation.fromJson(json); + assertSame("expect same severity", severity, simpleViolation.getSeverity()); + assertEquals("expect equal packageIds", Collections.singletonList(packId), + simpleViolation.getPackages()); + assertEquals("expect equal description", description, simpleViolation.getDescription()); + } + + @Test + public void testToString() { + final Severity severity = Severity.MINOR; + final PackageId packId = PackageId.fromString("my_pack"); + final String description = "some description"; + final Violation violation = new Violation() { + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public Collection getPackages() { + return Collections.singletonList(packId); + } + + @Override + public String getDescription() { + return description; + } + }; + + final JsonObject json = violation.toJson(); + final SimpleViolation simpleViolation = SimpleViolation.fromJson(json); + assertTrue("my_pack in toString", simpleViolation.toString().contains("my_pack")); + assertTrue("description in toString", simpleViolation.toString().contains(description)); + } + + @Test + public void testEquals() { + final Severity severity = Severity.MINOR; + final PackageId packId = PackageId.fromString("my_pack"); + final String description = "some description"; + final Violation violation = new Violation() { + @Override + public Severity getSeverity() { + return severity; + } + + @Override + public Collection getPackages() { + return Collections.singletonList(packId); + } + + @Override + public String getDescription() { + return description; + } + }; + + final JsonObject json = violation.toJson(); + final SimpleViolation simpleViolation = SimpleViolation.fromJson(json); + assertNotEquals("SimpleViolation not equal to Violation", simpleViolation, violation); + final SimpleViolation simpleViolationCopy = SimpleViolation.fromReported(violation); + assertEquals("SimpleViolation equal to copy SimpleViolation", simpleViolation, simpleViolationCopy); + + // also hashCode + assertNotEquals("SimpleViolation hashCode not equal to Violation hashCode", + simpleViolation.hashCode(), violation.hashCode()); + assertEquals("SimpleViolation hashCode equal to copy SimpleViolation hashCode", + simpleViolation.hashCode(), simpleViolationCopy.hashCode()); + } + + @Test + public void builder_testFactory() { + assertNotNull("expect instance of builder", SimpleViolation.builder()); + final ResourceBundle myBundle = ResourceBundle.getBundle(getClass().getName()); + assertNotNull("expect instance of builder with bundle", SimpleViolation.builder(myBundle)); + } + + @Test + public void builder_testNoBundle() { + final SimpleViolation.Builder builder = SimpleViolation.builder(); + final SimpleViolation defaults = builder.build(); + assertEquals("expect severity", Severity.MAJOR, defaults.getSeverity()); + assertNull("expect null description", defaults.getDescription()); + assertEquals("expect empty packages", 0, defaults.getPackages().size()); + final SimpleViolation minor = builder.withSeverity(Severity.MINOR).build(); + assertEquals("expect severity", Severity.MINOR, minor.getSeverity()); + assertNull("expect null description", minor.getDescription()); + assertEquals("expect empty packages", 0, minor.getPackages().size()); + final SimpleViolation severe = builder.withSeverity(Severity.SEVERE).build(); + assertEquals("expect severity", Severity.SEVERE, severe.getSeverity()); + assertNull("expect null description", severe.getDescription()); + assertEquals("expect empty packages", 0, severe.getPackages().size()); + final SimpleViolation major = builder.withSeverity(Severity.MAJOR).build(); + assertEquals("expect severity", Severity.MAJOR, major.getSeverity()); + assertNull("expect null description", major.getDescription()); + assertEquals("expect empty packages", 0, major.getPackages().size()); + + final SimpleViolation described = builder.withDescription("described").build(); + assertEquals("expect description", "described", described.getDescription()); + final PackageId firstPackage = PackageId.fromString("my_packages:first"); + final PackageId secondPackage = PackageId.fromString("my_packages:second"); + final SimpleViolation withPackagesVarargs = builder.withPackage(firstPackage, secondPackage).build(); + final SimpleViolation withPackagesList = builder.withPackages(Arrays.asList(firstPackage, secondPackage)).build(); + assertEquals("expect equal packages", withPackagesList, withPackagesVarargs); + final SimpleViolation withPackagesAppended = builder.withPackage(firstPackage, secondPackage).build(); + final SimpleViolation withPackagesDuplicated = builder.withPackages(Arrays.asList(firstPackage, secondPackage, + firstPackage, secondPackage)).build(); + assertEquals("expect packages appended", withPackagesDuplicated, withPackagesAppended); + + final SimpleViolation withArgumentsVarargs = builder.withDescription("test {0} {1}") + .withArgument("one", "two") + .build(); + assertEquals("expect description varargs", "test one two", withArgumentsVarargs.getDescription()); + final SimpleViolation withArgumentsList = builder.withArguments(Arrays.asList("one", "two")).build(); + assertEquals("expect description list", "test one two", withArgumentsList.getDescription()); + final SimpleViolation withArgumentsListReversed = builder.withArguments(Arrays.asList("two", "one")).build(); + assertEquals("expect description list reversed", "test two one", + withArgumentsListReversed.getDescription()); + assertEquals("expect description list reversed varargs appended", "test two one", + builder.withArgument("one", "two").build().getDescription()); + } + + @Test + public void builder_testWithBundle() { + final String key = "first {0} then {1}"; + final List arguments = Arrays.asList("one", "two"); + final SimpleViolation.Builder builder = SimpleViolation + .builder(ResourceBundle.getBundle(SimpleViolationTest.class.getName())); + final SimpleViolation forward = builder.withArguments(arguments) + .withDescription(key).build(); + assertEquals("expect forward string", "first one then two", forward.getDescription()); + final SimpleViolation.Builder builderReversed = SimpleViolation + .builder(ResourceBundle.getBundle(SimpleViolationTest.class.getName() + "Reversed")); + final SimpleViolation reversed = builderReversed.withArguments(arguments) + .withDescription(key).build(); + assertEquals("expect reverse string", "first two then one", reversed.getDescription()); + } +} diff --git a/api/src/test/java/net/adamcin/oakpal/api/ViolationReporterTest.java b/api/src/test/java/net/adamcin/oakpal/api/ViolationReporterTest.java new file mode 100644 index 000000000..dbfd5c4fb --- /dev/null +++ b/api/src/test/java/net/adamcin/oakpal/api/ViolationReporterTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.api; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; + +public class ViolationReporterTest { + + @Test + public void testDefaultMethods() { + ViolationReporter mock = new MockViolationReporter(); + + mock.setResourceBundle(null); + Assert.assertNotNull("expect nonnull getResourceBundleBaseName", mock.getResourceBundleBaseName()); + } + + static class MockViolationReporter implements ViolationReporter { + @Override + public Collection getReportedViolations() { + return Collections.emptyList(); + } + } +} \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/ViolationTest.java b/api/src/test/java/net/adamcin/oakpal/api/ViolationTest.java similarity index 74% rename from core/src/test/java/net/adamcin/oakpal/core/ViolationTest.java rename to api/src/test/java/net/adamcin/oakpal/api/ViolationTest.java index 8dad93743..75a5f5d60 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ViolationTest.java +++ b/api/src/test/java/net/adamcin/oakpal/api/ViolationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.api; import org.apache.jackrabbit.vault.packaging.PackageId; import org.junit.Test; @@ -23,15 +23,15 @@ import java.util.Arrays; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.ReportMapper.KEY_DESCRIPTION; -import static net.adamcin.oakpal.core.ReportMapper.KEY_PACKAGES; -import static net.adamcin.oakpal.core.ReportMapper.KEY_SEVERITY; -import static net.adamcin.oakpal.core.Violation.Severity.MAJOR; -import static net.adamcin.oakpal.core.Violation.Severity.MINOR; -import static net.adamcin.oakpal.core.Violation.Severity.SEVERE; -import static org.junit.Assert.*; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.Severity.MAJOR; +import static net.adamcin.oakpal.api.Severity.MINOR; +import static net.adamcin.oakpal.api.Severity.SEVERE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -40,17 +40,17 @@ public class ViolationTest { @Test public void testSeverity_byName() { - assertSame("severity for minor is", MINOR, Violation.Severity.byName("minor")); - assertSame("severity for minor is", MINOR, Violation.Severity.byName("MiNoR")); - assertSame("severity for major is", MAJOR, Violation.Severity.byName("major")); - assertSame("severity for major is", MAJOR, Violation.Severity.byName("MaJoR")); - assertSame("severity for severe is", SEVERE, Violation.Severity.byName("severe")); - assertSame("severity for severe is", SEVERE, Violation.Severity.byName("SeVeRe")); + assertSame("severity for minor is", MINOR, Severity.byName("minor")); + assertSame("severity for minor is", MINOR, Severity.byName("MiNoR")); + assertSame("severity for major is", MAJOR, Severity.byName("major")); + assertSame("severity for major is", MAJOR, Severity.byName("MaJoR")); + assertSame("severity for severe is", SEVERE, Severity.byName("severe")); + assertSame("severity for severe is", SEVERE, Severity.byName("SeVeRe")); } @Test(expected = IllegalArgumentException.class) public void testSeverity_byName_throws() { - Violation.Severity.byName("nothing"); + Severity.byName("nothing"); } @Test @@ -101,9 +101,9 @@ public void testToJson() { when(violation.getPackages()).thenReturn(Arrays.asList(id0, id1)); JsonObject expected = obj() - .key(KEY_SEVERITY, SEVERE) - .key(KEY_DESCRIPTION, "some failure") - .key(KEY_PACKAGES, arr().val(id0.toString()).val(id1.toString())) + .key(ApiConstants.violationKeys().severity(), SEVERE) + .key(ApiConstants.violationKeys().description(), "some failure") + .key(ApiConstants.violationKeys().packages(), arr().val(id0.toString()).val(id1.toString())) .get(); assertEquals("same json", expected, violation.toJson()); diff --git a/api/src/test/resources/logback-test.xml b/api/src/test/resources/logback-test.xml new file mode 100644 index 000000000..80b976e2a --- /dev/null +++ b/api/src/test/resources/logback-test.xml @@ -0,0 +1,34 @@ + + + + + + + + [%level] %logger{36} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/api/src/test/resources/net/adamcin/oakpal/api/SimpleProgressCheckTest.properties b/api/src/test/resources/net/adamcin/oakpal/api/SimpleProgressCheckTest.properties new file mode 100644 index 000000000..c10f9295e --- /dev/null +++ b/api/src/test/resources/net/adamcin/oakpal/api/SimpleProgressCheckTest.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +testKey=yeKtset \ No newline at end of file diff --git a/cli/Dockerfile b/cli/Dockerfile index 237402d3a..5104daabf 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,5 +1,5 @@ # prototype dockerfile for faster iterations -FROM adoptopenjdk/openjdk11-openj9:alpine-slim +FROM adoptopenjdk/openjdk14-openj9:alpine-slim RUN mkdir -p /app/oakpal-cli COPY target/oakpal-cli-*-dist.tar.gz /app RUN tar --strip-components 1 -C /app/oakpal-cli -zxf /app/oakpal-cli-*-dist.tar.gz \ diff --git a/cli/pom.xml b/cli/pom.xml index 3a682f42e..cf97e55f8 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -21,7 +21,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT .. @@ -40,10 +40,6 @@ HEAD - - 1.0.0 - - @@ -130,6 +126,10 @@ + + org.jetbrains + annotations + org.slf4j diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/AllReportsMessage.java b/cli/src/main/java/net/adamcin/oakpal/cli/AllReportsMessage.java index 4214cbfc9..6891f6928 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/AllReportsMessage.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/AllReportsMessage.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.Fun.compose; +import static net.adamcin.oakpal.api.Fun.compose; class AllReportsMessage implements StructuredMessage { diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/Command.java b/cli/src/main/java/net/adamcin/oakpal/cli/Command.java index 033500153..b71c3eb63 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/Command.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/Command.java @@ -1,10 +1,9 @@ package net.adamcin.oakpal.cli; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.result0; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.Fun.uncheck0; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.result0; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheck0; import java.io.BufferedReader; import java.io.File; @@ -15,20 +14,20 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.function.Function; import java.util.function.Supplier; +import net.adamcin.oakpal.api.Severity; import net.adamcin.oakpal.core.CheckReport; import net.adamcin.oakpal.core.DefaultErrorListener; import net.adamcin.oakpal.core.FileBlobMemoryNodeStore; -import net.adamcin.oakpal.core.Nothing; +import net.adamcin.oakpal.api.Nothing; import net.adamcin.oakpal.core.OakMachine; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.jetbrains.annotations.NotNull; @@ -107,7 +106,7 @@ Optional getHighestReportSeverity(final @NotNull Options opts, return reports.stream() .flatMap(compose(CheckReport::getViolations, Collection::stream)) .map(Violation::getSeverity) - .reduce(Violation.Severity::maxSeverity) + .reduce(Severity::maxSeverity) .filter(opts.getFailOnSeverity().meetsMinimumSeverity()) .map(severity -> { switch (severity) { @@ -201,6 +200,29 @@ Optional flipOpt(final @NotNull String wholeOpt) { builder.setNoPlan(isNoOpt); builder.setPlanName(isNoOpt ? null : args[++i]); break; + case "-pf": + case "--plan-file": + builder.setPlanFile(isNoOpt ? null : console.getCwd().toPath().resolve(args[++i]).toFile()); + break; + case "--plan-file-base": + builder.setPlanFileBaseDir(isNoOpt ? null : console.getCwd().toPath().resolve(args[++i]).toFile()); + break; + case "-pi": + case "--pre-install-file": + if (isNoOpt) { + builder.setPreInstallFiles(Collections.emptyList()); + } else { + builder.addPreInstallFile(console.getCwd().toPath().resolve(args[++i]).toFile()); + } + break; + case "-xp": + case "--extend-classpath": + if (isNoOpt) { + builder.setExtendedClassPathFiles(Collections.emptyList()); + } else { + builder.addExtendedClassPathFile(console.getCwd().toPath().resolve(args[++i]).toFile()); + } + break; case "-s": case "--severity-fail": if (isNoOpt) { @@ -208,7 +230,7 @@ Optional flipOpt(final @NotNull String wholeOpt) { break; } else { final String severityArg = args[++i]; - final Result severityResult = result1(Violation.Severity::byName) + final Result severityResult = result1(Severity::byName) .apply(severityArg); if (severityResult.isFailure()) { return Result.failure(severityResult.getError().get()); diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/Console.java b/cli/src/main/java/net/adamcin/oakpal/cli/Console.java index 2b0636666..1c5e9e467 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/Console.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/Console.java @@ -1,13 +1,12 @@ package net.adamcin.oakpal.cli; import java.io.File; -import java.io.FileNotFoundException; import java.util.Collections; import java.util.Map; import java.util.Properties; -import net.adamcin.oakpal.core.Nothing; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Nothing; +import net.adamcin.oakpal.api.Result; import org.jetbrains.annotations.NotNull; /** diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/DisposablePrinter.java b/cli/src/main/java/net/adamcin/oakpal/cli/DisposablePrinter.java index 76850df4b..447badef6 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/DisposablePrinter.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/DisposablePrinter.java @@ -2,7 +2,7 @@ import java.util.function.Function; -import net.adamcin.oakpal.core.Nothing; +import net.adamcin.oakpal.api.Nothing; /** * Extension of simple IO printer function type to add a dispose() method. diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/IO.java b/cli/src/main/java/net/adamcin/oakpal/cli/IO.java index f20581c9c..96ba29e61 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/IO.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/IO.java @@ -3,7 +3,7 @@ import java.util.function.Function; import java.util.function.Supplier; -import net.adamcin.oakpal.core.Nothing; +import net.adamcin.oakpal.api.Nothing; /** * Just a simple IO monad. diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/Main.java b/cli/src/main/java/net/adamcin/oakpal/cli/Main.java index 064347765..5d71b61f7 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/Main.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/Main.java @@ -1,6 +1,6 @@ package net.adamcin.oakpal.cli; -import static net.adamcin.oakpal.core.Fun.result1; +import static net.adamcin.oakpal.api.Fun.result1; import java.io.File; import java.io.PrintStream; @@ -9,12 +9,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Properties; import java.util.function.Function; import java.util.function.IntConsumer; -import net.adamcin.oakpal.core.Nothing; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Nothing; +import net.adamcin.oakpal.api.Result; import org.jetbrains.annotations.NotNull; /** diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/Options.java b/cli/src/main/java/net/adamcin/oakpal/cli/Options.java index 08383db10..76a9d6d27 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/Options.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/Options.java @@ -1,23 +1,31 @@ package net.adamcin.oakpal.cli; +import net.adamcin.oakpal.api.Nothing; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Severity; import net.adamcin.oakpal.core.InstallHookPolicy; -import net.adamcin.oakpal.core.Nothing; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.OpearFile; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.core.opear.AdhocOpear; +import net.adamcin.oakpal.core.opear.Opear; +import net.adamcin.oakpal.core.opear.OpearFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.net.URI; import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.jar.JarFile; +import java.util.stream.Collectors; + +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.result1; final class Options { static final String CACHE_DIR_NAME = ".oakpal-cache"; @@ -31,19 +39,25 @@ final class Options { private final File cacheDir; private final File opearFile; private final String planName; + private final File planFile; + private final File planFileBaseDir; + private final List preInstallFiles; + private final List extendedClassPathFiles; private final boolean noHooks; private final List scanFiles; private final Function> printer; - private final Violation.Severity failOnSeverity; + private final Severity failOnSeverity; Options() { this(true, true, false, OakpalPlan.BASIC_PLAN_URL, Options.class.getClassLoader(), new File(System.getProperty("java.io.tmpdir")), - null, null, false, + null, null, null, null, + Collections.emptyList(), + Collections.emptyList(), false, Collections.emptyList(), EMPTY_PRINTER, - Violation.Severity.MAJOR); + Severity.MAJOR); } Options(final boolean justHelp, @@ -54,10 +68,14 @@ final class Options { final @NotNull File cacheDir, final @Nullable File opearFile, final @Nullable String planName, + final @Nullable File planFile, + final @Nullable File planFileBaseDir, + final @NotNull List preInstallFiles, + final @NotNull List extendedClassPathFiles, final boolean noHooks, final @NotNull List scanFiles, final @NotNull Function> printer, - final @NotNull Violation.Severity failOnSeverity) { + final @NotNull Severity failOnSeverity) { this.justHelp = justHelp; this.justVersion = justVersion; this.storeBlobs = storeBlobs; @@ -66,6 +84,10 @@ final class Options { this.cacheDir = cacheDir; this.opearFile = opearFile; this.planName = planName; + this.planFile = planFile; + this.planFileBaseDir = planFileBaseDir; + this.preInstallFiles = preInstallFiles; + this.extendedClassPathFiles = extendedClassPathFiles; this.noHooks = noHooks; this.scanFiles = scanFiles; this.printer = printer; @@ -108,6 +130,22 @@ public File getCacheDir() { return planName; } + public @Nullable File getPlanFile() { + return planFile; + } + + public @Nullable File getPlanFileBaseDir() { + return planFileBaseDir; + } + + public @NotNull List getPreInstallFiles() { + return preInstallFiles; + } + + public @NotNull List getExtendedClassPathFiles() { + return extendedClassPathFiles; + } + public List getScanFiles() { return scanFiles; } @@ -116,18 +154,24 @@ public Function> getPrinter() { return printer; } - public Violation.Severity getFailOnSeverity() { + public Severity getFailOnSeverity() { return failOnSeverity; } boolean hasOverrides() { - return noHooks; + return noHooks || !getPreInstallFiles().isEmpty(); } public OakpalPlan applyOverrides(final @NotNull OakpalPlan basePlan) { if (hasOverrides()) { final OakpalPlan.Builder overridePlan = new OakpalPlan.Builder(basePlan.getBase(), basePlan.getName()) .startingWithPlan(basePlan); + if (!getPreInstallFiles().isEmpty()) { + List allUrls = new ArrayList<>(basePlan.getPreInstallUrls()); + getPreInstallFiles().stream().map(compose(File::toURI, result1(URI::toURL))) + .collect(Result.tryCollect(Collectors.toList())).forEach(allUrls::addAll); + overridePlan.withPreInstallUrls(allUrls); + } if (isNoHooks()) { overridePlan.withInstallHookPolicy(InstallHookPolicy.SKIP); overridePlan.withEnablePreInstallHooks(false); @@ -146,11 +190,15 @@ static final class Builder { private boolean noPlan; private boolean noHooks; private String planName; + private File planFile; + private File planFileBaseDir; + private List preInstallFiles = new ArrayList<>(); + private List extendedClassPathFiles = new ArrayList<>(); private File outFile; private File cacheDir; private File opearFile; private List scanFiles = new ArrayList<>(); - private Violation.Severity failOnSeverity; + private Severity failOnSeverity; public Builder setJustHelp(final boolean justHelp) { this.justHelp = justHelp; @@ -187,6 +235,36 @@ public Builder setPlanName(final @Nullable String planName) { return this; } + public Builder setPlanFileBaseDir(final @Nullable File planFileBaseDir) { + this.planFileBaseDir = planFileBaseDir; + return this; + } + + public Builder setPlanFile(final @Nullable File planFile) { + this.planFile = planFile; + return this; + } + + public Builder setPreInstallFiles(final @NotNull List preInstallFiles) { + this.preInstallFiles = preInstallFiles; + return this; + } + + public Builder addPreInstallFile(final @NotNull File preInstallFile) { + this.preInstallFiles.add(preInstallFile); + return this; + } + + public Builder setExtendedClassPathFiles(final @NotNull List extendedClassPathFiles) { + this.extendedClassPathFiles = new ArrayList<>(extendedClassPathFiles); + return this; + } + + public Builder addExtendedClassPathFile(final @NotNull File extendedClassPathFile) { + this.extendedClassPathFiles.add(extendedClassPathFile); + return this; + } + public Builder setOutFile(final @Nullable File outFile) { this.outFile = outFile; return this; @@ -207,24 +285,32 @@ public Builder addScanFile(final @NotNull File scanFile) { return this; } - public Builder setFailOnSeverity(final @Nullable Violation.Severity failOnSeverity) { + public Builder setFailOnSeverity(final @Nullable Severity failOnSeverity) { this.failOnSeverity = failOnSeverity; return this; } - Result build(final @NotNull Console console) { + Result buildOpear(final @NotNull Console console, final @NotNull File opearCache) { + final Result baseOpear; + if (planFile != null) { + baseOpear = buildAdhocOpear(console).map(Function.identity()); + } else { + baseOpear = buildOpearFile(console, opearCache).map(Function.identity()); + } + return baseOpear; + } + + Result buildAdhocOpear(final @NotNull Console console) { + return AdhocOpear.fromPlanFile(planFile, planFileBaseDir); + } + + Result buildOpearFile(final @NotNull Console console, final @NotNull File opearCache) { final File opearResolved = Optional.ofNullable(opearFile).orElseGet(() -> console.getCwd().toPath().resolve( console.getEnv().getOrDefault(Console.ENV_OAKPAL_OPEAR, ".")) .toFile()); - final File realCacheDir = this.cacheDir != null - ? this.cacheDir - : console.getCwd().toPath().resolve(CACHE_DIR_NAME).toFile().getAbsoluteFile(); - final File opearCache = new File(realCacheDir, "opears"); - opearCache.mkdirs(); - - final Result opearResult = Result.success(opearResolved.getAbsoluteFile()) + return Result.success(opearResolved.getAbsoluteFile()) .flatMap(file -> { if (file.isFile()) { try (JarFile jarFile = new JarFile(file, true)) { @@ -236,20 +322,41 @@ Result build(final @NotNull Console console) { return OpearFile.fromDirectory(file); } }); + } - return opearResult.flatMap(opear -> - Optional.ofNullable(planName) - .map(opear::getSpecificPlan) - .orElse(Result.success(noPlan ? OakpalPlan.EMPTY_PLAN_URL : opear.getDefaultPlan())) - .flatMap(planUrl -> - messageWriter(console, outputJson, outFile).map(writer -> - new Options(justHelp, justVersion, storeBlobs, planUrl, - opear.getPlanClassLoader(getClass().getClassLoader()), - realCacheDir, opearFile, - planName, noHooks, scanFiles, writer, - Optional.ofNullable(failOnSeverity).orElse(DEFAULT_OPTIONS.failOnSeverity))))); + Result getExtendedClassLoader(final @NotNull Opear opear, + final @NotNull ClassLoader parentClassLoader) { + if (this.extendedClassPathFiles.isEmpty()) { + return Result.success(opear.getPlanClassLoader(getClass().getClassLoader())); + } else { + final URL[] urls = this.extendedClassPathFiles.stream() + .filter(File::exists) + .filter(file -> file.isDirectory() || file.getName().endsWith(".jar")) + .flatMap(compose(compose(File::toURI, result1(URI::toURL)), Result::stream)) + .toArray(URL[]::new); + return Result.success(new URLClassLoader(urls, opear.getPlanClassLoader(parentClassLoader))); + } } + Result build(final @NotNull Console console) { + final File realCacheDir = this.cacheDir != null + ? this.cacheDir + : console.getCwd().toPath().resolve(CACHE_DIR_NAME).toFile().getAbsoluteFile(); + final File opearCache = new File(realCacheDir, "opears"); + opearCache.mkdirs(); + + Result opearResult = buildOpear(console, opearCache); + + return opearResult.flatMap(opear -> Optional.ofNullable(planName).map(opear::getSpecificPlan) + .orElse(Result.success(noPlan ? OakpalPlan.EMPTY_PLAN_URL : opear.getDefaultPlan())) + .flatMap(planUrl -> getExtendedClassLoader(opear, getClass().getClassLoader()) + .flatMap(classLoader -> messageWriter(console, outputJson, outFile).map(writer -> + new Options(justHelp, justVersion, storeBlobs, planUrl, + classLoader, realCacheDir, opearFile, planName, planFile, + planFileBaseDir, preInstallFiles, extendedClassPathFiles, + noHooks, scanFiles, writer, Optional.ofNullable(failOnSeverity) + .orElse(DEFAULT_OPTIONS.failOnSeverity)))))); + } } /** diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/ReportMessage.java b/cli/src/main/java/net/adamcin/oakpal/cli/ReportMessage.java index fe3f966dd..dc369619d 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/ReportMessage.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/ReportMessage.java @@ -1,20 +1,20 @@ package net.adamcin.oakpal.cli; +import net.adamcin.oakpal.api.Violation; +import net.adamcin.oakpal.core.CheckReport; +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; + +import javax.json.JsonObject; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Set; import java.util.stream.Collectors; -import javax.json.JsonObject; - -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.Violation; -import org.apache.jackrabbit.vault.packaging.PackageId; -import org.jetbrains.annotations.NotNull; class ReportMessage implements StructuredMessage { private final CheckReport report; - ReportMessage(final @NotNull CheckReport report) { + ReportMessage(@NotNull final CheckReport report) { this.report = report; } diff --git a/cli/src/main/java/net/adamcin/oakpal/cli/StructuredMessage.java b/cli/src/main/java/net/adamcin/oakpal/cli/StructuredMessage.java index 71c6420f0..704e9bd81 100644 --- a/cli/src/main/java/net/adamcin/oakpal/cli/StructuredMessage.java +++ b/cli/src/main/java/net/adamcin/oakpal/cli/StructuredMessage.java @@ -1,11 +1,11 @@ package net.adamcin.oakpal.cli; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.JsonObjectConvertible; /** - * Extends {@link net.adamcin.oakpal.core.JavaxJson.ObjectConvertible} to ensure conformance with --output json. When + * Extends {@link JsonObjectConvertible} to ensure conformance with --output json. When * rendered with --output text, - * {@link #toString()} will be called. + * {@code toString()} will be called. */ -public interface StructuredMessage extends JavaxJson.ObjectConvertible { +public interface StructuredMessage extends JsonObjectConvertible { } diff --git a/cli/src/main/resources/net/adamcin/oakpal/cli/help.txt b/cli/src/main/resources/net/adamcin/oakpal/cli/help.txt index 4713a6e8c..b8d0a4059 100644 --- a/cli/src/main/resources/net/adamcin/oakpal/cli/help.txt +++ b/cli/src/main/resources/net/adamcin/oakpal/cli/help.txt @@ -19,8 +19,17 @@ oakpal [ ] ... or if no opear is specified, the basic oakpal plan will be used. +p | --no-plan : Use no plan for the scan. Overrides the default behavior, which otherwise uses the oakpal core "basic-plan.json". + -pi | --pre-install-file : Add a preinstall package to the list specified in the plan, if any. + -pf | --plan-file : Override the opear plan with the specified json file. The base directory for the + plan will default to the parent directory of the file, unless a different + directory is specified using --plan-file-base. + --plan-file-base : Specify a different base classpath directory for --plan-file. This is + important for resolution of script checks referenced by relative path in the plan + check impl properties, but does not affect resolution of resources linked by URL, + like preInstallUrls. --no-hooks : Disable preinstall and scan install hooks for all packages, otherwise, rely on install hook policies configured in the selected plan. + -xp | --extend-classpath : Extend the opear classpath with the specified jar file or directory. -s | --severity-fail : Exit with a non-zero code if any violations are reported with a severity level equal to or higher than . Can be MINOR, MAJOR, or SEVERE. diff --git a/cli/src/test/filtered-resources/test-packages.properties b/cli/src/test/filtered-resources/test-packages.properties index 1c6d7919e..eae2b1bad 100644 --- a/cli/src/test/filtered-resources/test-packages.properties +++ b/cli/src/test/filtered-resources/test-packages.properties @@ -15,4 +15,4 @@ # test-packages.root=${project.build.directory} -test-packages.src=/jackrabbit-filevault-${vault-api.version}/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/ \ No newline at end of file +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/cli/src/test/java/net/adamcin/oakpal/cli/CommandTest.java b/cli/src/test/java/net/adamcin/oakpal/cli/CommandTest.java index b089e9e59..be4e9787b 100644 --- a/cli/src/test/java/net/adamcin/oakpal/cli/CommandTest.java +++ b/cli/src/test/java/net/adamcin/oakpal/cli/CommandTest.java @@ -1,18 +1,25 @@ package net.adamcin.oakpal.cli; -import static net.adamcin.oakpal.core.Fun.uncheck0; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.mock; +import net.adamcin.oakpal.api.Nothing; +import net.adamcin.oakpal.api.ReportCollector; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.core.CheckReport; +import net.adamcin.oakpal.core.FileBlobMemoryNodeStore; +import net.adamcin.oakpal.core.ReportMapper; +import net.adamcin.oakpal.core.SimpleReport; +import net.adamcin.oakpal.testing.TestPackageUtil; +import org.apache.commons.io.FileUtils; +import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.json.JsonObject; import java.io.BufferedReader; import java.io.File; import java.io.PrintWriter; @@ -27,26 +34,19 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; -import javax.json.JsonObject; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.FileBlobMemoryNodeStore; -import net.adamcin.oakpal.core.Nothing; -import net.adamcin.oakpal.core.ReportCollector; -import net.adamcin.oakpal.core.ReportMapper; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.SimpleReport; -import net.adamcin.oakpal.core.SimpleViolation; -import net.adamcin.oakpal.core.Violation; -import net.adamcin.oakpal.testing.TestPackageUtil; -import org.apache.commons.io.FileUtils; -import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; -import org.apache.jackrabbit.vault.packaging.PackageId; -import org.jetbrains.annotations.NotNull; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static net.adamcin.oakpal.api.Fun.uncheck0; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; public class CommandTest { private static final Logger LOGGER = LoggerFactory.getLogger(CommandTest.class); @@ -94,10 +94,10 @@ public void testWriteReports() { final List reports = new ArrayList<>(); reports.add(new SimpleReport("some check", Collections.emptyList())); reports.add(new SimpleReport("check with violations", Arrays.asList( - new SimpleViolation(Violation.Severity.MINOR, "minor violation"), - new SimpleViolation(Violation.Severity.SEVERE, "severe violation with one packageId", + new SimpleViolation(Severity.MINOR, "minor violation"), + new SimpleViolation(Severity.SEVERE, "severe violation with one packageId", PackageId.fromString("my_packages/acme/1.0")), - new SimpleViolation(Violation.Severity.MAJOR, "major violation with several packageIds", + new SimpleViolation(Severity.MAJOR, "major violation with several packageIds", PackageId.fromString("my_packages/alpha/1.0"), PackageId.fromString("my_packages/beta/1.0"), PackageId.fromString("my_packages/gamma/1.0")) @@ -132,27 +132,27 @@ private Console getMockConsole() { @Test public void testGetHighestReportSeverity() { ReportCollector collector1 = new ReportCollector(); - collector1.reportViolation(new SimpleViolation(Violation.Severity.MINOR, "")); - collector1.reportViolation(new SimpleViolation(Violation.Severity.MAJOR, "")); + collector1.reportViolation(new SimpleViolation(Severity.MINOR, "")); + collector1.reportViolation(new SimpleViolation(Severity.MAJOR, "")); CheckReport hasMajor = new SimpleReport("hasMajor", collector1.getReportedViolations()); ReportCollector collector2 = new ReportCollector(); - collector2.reportViolation(new SimpleViolation(Violation.Severity.MINOR, "")); - collector2.reportViolation(new SimpleViolation(Violation.Severity.SEVERE, "")); + collector2.reportViolation(new SimpleViolation(Severity.MINOR, "")); + collector2.reportViolation(new SimpleViolation(Severity.SEVERE, "")); CheckReport hasSevere = new SimpleReport("hasSevere", collector2.getReportedViolations()); ReportCollector collector3 = new ReportCollector(); - collector3.reportViolation(new SimpleViolation(Violation.Severity.MINOR, "")); + collector3.reportViolation(new SimpleViolation(Severity.MINOR, "")); CheckReport hasMinor = new SimpleReport("hasMinor", collector3.getReportedViolations()); final Console console = getMockConsole(); Options optsFailDefault = new Options.Builder() .build(console).getOrDefault(null); Options optsFailMinor = new Options.Builder() - .setFailOnSeverity(Violation.Severity.MINOR) + .setFailOnSeverity(Severity.MINOR) .build(console).getOrDefault(null); Options optsFailMajor = new Options.Builder() - .setFailOnSeverity(Violation.Severity.MAJOR) + .setFailOnSeverity(Severity.MAJOR) .build(console).getOrDefault(null); Options optsFailSevere = new Options.Builder() - .setFailOnSeverity(Violation.Severity.SEVERE) + .setFailOnSeverity(Severity.SEVERE) .build(console).getOrDefault(null); final Command command = new Command(); assertFalse("no exit minor default fail", @@ -267,22 +267,22 @@ public void testParseArgs_simpleOnes() { validator.expectFailure(args("-s", "extreme")); validator.expectSuccess(args(), options -> assertEquals("expect major by default", - Violation.Severity.MAJOR, options.getFailOnSeverity())); + Severity.MAJOR, options.getFailOnSeverity())); validator.expectSuccess(args("-s", "major"), options -> assertEquals("expect MAJOR", - Violation.Severity.MAJOR, options.getFailOnSeverity())); + Severity.MAJOR, options.getFailOnSeverity())); validator.expectSuccess(args("-s", "minor"), options -> assertEquals("expect MINOR", - Violation.Severity.MINOR, options.getFailOnSeverity())); + Severity.MINOR, options.getFailOnSeverity())); validator.expectSuccess(args("-s", "minor", "+s"), options -> assertEquals("expect MAJOR after resetting", - Violation.Severity.MAJOR, options.getFailOnSeverity())); + Severity.MAJOR, options.getFailOnSeverity())); validator.expectSuccess(args("-s", "severe"), options -> assertEquals("expect severe", - Violation.Severity.SEVERE, options.getFailOnSeverity())); + Severity.SEVERE, options.getFailOnSeverity())); validator.expectSuccess(args("-s", "severe", "+s"), options -> assertEquals("expect MAJOR after resetting", - Violation.Severity.MAJOR, options.getFailOnSeverity())); + Severity.MAJOR, options.getFailOnSeverity())); validator.expectSuccess(args("--no-plan"), options -> assertNull("expect no plan", options.getPlanName())); @@ -318,6 +318,68 @@ public void testParseArgs_realOpear() throws Exception { validator.expectFailure(args("-f", notAJar.getAbsolutePath())); } + + @Test + public void testParseArgs_adhocOpear() throws Exception { + final File testOutDir = new File(testOutputBaseDir, "testParseArgs_adhocOpear"); + FileUtils.deleteDirectory(testOutDir); + + final File testModuleSrc = new File("src/test/resources/checklists/test_module"); + final File testModuleJar = new File(testOutDir, "test_module.jar"); + TestPackageUtil.buildJarFromDir(testModuleSrc, testModuleJar, Collections.emptyMap()); + + final File contentPackageSrc = new File("src/test/resources/simple-content"); + final File contentPackageJar = new File(testOutDir, "simple-content.zip"); + TestPackageUtil.buildJarFromDir(contentPackageSrc, contentPackageJar, Collections.emptyMap()); + + final File planFromFile = new File("src/test/resources/opears/adhocPlan/plan.json"); + + final Console console = getMockConsole(); + final OptionsValidator validator = new OptionsValidator(console); + + validator.expectSuccess( + args( + "--plan-file", planFromFile.getAbsolutePath(), + "--plan-file-base", planFromFile.getParentFile().getAbsolutePath(), + "--pre-install-file", contentPackageJar.getAbsolutePath(), + "--extend-classpath", testModuleJar.getAbsolutePath()), + options -> { + assertEquals("expect plan file", + planFromFile.getAbsolutePath(), options.getPlanFile().getAbsolutePath()); + assertEquals("expect plan file base dir", + planFromFile.getParentFile().getAbsolutePath(), options.getPlanFileBaseDir().getAbsolutePath()); + assertTrue("expect pre-install file", options.getPreInstallFiles().stream() + .anyMatch(file -> file.getAbsolutePath().equals(contentPackageJar.getAbsolutePath()))); + assertTrue("expect classpath", options.getExtendedClassPathFiles().stream() + .anyMatch(file -> file.getAbsolutePath().equals(testModuleJar.getAbsolutePath()))); + assertNotNull("expect test_module-handler.js", options.getScanClassLoader() + .getResource("test_module-handler.js")); + }); + + validator.expectSuccess( + args( + "--plan-file", planFromFile.getAbsolutePath(), + "--no-plan-file", + "--plan-file-base", planFromFile.getParentFile().getAbsolutePath(), + "--no-plan-file-base", + "--pre-install-file", contentPackageJar.getAbsolutePath(), + "--no-pre-install-file", + "--extend-classpath", testModuleJar.getAbsolutePath(), + "--no-extend-classpath"), + + options -> { + assertNull("expect null plan file", options.getPlanFile()); + assertNull("expect null plan file base dir", options.getPlanFileBaseDir()); + assertFalse("expect no pre-install file", options.getPreInstallFiles().stream() + .anyMatch(file -> file.getAbsolutePath().equals(contentPackageJar.getAbsolutePath()))); + assertFalse("expect no classpath", options.getExtendedClassPathFiles().stream() + .anyMatch(file -> file.getAbsolutePath().equals(testModuleJar.getAbsolutePath()))); + assertNull("expect no test_module-handler.js", options.getScanClassLoader() + .getResource("test_module-handler.js")); + }); + } + + @Test public void testParseArgs_outputs() throws Exception { final Console console = getMockConsole(); @@ -404,7 +466,6 @@ public IO apply(Object o) { expectNotJson.apply(fileStack)); } - String captureOutput(final @NotNull BiFunction>, IO> commandStrategy) { final Command command = new Command(); final StringWriter sw = new StringWriter(); diff --git a/cli/src/test/java/net/adamcin/oakpal/cli/OptionsTest.java b/cli/src/test/java/net/adamcin/oakpal/cli/OptionsTest.java index 8e955b566..4fb21f288 100644 --- a/cli/src/test/java/net/adamcin/oakpal/cli/OptionsTest.java +++ b/cli/src/test/java/net/adamcin/oakpal/cli/OptionsTest.java @@ -1,36 +1,41 @@ package net.adamcin.oakpal.cli; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.core.InstallHookPolicy; +import net.adamcin.oakpal.core.OakpalPlan; +import net.adamcin.oakpal.testing.TestPackageUtil; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; +import java.util.Collections; -import net.adamcin.oakpal.core.InstallHookPolicy; -import net.adamcin.oakpal.core.JavaxJson; -import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.Violation; -import org.apache.commons.io.FileUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class OptionsTest { @@ -65,7 +70,7 @@ public void testSettersAndSimpleBuild() { builder.setJustHelp(false); builder.setJustVersion(false); builder.setStoreBlobs(false); - builder.setFailOnSeverity(Violation.Severity.MAJOR); + builder.setFailOnSeverity(Severity.MAJOR); builder.setPlanName(null); builder.setOutputJson(false); builder.setOutFile(null); @@ -112,11 +117,83 @@ public void testOpearFile() { options.getOpearFile().isDirectory())); } + @Test + public void testAdhocOpear() { + final Console console = getMockConsole(); + when(console.getCwd()).thenReturn(tempDir); + final Result defaultOptionsResult = new Options.Builder().build(console); + assertFalse("options build is successful", defaultOptionsResult.getError().isPresent()); + defaultOptionsResult.forEach(options -> { + assertNull("planFromFile is null by default", options.getPlanFile()); + assertNull("planFromFileBaseDir is null by default", options.getPlanFileBaseDir()); + }); + + final Result optionsResult = new Options.Builder() + .setPlanFile(new File("src/test/resources/opears/adhocPlan/plan.json")).build(console); + optionsResult.forEach(options -> { + assertTrue("planFromFile should be a file: " + options.getPlanFile().getAbsolutePath(), + !options.getPlanFile().isDirectory()); + assertNull("planFromFileBaseDir should be null", options.getPlanFileBaseDir()); + }); + + final Result optionsWithBaseResult = new Options.Builder() + .setPlanFile(new File("src/test/resources/opears/adhocPlan/plan.json")) + .setPlanFileBaseDir(console.getCwd()) + .build(console); + optionsWithBaseResult.forEach(options -> { + assertTrue("planFromFile should be a file: " + options.getPlanFile().getAbsolutePath(), + !options.getPlanFile().isDirectory()); + assertTrue("planFromFileBaseDir should be a directory: " + options.getPlanFileBaseDir() + .getAbsolutePath(), options.getPlanFileBaseDir().isDirectory()); + }); + } + + @Test + public void testWithPreInstallFiles() throws Exception { + final File testOutDir = new File(tempDir, "testWithPreInstallFiles"); + FileUtils.deleteDirectory(testOutDir); + final Console console = getMockConsole(); + when(console.getCwd()).thenReturn(tempDir); + final Result defaultOptionsResult = new Options.Builder().build(console); + assertFalse("options build is successful", defaultOptionsResult.getError().isPresent()); + defaultOptionsResult.forEach(options -> { + assertTrue("preInstallFiles is empty by default", options.getPreInstallFiles().isEmpty()); + assertFalse("false hasOverrides", options.hasOverrides()); + final OakpalPlan originalPlan = new OakpalPlan.Builder(null, null) + .withPreInstallUrls(Collections.emptyList()) + .build(); + final OakpalPlan overriddenPlan = options.applyOverrides(originalPlan); + assertSame("same plan with no overrides", originalPlan, overriddenPlan); + assertTrue("pre install urls is empty", overriddenPlan.getPreInstallUrls().isEmpty()); + }); + + final File contentPackageSrc = new File("src/test/resources/simple-content"); + final File contentPackageJar = new File(testOutDir, "simple-content.zip"); + TestPackageUtil.buildJarFromDir(contentPackageSrc, contentPackageJar, Collections.emptyMap()); + + final Result optionsResult = new Options.Builder() + .addPreInstallFile(contentPackageJar).build(console); + optionsResult.forEach(options -> { + assertFalse("preInstallFiles is not empty", options.getPreInstallFiles().isEmpty()); + assertTrue("true hasOverrides", options.hasOverrides()); + final OakpalPlan originalPlan = new OakpalPlan.Builder(null, null) + .withPreInstallUrls(Collections.emptyList()) + .build(); + final OakpalPlan overriddenPlan = options.applyOverrides(originalPlan); + assertNotSame("not same plan", originalPlan, overriddenPlan); + assertFalse("pre install urls is not empty", overriddenPlan.getPreInstallUrls().isEmpty()); + assertEquals("expect pre install file", contentPackageJar.getAbsolutePath(), + Fun.result1(URL::toURI).apply(overriddenPlan.getPreInstallUrls().get(0)) + .map(Fun.compose(File::new, File::getAbsolutePath)).getOrDefault("")); + }); + } + @Test public void testNoHooks() { final Console console = getMockConsole(); when(console.getCwd()).thenReturn(tempDir); - Options.Builder builder = new Options.Builder().setOpearFile(new File("src/test/resources/opears/hooksPlan")); + final Options.Builder builder = new Options.Builder() + .setOpearFile(new File("src/test/resources/opears/hooksPlan")); final Result defaultOptionsResult = builder.build(console); assertFalse("options build is successful", defaultOptionsResult.getError().isPresent()); defaultOptionsResult.forEach(options -> { diff --git a/cli/src/test/java/net/adamcin/oakpal/cli/ReportMessageTest.java b/cli/src/test/java/net/adamcin/oakpal/cli/ReportMessageTest.java index 85edac8d9..2a7fdd49b 100644 --- a/cli/src/test/java/net/adamcin/oakpal/cli/ReportMessageTest.java +++ b/cli/src/test/java/net/adamcin/oakpal/cli/ReportMessageTest.java @@ -16,14 +16,13 @@ package net.adamcin.oakpal.cli; -import net.adamcin.oakpal.core.CheckReport; import net.adamcin.oakpal.core.SimpleReport; import org.junit.Test; import javax.json.JsonObject; import java.util.Collections; -import static net.adamcin.oakpal.core.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.key; import static org.junit.Assert.*; public class ReportMessageTest { diff --git a/cli/src/test/resources/checklists/.gitkeep b/cli/src/test/resources/checklists/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/checklists/test_module/META-INF/MANIFEST.MF b/cli/src/test/resources/checklists/test_module/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e508d1a35 --- /dev/null +++ b/cli/src/test/resources/checklists/test_module/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Oakpal-ModuleName: test +Oakpal-Checklist: test-checklist.json diff --git a/cli/src/test/resources/checklists/test_module/test-checklist.json b/cli/src/test/resources/checklists/test_module/test-checklist.json new file mode 100644 index 000000000..4b719618f --- /dev/null +++ b/cli/src/test/resources/checklists/test_module/test-checklist.json @@ -0,0 +1,8 @@ +{ + "checks": [ + { + "name": "test-check", + "impl": "test_module-handler.js" + } + ] +} \ No newline at end of file diff --git a/cli/src/test/resources/checklists/test_module/test_module-handler.js b/cli/src/test/resources/checklists/test_module/test_module-handler.js new file mode 100644 index 000000000..13d23a42f --- /dev/null +++ b/cli/src/test/resources/checklists/test_module/test_module-handler.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/core/src/main/java/net/adamcin/oakpal/core/ViolationReporter.java b/cli/src/test/resources/opears/adhocPlan/echo.js similarity index 63% rename from core/src/main/java/net/adamcin/oakpal/core/ViolationReporter.java rename to cli/src/test/resources/opears/adhocPlan/echo.js index ac39afe3c..f2335d050 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ViolationReporter.java +++ b/cli/src/test/resources/opears/adhocPlan/echo.js @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2019 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,10 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; - -import java.util.Collection; - -public interface ViolationReporter { - - /** - * Called at the end of execution to collect any detected violations. - * - * @return any reported violations. - */ - Collection getReportedViolations(); - - +function startedScan() { + print("startedScan"); } + +function importedPath(path) { + print("importedPath("+path+")"); +} \ No newline at end of file diff --git a/cli/src/test/resources/opears/adhocPlan/plan.json b/cli/src/test/resources/opears/adhocPlan/plan.json new file mode 100644 index 000000000..bc1e5e2b0 --- /dev/null +++ b/cli/src/test/resources/opears/adhocPlan/plan.json @@ -0,0 +1,8 @@ +{ + "checks": [ + { + "name": "simpleEcho", + "impl": "echo.js" + } + ] +} \ No newline at end of file diff --git a/cli/src/test/resources/simple-content/.gitkeep b/cli/src/test/resources/simple-content/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/simple-content/META-INF/vault/filter.xml b/cli/src/test/resources/simple-content/META-INF/vault/filter.xml new file mode 100644 index 000000000..2c3a6af00 --- /dev/null +++ b/cli/src/test/resources/simple-content/META-INF/vault/filter.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cli/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd b/cli/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd new file mode 100644 index 000000000..001ecf462 --- /dev/null +++ b/cli/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd @@ -0,0 +1,7 @@ +<'sling'='http://sling.apache.org/jcr/sling/1.0'> + +[sling:Folder] > nt:folder + - * (undefined) + - * (undefined) multiple + + * (nt:base) = sling:Folder version + diff --git a/cli/src/test/resources/simple-content/META-INF/vault/properties.xml b/cli/src/test/resources/simple-content/META-INF/vault/properties.xml new file mode 100644 index 000000000..2cc942719 --- /dev/null +++ b/cli/src/test/resources/simple-content/META-INF/vault/properties.xml @@ -0,0 +1,18 @@ + + + + FileVault Package Properties + admin + simple-content + 2012-04-24T10:17:21.641+05:30 + admin + 2012-04-24T10:17:21.969+05:30 + 1 + 1.0 + + 2 + my_packages + 2012-04-24T10:17:21.641+05:30 + + admin + \ No newline at end of file diff --git a/cli/src/test/resources/simple-content/jcr_root/content/example/.content.xml b/cli/src/test/resources/simple-content/jcr_root/content/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/cli/src/test/resources/simple-content/jcr_root/content/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/cli/src/test/resources/simple-content/jcr_root/etc/example/.content.xml b/cli/src/test/resources/simple-content/jcr_root/etc/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/cli/src/test/resources/simple-content/jcr_root/etc/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/README.md b/core/README.md index 3b1ab28de..da969e4c9 100644 --- a/core/README.md +++ b/core/README.md @@ -1,7 +1,3 @@ # oakpal-core -This is the core API for oakpal. - -Some cool things to see while you are here: - -TBD +This is the core runtime library for oakpal. diff --git a/core/pom.xml b/core/pom.xml index def30128b..9380a53ad 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT .. @@ -40,10 +40,6 @@ HEAD - - 1.0.0 - - @@ -57,7 +53,6 @@ - org.apache.maven.plugins maven-dependency-plugin @@ -88,7 +83,9 @@ Bundle-SymbolicName: net.adamcin.oakpal.core Automatic-Module-Name: net.adamcin.oakpal.core Oakpal-Checklist: OAKPAL-INF/checklists/basic.json - Export-Package: net.adamcin.oakpal.core,net.adamcin.oakpal.core.checks + Export-Package: net.adamcin.oakpal.core,\ + net.adamcin.oakpal.core.checks,\ + net.adamcin.oakpal.core.opear Private-Package: net.adamcin.oakpal.core.jcrfacade.* Import-Package: !aQute.*,* ]]> @@ -174,7 +171,18 @@ - + + org.jetbrains + annotations + + + org.osgi + org.osgi.annotation.versioning + + + net.adamcin.oakpal + oakpal-api + org.apache.jackrabbit.vault org.apache.jackrabbit.vault @@ -192,7 +200,6 @@ org.apache.jackrabbit jackrabbit-spi-commons - ${jackrabbit.version} org.apache.jackrabbit diff --git a/core/src/main/java/net/adamcin/oakpal/core/AbortedScanException.java b/core/src/main/java/net/adamcin/oakpal/core/AbortedScanException.java index 5e2a57d59..1f4a487f4 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/AbortedScanException.java +++ b/core/src/main/java/net/adamcin/oakpal/core/AbortedScanException.java @@ -22,7 +22,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheck1; /** * Represents an error that causes a package scan to abort without notifying the {@link ErrorListener}. diff --git a/core/src/main/java/net/adamcin/oakpal/core/CheckReport.java b/core/src/main/java/net/adamcin/oakpal/core/CheckReport.java index 562b1d930..d1fff2b76 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/CheckReport.java +++ b/core/src/main/java/net/adamcin/oakpal/core/CheckReport.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,26 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.JsonObjectConvertible; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.Violation; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.json.JsonObject; import javax.json.stream.JsonCollectors; import java.util.Collection; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.ReportMapper.KEY_CHECK_NAME; -import static net.adamcin.oakpal.core.ReportMapper.KEY_VIOLATIONS; -import static net.adamcin.oakpal.core.Util.isEmpty; +import static net.adamcin.oakpal.api.JavaxJson.obj; /** * Type for collected {@link Violation}s from a particlular {@link ProgressCheck}. */ -public interface CheckReport extends JavaxJson.ObjectConvertible { - +@ProviderType +public interface CheckReport extends JsonObjectConvertible { /** * The serialized display name of the package check that created the report. * @@ -53,7 +56,7 @@ public interface CheckReport extends JavaxJson.ObjectConvertible { * @param atLeastAsSevere lower bound for severity * @return the reported violations filtered by severity */ - default Collection getViolations(Violation.Severity atLeastAsSevere) { + default Collection getViolations(Severity atLeastAsSevere) { if (atLeastAsSevere == null) { return getViolations(); } @@ -71,14 +74,10 @@ default Collection getViolations(Violation.Severity atLeastAsSevere) default JsonObject toJson() { JavaxJson.Obj jsonReport = obj(); - if (!isEmpty(this.getCheckName())) { - jsonReport.key(KEY_CHECK_NAME, this.getCheckName()); - } - if (!this.getViolations().isEmpty()) { - jsonReport.key(KEY_VIOLATIONS, this.getViolations().stream() - .map(Violation::toJson) - .collect(JsonCollectors.toJsonArray())); - } + jsonReport.key(CoreConstants.checkReportKeys().checkName()).opt(this.getCheckName()); + jsonReport.key(CoreConstants.checkReportKeys().violations()).opt(this.getViolations().stream() + .map(Violation::toJson) + .collect(JsonCollectors.toJsonArray())); return jsonReport.get(); } diff --git a/core/src/main/java/net/adamcin/oakpal/core/CheckSpec.java b/core/src/main/java/net/adamcin/oakpal/core/CheckSpec.java index fde6e0798..ee6623f34 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/CheckSpec.java +++ b/core/src/main/java/net/adamcin/oakpal/core/CheckSpec.java @@ -16,8 +16,13 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.JsonObjectConvertible; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import javax.json.Json; import javax.json.JsonObject; @@ -25,22 +30,77 @@ import java.util.Objects; import static java.util.Optional.ofNullable; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static net.adamcin.oakpal.core.Util.isEmpty; /** * DTO for full-featured check spec. */ @SuppressWarnings("WeakerAccess") -public class CheckSpec implements JavaxJson.ObjectConvertible { - static final String KEY_IMPL = "impl"; - static final String KEY_INLINE_SCRIPT = "inlineScript"; - static final String KEY_INLINE_ENGINE = "inlineEngine"; - static final String KEY_NAME = "name"; - static final String KEY_TEMPLATE = "template"; - static final String KEY_SKIP = "skip"; - static final String KEY_CONFIG = "config"; +@ProviderType +public class CheckSpec implements JsonObjectConvertible { + /** + * Json keys for CheckSpec. Use {@link #keys()} to access singleton. + */ + @ProviderType + public interface JsonKeys { + String impl(); + + String inlineScript(); + + String inlineEngine(); + + String name(); + + String template(); + + String skip(); + + String config(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String impl() { + return "impl"; + } + + @Override + public String inlineScript() { + return "inlineScript"; + } + + @Override + public String inlineEngine() { + return "inlineEngine"; + } + + @Override + public String name() { + return "name"; + } + + @Override + public String template() { + return "template"; + } + + @Override + public String skip() { + return "skip"; + } + + @Override + public String config() { + return "config"; + } + }; + + @NotNull + public static CheckSpec.JsonKeys keys() { + return KEYS; + } private String impl; private String inlineScript; @@ -50,6 +110,7 @@ public class CheckSpec implements JavaxJson.ObjectConvertible { private boolean skip; private JsonObject config; + /** * The direct classpath lookup name for a particular check. If not provided, indicates that a check should be * looked up by name from a catalog on the classpath. @@ -293,7 +354,7 @@ final CheckSpec baseCompositeOver(final @NotNull CheckSpec that) { composite.setInlineEngine(null); composite.setImpl(this.getImpl()); } - composite.setConfig(merge(that.getConfig(), this.getConfig())); + composite.setConfig(JavaxJson.shallowMergeObjects(that.getConfig(), this.getConfig())); return composite; } @@ -350,21 +411,6 @@ public final CheckSpec inherit(final @NotNull CheckSpec that) { return composite; } - /** - * Merge an overlay json object's entries into a base json object, replacing values - * for duplicate keys. - * - * @param base the base json object - * @param overlay the overlay json object - * @return a merged json object - */ - static JsonObject merge(final JsonObject base, final JsonObject overlay) { - JsonObjectBuilder init = Json.createObjectBuilder(); - ofNullable(base).ifPresent(json -> json.forEach(init::add)); - ofNullable(overlay).ifPresent(json -> json.forEach(init::add)); - return init.build(); - } - /** * Build a {@link CheckSpec} from a {@link Checklist} json snippet. * @@ -372,27 +418,28 @@ static JsonObject merge(final JsonObject base, final JsonObject overlay) { * @return a new CheckSpec */ public static CheckSpec fromJson(final @NotNull JsonObject json) { + final JsonKeys keys = keys(); final CheckSpec checkSpec = new CheckSpec(); - if (hasNonNull(json, KEY_IMPL)) { - checkSpec.setImpl(json.getString(KEY_IMPL)); + if (hasNonNull(json, keys.impl())) { + checkSpec.setImpl(json.getString(keys.impl())); } - if (hasNonNull(json, KEY_INLINE_SCRIPT)) { - checkSpec.setInlineScript(json.getString(KEY_INLINE_SCRIPT)); + if (hasNonNull(json, keys.inlineScript())) { + checkSpec.setInlineScript(json.getString(keys.inlineScript())); } - if (hasNonNull(json, KEY_INLINE_ENGINE)) { - checkSpec.setInlineEngine(json.getString(KEY_INLINE_ENGINE)); + if (hasNonNull(json, keys.inlineEngine())) { + checkSpec.setInlineEngine(json.getString(keys.inlineEngine())); } - if (hasNonNull(json, KEY_NAME)) { - checkSpec.setName(json.getString(KEY_NAME)); + if (hasNonNull(json, keys.name())) { + checkSpec.setName(json.getString(keys.name())); } - if (hasNonNull(json, KEY_TEMPLATE)) { - checkSpec.setTemplate(json.getString(KEY_TEMPLATE)); + if (hasNonNull(json, keys.template())) { + checkSpec.setTemplate(json.getString(keys.template())); } - if (hasNonNull(json, KEY_SKIP)) { - checkSpec.setSkip(json.getBoolean(KEY_SKIP)); + if (hasNonNull(json, keys.skip())) { + checkSpec.setSkip(json.getBoolean(keys.skip())); } - if (hasNonNull(json, KEY_CONFIG)) { - checkSpec.setConfig(json.getJsonObject(KEY_CONFIG)); + if (hasNonNull(json, keys.config())) { + checkSpec.setConfig(json.getJsonObject(keys.config())); } return checkSpec; @@ -410,16 +457,17 @@ protected void editJson(final JsonObjectBuilder builder) { @Override public final JsonObject toJson() { + final JsonKeys keys = keys(); final JsonObjectBuilder builder = Json.createObjectBuilder(); final JavaxJson.Obj obj = obj() - .key(KEY_NAME).opt(getName()) - .key(KEY_IMPL).opt(getImpl()) - .key(KEY_INLINE_SCRIPT).opt(getInlineScript()) - .key(KEY_INLINE_ENGINE).opt(getInlineEngine()) - .key(KEY_CONFIG).opt(getConfig()) - .key(KEY_TEMPLATE).opt(getTemplate()); + .key(keys.name()).opt(getName()) + .key(keys.impl()).opt(getImpl()) + .key(keys.inlineScript()).opt(getInlineScript()) + .key(keys.inlineEngine()).opt(getInlineEngine()) + .key(keys.config()).opt(getConfig()) + .key(keys.template()).opt(getTemplate()); if (isSkip()) { - obj.key(KEY_SKIP, true); + obj.key(keys.skip(), true); } final JsonObject base = obj.get(); base.forEach(builder::add); @@ -474,6 +522,7 @@ public static ImmutableSpec immutableCopyOf(final @NotNull CheckSpec original) { original.isSkip()); } + /** * This is an Immutable variant of {@link CheckSpec} for composition in {@link Checklist}. */ diff --git a/core/src/main/java/net/adamcin/oakpal/core/Checklist.java b/core/src/main/java/net/adamcin/oakpal/core/Checklist.java index 0a9dbfda2..3bfc4c7f9 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Checklist.java +++ b/core/src/main/java/net/adamcin/oakpal/core/Checklist.java @@ -16,6 +16,8 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.JsonObjectConvertible; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; @@ -23,6 +25,7 @@ import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,47 +42,115 @@ import java.util.stream.Collectors; import static java.util.Optional.ofNullable; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.JavaxJson.optArray; -import static net.adamcin.oakpal.core.JavaxJson.optObject; -import static net.adamcin.oakpal.core.JavaxJson.parseFromArray; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.optArray; +import static net.adamcin.oakpal.api.JavaxJson.optObject; +import static net.adamcin.oakpal.api.JavaxJson.parseFromArray; /** * It's a list of checks, along with initStage properties allowing jars to share CNDs, JCR namespaces and privileges, * and forced roots. */ -public final class Checklist implements JavaxJson.ObjectConvertible { +public final class Checklist implements JsonObjectConvertible { - private static final Logger LOGGER = LoggerFactory.getLogger(Checklist.class); + /** + * Json keys for Checklist. Use {@link #keys()} to access singleton. + */ + @ProviderType + public interface JsonKeys { + String name(); + + String cndUrls(); + + String cndNames(); + + String jcrNodetypes(); + + String jcrNamespaces(); + + String jcrPrivileges(); + + String forcedRoots(); + + String checks(); + + List orderedKeys(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + private final List allKeys = Arrays.asList( + name(), + checks(), + forcedRoots(), + cndNames(), + cndUrls(), + jcrNodetypes(), + jcrPrivileges(), + jcrNamespaces()); + + @Override + public String name() { + return "name"; + } + + @Override + public String cndUrls() { + return "cndUrls"; + } + + @Override + public String cndNames() { + return "cndNames"; + } - static final String KEY_NAME = "name"; - static final String KEY_CND_URLS = "cndUrls"; - static final String KEY_CND_NAMES = "cndNames"; - public static final String KEY_JCR_NODETYPES = "jcrNodetypes"; - public static final String KEY_JCR_NAMESPACES = "jcrNamespaces"; - public static final String KEY_JCR_PRIVILEGES = "jcrPrivileges"; - public static final String KEY_FORCED_ROOTS = "forcedRoots"; - static final String KEY_CHECKS = "checks"; - - static final List KEY_ORDER = Arrays.asList( - Checklist.KEY_NAME, - Checklist.KEY_CHECKS, - Checklist.KEY_FORCED_ROOTS, - Checklist.KEY_CND_NAMES, - Checklist.KEY_CND_URLS, - Checklist.KEY_JCR_NODETYPES, - Checklist.KEY_JCR_PRIVILEGES, - Checklist.KEY_JCR_NAMESPACES); + @Override + public String jcrNodetypes() { + return "jcrNodetypes"; + } + + @Override + public String jcrNamespaces() { + return "jcrNamespaces"; + } + + @Override + public String jcrPrivileges() { + return "jcrPrivileges"; + } + + @Override + public String forcedRoots() { + return "forcedRoots"; + } + + @Override + public String checks() { + return "checks"; + } + + @Override + public List orderedKeys() { + return allKeys; + } + }; + + @NotNull + public static Checklist.JsonKeys keys() { + return KEYS; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(Checklist.class); static final Comparator checklistKeyComparator = (s1, s2) -> { - if (KEY_ORDER.contains(s1) && KEY_ORDER.contains(s2)) { - return Integer.compare(KEY_ORDER.indexOf(s1), KEY_ORDER.indexOf(s2)); - } else if (KEY_ORDER.contains(s1)) { + final List keyOrder = keys().orderedKeys(); + if (keyOrder.contains(s1) && keyOrder.contains(s2)) { + return Integer.compare(keyOrder.indexOf(s1), keyOrder.indexOf(s2)); + } else if (keyOrder.contains(s1)) { return -1; - } else if (KEY_ORDER.contains(s2)) { + } else if (keyOrder.contains(s2)) { return 1; } else { return s1.compareTo(s2); @@ -120,6 +191,7 @@ public static Comparator comparingJsonKeys(final @NotNull Function jcrNodetypes = new ArrayList<>(); @@ -287,15 +359,15 @@ public JsonObject toJson() { return this.originalJson; } else { final NamespaceMapping mapping = JsonCnd.toNamespaceMapping(getJcrNamespaces()); + final JsonKeys jsonKeys = keys(); return obj() - .key(KEY_NAME).opt(getName()) - - .key(KEY_CHECKS).opt(checks) - .key(KEY_FORCED_ROOTS).opt(getForcedRoots()) - .key(KEY_CND_URLS).opt(getCndUrls()) - .key(KEY_JCR_NODETYPES).opt(JsonCnd.toJson(getJcrNodetypes(), mapping)) - .key(KEY_JCR_PRIVILEGES).opt(JsonCnd.privilegesToJson(getJcrPrivileges(), mapping)) - .key(KEY_JCR_NAMESPACES).opt(getJcrNamespaces()) + .key(jsonKeys.name()).opt(getName()) + .key(jsonKeys.checks()).opt(checks) + .key(jsonKeys.forcedRoots()).opt(getForcedRoots()) + .key(jsonKeys.cndUrls()).opt(getCndUrls()) + .key(jsonKeys.jcrNodetypes()).opt(JsonCnd.toJson(getJcrNodetypes(), mapping)) + .key(jsonKeys.jcrPrivileges()).opt(JsonCnd.privilegesToJson(getJcrPrivileges(), mapping)) + .key(jsonKeys.jcrNamespaces()).opt(getJcrNamespaces()) .get(); } } @@ -313,12 +385,13 @@ public static Checklist fromJson(final @NotNull String moduleName, final @NotNull JsonObject json) { LOGGER.trace("[fromJson] module: {}, manifestUrl: {}, json: {}", moduleName, manifestUrl, json); Builder builder = new Builder(moduleName); - if (hasNonNull(json, KEY_NAME)) { - builder.withName(json.getString(KEY_NAME)); + final JsonKeys jsonKeys = keys(); + if (hasNonNull(json, jsonKeys.name())) { + builder.withName(json.getString(jsonKeys.name())); } if (manifestUrl != null && manifestUrl.toExternalForm().endsWith(JarFile.MANIFEST_NAME)) { - ofNullable(json.getJsonArray(KEY_CND_NAMES)) + ofNullable(json.getJsonArray(jsonKeys.cndNames())) .filter(Util.traceFilter(LOGGER, "[fromJson#cndNames] cndNames: {}")) .map(cndNames -> JavaxJson.unwrapArray(cndNames).stream() .map(String::valueOf) @@ -328,28 +401,28 @@ public static Checklist fromJson(final @NotNull String moduleName, } builder.withCndUrls(parseFromArray( - optArray(json, KEY_CND_URLS).orElse(JsonValue.EMPTY_JSON_ARRAY), URL::new, + optArray(json, jsonKeys.cndUrls()).orElse(JsonValue.EMPTY_JSON_ARRAY), URL::new, (element, error) -> LOGGER.debug("[fromJson#cndUrls] (URL ERROR) {}", error.getMessage()))); final List jcrNsList = new ArrayList<>(); - optArray(json, KEY_JCR_NAMESPACES).ifPresent(jsonArray -> { + optArray(json, jsonKeys.jcrNamespaces()).ifPresent(jsonArray -> { jcrNsList.addAll(JavaxJson.mapArrayOfObjects(jsonArray, JcrNs::fromJson)); builder.withJcrNamespaces(jcrNsList); }); - optObject(json, KEY_JCR_NODETYPES).ifPresent(jsonObject -> { + optObject(json, jsonKeys.jcrNodetypes()).ifPresent(jsonObject -> { builder.withJcrNodetypes( JsonCnd.getQTypesFromJson(jsonObject, JsonCnd.toNamespaceMapping(jcrNsList))); }); - if (json.containsKey(KEY_JCR_PRIVILEGES)) { + if (json.containsKey(jsonKeys.jcrPrivileges())) { builder.withJcrPrivileges( - JsonCnd.getPrivilegesFromJson(json.get(KEY_JCR_PRIVILEGES), - JsonCnd.toNamespaceMapping(jcrNsList))); + JsonCnd.getPrivilegesFromJson(json.get(jsonKeys.jcrPrivileges()), + JsonCnd.toNamespaceMapping(jcrNsList))); } - optArray(json, KEY_FORCED_ROOTS).ifPresent(jsonArray -> { + optArray(json, jsonKeys.forcedRoots()).ifPresent(jsonArray -> { builder.withForcedRoots(JavaxJson.mapArrayOfObjects(jsonArray, ForcedRoot::fromJson)); }); - optArray(json, KEY_CHECKS).ifPresent(jsonArray -> { + optArray(json, jsonKeys.checks()).ifPresent(jsonArray -> { builder.withChecks(JavaxJson.mapArrayOfObjects(jsonArray, CheckSpec::fromJson)); }); final Checklist checklist = builder.build(json); diff --git a/core/src/main/java/net/adamcin/oakpal/core/ChecklistPlanner.java b/core/src/main/java/net/adamcin/oakpal/core/ChecklistPlanner.java index 58a01c5bc..26f48fd19 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ChecklistPlanner.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ChecklistPlanner.java @@ -113,18 +113,18 @@ Stream getAllChecklists() { /** * Compute the checklist plan for progress checks, given a list of override specs to apply. - * + *

* Intended Algorithm: * from selected checklists, stream specs - * - apply overrides - * - drop skipped specs - * - collect overlaid + * - apply overrides + * - drop skipped specs + * - collect overlaid * from overrides, stream as remaining specs - * - drop skipped specs - * - drop if already applied as overlay - * - apply as overlay to inactive checklist specs if possible - * - apply inheritance from any checklist spec if necessary - * - add result if not abstract + * - drop skipped specs + * - drop if already applied as overlay + * - apply as overlay to inactive checklist specs if possible + * - apply inheritance from any checklist spec if necessary + * - add result if not abstract * * @param checkOverrides the list of check overrides * @return a final list of {@link CheckSpec}s to use for a scan diff --git a/core/src/main/java/net/adamcin/oakpal/core/CoreConstants.java b/core/src/main/java/net/adamcin/oakpal/core/CoreConstants.java new file mode 100644 index 000000000..568a2966f --- /dev/null +++ b/core/src/main/java/net/adamcin/oakpal/core/CoreConstants.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core; + +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; + +/** + * Hosts constants for interface types as static singleton getter methods defined by associated interfaces. + * This reduces the impact on semantic versioning rules of adding or modifying interface constants. A similar pattern is + * followed on concrete classes when they must define JSON keys, but each concrete class owns its own constants + * interfaces. + */ +public final class CoreConstants { + private CoreConstants() { + /* no constructor */ + } + + /** + * Json keys for CheckReport. Use {@link CoreConstants#checkReportKeys()} to access singleton. + */ + @ProviderType + public interface CheckReportKeys { + String checkName(); + + String violations(); + } + + private static final CheckReportKeys CHECK_REPORT_KEYS = new CheckReportKeys() { + @Override + public String checkName() { + return "checkName"; + } + + @Override + public String violations() { + return "violations"; + } + }; + + @NotNull + public static CheckReportKeys checkReportKeys() { + return CHECK_REPORT_KEYS; + } +} diff --git a/core/src/main/java/net/adamcin/oakpal/core/DefaultErrorListener.java b/core/src/main/java/net/adamcin/oakpal/core/DefaultErrorListener.java index 8215cb4ef..8d9bb5dd2 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/DefaultErrorListener.java +++ b/core/src/main/java/net/adamcin/oakpal/core/DefaultErrorListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,24 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ReportCollector; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; +import net.adamcin.oakpal.api.ViolationReporter; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.PathNotFoundException; import java.net.URL; +import java.text.MessageFormat; import java.util.Collection; +import java.util.MissingResourceException; import java.util.Optional; +import java.util.ResourceBundle; /** * Default implementation which reports all exceptions as violations. @@ -33,10 +43,52 @@ public class DefaultErrorListener implements ErrorListener { private final ReportCollector collector = new ReportCollector(); + private ResourceBundle resourceBundle; + protected void reportViolation(final Violation violation) { this.collector.reportViolation(violation); } + @Override + public void setResourceBundle(final ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + /** + * Used by {@link #getString(String)} to retrieve localized messages. + * NOTE: If this method is called before a non-null ResourceBundle has been injected via + * {@link ViolationReporter#setResourceBundle(ResourceBundle)}, it will try to get a fallback ResourceBundle + * by calling {@link ResourceBundle#getBundle(String)}, which invokes the default classloader and locale behavior, + * using {@link ViolationReporter#getResourceBundleBaseName()} as the resource bundle base name. + * + * @return the resource bundle + * @see ResourceBundle#getBundle(String) + * @see ViolationReporter#getResourceBundleBaseName() + */ + @NotNull + protected ResourceBundle getResourceBundle() throws MissingResourceException { + if (this.resourceBundle == null) { + this.resourceBundle = ResourceBundle.getBundle(getResourceBundleBaseName()); + } + return this.resourceBundle; + } + + /** + * Lookup a localized string from the resource bundle. + * + * @param key the i18n key + * @return the localized string + * @throws MissingResourceException if an attempt is made to load a missing ResourceBundle + */ + @NotNull + protected String getString(@NotNull final String key) { + if (getResourceBundle().containsKey(key)) { + return getResourceBundle().getString(key); + } else { + return key; + } + } + @Override public Collection getReportedViolations() { return collector.getReportedViolations(); @@ -47,10 +99,10 @@ public void onNodeTypeRegistrationError(final Throwable e, final URL resource) { if (e.getCause() != null) { onNodeTypeRegistrationError(e.getCause(), resource); } else { - final String message = String.format("NodeType registration error (%s): %s \"%s\"", + final String message = MessageFormat.format(getString("NodeType registration error ({0}): {1} \"{2}\""), String.valueOf(resource), e.getClass().getName(), e.getMessage()); LOGGER.trace("[onNodeTypeRegistrationError] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message)); + reportViolation(new SimpleViolation(Severity.MAJOR, message)); } } @@ -59,10 +111,10 @@ public void onJcrNamespaceRegistrationError(final Throwable e, final String pref if (e.getCause() != null) { onJcrNamespaceRegistrationError(e.getCause(), prefix, uri); } else { - final String message = String.format("JCR namespace registration error (%s=%s): %s \"%s\"", + final String message = MessageFormat.format(getString("JCR namespace registration error ({0}={1}): {2} \"{3}\""), prefix, uri, e.getClass().getName(), e.getMessage()); LOGGER.trace("[onJcrNamespaceRegistrationError] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message)); + reportViolation(new SimpleViolation(Severity.MAJOR, message)); } } @@ -71,10 +123,10 @@ public void onJcrPrivilegeRegistrationError(final Throwable e, final String jcrP if (e.getCause() != null) { onJcrPrivilegeRegistrationError(e.getCause(), jcrPrivilege); } else { - final String message = String.format("JCR privilege registration error (%s): %s \"%s\"", + final String message = MessageFormat.format(getString("JCR privilege registration error ({0}): {1} \"{2}\""), jcrPrivilege, e.getClass().getName(), e.getMessage()); LOGGER.trace("[onJcrPrivilegeRegistrationError] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message)); + reportViolation(new SimpleViolation(Severity.MAJOR, message)); } } @@ -83,59 +135,62 @@ public void onForcedRootCreationError(final Throwable e, final ForcedRoot forced if (e.getCause() != null) { onForcedRootCreationError(e.getCause(), forcedRoot); } else { - final String message = String.format("Forced root creation error (%s): %s \"%s\"", + final String message = MessageFormat.format(getString("Forced root creation error ({0}): {1} \"{2}\""), forcedRoot, e.getClass().getName(), e.getMessage()); LOGGER.trace("[onForcedRootCreationError] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message)); + reportViolation(new SimpleViolation(Severity.MAJOR, message)); } } @Override public void onListenerException(final Exception e, final ProgressCheck listener, final PackageId packageId) { - final String message = String.format("Listener error (%s): %s \"%s\"", + final String message = MessageFormat.format(getString("Listener error ({0}): {1} \"{2}\""), Optional.ofNullable(listener).map(lstr -> lstr.getClass().getName()).orElse(null), e.getClass().getName(), e.getMessage()); LOGGER.trace("[onListenerException] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message, packageId)); + reportViolation(new SimpleViolation(Severity.MAJOR, message, packageId)); } @Override public void onSubpackageException(final Exception e, final PackageId packageId) { - final String message = String.format("Package error: %s \"%s\"", e.getClass().getName(), e.getMessage()); + final String message = MessageFormat.format(getString("Package error: {0} \"{1}\""), + e.getClass().getName(), e.getMessage()); LOGGER.trace("[onSubpackageException] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message, packageId)); + reportViolation(new SimpleViolation(Severity.MAJOR, message, packageId)); } @Override public void onImporterException(final Exception e, final PackageId packageId, final String path) { // Ignore PathNotFoundException, as it is thrown A LOT if (!(e instanceof PathNotFoundException)) { - final String message = String.format("%s - Importer error: %s \"%s\"", path, e.getClass().getName(), e.getMessage()); + final String message = MessageFormat.format(getString("{0} - Importer error: {1} \"{2}\""), + path, e.getClass().getName(), e.getMessage()); LOGGER.trace("[onImporterException] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message, packageId)); + reportViolation(new SimpleViolation(Severity.MAJOR, message, packageId)); } } @Override public void onListenerPathException(final Exception e, final ProgressCheck handler, final PackageId packageId, final String path) { - final String message = String.format("%s - Listener error: %s \"%s\"", path, e.getClass().getName(), e.getMessage()); + final String message = MessageFormat.format(getString("{0} - Listener error: {1} \"{2}\""), + path, e.getClass().getName(), e.getMessage()); LOGGER.trace("[onListenerPathException] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message, packageId)); + reportViolation(new SimpleViolation(Severity.MAJOR, message, packageId)); } @Override public void onInstallHookError(final Throwable e, final PackageId packageId) { - final String message = String.format("InstallHook error: %s \"%s\"", + final String message = MessageFormat.format(getString("InstallHook error: {0} \"{1}\""), Optional.ofNullable(e.getCause()).orElse(e).getClass().getName(), e.getMessage()); LOGGER.trace("[onInstallHookError] stack trace for: " + message, e); - reportViolation(new SimpleViolation(Violation.Severity.MAJOR, message, packageId)); + reportViolation(new SimpleViolation(Severity.MAJOR, message, packageId)); } @Override public void onProhibitedInstallHookRegistration(final PackageId packageId) { reportViolation( - new SimpleViolation(Violation.Severity.MAJOR, - "Policy prohibits the use of InstallHooks in packages", packageId)); + new SimpleViolation(Severity.MAJOR, + getString("Policy prohibits the use of InstallHooks in packages"), packageId)); } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/DefaultPackagingService.java b/core/src/main/java/net/adamcin/oakpal/core/DefaultPackagingService.java index 4c6a717ce..42e9dc7b7 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/DefaultPackagingService.java +++ b/core/src/main/java/net/adamcin/oakpal/core/DefaultPackagingService.java @@ -35,7 +35,6 @@ * {@link PackagingService#getPackageManager(Session)} method to avoid the nasty stack trace. * See recommendation here: https://issues.apache.org/jira/browse/JCRVLT-285 */ -@SuppressWarnings("CQRules:CQBP-84--dependencies") // suppress warnings final class DefaultPackagingService implements Packaging { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPackagingService.class); private static final String JCR_PACK_MAN_IMPL_CLASS = "org.apache.jackrabbit.vault.packaging.impl.JcrPackageManagerImpl"; diff --git a/core/src/main/java/net/adamcin/oakpal/core/ErrorListener.java b/core/src/main/java/net/adamcin/oakpal/core/ErrorListener.java index 3f267f6ef..0485ee93f 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ErrorListener.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ErrorListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,18 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ScanListener; +import net.adamcin.oakpal.api.ViolationReporter; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.osgi.annotation.versioning.ConsumerType; import java.net.URL; /** * A single error handler is used during an OakPAL scan. */ +@ConsumerType public interface ErrorListener extends ScanListener, ViolationReporter { /** diff --git a/core/src/main/java/net/adamcin/oakpal/core/ForcedRoot.java b/core/src/main/java/net/adamcin/oakpal/core/ForcedRoot.java index 178c562a2..572e78347 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ForcedRoot.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ForcedRoot.java @@ -16,8 +16,10 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JsonObjectConvertible; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import javax.json.JsonObject; import java.util.Arrays; @@ -28,19 +30,49 @@ import java.util.function.Function; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.streamIt; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.JavaxJson.optArray; +import static net.adamcin.oakpal.api.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.streamIt; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.optArray; /** * Encapsulation of details necessary to force creation of a particular root path. */ -public final class ForcedRoot implements JavaxJson.ObjectConvertible, Comparable { - static final String KEY_PATH = "path"; - static final String KEY_PRIMARY_TYPE = "primaryType"; - static final String KEY_MIXIN_TYPES = "mixinTypes"; +public final class ForcedRoot implements JsonObjectConvertible, Comparable { + /** + * Json keys for ForcedRoot. Use {@link #keys()} to access singleton. + */ + @ProviderType + public interface JsonKeys { + String path(); + + String primaryType(); + + String mixinTypes(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String path() { + return "path"; + } + + @Override + public String primaryType() { + return "primaryType"; + } + + @Override + public String mixinTypes() { + return "mixinTypes"; + } + }; + + @NotNull + public static ForcedRoot.JsonKeys keys() { + return KEYS; + } @Nullable private String path; @@ -51,6 +83,7 @@ public final class ForcedRoot implements JavaxJson.ObjectConvertible, Comparable @NotNull private List mixinTypes = Collections.emptyList(); + public @Nullable String getPath() { return path; } @@ -113,14 +146,15 @@ public final boolean hasPath() { * @return a new forced root */ public static @NotNull ForcedRoot fromJson(final @NotNull JsonObject json) { + final JsonKeys keys = keys(); final ForcedRoot forcedRoot = new ForcedRoot(); - if (json.containsKey(KEY_PATH)) { - forcedRoot.setPath(json.getString(KEY_PATH)); + if (json.containsKey(keys.path())) { + forcedRoot.setPath(json.getString(keys.path())); } - if (json.containsKey(KEY_PRIMARY_TYPE)) { - forcedRoot.setPrimaryType(json.getString(KEY_PRIMARY_TYPE)); + if (json.containsKey(keys.primaryType())) { + forcedRoot.setPrimaryType(json.getString(keys.primaryType())); } - optArray(json, KEY_MIXIN_TYPES).ifPresent(jsonArray -> { + optArray(json, keys.mixinTypes()).ifPresent(jsonArray -> { forcedRoot.setMixinTypes(mapArrayOfStrings(jsonArray, Function.identity())); }); return forcedRoot; @@ -145,9 +179,10 @@ public final boolean hasPath() { @Override public JsonObject toJson() { - return obj().key(KEY_PATH).opt(this.path) - .key(KEY_PRIMARY_TYPE).opt(this.primaryType) - .key(KEY_MIXIN_TYPES).opt(this.mixinTypes) + final JsonKeys keys = keys(); + return obj().key(keys.path()).opt(this.path) + .key(keys.primaryType()).opt(this.primaryType) + .key(keys.mixinTypes()).opt(this.mixinTypes) .get(); } @@ -176,4 +211,6 @@ public int hashCode() { public String toString() { return toJson().toString(); } + + } diff --git a/core/src/main/java/net/adamcin/oakpal/core/InitStage.java b/core/src/main/java/net/adamcin/oakpal/core/InitStage.java index d54eea136..6e11bfee9 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/InitStage.java +++ b/core/src/main/java/net/adamcin/oakpal/core/InitStage.java @@ -23,9 +23,7 @@ import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; -import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; import org.apache.jackrabbit.spi.commons.nodetype.NodeTypeDefinitionFactory; -import org.apache.jackrabbit.spi.commons.privilege.PrivilegeDefinitionImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,15 +45,11 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.mapEntry; -import static net.adamcin.oakpal.core.Fun.onEntry; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; + +import static net.adamcin.oakpal.api.Fun.onEntry; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; import static net.adamcin.oakpal.core.OakMachine.NT_UNDECLARED; /** diff --git a/core/src/main/java/net/adamcin/oakpal/core/JcrNs.java b/core/src/main/java/net/adamcin/oakpal/core/JcrNs.java index 676d5da21..f021ec529 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/JcrNs.java +++ b/core/src/main/java/net/adamcin/oakpal/core/JcrNs.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,47 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JsonObjectConvertible; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import javax.json.JsonObject; import java.util.Objects; import java.util.Optional; -import static net.adamcin.oakpal.core.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.key; /** * Config DTO for JCR Namespace Prefix to URI Mappings. */ -public final class JcrNs implements JavaxJson.ObjectConvertible, Comparable { - static final String KEY_PREFIX = "prefix"; - static final String KEY_URI = "uri"; +public final class JcrNs implements JsonObjectConvertible, Comparable { + /** + * Json keys for JcrNs. Use {@link #keys()} to access singleton. + */ + @ProviderType + public interface JsonKeys { + String prefix(); + + String uri(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String prefix() { + return "prefix"; + } + + @Override + public String uri() { + return "uri"; + } + }; + + @NotNull + public static JcrNs.JsonKeys keys() { + return KEYS; + } private String prefix; private String uri; @@ -68,12 +94,12 @@ public void setUri(String uri) { * @return a new JCR NS mapping */ public static @Nullable JcrNs fromJson(final @NotNull JsonObject json) { - if (!json.containsKey(KEY_PREFIX) || !json.containsKey(KEY_URI)) { + if (!json.containsKey(KEYS.prefix()) || !json.containsKey(KEYS.uri())) { return null; } JcrNs jcrNs = new JcrNs(); - jcrNs.setPrefix(json.getString(KEY_PREFIX, "")); - jcrNs.setUri(json.getString(KEY_URI, "")); + jcrNs.setPrefix(json.getString(KEYS.prefix(), "")); + jcrNs.setUri(json.getString(KEYS.uri(), "")); return jcrNs; } @@ -107,7 +133,8 @@ public int hashCode() { @Override public JsonObject toJson() { - return key(KEY_PREFIX, getPrefix()).key(KEY_URI, getUri()).get(); + final JsonKeys keys = keys(); + return key(keys.prefix(), getPrefix()).key(keys.uri(), getUri()).get(); } @Override @@ -120,4 +147,6 @@ public int compareTo(final @NotNull JcrNs o) { return Optional.of(getPrefix().compareTo(o.getPrefix())) .filter(comp -> comp != 0).orElseGet(() -> getUri().compareTo(o.getUri())); } + + } diff --git a/core/src/main/java/net/adamcin/oakpal/core/JsonCnd.java b/core/src/main/java/net/adamcin/oakpal/core/JsonCnd.java index 892f5519d..138448112 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/JsonCnd.java +++ b/core/src/main/java/net/adamcin/oakpal/core/JsonCnd.java @@ -16,6 +16,9 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.commons.cnd.Lexer; import org.apache.jackrabbit.commons.query.qom.Operator; @@ -98,28 +101,28 @@ import java.util.stream.StreamSupport; import static java.util.Optional.ofNullable; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.constantly1; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.mapEntry; -import static net.adamcin.oakpal.core.Fun.mapKey; -import static net.adamcin.oakpal.core.Fun.mapValue; -import static net.adamcin.oakpal.core.Fun.onEntry; -import static net.adamcin.oakpal.core.Fun.testKey; -import static net.adamcin.oakpal.core.Fun.testValue; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheck2; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid2; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; -import static net.adamcin.oakpal.core.JavaxJson.JSON_VALUE_STRING; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.JavaxJson.optArray; -import static net.adamcin.oakpal.core.JavaxJson.unwrap; -import static net.adamcin.oakpal.core.JavaxJson.wrap; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.constantly1; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.mapEntry; +import static net.adamcin.oakpal.api.Fun.mapKey; +import static net.adamcin.oakpal.api.Fun.mapValue; +import static net.adamcin.oakpal.api.Fun.onEntry; +import static net.adamcin.oakpal.api.Fun.testKey; +import static net.adamcin.oakpal.api.Fun.testValue; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheck2; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid2; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.JavaxJson.JSON_VALUE_STRING; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.optArray; +import static net.adamcin.oakpal.api.JavaxJson.unwrap; +import static net.adamcin.oakpal.api.JavaxJson.wrap; /** * Methods and types used to encode/decode QNodeTypeDefinitions as JSON for use in checklists. diff --git a/core/src/main/java/net/adamcin/oakpal/core/Locator.java b/core/src/main/java/net/adamcin/oakpal/core/Locator.java index ade839093..10bf54b02 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Locator.java +++ b/core/src/main/java/net/adamcin/oakpal/core/Locator.java @@ -16,6 +16,9 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; + import javax.json.JsonObject; import javax.json.JsonValue; import java.net.URL; @@ -163,6 +166,5 @@ public static List loadFromCheckSpecs(final List check } } return allChecks; - } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/NamespaceMappingRequest.java b/core/src/main/java/net/adamcin/oakpal/core/NamespaceMappingRequest.java index 46b24df26..68333bc96 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/NamespaceMappingRequest.java +++ b/core/src/main/java/net/adamcin/oakpal/core/NamespaceMappingRequest.java @@ -1,5 +1,6 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; @@ -12,8 +13,8 @@ import java.util.Set; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.result1; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.result1; /** * Given all the JCR namespace gymnastics that have to be performed for {@link JsonCnd} and webster, dealing with diff --git a/core/src/main/java/net/adamcin/oakpal/core/OakMachine.java b/core/src/main/java/net/adamcin/oakpal/core/OakMachine.java index 21d59eca4..12947a1c0 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/OakMachine.java +++ b/core/src/main/java/net/adamcin/oakpal/core/OakMachine.java @@ -16,6 +16,9 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.api.JackrabbitRepository; import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory; @@ -74,8 +77,8 @@ import java.util.jar.Manifest; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; /** * Entry point for OakPAL Acceptance Library. See {@link ProgressCheck} for the event listener interface. @@ -468,7 +471,7 @@ public interface InspectBody { } /** - * Functional interface for {@link Builder#withSubpackageSilencer(SubpackageSilencer)}. + * Functional interface for {@code Builder.withSubpackageSilencer(SubpackageSilencer)}. */ @FunctionalInterface public interface SubpackageSilencer extends BiPredicate { @@ -936,7 +939,7 @@ public void onMessage(Mode mode, String action, String path) { Node node = session.getNode(path); handlers.forEach(handler -> { try { - handler.importedPath(packageId, path, node); + handler.importedPath(packageId, path, node, PathAction.fromShortCode(action)); } catch (final Exception e) { OakMachine.this.getErrorListener().onListenerPathException(e, handler, packageId, path); } diff --git a/core/src/main/java/net/adamcin/oakpal/core/OakpalPlan.java b/core/src/main/java/net/adamcin/oakpal/core/OakpalPlan.java index 56020b14a..9697480a7 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/OakpalPlan.java +++ b/core/src/main/java/net/adamcin/oakpal/core/OakpalPlan.java @@ -1,11 +1,18 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.JsonObjectConvertible; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.ViolationReporter; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; import org.apache.jackrabbit.util.Text; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,14 +25,16 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Optional; +import java.util.ResourceBundle; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; /** * A plan is a reproducible execution plan, similar in design to a Checklist, but with the following differences: @@ -34,7 +43,80 @@ * 3. Can reference pre-install packages by URL. * 4. Can not aggregate multiple plans per execution. */ -public final class OakpalPlan implements JavaxJson.ObjectConvertible { +public final class OakpalPlan implements JsonObjectConvertible { + @ProviderType + public interface JsonKeys { + String checklists(); + + String checks(); + + String forcedRoots(); + + String jcrNamespaces(); + + String jcrNodetypes(); + + String jcrPrivileges(); + + String preInstallUrls(); + + String enablePreInstallHooks(); + + String installHookPolicy(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String checklists() { + return "checklists"; + } + + @Override + public String checks() { + return "checks"; + } + + @Override + public String forcedRoots() { + return "forcedRoots"; + } + + @Override + public String jcrNamespaces() { + return "jcrNamespaces"; + } + + @Override + public String jcrNodetypes() { + return "jcrNodetypes"; + } + + @Override + public String jcrPrivileges() { + return "jcrPrivileges"; + } + + @Override + public String preInstallUrls() { + return "preInstallUrls"; + } + + @Override + public String enablePreInstallHooks() { + return "enablePreInstallHooks"; + } + + @Override + public String installHookPolicy() { + return "installHookPolicy"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + private static final Logger LOGGER = LoggerFactory.getLogger(OakpalPlan.class); /** * Preferred default plan when a custom classpath is specified without specifying a plan name. This is also @@ -55,16 +137,6 @@ public final class OakpalPlan implements JavaxJson.ObjectConvertible { */ public static final String DEFAULT_PLAN_NAME = "plan.json"; - public static final String KEY_CHECKLISTS = "checklists"; - public static final String KEY_CHECKS = "checks"; - public static final String KEY_FORCED_ROOTS = "forcedRoots"; - public static final String KEY_JCR_NAMESPACES = "jcrNamespaces"; - public static final String KEY_JCR_NODETYPES = "jcrNodetypes"; - public static final String KEY_JCR_PRIVILEGES = "jcrPrivileges"; - public static final String KEY_PREINSTALL_URLS = "preInstallUrls"; - public static final String KEY_ENABLE_PRE_INSTALL_HOOKS = "enablePreInstallHooks"; - public static final String KEY_INSTALL_HOOK_POLICY = "installHookPolicy"; - private final URL base; private final String name; private final JsonObject originalJson; @@ -190,15 +262,15 @@ public JsonObject toJson() { final NamespaceMapping mapping = JsonCnd.toNamespaceMapping(jcrNamespaces); return JavaxJson.obj() - .key(KEY_PREINSTALL_URLS).opt(preInstallStrings) - .key(KEY_CHECKLISTS).opt(checklists) - .key(KEY_CHECKS).opt(checks) - .key(KEY_FORCED_ROOTS).opt(forcedRoots) - .key(KEY_JCR_NODETYPES).opt(JsonCnd.toJson(jcrNodetypes, mapping)) - .key(KEY_JCR_PRIVILEGES).opt(JsonCnd.privilegesToJson(jcrPrivileges, mapping)) - .key(KEY_JCR_NAMESPACES).opt(jcrNamespaces) - .key(KEY_ENABLE_PRE_INSTALL_HOOKS).opt(enablePreInstallHooks, false) - .key(KEY_INSTALL_HOOK_POLICY).opt(installHookPolicy) + .key(keys().preInstallUrls()).opt(preInstallStrings) + .key(keys().checklists()).opt(checklists) + .key(keys().checks()).opt(checks) + .key(keys().forcedRoots()).opt(forcedRoots) + .key(keys().jcrNodetypes()).opt(JsonCnd.toJson(jcrNodetypes, mapping)) + .key(keys().jcrPrivileges()).opt(JsonCnd.privilegesToJson(jcrPrivileges, mapping)) + .key(keys().jcrNamespaces()).opt(jcrNamespaces) + .key(keys().enablePreInstallHooks()).opt(enablePreInstallHooks, false) + .key(keys().installHookPolicy()).opt(installHookPolicy) .get(); } @@ -236,6 +308,11 @@ public OakMachine.Builder toOakMachineBuilder(final @Nullable ErrorListener erro throw new Exception("Error while loading progress checks.", e); } + final Locale locale = Locale.getDefault(); + for (final ProgressCheck progressCheck : allChecks) { + initResourceBundle(progressCheck, locale, classLoader); + } + return new OakMachine.Builder() .withErrorListener(errorListener) .withProgressChecks(allChecks) @@ -247,43 +324,51 @@ public OakMachine.Builder toOakMachineBuilder(final @Nullable ErrorListener erro .withEnablePreInstallHooks(enablePreInstallHooks); } + void initResourceBundle(final ViolationReporter reporter, final Locale locale, final ClassLoader classLoader) { + if (reporter.getResourceBundleBaseName() != null) { + Fun.result0(() -> ResourceBundle + .getBundle(reporter.getResourceBundleBaseName(), locale, classLoader)).get() + .forEach(reporter::setResourceBundle); + } + } private static OakpalPlan fromJson(final @NotNull Builder builder, final @NotNull JsonObject json) { - if (hasNonNull(json, KEY_PREINSTALL_URLS) && builder.base != null) { - builder.withPreInstallUrls(JavaxJson.mapArrayOfStrings(json.getJsonArray(KEY_PREINSTALL_URLS), + if (hasNonNull(json, keys().preInstallUrls()) && builder.base != null) { + builder.withPreInstallUrls(JavaxJson.mapArrayOfStrings(json.getJsonArray(keys().preInstallUrls()), uncheck1(url -> new URL(builder.base, url)))); } - if (hasNonNull(json, KEY_CHECKLISTS)) { - builder.withChecklists(JavaxJson.mapArrayOfStrings(json.getJsonArray(KEY_CHECKLISTS))); + if (hasNonNull(json, keys().checklists())) { + builder.withChecklists(JavaxJson.mapArrayOfStrings(json.getJsonArray(keys().checklists()))); } - if (hasNonNull(json, KEY_CHECKS)) { - builder.withChecks(JavaxJson.mapArrayOfObjects(json.getJsonArray(KEY_CHECKS), CheckSpec::fromJson)); + if (hasNonNull(json, keys().checks())) { + builder.withChecks(JavaxJson.mapArrayOfObjects(json.getJsonArray(keys().checks()), CheckSpec::fromJson)); } - if (hasNonNull(json, KEY_FORCED_ROOTS)) { - builder.withForcedRoots(JavaxJson.mapArrayOfObjects(json.getJsonArray(KEY_FORCED_ROOTS), ForcedRoot::fromJson)); + if (hasNonNull(json, keys().forcedRoots())) { + builder.withForcedRoots(JavaxJson.mapArrayOfObjects(json.getJsonArray(keys().forcedRoots()), + ForcedRoot::fromJson)); } final NamespaceMapping mapping; - if (hasNonNull(json, KEY_JCR_NAMESPACES)) { - final List jcrNsList = JavaxJson.mapArrayOfObjects(json.getJsonArray(KEY_JCR_NAMESPACES), + if (hasNonNull(json, keys().jcrNamespaces())) { + final List jcrNsList = JavaxJson.mapArrayOfObjects(json.getJsonArray(keys().jcrNamespaces()), JcrNs::fromJson); mapping = JsonCnd.toNamespaceMapping(jcrNsList); builder.withJcrNamespaces(jcrNsList); } else { mapping = JsonCnd.BUILTIN_MAPPINGS; } - if (hasNonNull(json, KEY_JCR_NODETYPES)) { - builder.withJcrNodetypes(JsonCnd.getQTypesFromJson(json.getJsonObject(KEY_JCR_NODETYPES), mapping)); + if (hasNonNull(json, keys().jcrNodetypes())) { + builder.withJcrNodetypes(JsonCnd.getQTypesFromJson(json.getJsonObject(keys().jcrNodetypes()), mapping)); } - if (hasNonNull(json, KEY_JCR_PRIVILEGES)) { - builder.withJcrPrivileges(JsonCnd.getPrivilegesFromJson(json.get(KEY_JCR_PRIVILEGES), mapping)); + if (hasNonNull(json, keys().jcrPrivileges())) { + builder.withJcrPrivileges(JsonCnd.getPrivilegesFromJson(json.get(keys().jcrPrivileges()), mapping)); } - if (hasNonNull(json, KEY_ENABLE_PRE_INSTALL_HOOKS)) { - builder.withEnablePreInstallHooks(json.getBoolean(KEY_ENABLE_PRE_INSTALL_HOOKS)); + if (hasNonNull(json, keys().enablePreInstallHooks())) { + builder.withEnablePreInstallHooks(json.getBoolean(keys().enablePreInstallHooks())); } - if (hasNonNull(json, KEY_INSTALL_HOOK_POLICY)) { + if (hasNonNull(json, keys().installHookPolicy())) { builder.withInstallHookPolicy(InstallHookPolicy.forName( - json.getString(KEY_INSTALL_HOOK_POLICY))); + json.getString(keys().installHookPolicy()))); } return builder.build(json); } diff --git a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckAliasFacade.java b/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckAliasFacade.java index 4ee7c05de..2296f173f 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckAliasFacade.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ProgressCheckAliasFacade.java @@ -16,6 +16,10 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; @@ -28,6 +32,7 @@ import java.io.File; import java.util.Collection; import java.util.List; +import java.util.ResourceBundle; import java.util.jar.Manifest; /** @@ -55,6 +60,16 @@ public String getCheckName() { } } + @Override + public @Nullable String getResourceBundleBaseName() { + return wrapped.getResourceBundleBaseName(); + } + + @Override + public void setResourceBundle(final ResourceBundle resourceBundle) { + wrapped.setResourceBundle(resourceBundle); + } + @Override public void startedScan() { wrapped.startedScan(); @@ -88,8 +103,9 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio } @Override - public void importedPath(final PackageId packageId, final String path, final Node node) throws RepositoryException { - wrapped.importedPath(packageId, path, node); + public void importedPath(final PackageId packageId, final String path, final Node node, + final PathAction action) throws RepositoryException { + wrapped.importedPath(packageId, path, node, action); } @Override diff --git a/core/src/main/java/net/adamcin/oakpal/core/ReportMapper.java b/core/src/main/java/net/adamcin/oakpal/core/ReportMapper.java index 750786b03..59ed7f72b 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ReportMapper.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ReportMapper.java @@ -16,7 +16,10 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.json.Json; import javax.json.JsonArray; @@ -39,20 +42,43 @@ import java.util.Collections; import java.util.List; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfObjects; -import static net.adamcin.oakpal.core.JavaxJson.optArray; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfObjects; +import static net.adamcin.oakpal.api.JavaxJson.optArray; /** * Serialize violations to/from json. */ public final class ReportMapper { - public static final String KEY_REPORTS = "reports"; - public static final String KEY_CHECK_NAME = "checkName"; - public static final String KEY_VIOLATIONS = "violations"; - public static final String KEY_DESCRIPTION = "description"; - public static final String KEY_SEVERITY = "severity"; - public static final String KEY_PACKAGES = "packages"; + @ProviderType + public interface JsonKeys { + String reports(); + + String checkName(); + + String violations(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String reports() { + return "reports"; + } + + @Override + public String checkName() { + return "checkName"; + } + + @Override + public String violations() { + return "violations"; + } + }; + + public static JsonKeys keys() { + return KEYS; + } private ReportMapper() { /* No instantiation */ @@ -61,6 +87,7 @@ private ReportMapper() { /** * Functional interface that indicates that the consuming method will open AND close the stream for reading. */ + @ProviderType @FunctionalInterface public interface ReaderSupplier { Reader open() throws IOException; @@ -69,6 +96,7 @@ public interface ReaderSupplier { /** * Functional interface that indicates that the consuming method will open AND close the stream for writing. */ + @ProviderType @FunctionalInterface public interface WriterSupplier { Writer open() throws IOException; @@ -87,7 +115,7 @@ public static List readReports(final @NotNull ReaderSupplier reader JsonObject json = jsonReader.readObject(); - List reports = optArray(json, KEY_REPORTS) + List reports = optArray(json, keys().reports()) .map(jsonReports -> mapArrayOfObjects(jsonReports, ReportMapper::reportFromJson)) .orElseGet(Collections::emptyList); @@ -142,12 +170,13 @@ public static JsonArray reportsToJson(final @NotNull Collection rep } /** - * Transforms a collection of CheckReports to a JsonArray assigned to a key {@link #KEY_REPORTS} in an outer object. + * Transforms a collection of CheckReports to a JsonArray assigned to a key {@link JsonKeys#reports()} in + * an outer object. * * @param reports the reports to serialize * @return a JsonObject with a JsonArray of CheckReport json objects */ public static JsonObject reportsToJsonObject(final @NotNull Collection reports) { - return key(KEY_REPORTS, reportsToJson(reports)).get(); + return key(keys().reports(), reportsToJson(reports)).get(); } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/ScriptProgressCheck.java b/core/src/main/java/net/adamcin/oakpal/core/ScriptProgressCheck.java index e6483986c..62150a7c5 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/ScriptProgressCheck.java +++ b/core/src/main/java/net/adamcin/oakpal/core/ScriptProgressCheck.java @@ -16,6 +16,15 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.ReportCollector; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; @@ -43,6 +52,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.ResourceBundle; import java.util.Set; import java.util.jar.Manifest; @@ -64,8 +74,8 @@ *

{@link ProgressCheck#identifySubpackage(PackageId, PackageId)}
*
beforeExtract(packageId, inspectSession, packageProperties, metaInf, subpackageIds)
*
{@link ProgressCheck#beforeExtract(PackageId, Session, PackageProperties, MetaInf, List)}
- *
importedPath(packageId, path, node)
- *
{@link ProgressCheck#importedPath(PackageId, String, Node)}
+ *
importedPath(packageId, path, node, action)
+ *
{@link ProgressCheck#importedPath(PackageId, String, Node, PathAction)}
*
deletedPath(packageId, path, inspectSession)
*
{@link ProgressCheck#deletedPath(PackageId, String, Session)}
*
afterExtract(packageId, inspectSession)
@@ -105,19 +115,38 @@ public final class ScriptProgressCheck implements ProgressCheck { this.scriptUrl = scriptUrl; } - private String getFilename() { + @Override + public @Nullable String getResourceBundleBaseName() { + return null; + } + + @Override + public void setResourceBundle(final ResourceBundle resourceBundle) { + this.helper.setResourceBundle(resourceBundle); + } + + ScriptHelper getHelper() { + return helper; + } + + private String getScriptPath() { if (this.scriptUrl != null) { - final int lastSlash = this.scriptUrl.getPath().lastIndexOf("/"); - if (lastSlash >= 0 && this.scriptUrl.getPath().length() > lastSlash + 1) { - return this.scriptUrl.getPath().substring(lastSlash + 1); - } else { - return this.scriptUrl.getPath(); - } + return this.scriptUrl.getPath(); } else { return FILENAME_INLINE_SCRIPT; } } + private String getFilename() { + final String scriptPath = this.getScriptPath(); + final int lastSlash = scriptPath.lastIndexOf("/"); + if (lastSlash >= 0 && scriptPath.length() > lastSlash + 1) { + return scriptPath.substring(lastSlash + 1); + } else { + return scriptPath; + } + } + @Override public String getCheckName() { try { @@ -224,8 +253,9 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio } @Override - public void importedPath(final PackageId packageId, final String path, final Node node) throws RepositoryException { - guardSessionHandler(INVOKE_ON_IMPORTED_PATH, handle -> handle.apply(packageId, path, node)); + public void importedPath(final PackageId packageId, final String path, final Node node, + final PathAction action) throws RepositoryException { + guardSessionHandler(INVOKE_ON_IMPORTED_PATH, handle -> handle.apply(packageId, path, node, action)); } @Override @@ -255,17 +285,30 @@ public final Collection getReportedViolations() { @SuppressWarnings("WeakerAccess") public static class ScriptHelper { private final ReportCollector collector = new ReportCollector(); + private ResourceBundle resourceBundle; public void minorViolation(String description, PackageId... packageIds) { - collector.reportViolation(new SimpleViolation(Violation.Severity.MINOR, description, packageIds)); + collector.reportViolation(new SimpleViolation(Severity.MINOR, description, packageIds)); } public void majorViolation(String description, PackageId... packageIds) { - collector.reportViolation(new SimpleViolation(Violation.Severity.MAJOR, description, packageIds)); + collector.reportViolation(new SimpleViolation(Severity.MAJOR, description, packageIds)); } public void severeViolation(String description, PackageId... packageIds) { - collector.reportViolation(new SimpleViolation(Violation.Severity.SEVERE, description, packageIds)); + collector.reportViolation(new SimpleViolation(Severity.SEVERE, description, packageIds)); + } + + private void setResourceBundle(final ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + public String getString(final String key) { + if (key != null && resourceBundle != null && resourceBundle.containsKey(key)) { + return resourceBundle.getString(key); + } else { + return key; + } } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/SimpleProgressCheck.java b/core/src/main/java/net/adamcin/oakpal/core/SimpleProgressCheck.java deleted file mode 100644 index e419ae99b..000000000 --- a/core/src/main/java/net/adamcin/oakpal/core/SimpleProgressCheck.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2018 Mark Adamcin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.adamcin.oakpal.core; - -import org.apache.jackrabbit.vault.packaging.PackageId; - -import java.util.Collection; - -/** - * Simple implementation of a {@link ProgressCheck} with convenient methods for reporting and collecting violations. - */ -public class SimpleProgressCheck implements ProgressCheck { - protected final ReportCollector collector = new ReportCollector(); - - protected void reportViolation(final Violation violation) { - collector.reportViolation(violation); - } - - protected final void reportViolation(final Violation.Severity severity, - final String description, - final PackageId... packages) { - this.reportViolation(new SimpleViolation(severity, description, packages)); - } - - protected final void minorViolation(final String description, final PackageId... packages) { - this.reportViolation(new SimpleViolation(Violation.Severity.MINOR, description, packages)); - } - - protected final void majorViolation(final String description, final PackageId... packages) { - this.reportViolation(new SimpleViolation(Violation.Severity.MAJOR, description, packages)); - } - - protected final void severeViolation(final String description, final PackageId... packages) { - this.reportViolation(new SimpleViolation(Violation.Severity.SEVERE, description, packages)); - } - - @Override - public void startedScan() { - collector.clearViolations(); - } - - @Override - public Collection getReportedViolations() { - return collector.getReportedViolations(); - } -} diff --git a/core/src/main/java/net/adamcin/oakpal/core/SimpleReport.java b/core/src/main/java/net/adamcin/oakpal/core/SimpleReport.java index 9b514f9cb..05e852234 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/SimpleReport.java +++ b/core/src/main/java/net/adamcin/oakpal/core/SimpleReport.java @@ -16,6 +16,9 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.jetbrains.annotations.NotNull; import javax.json.JsonObject; @@ -27,10 +30,8 @@ import java.util.Optional; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfObjects; -import static net.adamcin.oakpal.core.JavaxJson.optArray; -import static net.adamcin.oakpal.core.ReportMapper.KEY_CHECK_NAME; -import static net.adamcin.oakpal.core.ReportMapper.KEY_VIOLATIONS; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfObjects; +import static net.adamcin.oakpal.api.JavaxJson.optArray; /** * Simple POJO implementing a {@link CheckReport}, used for deserialization. @@ -94,8 +95,8 @@ public static SimpleReport generateReport(final @NotNull ErrorListener reporter) } public static SimpleReport fromJson(final @NotNull JsonObject jsonReport) { - String vCheckName = jsonReport.getString(KEY_CHECK_NAME, ""); - List violations = optArray(jsonReport, KEY_VIOLATIONS) + String vCheckName = jsonReport.getString(CoreConstants.checkReportKeys().checkName(), ""); + List violations = optArray(jsonReport, CoreConstants.checkReportKeys().violations()) .map(array -> mapArrayOfObjects(array, ReportMapper::violationFromJson)) .orElseGet(Collections::emptyList); diff --git a/core/src/main/java/net/adamcin/oakpal/core/SimpleViolation.java b/core/src/main/java/net/adamcin/oakpal/core/SimpleViolation.java deleted file mode 100644 index 65b297e4b..000000000 --- a/core/src/main/java/net/adamcin/oakpal/core/SimpleViolation.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2018 Mark Adamcin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.adamcin.oakpal.core; - -import org.apache.jackrabbit.vault.packaging.PackageId; - -import javax.json.JsonObject; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; -import static net.adamcin.oakpal.core.JavaxJson.optArray; -import static net.adamcin.oakpal.core.ReportMapper.KEY_DESCRIPTION; -import static net.adamcin.oakpal.core.ReportMapper.KEY_PACKAGES; -import static net.adamcin.oakpal.core.ReportMapper.KEY_SEVERITY; - -/** - * Simple implementation of a {@link Violation}. - */ -public final class SimpleViolation implements Violation { - private final Severity severity; - private final String description; - private final List packages; - - public SimpleViolation(final Severity severity, final String description, final PackageId... packages) { - this(severity, description, packages != null ? Arrays.asList(packages) : null); - } - - public SimpleViolation(final Severity severity, final String description, final List packages) { - this.severity = severity; - this.description = description; - this.packages = Collections.unmodifiableList( - packages != null ? new ArrayList<>(packages) : Collections.emptyList()); - } - - @Override - public Severity getSeverity() { - return severity; - } - - @Override - public Collection getPackages() { - return packages; - } - - @Override - public String getDescription() { - return description; - } - - public static SimpleViolation fromReported(final Violation violation) { - Severity severity = violation.getSeverity(); - String description = violation.getDescription(); - List packages = new ArrayList<>(violation.getPackages()); - return new SimpleViolation(severity, description, packages); - } - - public static SimpleViolation fromJson(final JsonObject jsonViolation) { - String vSeverity = jsonViolation.getString(KEY_SEVERITY, Violation.Severity.MINOR.name()); - Violation.Severity severity = Violation.Severity.valueOf(vSeverity); - String description = jsonViolation.getString(KEY_DESCRIPTION, ""); - List packages = optArray(jsonViolation, KEY_PACKAGES) - .map(array -> mapArrayOfStrings(array, PackageId::fromString, true)) - .orElseGet(Collections::emptyList); - - return new SimpleViolation(severity, description, packages); - } - - @Override - public String toString() { - return "SimpleViolation{" + - "severity=" + severity + - ", description='" + description + '\'' + - ", packages=" + packages + - '}'; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SimpleViolation that = (SimpleViolation) o; - return severity == that.severity && - Objects.equals(description, that.description) && - packages.equals(that.packages); - } - - @Override - public int hashCode() { - return Objects.hash(severity, description, packages); - } -} diff --git a/core/src/main/java/net/adamcin/oakpal/core/Util.java b/core/src/main/java/net/adamcin/oakpal/core/Util.java index 854069107..d5f56cf73 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Util.java +++ b/core/src/main/java/net/adamcin/oakpal/core/Util.java @@ -19,6 +19,7 @@ import aQute.bnd.header.Attrs; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.Domain; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.jcrfacade.SessionFacade; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; diff --git a/core/src/main/java/net/adamcin/oakpal/core/Violation.java b/core/src/main/java/net/adamcin/oakpal/core/Violation.java deleted file mode 100644 index 62984c1cd..000000000 --- a/core/src/main/java/net/adamcin/oakpal/core/Violation.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2018 Mark Adamcin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.adamcin.oakpal.core; - -import aQute.bnd.annotation.ConsumerType; -import org.apache.jackrabbit.vault.packaging.PackageId; -import org.jetbrains.annotations.NotNull; - -import javax.json.JsonObject; -import java.util.Collection; -import java.util.function.Predicate; - -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.ReportMapper.KEY_DESCRIPTION; -import static net.adamcin.oakpal.core.ReportMapper.KEY_PACKAGES; -import static net.adamcin.oakpal.core.ReportMapper.KEY_SEVERITY; - -/** - * Report type for validations. - */ -@ConsumerType -public interface Violation extends JavaxJson.ObjectConvertible { - - /** - * Levels of severity for violations detected during package scans. - */ - enum Severity { - /** - * Unlikely to disrupt application functionality. Appropriate for reporting violations of - * code or style conventions, or inconsistency between modes of installation. - */ - MINOR(2), - - /** - * Likely to be the source of component instability. Appropriate for importer errors, mistaken - * assumptions about root path dependencies or namespaces, or failures related to unit testing of - * application packages. - */ - MAJOR(1), - - /** - * Likely to be the source of platform instability. Appropriate for reporting cross-package filter - * overlap, destructive ACL handling modes, destruction of authorable content, or security violations. - */ - SEVERE(0); - - private final int ordinal; - - Severity(int ordinal) { - this.ordinal = ordinal; - } - - /** - * Runtime throwing function to lookup severity codes by name. - * - * @param name the severity level name - * @return the associated severity level - */ - public static Severity byName(final @NotNull String name) { - for (Severity value : values()) { - if (value.name().equalsIgnoreCase(name)) { - return value; - } - } - throw new IllegalArgumentException("Unknown severity level: " + name); - } - - public boolean isLessSevereThan(Severity other) { - return this.ordinal > other.ordinal; - } - - public Predicate meetsMinimumSeverity() { - return other -> !other.isLessSevereThan(this); - } - - public Severity maxSeverity(final @NotNull Severity other) { - return this.isLessSevereThan(other) ? other : this; - } - } - - /** - * Describe the severity of the violation. - * - * @return the severity of the violation - */ - Severity getSeverity(); - - /** - * Provides a list of one or more Packages responsible for the violation. - * - * @return a list of package IDs responsible for the violation. - */ - Collection getPackages(); - - /** - * Describes the nature of the violation. - * - * @return the description - */ - String getDescription(); - - /** - * Serializes the Violation to a JsonObject. - * - * @return the json representation of the violation - */ - @Override - default JsonObject toJson() { - return obj() - .key(KEY_SEVERITY).opt(this.getSeverity()) - .key(KEY_DESCRIPTION).opt(this.getDescription()) - .key(KEY_PACKAGES).opt(this.getPackages()).get(); - } -} diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/AcHandling.java b/core/src/main/java/net/adamcin/oakpal/core/checks/AcHandling.java index 8e2ed1305..4c1882c4e 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/AcHandling.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/AcHandling.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,15 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.fs.io.AccessControlHandling; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -33,10 +34,10 @@ import java.util.List; import static java.util.Optional.ofNullable; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; import static org.apache.jackrabbit.vault.fs.io.AccessControlHandling.IGNORE; import static org.apache.jackrabbit.vault.fs.io.AccessControlHandling.MERGE; import static org.apache.jackrabbit.vault.fs.io.AccessControlHandling.MERGE_PRESERVE; @@ -69,18 +70,44 @@ * */ public final class AcHandling implements ProgressCheckFactory { - public static final String CONFIG_ALLOWED_MODES = "allowedModes"; - public static final String CONFIG_LEVEL_SET = "levelSet"; + @ProviderType + public interface JsonKeys { + String allowedModes(); + + String levelSet(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String allowedModes() { + return "allowedModes"; + } + + @Override + public String levelSet() { + return "levelSet"; + } + }; + + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_ALLOWED_MODES = keys().allowedModes(); + @Deprecated + public static final String CONFIG_LEVEL_SET = keys().levelSet(); + static final ACHandlingLevelSet DEFAULT_LEVEL_SET = ACHandlingLevelSet.NO_UNSAFE; @Override public ProgressCheck newInstance(final JsonObject config) { - if (hasNonNull(config, CONFIG_ALLOWED_MODES)) { - List allowedModes = mapArrayOfStrings(arrayOrEmpty(config, CONFIG_ALLOWED_MODES), + if (hasNonNull(config, keys().allowedModes())) { + List allowedModes = mapArrayOfStrings(arrayOrEmpty(config, keys().allowedModes()), compose(String::toUpperCase, AccessControlHandling::valueOf), true); return new Check(ACHandlingLevelSet.EXPLICIT, allowedModes); - } else if (hasNonNull(config, CONFIG_LEVEL_SET)) { - ACHandlingLevelSet levelSet = ACHandlingLevelSet.valueOf(config.getString(CONFIG_LEVEL_SET).toUpperCase()); + } else if (hasNonNull(config, keys().levelSet())) { + ACHandlingLevelSet levelSet = ACHandlingLevelSet.valueOf(config.getString(keys().levelSet()).toUpperCase()); return new Check(levelSet, Collections.emptyList()); } else { return new Check(DEFAULT_LEVEL_SET, Collections.emptyList()); @@ -129,21 +156,17 @@ public List getAllowedModes() { } } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { final ACHandlingLevelSet levelSet; final List allowedModes; Check(final ACHandlingLevelSet levelSet, final List allowedModes) { + super(AcHandling.class); this.levelSet = levelSet; this.allowedModes = allowedModes; } - @Override - public String getCheckName() { - return AcHandling.class.getSimpleName(); - } - @Override public void beforeExtract(final PackageId packageId, final Session inspectSession, final PackageProperties packageProperties, final MetaInf metaInf, @@ -155,16 +178,19 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio AccessControlHandling packageMode = ofNullable(packageProperties.getACHandling()).orElse(IGNORE); if (this.levelSet == ACHandlingLevelSet.EXPLICIT) { if (!allowedModes.contains(packageMode)) { - reportViolation(Violation.Severity.MAJOR, - String.format("acHandling mode %s is forbidden. acHandling values in allowedModes are %s", - packageMode, allowedModes), packageId); + reporting(builder -> builder + .withSeverity(Severity.MAJOR) + .withDescription("acHandling mode {0} is forbidden. acHandling values in allowedModes are {1}") + .withArgument(packageMode, allowedModes) + .withPackage(packageId)); } } else { if (!this.levelSet.getAllowedModes().contains(packageMode)) { - reportViolation(Violation.Severity.MAJOR, - String.format("acHandling mode %s is forbidden. allowed acHandling values in levelSet:%s are %s", - packageMode, this.levelSet.name().toLowerCase(), this.levelSet.getAllowedModes()), - packageId); + reporting(builder -> builder + .withSeverity(Severity.MAJOR) + .withDescription("acHandling mode {0} is forbidden. allowed acHandling values in levelSet:{1} are {2}") + .withArgument(packageMode, this.levelSet.name().toLowerCase(), this.levelSet.getAllowedModes()) + .withPackage(packageId)); } } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.java b/core/src/main/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.java new file mode 100644 index 000000000..9db832148 --- /dev/null +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.java @@ -0,0 +1,249 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2013 - 2019 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package net.adamcin.oakpal.core.checks; + +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; +import org.apache.jackrabbit.oak.spi.mount.Mount; +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider; +import org.apache.jackrabbit.oak.spi.mount.Mounts; +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.apache.jackrabbit.vault.packaging.registry.impl.JcrPackageRegistry; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; + +/** + * Report packages which affect paths in more than one composite node store. + * {@code config} items: + *
+ *
{@code severity}
+ *
(default: {@link Severity#MAJOR}) specify the severity of violations reported + * by this check.
+ *
{@code scopePackageIds} ({@link Rule[]})
+ *
(default: include all) List of scope rules matching PackageId values (group:name:version) for inclusion in the scope for + * composite store alignment check.
+ *
{@code mounts} ({@code {"name": "path1" [, "path2", ...] }} )
+ *
(default: {@code {"apps": ["/apps", "/libs"]}}) In a JSON Object, define a named {@link Mount} for each key, + * with the list of paths to assign to the mount. The default mount (named {@code }) is implicitly defined and + * cannot be overridden. By default, this value is configured to simulate future standard oak deployments where the /apps + * and /libs root paths are mounted from a separate read-only node store at runtime. + * .
+ *
+ */ +public final class CompositeStoreAlignment implements ProgressCheckFactory { + @ProviderType + public interface JsonKeys { + String severity(); + + String scopePackageIds(); + + String mounts(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String severity() { + return "severity"; + } + + @Override + public String scopePackageIds() { + return "scopePackageIds"; + } + + @Override + public String mounts() { + return "mounts"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_SEVERITY = keys().severity(); + @Deprecated + public static final String CONFIG_SCOPE_PACKAGE_IDS = keys().scopePackageIds(); + @Deprecated + public static final String CONFIG_MOUNTS = keys().mounts(); + + @Override + public ProgressCheck newInstance(final JsonObject config) throws Exception { + final Severity severity = Severity.valueOf(config.getString(keys().severity(), + Severity.MAJOR.name()).toUpperCase()); + final List scopePackageIds = Rule.fromJsonArray(arrayOrEmpty(config, keys().scopePackageIds())); + final MountInfoProvider defaultProvider = Mounts.defaultMountInfoProvider(); + final MountInfoProvider configProvider; + if (config.containsKey(keys().mounts())) { + Mounts.Builder builder = Mounts.newBuilder(); + final JsonObject mountsObj = config.getJsonObject(keys().mounts()); + for (Map.Entry mountEntry : mountsObj.entrySet()) { + if (defaultProvider.getDefaultMount().getName().equals(mountEntry.getKey())) { + continue; + } + if (mountEntry.getValue().getValueType() == JsonValue.ValueType.ARRAY) { + List paths = JavaxJson.mapArrayOfStrings(mountEntry.getValue().asJsonArray()); + builder.mount(mountEntry.getKey(), paths.toArray(new String[0])); + } else if (mountEntry.getValue().getValueType() == JsonValue.ValueType.STRING) { + builder.mount(mountEntry.getKey(), ((JsonString) mountEntry.getValue()).getString()); + } + } + configProvider = builder.build(); + } else { + configProvider = Mounts.newBuilder().mount("apps", "/apps", "/libs").build(); + } + + return new Check(severity, scopePackageIds, configProvider); + } + + static final class Check extends SimpleProgressCheckFactoryCheck { + private final Severity severity; + private final List scopePackageIds; + private final MountInfoProvider mounts; + + private final Map> subPackages = new HashMap<>(); + private final Map> affectedMounts = new HashMap<>(); + private transient Set currentPackageMounts = new LinkedHashSet<>(); + + Check(final Severity severity, final List scopePackageIds, final MountInfoProvider mounts) { + super(CompositeStoreAlignment.class); + this.severity = severity; + this.scopePackageIds = scopePackageIds; + this.mounts = mounts; + } + + @Override + public void identifyPackage(final PackageId packageId, final File file) { + subPackages.put(packageId, new ArrayList<>()); + } + + @Override + public void identifySubpackage(final PackageId packageId, final PackageId parentId) { + subPackages.put(packageId, new ArrayList<>()); + subPackages.getOrDefault(parentId, Collections.emptyList()).add(packageId); + } + + @Override + public void afterExtract(PackageId packageId, Session inspectSession) throws RepositoryException { + affectedMounts.put(packageId, new LinkedHashSet<>(currentPackageMounts)); + currentPackageMounts.clear(); + } + + /** + * Always ignore: + * 1. the root path because every package potentially marks it as imported + * 2. /etc, /etc/packages, or paths that start with /etc/packages/, because packages with subpackages will import + * these paths, even if they are installed to a different JcrPackageRegistry. + * + * @param path the imported or deleted path to handle + */ + private void handlePath(final String path) { + if (!mounts.hasNonDefaultMounts() + || path.equals("/") + || path.equals("/etc") + || path.equals(JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH) + || path.startsWith(JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH_PREFIX)) { + return; + } + currentPackageMounts.add(mounts.getMountByPath(path)); + } + + @Override + public void importedPath(final PackageId packageId, + final String path, + final Node node, + final PathAction actionType) throws RepositoryException { + handlePath(path); + } + + @Override + public void deletedPath(final PackageId packageId, + final String path, + final Session inspectSession) throws RepositoryException { + handlePath(path); + } + + private Set getMountsAffectedByPackage(final PackageId packageId) { + return new HashSet<>(affectedMounts.getOrDefault(packageId, Collections.emptySet())); + } + + private Set getMountsAffectedByPackageGraph(final PackageId root) { + Set allAffectedMounts = new HashSet<>(getMountsAffectedByPackage(root)); + for (PackageId subPackageId : subPackages.getOrDefault(root, Collections.emptyList())) { + allAffectedMounts.addAll(getMountsAffectedByPackageGraph(subPackageId)); + } + return allAffectedMounts; + } + + @Override + public void finishedScan() { + for (PackageId affectingPackageId : affectedMounts.keySet()) { + if (Rule.lastMatch(scopePackageIds, affectingPackageId.toString()).isExclude()) { + continue; + } + final Set affectedByPackage = getMountsAffectedByPackage(affectingPackageId); + if (affectedByPackage.size() > 1) { + reporting(violation -> violation + .withSeverity(severity) + .withPackage(affectingPackageId) + .withDescription("package content not aligned to a single composite store mount ({0})") + .withArgument(affectedByPackage.stream() + .map(Mount::getName).collect(Collectors.joining(" " + getString("and") + " ")))); + } else if (affectedByPackage.size() > 0) { // filter out container packages only contain subpackages + final Set affectedByPackageGraph = getMountsAffectedByPackageGraph(affectingPackageId); + if (affectedByPackageGraph.size() > 1) { + reporting(violation -> violation + .withSeverity(severity) + .withPackage(affectingPackageId) + .withDescription("recursive package installation not aligned to a single composite store mount ({0})") + .withArgument(affectedByPackageGraph.stream() + .map(Mount::getName).collect(Collectors.joining(" " + getString("and") + " ")))); + } + } + } + } + } +} diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/Echo.java b/core/src/main/java/net/adamcin/oakpal/core/checks/Echo.java index 6c5d48c1d..76e1d760b 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/Echo.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/Echo.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,14 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ConsumerType; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -38,6 +40,7 @@ /** * Simple verbose package check that logs all scan events to standard out. Extend to override methods. */ +@ConsumerType public class Echo implements ProgressCheck { private final AtomicLong lastEvent = new AtomicLong(System.nanoTime()); @@ -111,9 +114,15 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio echo("beforeExtract(packageId: %s, ..., subpackages: %s)", packageId, subpackages); } - @Override + @Deprecated public void importedPath(final PackageId packageId, final String path, final Node node) throws RepositoryException { - echo("importedPath(packageId: %s, path: %s, ...)", packageId, path); + importedPath(packageId, path, node, PathAction.UNKNOWN); + } + + @Override + public void importedPath(final PackageId packageId, final String path, final Node node, final PathAction action) + throws RepositoryException { + echo("importedPath(packageId: %s, path: %s, ..., action: %s)", packageId, path, action); } @Override diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectAces.java b/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectAces.java index efeb475ea..395e49ec9 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectAces.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectAces.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,21 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.Fun; -import net.adamcin.oakpal.core.JavaxJson; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; import org.apache.jackrabbit.vault.packaging.PackageId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +40,6 @@ import javax.jcr.security.AccessControlEntry; import javax.jcr.security.Privilege; import javax.json.JsonObject; -import javax.json.JsonValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -54,15 +55,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.composeTest; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.mapEntry; -import static net.adamcin.oakpal.core.Fun.result0; -import static net.adamcin.oakpal.core.Fun.uncheck0; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.composeTest; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.mapEntry; +import static net.adamcin.oakpal.api.Fun.result0; +import static net.adamcin.oakpal.api.Fun.uncheck0; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; /** * ExpectAces: assert the existence or non-existence of specific access control entries after extracting a package. @@ -121,39 +122,120 @@ * definition so indicates. Otherwise the comma-separated values are treated as an opaque string. */ public final class ExpectAces implements ProgressCheckFactory { + @ProviderType + public interface JsonKeys { + String principal(); + + String principals(); + + String expectedAces(); + + String notExpectedAces(); + + String afterPackageIdRules(); + + String severity(); + + String type(); + + String privileges(); + + String path(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String principal() { + return "principal"; + } + + @Override + public String principals() { + return "principals"; + } + + @Override + public String expectedAces() { + return "expectedAces"; + } + + @Override + public String notExpectedAces() { + return "notExpectedAces"; + } + + @Override + public String afterPackageIdRules() { + return "afterPackageIdRules"; + } + + @Override + public String severity() { + return "severity"; + } + + @Override + public String type() { + return "type"; + } + + @Override + public String privileges() { + return "privileges"; + } + + @Override + public String path() { + return "path"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_PRINCIPAL = keys().principal(); + @Deprecated + public static final String CONFIG_PRINCIPALS = keys().principals(); + @Deprecated + public static final String CONFIG_EXPECTED_ACES = keys().expectedAces(); + @Deprecated + public static final String CONFIG_NOT_EXPECTED_ACES = keys().notExpectedAces(); + @Deprecated + public static final String CONFIG_AFTER_PACKAGE_ID_RULES = keys().afterPackageIdRules(); + @Deprecated + public static final String ACE_PARAM_TYPE = keys().type(); + @Deprecated + public static final String ACE_PARAM_PRIVILEGES = keys().privileges(); + @Deprecated + public static final String ACE_PARAM_PATH = keys().path(); + private static final Logger LOGGER = LoggerFactory.getLogger(ExpectAces.class); - public static final String CONFIG_PRINCIPAL = "principal"; - static final String CONFIG_PRINCIPALS = "principals"; - public static final String CONFIG_EXPECTED_ACES = "expectedAces"; - public static final String CONFIG_NOT_EXPECTED_ACES = "notExpectedAces"; - public static final String CONFIG_AFTER_PACKAGE_ID_RULES = "afterPackageIdRules"; - static final String CONFIG_SEVERITY = "severity"; - static final Violation.Severity DEFAULT_SEVERITY = Violation.Severity.MAJOR; - public static final String ACE_PARAM_TYPE = "type"; - public static final String ACE_PARAM_PRIVILEGES = "privileges"; - public static final String ACE_PARAM_PATH = "path"; + static final Severity DEFAULT_SEVERITY = Severity.MAJOR; public static final String DELIM_PARAM = ";"; public static final String DELIM_VALUE = "="; public static final String DELIM_LIST = ","; @Override public ProgressCheck newInstance(final JsonObject config) throws Exception { - final String principal = config.getString(CONFIG_PRINCIPAL, "").trim(); - final String[] principals = JavaxJson.mapArrayOfStrings(JavaxJson.arrayOrEmpty(config, CONFIG_PRINCIPALS)) + final String principal = config.getString(keys().principal(), "").trim(); + final String[] principals = JavaxJson.mapArrayOfStrings(JavaxJson.arrayOrEmpty(config, keys().principals())) .stream().map(String::trim).filter(inferTest1(String::isEmpty).negate()).toArray(String[]::new); final String[] precedingPrincipals = principal.isEmpty() ? principals : new String[]{principal}; - final List expectedAces = parseAceCriteria(config, precedingPrincipals, CONFIG_EXPECTED_ACES); - final List notExpectedAces = parseAceCriteria(config, precedingPrincipals, CONFIG_NOT_EXPECTED_ACES); - final Violation.Severity severity = Violation.Severity.valueOf( - config.getString(CONFIG_SEVERITY, DEFAULT_SEVERITY.name()).toUpperCase()); + final List expectedAces = parseAceCriteria(config, precedingPrincipals, keys().expectedAces()); + final List notExpectedAces = parseAceCriteria(config, precedingPrincipals, keys().notExpectedAces()); + final Severity severity = Severity.valueOf( + config.getString(keys().severity(), DEFAULT_SEVERITY.name()).toUpperCase()); return new Check(expectedAces, notExpectedAces, - Rule.fromJsonArray(JavaxJson.arrayOrEmpty(config, CONFIG_AFTER_PACKAGE_ID_RULES)), severity); + Rule.fromJsonArray(JavaxJson.arrayOrEmpty(config, keys().afterPackageIdRules())), severity); } static boolean isPrincipalSpec(final @NotNull String spec) { - return spec.contains(CONFIG_PRINCIPAL + "="); + return spec.contains(keys().principal() + "="); } static boolean isGeneralSpec(final @NotNull String spec) { @@ -193,29 +275,25 @@ static List parseAceCriteria(final @NotNull JsonObject config, return allCriterias; } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { final List expectedAces; final List notExpectedAces; final Map> expectedViolators = new LinkedHashMap<>(); final Map> notExpectedViolators = new LinkedHashMap<>(); final List afterPackageIdRules; - final Violation.Severity severity; + final Severity severity; Check(final @NotNull List expectedAces, final @NotNull List notExpectedAces, final @NotNull List afterPackageIdRules, - final @NotNull Violation.Severity severity) { + final @NotNull Severity severity) { + super(ExpectAces.class); this.expectedAces = expectedAces; this.notExpectedAces = notExpectedAces; this.afterPackageIdRules = afterPackageIdRules; this.severity = severity; } - @Override - public String getCheckName() { - return ExpectAces.class.getSimpleName(); - } - @Override public void startedScan() { super.startedScan(); @@ -274,15 +352,21 @@ public void afterExtract(final PackageId packageId, final Session inspectSession public void finishedScan() { for (Map.Entry> violatorsEntry : expectedViolators.entrySet()) { if (!violatorsEntry.getValue().isEmpty()) { - this.reportViolation(severity, "expected: " + violatorsEntry.getKey().toString(), - violatorsEntry.getValue().toArray(new PackageId[0])); + this.reporting(violation -> violation + .withSeverity(severity) + .withDescription("expected: {0}") + .withArgument(violatorsEntry.getKey().toString()) + .withPackages(violatorsEntry.getValue())); } } expectedViolators.clear(); for (Map.Entry> violatorsEntry : notExpectedViolators.entrySet()) { if (!violatorsEntry.getValue().isEmpty()) { - this.reportViolation(severity, "unexpected: " + violatorsEntry.getKey().toString(), - violatorsEntry.getValue().toArray(new PackageId[0])); + this.reporting(violation -> violation + .withSeverity(severity) + .withDescription("unexpected: {0}") + .withArgument(violatorsEntry.getKey().toString()) + .withPackages(violatorsEntry.getValue())); } } notExpectedViolators.clear(); @@ -415,32 +499,32 @@ static Result parse(final @NotNull String principal, final @NotNull .map(Fun.mapValue(Optional::get)) .collect(Fun.entriesToMap()); - final String effectivePrincipal = valueMap.getOrDefault(CONFIG_PRINCIPAL, principal).trim(); + final String effectivePrincipal = valueMap.getOrDefault(keys().principal(), principal).trim(); if (effectivePrincipal.isEmpty()) { return Result.failure("principal must be non-empty: " + spec); } - keys.remove(CONFIG_PRINCIPAL); + keys.remove(keys().principal()); - if (!valueMap.containsKey(ACE_PARAM_TYPE)) { - return Result.failure(ACE_PARAM_TYPE + " is a required ace parameter: " + spec); + if (!valueMap.containsKey(keys().type())) { + return Result.failure(keys().type() + " is a required ace parameter: " + spec); } - final Type type = Type.forName(valueMap.get(ACE_PARAM_TYPE)); + final Type type = Type.forName(valueMap.get(keys().type())); if (type == null) { - return Result.failure(valueMap.get(ACE_PARAM_TYPE) + " is not a valid value for parameter " + ACE_PARAM_TYPE + ": " + spec); + return Result.failure(valueMap.get(keys().type()) + " is not a valid value for parameter " + keys().type() + ": " + spec); } - keys.remove(ACE_PARAM_TYPE); + keys.remove(keys().type()); - if (!keys.contains(ACE_PARAM_PATH)) { - return Result.failure(ACE_PARAM_PATH + " parameter is required, but can be left empty to match rep:repoPolicy aces: " + spec); + if (!keys.contains(keys().path())) { + return Result.failure(keys().path() + " parameter is required, but can be left empty to match rep:repoPolicy aces: " + spec); } - final String path = Optional.ofNullable(valueMap.get(ACE_PARAM_PATH)).map(String::trim).orElse(""); + final String path = Optional.ofNullable(valueMap.get(keys().path())).map(String::trim).orElse(""); if (path.isEmpty()) { LOGGER.debug("empty path param. spec will evaluate /rep:repoPolicy : {}", spec); } - keys.remove(ACE_PARAM_PATH); + keys.remove(keys().path()); - final String[] privileges = Stream.of(Optional.ofNullable(valueMap.get(ACE_PARAM_PRIVILEGES)) + final String[] privileges = Stream.of(Optional.ofNullable(valueMap.get(keys().privileges())) .orElse("").split(DELIM_LIST)) .map(String::trim) .filter(inferTest1(String::isEmpty).negate()) @@ -449,7 +533,7 @@ static Result parse(final @NotNull String principal, final @NotNull if (privileges.length == 0) { return Result.failure("privileges must be specified with at least one element: " + spec); } - keys.remove(ACE_PARAM_PRIVILEGES); + keys.remove(keys().privileges()); RestrictionCriteria[] restrictions = keys.stream() .map(Fun.zipKeysWithValueFunc(valueMap::get)) @@ -465,9 +549,11 @@ String getSpec() { return spec; } else { final StringBuilder common = new StringBuilder() - .append(ACE_PARAM_TYPE + DELIM_VALUE).append(isAllow ? "allow" : "deny") - .append(DELIM_PARAM + ACE_PARAM_PATH + DELIM_VALUE).append(path) - .append(DELIM_PARAM + ACE_PARAM_PRIVILEGES + DELIM_VALUE).append(String.join(DELIM_LIST, privileges)); + .append(keys().type()).append(DELIM_VALUE).append(isAllow ? "allow" : "deny") + .append(DELIM_PARAM) + .append(keys().path()).append(DELIM_VALUE).append(path) + .append(DELIM_PARAM) + .append(keys().privileges()).append(DELIM_VALUE).append(String.join(DELIM_LIST, privileges)); for (RestrictionCriteria restriction : restrictions) { common.append(DELIM_PARAM).append(restriction.name); if (restriction.value != null) { diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectPaths.java b/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectPaths.java index 23ef2c18d..3a363c3bc 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectPaths.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/ExpectPaths.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,15 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.JavaxJson; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.packaging.PackageId; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -33,8 +35,8 @@ import java.util.List; import java.util.Map; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.optArray; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.optArray; /** * ExpectPaths: assert the existence or non-existence of specific repository item paths after extracting a package. @@ -52,50 +54,88 @@ * */ public final class ExpectPaths implements ProgressCheckFactory { - public static final String CONFIG_EXPECTED_PATHS = "expectedPaths"; - public static final String CONFIG_NOT_EXPECTED_PATHS = "notExpectedPaths"; - public static final String CONFIG_AFTER_PACKAGE_ID_RULES = "afterPackageIdRules"; - static final String CONFIG_SEVERITY = "severity"; - static final Violation.Severity DEFAULT_SEVERITY = Violation.Severity.MAJOR; + @ProviderType + public interface JsonKeys { + String expectedPaths(); + + String notExpectedPaths(); + + String afterPackageIdRules(); + + String severity(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String expectedPaths() { + return "expectedPaths"; + } + + @Override + public String notExpectedPaths() { + return "notExpectedPaths"; + } + + @Override + public String afterPackageIdRules() { + return "afterPackageIdRules"; + } + + @Override + public String severity() { + return "severity"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_EXPECTED_PATHS = keys().expectedPaths(); + @Deprecated + public static final String CONFIG_NOT_EXPECTED_PATHS = keys().notExpectedPaths(); + @Deprecated + public static final String CONFIG_AFTER_PACKAGE_ID_RULES = keys().afterPackageIdRules(); + @Deprecated + public static final String CONFIG_SEVERITY = keys().severity(); + static final Severity DEFAULT_SEVERITY = Severity.MAJOR; @Override public ProgressCheck newInstance(final JsonObject config) { - final List expectedPaths = optArray(config, CONFIG_EXPECTED_PATHS) + final List expectedPaths = optArray(config, keys().expectedPaths()) .map(JavaxJson::mapArrayOfStrings) .orElse(Collections.emptyList()); - final List notExpectedPaths = optArray(config, CONFIG_NOT_EXPECTED_PATHS) + final List notExpectedPaths = optArray(config, keys().notExpectedPaths()) .map(JavaxJson::mapArrayOfStrings) .orElse(Collections.emptyList()); - final List afterPackageIdRules = Rule.fromJsonArray(arrayOrEmpty(config, CONFIG_AFTER_PACKAGE_ID_RULES)); - final Violation.Severity severity = Violation.Severity.valueOf( - config.getString(CONFIG_SEVERITY, DEFAULT_SEVERITY.name()).toUpperCase()); + final List afterPackageIdRules = Rule.fromJsonArray(arrayOrEmpty(config, keys().afterPackageIdRules())); + final Severity severity = Severity.valueOf( + config.getString(keys().severity(), DEFAULT_SEVERITY.name()).toUpperCase()); return new Check(expectedPaths, notExpectedPaths, afterPackageIdRules, severity); } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { final List expectedPaths; final List notExpectedPaths; final List afterPackageIdRules; - final Violation.Severity severity; + final Severity severity; final Map> expectedViolators = new LinkedHashMap<>(); final Map> notExpectedViolators = new LinkedHashMap<>(); Check(final @NotNull List expectedPaths, final @NotNull List notExpectedPaths, final @NotNull List afterPackageIdRules, - final @NotNull Violation.Severity severity) { + final @NotNull Severity severity) { + super(ExpectPaths.class); this.expectedPaths = expectedPaths; this.notExpectedPaths = notExpectedPaths; this.afterPackageIdRules = afterPackageIdRules; this.severity = severity; } - @Override - public String getCheckName() { - return ExpectPaths.class.getSimpleName(); - } - @Override public void startedScan() { super.startedScan(); @@ -135,15 +175,21 @@ public void afterExtract(final PackageId packageId, final Session inspectSession public void finishedScan() { for (Map.Entry> violatorsEntry : expectedViolators.entrySet()) { if (!violatorsEntry.getValue().isEmpty()) { - this.reportViolation(severity, "expected path missing: " + violatorsEntry.getKey(), - violatorsEntry.getValue().toArray(new PackageId[0])); + this.reporting(violation -> violation + .withSeverity(severity) + .withDescription("expected path missing: {0}") + .withArgument(violatorsEntry.getKey()) + .withPackages(violatorsEntry.getValue())); } } expectedViolators.clear(); for (Map.Entry> violatorsEntry : notExpectedViolators.entrySet()) { if (!violatorsEntry.getValue().isEmpty()) { - this.reportViolation(severity, "unexpected path present: " + violatorsEntry.getKey(), - violatorsEntry.getValue().toArray(new PackageId[0])); + this.reporting(violation -> violation + .withSeverity(severity) + .withDescription("unexpected path present: {0}") + .withArgument(violatorsEntry.getKey()) + .withPackages(violatorsEntry.getValue())); } } notExpectedViolators.clear(); diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/FilterSets.java b/core/src/main/java/net/adamcin/oakpal/core/checks/FilterSets.java index a3f469867..4a2551f6c 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/FilterSets.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/FilterSets.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,25 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.fs.api.ImportMode; import org.apache.jackrabbit.vault.fs.api.PathFilterSet; import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.json.JsonObject; import java.util.List; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; /** * Sanity check for {@link WorkspaceFilter}. @@ -40,7 +42,7 @@ * {@code config} options: *
*
{@code importModeSeverity}
- *
(default: {@link net.adamcin.oakpal.core.Violation.Severity#MINOR}) The default {@link ImportMode} for a + *
(default: {@link Severity#MINOR}) The default {@link ImportMode} for a * filter set is {@link ImportMode#REPLACE}. FileVault also supports {@link ImportMode#UPDATE} and * {@link ImportMode#MERGE}, but it also supports forcing the import mode for an entire package via * {@link org.apache.jackrabbit.vault.fs.io.ImportOptions#setImportMode(ImportMode)}, which certain platforms use to @@ -56,39 +58,70 @@ *
*/ public final class FilterSets implements ProgressCheckFactory { - public static final String CONFIG_IMPORT_MODE_SEVERITY = "importModeSeverity"; - public static final String CONFIG_ALLOW_EMPTY_FILTER = "allowEmptyFilter"; - public static final String CONFIG_ALLOW_ROOT_FILTER = "allowRootFilter"; - public static final Violation.Severity DEFAULT_IMPORT_MODE_SEVERITY = Violation.Severity.MINOR; + @ProviderType + public interface JsonKeys { + String importModeSeverity(); + + String allowEmptyFilter(); + + String allowRootFilter(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String importModeSeverity() { + return "importModeSeverity"; + } + + @Override + public String allowEmptyFilter() { + return "allowEmptyFilter"; + } + + @Override + public String allowRootFilter() { + return "allowRootFilter"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_IMPORT_MODE_SEVERITY = keys().importModeSeverity(); + @Deprecated + public static final String CONFIG_ALLOW_EMPTY_FILTER = keys().allowEmptyFilter(); + @Deprecated + public static final String CONFIG_ALLOW_ROOT_FILTER = keys().allowRootFilter(); + + public static final Severity DEFAULT_IMPORT_MODE_SEVERITY = Severity.MINOR; @Override public ProgressCheck newInstance(final JsonObject config) { - final Violation.Severity importModeSeverity = Violation.Severity - .valueOf(config.getString(CONFIG_IMPORT_MODE_SEVERITY, DEFAULT_IMPORT_MODE_SEVERITY.name()) + final Severity importModeSeverity = Severity + .valueOf(config.getString(keys().importModeSeverity(), DEFAULT_IMPORT_MODE_SEVERITY.name()) .toUpperCase()); - final boolean allowEmptyFilter = hasNonNull(config, CONFIG_ALLOW_EMPTY_FILTER) - && config.getBoolean(CONFIG_ALLOW_EMPTY_FILTER); - final boolean allowRootFilter = hasNonNull(config, CONFIG_ALLOW_ROOT_FILTER) - && config.getBoolean(CONFIG_ALLOW_ROOT_FILTER); + final boolean allowEmptyFilter = hasNonNull(config, keys().allowEmptyFilter()) + && config.getBoolean(keys().allowEmptyFilter()); + final boolean allowRootFilter = hasNonNull(config, keys().allowRootFilter()) + && config.getBoolean(keys().allowRootFilter()); return new Check(importModeSeverity, allowEmptyFilter, allowRootFilter); } - static final class Check extends SimpleProgressCheck { - final Violation.Severity importModeSeverity; + static final class Check extends SimpleProgressCheckFactoryCheck { + final Severity importModeSeverity; final boolean allowEmptyFilter; final boolean allowRootFilter; - Check(final Violation.Severity importModeSeverity, final boolean allowEmptyFilter, final boolean allowRootFilter) { + Check(final Severity importModeSeverity, final boolean allowEmptyFilter, final boolean allowRootFilter) { + super(FilterSets.class); this.importModeSeverity = importModeSeverity; this.allowEmptyFilter = allowEmptyFilter; this.allowRootFilter = allowRootFilter; } - @Override - public String getCheckName() { - return FilterSets.class.getSimpleName(); - } - @Override public void beforeExtract(final PackageId packageId, final Session inspectSession, final PackageProperties packageProperties, final MetaInf metaInf, @@ -96,19 +129,19 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio final WorkspaceFilter filter = metaInf.getFilter(); if (filter == null || filter.getFilterSets().isEmpty()) { if (!allowEmptyFilter) { - reportViolation(Violation.Severity.MAJOR, + reportViolation(Severity.MAJOR, "empty workspace filter is not allowed", packageId); } } else { for (PathFilterSet filterSet : filter.getFilterSets()) { if (filterSet.getImportMode() != ImportMode.REPLACE) { - reportViolation(importModeSeverity, - String.format("non-default import mode %s defined for filterSet with root %s", - filterSet.getImportMode(), filterSet.getRoot()), - packageId); + reporting(violation -> violation.withSeverity(importModeSeverity) + .withDescription("non-default import mode {0} defined for filterSet with root {1}") + .withArgument(filterSet.getImportMode(), filterSet.getRoot()) + .withPackage(packageId)); } if (!allowRootFilter && "/".equals(filterSet.getRoot())) { - reportViolation(Violation.Severity.MAJOR, + reportViolation(Severity.MAJOR, "root filter sets are not allowed", packageId); } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/JcrProperties.java b/core/src/main/java/net/adamcin/oakpal/core/checks/JcrProperties.java index 8d8f8ce51..e818fa658 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/JcrProperties.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/JcrProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,19 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -30,11 +36,13 @@ import javax.jcr.nodetype.NodeTypeDefinition; import javax.json.JsonObject; import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfStrings; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfStrings; /** * A complex check for enforcing characteristics of JCR Properties of imported nodes and their descendants within the @@ -44,7 +52,7 @@ *
*
{@code scopePaths} ({@link Rule}{@code []})
*
A list of rules, with each pattern matched against an import path, and the {@code type} - * ({@link net.adamcin.oakpal.core.checks.Rule.RuleType}) of the last matching rule determines whether the matched + * ({@link Rule.RuleType}) of the last matching rule determines whether the matched * path is in scope for checking the properties on the node and its descendants.
*
{@code denyNodeTypes}
*
A list of nodeType strings, which specify primary or mixin types that should be disallowed. If matched, the path is @@ -91,42 +99,110 @@ * */ public final class JcrProperties implements ProgressCheckFactory { - public static final String CONFIG_SCOPE_PATHS = "scopePaths"; - public static final String CONFIG_DENY_NODE_TYPES = "denyNodeTypes"; - public static final String CONFIG_SCOPE_NODE_TYPES = "scopeNodeTypes"; - public static final String CONFIG_PROPERTIES = "properties"; + @ProviderType + public interface JsonKeys { + String scopePaths(); + + String denyNodeTypes(); + + String scopeNodeTypes(); + + String properties(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String scopePaths() { + return "scopePaths"; + } + + @Override + public String denyNodeTypes() { + return "denyNodeTypes"; + } + + @Override + public String scopeNodeTypes() { + return "scopeNodeTypes"; + } + + @Override + public String properties() { + return "properties"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_SCOPE_PATHS = keys().scopePaths(); + @Deprecated + public static final String CONFIG_DENY_NODE_TYPES = keys().denyNodeTypes(); + @Deprecated + public static final String CONFIG_SCOPE_NODE_TYPES = keys().scopeNodeTypes(); + @Deprecated + public static final String CONFIG_PROPERTIES = keys().properties(); @Override public ProgressCheck newInstance(final JsonObject config) { - List pathScope = Rule.fromJsonArray(arrayOrEmpty(config, CONFIG_SCOPE_PATHS)); + List pathScope = Rule.fromJsonArray(arrayOrEmpty(config, keys().scopePaths())); - List denyNodeTypes = mapArrayOfStrings(arrayOrEmpty(config, CONFIG_DENY_NODE_TYPES)); - List nodeTypeScope = mapArrayOfStrings(arrayOrEmpty(config, CONFIG_SCOPE_NODE_TYPES)); + List denyNodeTypes = mapArrayOfStrings(arrayOrEmpty(config, keys().denyNodeTypes())); + List nodeTypeScope = mapArrayOfStrings(arrayOrEmpty(config, keys().scopeNodeTypes())); + final ResourceBundleHolder resourceBundleHolder = new ResourceBundleHolder(); List propertyChecks = JcrPropertyConstraints - .fromJsonArray(arrayOrEmpty(config, CONFIG_PROPERTIES)); - return new Check(pathScope, denyNodeTypes, nodeTypeScope, propertyChecks); + .fromJsonArray(resourceBundleHolder::getResourceBundle, arrayOrEmpty(config, keys().properties())); + return new Check(pathScope, denyNodeTypes, nodeTypeScope, propertyChecks, resourceBundleHolder); + } + + static final class ResourceBundleHolder { + private ResourceBundle resourceBundle; + + public ResourceBundle getResourceBundle() { + if (resourceBundle == null) { + resourceBundle = ResourceBundle.getBundle(JcrProperties.class.getName()); + } + return resourceBundle; + } + + public void setResourceBundle(final ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { private final List scopePaths; private final List denyNodeTypes; private final List scopeNodeTypes; private final List propertyChecks; + private final ResourceBundleHolder resourceBundleHolder; private WorkspaceFilter wspFilter; Check(final List scopePaths, final List denyNodeTypes, final List scopeNodeTypes, - final List propertyChecks) { + final List propertyChecks, + final ResourceBundleHolder resourceBundleHolder) { + super(JcrProperties.class); this.scopePaths = scopePaths; this.denyNodeTypes = denyNodeTypes; this.scopeNodeTypes = scopeNodeTypes; this.propertyChecks = propertyChecks; + this.resourceBundleHolder = resourceBundleHolder; + } + + @Override + public void setResourceBundle(final ResourceBundle resourceBundle) { + super.setResourceBundle(resourceBundle); + resourceBundleHolder.setResourceBundle(resourceBundle); } @Override - public String getCheckName() { - return JcrProperties.class.getSimpleName(); + protected @Nullable ResourceBundle getResourceBundle() throws MissingResourceException { + return super.getResourceBundle(); } @Override @@ -137,7 +213,8 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio } @Override - public void importedPath(final PackageId packageId, final String path, final Node node) throws RepositoryException { + public void importedPath(final PackageId packageId, final String path, final Node node, + final PathAction action) throws RepositoryException { if (!wspFilter.contains(path)) { return; } @@ -151,14 +228,19 @@ public void importedPath(final PackageId packageId, final String path, final Nod void checkNode(final PackageId packageId, final Node node) throws RepositoryException { for (String denyNodeType : denyNodeTypes) { if (node.isNodeType(denyNodeType)) { - majorViolation(String.format("%s (t: %s, m: %s): denied node type %s", + final Object[] arguments = new Object[]{ node.getPath(), node.getPrimaryNodeType().getName(), Stream.of(node.getMixinNodeTypes()) .map(NodeTypeDefinition::getName) .collect(Collectors.toList()), - denyNodeType), - packageId); + denyNodeType + }; + reporting(violation -> violation + .withSeverity(Severity.MAJOR) + .withDescription("{0} (t: {1}, m: {2}): denied node type {3}") + .withArgument(arguments) + .withPackage(packageId)); return; } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/JcrPropertyConstraints.java b/core/src/main/java/net/adamcin/oakpal/core/checks/JcrPropertyConstraints.java index 201b32853..edb451d99 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/JcrPropertyConstraints.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/JcrPropertyConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.SimpleViolation; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.Node; import javax.jcr.Property; @@ -28,15 +32,19 @@ import javax.jcr.nodetype.NodeTypeDefinition; import javax.json.JsonArray; import javax.json.JsonObject; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.ResourceBundle; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfObjects; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfObjects; /** * Encapsulation of constraints on a JCR property for the {@link JcrProperties} check. @@ -58,39 +66,111 @@ *
A list of patterns to match against string values of this property. All rules are applied in sequence to each * value of the property. If the type of the last rule to match any value is DENY, a violation is reported.
*
{@code severity}
- *
(default: {@link net.adamcin.oakpal.core.Violation.Severity#MAJOR}) specify the severity if a violation is + *
(default: {@link Severity#MAJOR}) specify the severity if a violation is * reported by this set of constraints.
*
*/ public final class JcrPropertyConstraints { - public static final String CONFIG_NAME = "name"; - public static final String CONFIG_DENY_IF_ABSENT = "denyIfAbsent"; - public static final String CONFIG_DENY_IF_PRESENT = "denyIfPresent"; - public static final String CONFIG_DENY_IF_MULTIVALUED = "denyIfMultivalued"; - public static final String CONFIG_REQUIRE_TYPE = "requireType"; - public static final String CONFIG_VALUE_RULES = "valueRules"; - public static final String CONFIG_SEVERITY = "severity"; - public static final Violation.Severity DEFAULT_SEVERITY = Violation.Severity.MAJOR; - - public static JcrPropertyConstraints fromJson(final JsonObject checkJson) { - final String name = checkJson.getString(CONFIG_NAME); - final boolean denyIfAbsent = hasNonNull(checkJson, CONFIG_DENY_IF_ABSENT) - && checkJson.getBoolean(CONFIG_DENY_IF_ABSENT); - final boolean denyIfPresent = hasNonNull(checkJson, CONFIG_DENY_IF_PRESENT) - && checkJson.getBoolean(CONFIG_DENY_IF_PRESENT); - final boolean denyIfMultivalued = hasNonNull(checkJson, CONFIG_DENY_IF_MULTIVALUED) - && checkJson.getBoolean(CONFIG_DENY_IF_MULTIVALUED); - final String requireType = checkJson.getString(CONFIG_REQUIRE_TYPE, null); - final List valueRules = Rule.fromJsonArray(arrayOrEmpty(checkJson, CONFIG_VALUE_RULES)); - final Violation.Severity severity = Violation.Severity - .valueOf(checkJson.getString(CONFIG_SEVERITY, DEFAULT_SEVERITY.name()).toUpperCase()); - - return new JcrPropertyConstraints(name, denyIfAbsent, denyIfPresent, denyIfMultivalued, requireType, valueRules, - severity); + @ProviderType + public interface JsonKeys { + String name(); + + String denyIfAbsent(); + + String denyIfPresent(); + + String denyIfMultivalued(); + + String requireType(); + + String valueRules(); + + String severity(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String name() { + return "name"; + } + + @Override + public String denyIfAbsent() { + return "denyIfAbsent"; + } + + @Override + public String denyIfPresent() { + return "denyIfPresent"; + } + + @Override + public String denyIfMultivalued() { + return "denyIfMultivalued"; + } + + @Override + public String requireType() { + return "requireType"; + } + + @Override + public String valueRules() { + return "valueRules"; + } + + @Override + public String severity() { + return "severity"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_NAME = keys().name(); + @Deprecated + public static final String CONFIG_DENY_IF_ABSENT = keys().denyIfAbsent(); + @Deprecated + public static final String CONFIG_DENY_IF_PRESENT = keys().denyIfPresent(); + @Deprecated + public static final String CONFIG_DENY_IF_MULTIVALUED = keys().denyIfMultivalued(); + @Deprecated + public static final String CONFIG_REQUIRE_TYPE = keys().requireType(); + @Deprecated + public static final String CONFIG_VALUE_RULES = keys().valueRules(); + @Deprecated + public static final String CONFIG_SEVERITY = keys().severity(); + + public static final Severity DEFAULT_SEVERITY = Severity.MAJOR; + + public static Function + fromJson(@NotNull final Supplier resourceBundleSupplier) { + return checkJson -> { + final String name = checkJson.getString(keys().name()); + final boolean denyIfAbsent = hasNonNull(checkJson, keys().denyIfAbsent()) + && checkJson.getBoolean(keys().denyIfAbsent()); + final boolean denyIfPresent = hasNonNull(checkJson, keys().denyIfPresent()) + && checkJson.getBoolean(keys().denyIfPresent()); + final boolean denyIfMultivalued = hasNonNull(checkJson, keys().denyIfMultivalued()) + && checkJson.getBoolean(keys().denyIfMultivalued()); + final String requireType = checkJson.getString(keys().requireType(), null); + final List valueRules = Rule.fromJsonArray(arrayOrEmpty(checkJson, keys().valueRules())); + final Severity severity = Severity + .valueOf(checkJson.getString(keys().severity(), DEFAULT_SEVERITY.name()).toUpperCase()); + + return new JcrPropertyConstraints(name, denyIfAbsent, denyIfPresent, denyIfMultivalued, requireType, valueRules, + severity, resourceBundleSupplier); + }; } - public static List fromJsonArray(final JsonArray rulesArray) { - return mapArrayOfObjects(rulesArray, JcrPropertyConstraints::fromJson); + public static List fromJsonArray( + @NotNull final Supplier resourceBundleSupplier, + final JsonArray rulesArray) { + return mapArrayOfObjects(rulesArray, fromJson(resourceBundleSupplier)); } private final String name; @@ -99,7 +179,8 @@ public static List fromJsonArray(final JsonArray rulesAr private final boolean denyIfMultivalued; private final String requireType; private final List valueRules; - private final Violation.Severity severity; + private final Severity severity; + private final Supplier resourceBundleSupplier; public JcrPropertyConstraints(final String name, final boolean denyIfAbsent, @@ -107,7 +188,8 @@ public JcrPropertyConstraints(final String name, final boolean denyIfMultivalued, final String requireType, final List valueRules, - final Violation.Severity severity) { + final Severity severity, + final Supplier resourceBundleSupplier) { this.name = name; this.denyIfAbsent = denyIfAbsent; this.denyIfPresent = denyIfPresent; @@ -115,6 +197,7 @@ public JcrPropertyConstraints(final String name, this.requireType = requireType; this.valueRules = valueRules; this.severity = severity; + this.resourceBundleSupplier = resourceBundleSupplier; } public String getName() { @@ -141,14 +224,24 @@ public List getValueRules() { return valueRules; } - public Violation.Severity getSeverity() { + public Severity getSeverity() { return severity; } + @NotNull + String getString(@NotNull final String key) { + final ResourceBundle resourceBundle = resourceBundleSupplier.get(); + if (resourceBundle.containsKey(key)) { + return resourceBundle.getString(key); + } else { + return key; + } + } + Violation constructViolation(final PackageId packageId, final Node node, final String reason) throws RepositoryException { return new SimpleViolation(getSeverity(), - String.format("%s (t: %s, m: %s): %s -> %s", + MessageFormat.format("{0} (t: {1}, m: {2}): {3} -> {4}", node.getPath(), node.getPrimaryNodeType().getName(), Stream.of(node.getMixinNodeTypes()) @@ -162,23 +255,23 @@ Violation constructViolation(final PackageId packageId, final Node node, final S Optional evaluate(final PackageId packageId, final Node node) throws RepositoryException { if (!node.hasProperty(getName())) { if (isDenyIfAbsent()) { - return Optional.of(constructViolation(packageId, node, "property absent")); + return Optional.of(constructViolation(packageId, node, getString("property absent"))); } } else { if (isDenyIfPresent()) { - return Optional.of(constructViolation(packageId, node, "property present")); + return Optional.of(constructViolation(packageId, node, getString("property present"))); } Property property = node.getProperty(getName()); if (isDenyIfMultivalued() && property.isMultiple()) { - return Optional.of(constructViolation(packageId, node, "property is multivalued")); + return Optional.of(constructViolation(packageId, node, getString("property is multivalued"))); } if (getRequireType() != null && !getRequireType().isEmpty() && !getRequireType().equals(PropertyType.nameFromValue(property.getType()))) { return Optional.of(constructViolation(packageId, node, - String.format("required type mismatch: %s != %s", + MessageFormat.format(getString("required type mismatch: {0} != {1}"), PropertyType.nameFromValue(property.getType()), getRequireType()))); } @@ -195,7 +288,7 @@ Optional evaluate(final PackageId packageId, final Node node) throws final Rule lastMatch = Rule.lastMatch(getValueRules(), value); if (lastMatch.isDeny()) { return Optional.of(constructViolation(packageId, node, - String.format("value %s denied by pattern %s", + MessageFormat.format(getString("value {0} denied by pattern {1}"), value, lastMatch.getPattern().pattern()))); } } @@ -203,5 +296,4 @@ Optional evaluate(final PackageId packageId, final Node node) throws return Optional.empty(); } - } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/Overlaps.java b/core/src/main/java/net/adamcin/oakpal/core/checks/Overlaps.java index 674b40d41..ecc64b434 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/Overlaps.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/Overlaps.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,17 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -35,13 +38,13 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; /** * The {@code overlaps} check keeps track of installed package workspace filters, and checks every affected path going * forward against previous workspace filters for overlap, using {@link WorkspaceFilter#contains(String)}. Overlapping - * deletions are reported as {@link net.adamcin.oakpal.core.Violation.Severity#MAJOR}, whereas other affected paths are - * reported as {@link net.adamcin.oakpal.core.Violation.Severity#MINOR}. + * deletions are reported as {@link Severity#MAJOR}, whereas other affected paths are + * reported as {@link Severity#MINOR}. *

* This check is sequence-dependent, in that changing the sequence of packages in the scan may result in a different * outcome. It is recommended to test multiple sequences if the actual process for package deployment is undefined or @@ -56,31 +59,45 @@ * */ public final class Overlaps implements ProgressCheckFactory { - public static final String CONFIG_REPORT_ALL_OVERLAPS = "reportAllOverlaps"; + @ProviderType + public interface JsonKeys { + String reportAllOverlaps(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String reportAllOverlaps() { + return "reportAllOverlaps"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_REPORT_ALL_OVERLAPS = keys().reportAllOverlaps(); @Override public ProgressCheck newInstance(final JsonObject config) { - final boolean reportAllOverlaps = hasNonNull(config, CONFIG_REPORT_ALL_OVERLAPS) - && config.getBoolean(CONFIG_REPORT_ALL_OVERLAPS); + final boolean reportAllOverlaps = hasNonNull(config, keys().reportAllOverlaps()) + && config.getBoolean(keys().reportAllOverlaps()); return new Check(reportAllOverlaps); } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { final Map filters = new HashMap<>(); - final Map reported = new HashMap<>(); + final Map reported = new HashMap<>(); final boolean reportAllOverlaps; Check(final boolean reportAllOverlaps) { + super(Overlaps.class); this.reportAllOverlaps = reportAllOverlaps; } - @Override - public String getCheckName() { - return Overlaps.class.getSimpleName(); - } - @Override public void startedScan() { super.startedScan(); @@ -100,7 +117,7 @@ Predicate> overlaps(final String p } void findOverlaps(final PackageId currentPackageId, final String path, - final Violation.Severity severity) { + final Severity severity) { // fast escape! no need to belabor the point. if (!reportAllOverlaps && reported.containsKey(currentPackageId) @@ -120,18 +137,20 @@ void findOverlaps(final PackageId currentPackageId, final String path, if (!reportAllOverlaps) { reported.put(currentPackageId, severity); } - reportViolation(severity, - String.format("affected path %s overlaps %s", path, overlapping), - currentPackageId); + reporting(violation -> violation.withSeverity(severity) + .withPackage(currentPackageId) + .withDescription("affected path {0} overlaps {1}") + .withArgument(path, overlapping)); } } @Override - public void importedPath(final PackageId packageId, final String path, final Node node) + public void importedPath(final PackageId packageId, final String path, final Node node, + final PathAction action) throws RepositoryException { // don't worry about nodes outside of our own scope. if (filters.get(packageId).contains(path)) { - findOverlaps(packageId, path, Violation.Severity.MINOR); + findOverlaps(packageId, path, Severity.MINOR); } } @@ -139,7 +158,7 @@ public void importedPath(final PackageId packageId, final String path, final Nod public void deletedPath(final PackageId packageId, final String path, final Session inspectSession) throws RepositoryException { // any deletions should be considered major overlap violations. - findOverlaps(packageId, path, Violation.Severity.MAJOR); + findOverlaps(packageId, path, Severity.MAJOR); } } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/Paths.java b/core/src/main/java/net/adamcin/oakpal/core/checks/Paths.java index f690e4fc9..60109984b 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/Paths.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/Paths.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,15 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -28,8 +32,8 @@ import javax.json.JsonObject; import java.util.List; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; /** * Deny path imports/deletes by regular expression. @@ -65,49 +69,83 @@ * */ public final class Paths implements ProgressCheckFactory { - public static final String CONFIG_RULES = "rules"; - public static final String CONFIG_DENY_ALL_DELETES = "denyAllDeletes"; - public static final String CONFIG_SEVERITY = "severity"; - public static final Violation.Severity DEFAULT_SEVERITY = Violation.Severity.MAJOR; + @ProviderType + public interface JsonKeys { + String rules(); + + String denyAllDeletes(); + + String severity(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String rules() { + return "rules"; + } + + @Override + public String denyAllDeletes() { + return "denyAllDeletes"; + } + + @Override + public String severity() { + return "severity"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_RULES = keys().rules(); + @Deprecated + public static final String CONFIG_DENY_ALL_DELETES = keys().denyAllDeletes(); + @Deprecated + public static final String CONFIG_SEVERITY = keys().severity(); + + public static final Severity DEFAULT_SEVERITY = Severity.MAJOR; @Override public ProgressCheck newInstance(final JsonObject config) { - List rules = Rule.fromJsonArray(arrayOrEmpty(config, CONFIG_RULES)); + List rules = Rule.fromJsonArray(arrayOrEmpty(config, keys().rules())); - final boolean denyAllDeletes = hasNonNull(config, CONFIG_DENY_ALL_DELETES) - && config.getBoolean(CONFIG_DENY_ALL_DELETES); + final boolean denyAllDeletes = hasNonNull(config, keys().denyAllDeletes()) + && config.getBoolean(keys().denyAllDeletes()); - final Violation.Severity severity = Violation.Severity.valueOf( - config.getString(CONFIG_SEVERITY, DEFAULT_SEVERITY.name()).toUpperCase()); + final Severity severity = Severity.valueOf( + config.getString(keys().severity(), DEFAULT_SEVERITY.name()).toUpperCase()); return new Check(rules, denyAllDeletes, severity); } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { private final List rules; private final boolean denyAllDeletes; - private final Violation.Severity severity; + private final Severity severity; - Check(final List rules, final boolean denyAllDeletes, final Violation.Severity severity) { + Check(final List rules, final boolean denyAllDeletes, final Severity severity) { + super(Paths.class); this.rules = rules; this.denyAllDeletes = denyAllDeletes; this.severity = severity; } @Override - public String getCheckName() { - return Paths.class.getSimpleName(); - } - - @Override - public void importedPath(final PackageId packageId, final String path, final Node node) + public void importedPath(final PackageId packageId, final String path, final Node node, + final PathAction action) throws RepositoryException { Rule lastMatch = Rule.lastMatch(rules, path); if (lastMatch.isDeny()) { - reportViolation(severity, - String.format("imported path %s matches deny pattern %s", path, - lastMatch.getPattern().pattern()), packageId); + reporting(violation -> violation + .withSeverity(severity) + .withPackage(packageId) + .withDescription("imported path {0} matches deny pattern {1}") + .withArgument(path, lastMatch.getPattern().pattern())); } } @@ -115,14 +153,19 @@ public void importedPath(final PackageId packageId, final String path, final Nod public void deletedPath(final PackageId packageId, final String path, final Session inspectSession) throws RepositoryException { if (this.denyAllDeletes) { - reportViolation(severity, - String.format("deleted path %s. All deletions are denied.", path), packageId); + reporting(violation -> violation + .withSeverity(severity) + .withPackage(packageId) + .withDescription("deleted path {0}. All deletions are denied.") + .withArgument(path)); } else { final Rule lastMatch = Rule.lastMatch(rules, path); if (lastMatch.isDeny()) { - reportViolation(severity, - String.format("deleted path %s matches deny rule %s", path, - lastMatch.getPattern().pattern()), packageId); + reporting(violation -> violation + .withSeverity(severity) + .withPackage(packageId) + .withDescription("deleted path {0} matches deny rule {1}") + .withArgument(path, lastMatch.getPattern().pattern())); } } } diff --git a/core/src/main/java/net/adamcin/oakpal/core/checks/Subpackages.java b/core/src/main/java/net/adamcin/oakpal/core/checks/Subpackages.java index 0849c64b4..d3d4905b1 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/checks/Subpackages.java +++ b/core/src/main/java/net/adamcin/oakpal/core/checks/Subpackages.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,20 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.ProgressCheckFactory; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheckFactoryCheck; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import javax.json.JsonObject; import java.util.List; -import static net.adamcin.oakpal.core.JavaxJson.arrayOrEmpty; -import static net.adamcin.oakpal.core.JavaxJson.hasNonNull; +import static net.adamcin.oakpal.api.JavaxJson.arrayOrEmpty; +import static net.adamcin.oakpal.api.JavaxJson.hasNonNull; /** * Check for subpackage inclusion. @@ -59,49 +62,72 @@ * */ public final class Subpackages implements ProgressCheckFactory { - public static final String CONFIG_RULES = "rules"; - public static final String CONFIG_DENY_ALL = "denyAll"; + @ProviderType + public interface JsonKeys { + String rules(); + + String denyAll(); + } + + private static final JsonKeys KEYS = new JsonKeys() { + @Override + public String rules() { + return "rules"; + } + + @Override + public String denyAll() { + return "denyAll"; + } + }; + + @NotNull + public static JsonKeys keys() { + return KEYS; + } + + @Deprecated + public static final String CONFIG_RULES = keys().rules(); + @Deprecated + public static final String CONFIG_DENY_ALL = keys().denyAll(); @Override public ProgressCheck newInstance(final JsonObject config) { - List rules = Rule.fromJsonArray(arrayOrEmpty(config, CONFIG_RULES)); + List rules = Rule.fromJsonArray(arrayOrEmpty(config, keys().rules())); - final boolean denyAll = hasNonNull(config, CONFIG_DENY_ALL) && config.getBoolean(CONFIG_DENY_ALL); + final boolean denyAll = hasNonNull(config, keys().denyAll()) && config.getBoolean(keys().denyAll()); return new Check(rules, denyAll); } - static final class Check extends SimpleProgressCheck { + static final class Check extends SimpleProgressCheckFactoryCheck { private final List rules; private final boolean denyAll; Check(final List rules, final boolean denyAll) { + super(Subpackages.class); this.rules = rules; this.denyAll = denyAll; } - @Override - public String getCheckName() { - return Subpackages.class.getSimpleName(); - } - @Override public void identifySubpackage(final PackageId packageId, final PackageId parentId) { if (denyAll) { - reportViolation(Violation.Severity.MAJOR, - String.format("subpackage %s included by %s. no subpackages are allowed.", - packageId, parentId), packageId); + reporting(violation -> violation + .withSeverity(Severity.MAJOR) + .withPackage(packageId) + .withDescription("subpackage {0} included by {1}. no subpackages are allowed.") + .withArgument(packageId, parentId)); } else { final Rule lastMatch = Rule.lastMatch(rules, packageId.toString()); if (lastMatch.isDeny()) { - reportViolation(Violation.Severity.MAJOR, - String.format("subpackage %s included by %s matches deny pattern %s", - packageId.toString(), parentId.toString(), - lastMatch.getPattern().pattern()), packageId); + reporting(violation -> violation + .withSeverity(Severity.MAJOR) + .withPackage(packageId) + .withDescription("subpackage {0} included by {1} matches deny pattern {2}") + .withArgument(packageId, parentId, lastMatch.getPattern().pattern())); } } } } - - } diff --git a/core/src/main/java/net/adamcin/oakpal/core/opear/AdhocOpear.java b/core/src/main/java/net/adamcin/oakpal/core/opear/AdhocOpear.java new file mode 100644 index 000000000..2da5480c9 --- /dev/null +++ b/core/src/main/java/net/adamcin/oakpal/core/opear/AdhocOpear.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core.opear; + +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.core.OakpalPlan; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Optional; +import java.util.function.Function; + +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.result1; + +/** + * Simpler opear implementation for CLI and other runtime-generated contexts. + */ +public final class AdhocOpear implements Opear { + private final URL planFileUrl; + private final URL baseUrl; + + public AdhocOpear(final @NotNull URL planFileUrl, final @NotNull URL baseUrl) { + this.planFileUrl = planFileUrl; + this.baseUrl = baseUrl; + } + + @Override + public URL getDefaultPlan() { + return planFileUrl; + } + + @Override + public Result getSpecificPlan(final @NotNull String planName) { + return Result.success(planFileUrl); + } + + @Override + public ClassLoader getPlanClassLoader(final @NotNull ClassLoader parent) { + return new URLClassLoader(new URL[]{baseUrl}, parent); + } + + public static Result fromPlanFile(final @NotNull File planFile, final @Nullable File baseDir) { + final Function> fnFileUrl = compose(File::toURI, result1(URI::toURL)); + final Result planFileUrlResult = fnFileUrl.apply(planFile); + return planFileUrlResult + .flatMap(planFileUrl -> fnFileUrl.apply(Optional.ofNullable(baseDir).orElse(planFile.getParentFile())) + .flatMap(baseUrl -> + OakpalPlan.fromJson(planFileUrl).map(plan -> + new AdhocOpear(planFileUrl, baseUrl)))); + } +} diff --git a/core/src/main/java/net/adamcin/oakpal/core/Opear.java b/core/src/main/java/net/adamcin/oakpal/core/opear/Opear.java similarity index 64% rename from core/src/main/java/net/adamcin/oakpal/core/Opear.java rename to core/src/main/java/net/adamcin/oakpal/core/opear/Opear.java index 9fad11cfc..ce0eea0ad 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/Opear.java +++ b/core/src/main/java/net/adamcin/oakpal/core/opear/Opear.java @@ -1,12 +1,32 @@ -package net.adamcin.oakpal.core; +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core.opear; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.core.OakpalPlan; import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; import java.net.URL; /** * OPEAR stands for "OakPal Encapsulated ARchive". */ +@ProviderType public interface Opear { String MF_BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName"; diff --git a/core/src/main/java/net/adamcin/oakpal/core/OpearFile.java b/core/src/main/java/net/adamcin/oakpal/core/opear/OpearFile.java similarity index 93% rename from core/src/main/java/net/adamcin/oakpal/core/OpearFile.java rename to core/src/main/java/net/adamcin/oakpal/core/opear/OpearFile.java index fcb03ece0..ed03b5b45 100644 --- a/core/src/main/java/net/adamcin/oakpal/core/OpearFile.java +++ b/core/src/main/java/net/adamcin/oakpal/core/opear/OpearFile.java @@ -1,5 +1,24 @@ -package net.adamcin.oakpal.core; +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core.opear; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.core.OakpalPlan; +import net.adamcin.oakpal.core.Util; import org.apache.jackrabbit.oak.commons.FileIOUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,8 +45,8 @@ import static java.lang.String.format; import static java.util.Optional.ofNullable; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.result1; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.result1; /** * The default implemenation of {@link Opear}. This is backed by an extracted JAR directory, including @@ -256,6 +275,4 @@ static Result cacheJar(final @NotNull JarFile jarFile, final @NotNull File .orElseGet(() -> Result.failure("failed to cache jarFile" + jarFile.getName()))) .orElse(Result.success(cacheDir)); } - - } diff --git a/core/src/main/resources/OAKPAL-INF/checklists/basic.json b/core/src/main/resources/OAKPAL-INF/checklists/basic.json index 1a6a76f1b..3aa3177eb 100644 --- a/core/src/main/resources/OAKPAL-INF/checklists/basic.json +++ b/core/src/main/resources/OAKPAL-INF/checklists/basic.json @@ -30,6 +30,10 @@ "name": "overlaps", "impl": "net.adamcin.oakpal.core.checks.Overlaps" }, + { + "name": "composite-store-alignment", + "impl": "net.adamcin.oakpal.core.checks.CompositeStoreAlignment" + }, { "name": "echo", "impl": "net.adamcin.oakpal.core.checks.Echo", diff --git a/core/src/main/resources/net/adamcin/oakpal/core/DefaultErrorListener.properties b/core/src/main/resources/net/adamcin/oakpal/core/DefaultErrorListener.properties new file mode 100644 index 000000000..82e04a2c8 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/DefaultErrorListener.properties @@ -0,0 +1,26 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +NodeType\ registration\ error\ ({0})\:\ {1}\ "{2}"=NodeType registration error ({0}): {1} "{2}" +JCR\ namespace\ registration\ error\ ({0}\={1})\:\ {2}\ "{3}"=JCR namespace registration error ({0}={1}): {2} "{3}" +JCR\ privilege\ registration\ error\ ({0})\:\ {1}\ "{2}"=JCR privilege registration error ({0}): {1} "{2}" +Forced\ root\ creation\ error\ ({0})\:\ {1}\ "{2}"=Forced root creation error ({0}): {1} "{2}" +Listener\ error\ ({0})\:\ {1}\ "{2}"=Listener error ({0}): {1} "{2}" +Package\ error\:\ {0}\ "{1}"=Package error: {0} "{1}" +{0}\ -\ Importer\ error\:\ {1}\ "{2}"={0} - Importer error: {1} "{2}" +{0}\ -\ Listener\ error\:\ {1}\ "{2}"={0} - Listener error: {1} "{2}" +InstallHook\ error\:\ {0}\ "{1}"=InstallHook error: {0} "{1}" +Policy\ prohibits\ the\ use\ of\ InstallHooks\ in\ packages=Policy prohibits the use of InstallHooks in packages \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/AcHandling.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/AcHandling.properties new file mode 100644 index 000000000..c66b970ee --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/AcHandling.properties @@ -0,0 +1,18 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +acHandling\ mode\ {0}\ is\ forbidden.\ acHandling\ values\ in\ allowedModes\ are\ {1}=acHandling mode {0} is forbidden. acHandling values in allowedModes are {1} +acHandling\ mode\ {0}\ is\ forbidden.\ allowed\ acHandling\ values\ in\ levelSet\:{1}\ are\ {2}=acHandling mode {0} is forbidden. allowed acHandling values in levelSet:{1} are {2} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.properties new file mode 100644 index 000000000..cea766efb --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/CompositeStoreAlignment.properties @@ -0,0 +1,19 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package\ content\ not\ aligned\ to\ a\ single\ composite\ store\ mount\ ({0})=package content not aligned to a single composite store mount ({0}) +recursive\ package\ installation\ not\ aligned\ to\ a\ single\ composite\ store\ mount\ ({0})=recursive package installation not aligned to a single composite store mount ({0}) +and=and \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectAces.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectAces.properties new file mode 100644 index 000000000..ecd1918a7 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectAces.properties @@ -0,0 +1,18 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +expected\:\ {0}=expected: {0} +unexpected\:\ {0}=unexpected: {0} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectPaths.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectPaths.properties new file mode 100644 index 000000000..3c5ada428 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/ExpectPaths.properties @@ -0,0 +1,18 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +expected\ path\ missing\:\ {0}=expected path missing: {0} +unexpected\ path\ present\:\ {0}=unexpected path present: {0} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/FilterSets.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/FilterSets.properties new file mode 100644 index 000000000..bca46d509 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/FilterSets.properties @@ -0,0 +1,19 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +empty\ workspace\ filter\ is\ not\ allowed=empty workspace filter is not allowed +non-default\ import\ mode\ {0}\ defined\ for\ filterSet\ with\ root\ {1}=non-default import mode {0} defined for filterSet with root {1} +root\ filter\ sets\ are\ not\ allowed=root filter sets are not allowed \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/JcrProperties.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/JcrProperties.properties new file mode 100644 index 000000000..b70498888 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/JcrProperties.properties @@ -0,0 +1,22 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{0}\ (t\:\ {1},\ m\:\ {2})\:\ denied\ node\ type\ {3}={0} (t: {1}, m: {2}): denied node type {3} +property\ absent=property absent +property\ present=property present +property\ is\ multivalued=property is multivalued +required\ type\ mismatch\:\ {0}\ !\=\ {1}=required type mismatch: {0} != {1} +value\ {0}\ denied\ by\ pattern\ {1}=value {0} denied by pattern {1} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/Overlaps.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/Overlaps.properties new file mode 100644 index 000000000..70e936115 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/Overlaps.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +affected\ path\ {0}\ overlaps\ {1}=affected path {0} overlaps {1} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths.properties new file mode 100644 index 000000000..51fe443e6 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths.properties @@ -0,0 +1,19 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +imported\ path\ {0}\ matches\ deny\ pattern\ {1}=imported path {0} matches deny pattern {1} +deleted\ path\ {0}.\ All\ deletions\ are\ denied.=deleted path {0}. All deletions are denied. +deleted\ path\ {0}\ matches\ deny\ rule\ {1}=deleted path {0} matches deny rule {1} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths_de.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths_de.properties new file mode 100644 index 000000000..9f512caab --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/Paths_de.properties @@ -0,0 +1,19 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +imported\ path\ {0}\ matches\ deny\ pattern\ {1}=importierte Pfad {0} stimmt mit dem deny Muster \u00fcberein {1} +deleted\ path\ {0}.\ All\ deletions\ are\ denied.=gel\u00f6schte Pfad {0}. Alle L\u00f6chungen werden verweigert. +deleted\ path\ {0}\ matches\ deny\ rule\ {1}=gel\u00f6schte Pfad {0} stimmt mit dem deny Muster \u00fcberein {1} \ No newline at end of file diff --git a/core/src/main/resources/net/adamcin/oakpal/core/checks/Subpackages.properties b/core/src/main/resources/net/adamcin/oakpal/core/checks/Subpackages.properties new file mode 100644 index 000000000..ff66b1a04 --- /dev/null +++ b/core/src/main/resources/net/adamcin/oakpal/core/checks/Subpackages.properties @@ -0,0 +1,18 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +subpackage\ {0}\ included\ by\ {1}.\ no\ subpackages\ are\ allowed.=subpackage {0} included by {1}. no subpackages are allowed. +subpackage\ {0}\ included\ by\ {1}\ matches\ deny\ pattern\ {2}=subpackage {0} included by {1} matches deny pattern {2} \ No newline at end of file diff --git a/core/src/site/xdoc/the-basic-checklist.xml.vm b/core/src/site/xdoc/the-basic-checklist.xml.vm index 21395ba00..194554e48 100644 --- a/core/src/site/xdoc/the-basic-checklist.xml.vm +++ b/core/src/site/xdoc/the-basic-checklist.xml.vm @@ -45,6 +45,10 @@ "name": "overlaps", "impl": "net.adamcin.oakpal.core.checks.Overlaps" }, + { + "name": "composite-store-alignment", + "impl": "net.adamcin.oakpal.core.checks.CompositeStoreAlignment" + }, { "name": "echo", "impl": "net.adamcin.oakpal.core.checks.Echo", @@ -83,6 +87,8 @@

see net.adamcin.oakpal.core.checks.FilterSets
overlaps (net.adamcin.oakpal.core/basic/overlaps)
see net.adamcin.oakpal.core.checks.Overlaps
+
composite-store-alignment (net.adamcin.oakpal.core/basic/composite-store-alignment)
+
see net.adamcin.oakpal.core.checks.CompositeStoreAlignment
echo (net.adamcin.oakpal.core/basic/echo)
(skipped, template-only) see net.adamcin.oakpal.core.checks.Echo
jcrProperties (net.adamcin.oakpal.core/basic/jcrProperties)
diff --git a/core/src/test/filtered-resources/test-packages.properties b/core/src/test/filtered-resources/test-packages.properties index 1c6d7919e..eae2b1bad 100644 --- a/core/src/test/filtered-resources/test-packages.properties +++ b/core/src/test/filtered-resources/test-packages.properties @@ -15,4 +15,4 @@ # test-packages.root=${project.build.directory} -test-packages.src=/jackrabbit-filevault-${vault-api.version}/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/ \ No newline at end of file +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/core/src/test/java/net/adamcin/oakpal/core/BadInstallHookTest.java b/core/src/test/java/net/adamcin/oakpal/core/BadInstallHookTest.java index b620beac5..8551f7be5 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/BadInstallHookTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/BadInstallHookTest.java @@ -20,12 +20,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import net.adamcin.oakpal.api.Violation; import net.adamcin.oakpal.testing.TestPackageUtil; import org.junit.Test; import java.io.File; import java.util.Collection; -import java.util.List; import java.util.Optional; public class BadInstallHookTest { diff --git a/core/src/test/java/net/adamcin/oakpal/core/CheckReportTest.java b/core/src/test/java/net/adamcin/oakpal/core/CheckReportTest.java index b853e3130..f930e6423 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/CheckReportTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/CheckReportTest.java @@ -30,6 +30,10 @@ import javax.json.JsonArray; import javax.json.JsonObject; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.packaging.PackageId; import org.junit.Test; @@ -40,18 +44,18 @@ public void testGetViolationsBySeverity() { final CheckReport report = mock(CheckReport.class); final List violations = new ArrayList<>(); when(report.getViolations()).thenReturn(violations); - doCallRealMethod().when(report).getViolations(nullable(Violation.Severity.class)); + doCallRealMethod().when(report).getViolations(nullable(Severity.class)); assertEquals("violations size", 0, report.getViolations(null).size()); - violations.add(new SimpleViolation(Violation.Severity.MINOR, "minor violation")); - violations.add(new SimpleViolation(Violation.Severity.MAJOR, "major violation")); - violations.add(new SimpleViolation(Violation.Severity.SEVERE, "severe violation")); + violations.add(new SimpleViolation(Severity.MINOR, "minor violation")); + violations.add(new SimpleViolation(Severity.MAJOR, "major violation")); + violations.add(new SimpleViolation(Severity.SEVERE, "severe violation")); assertEquals("violations size after populating", 3, report.getViolations(null).size()); - assertEquals("violations size at or above minor", 3, report.getViolations(Violation.Severity.MINOR).size()); - assertEquals("violations size at or above major", 2, report.getViolations(Violation.Severity.MAJOR).size()); - assertEquals("violations size at or above severe", 1, report.getViolations(Violation.Severity.SEVERE).size()); + assertEquals("violations size at or above minor", 3, report.getViolations(Severity.MINOR).size()); + assertEquals("violations size at or above major", 2, report.getViolations(Severity.MAJOR).size()); + assertEquals("violations size at or above severe", 1, report.getViolations(Severity.SEVERE).size()); } @Test @@ -60,18 +64,18 @@ public void testToJson() { final List violations = new ArrayList<>(); when(report.getViolations()).thenReturn(violations); when(report.getCheckName()).thenReturn("mock"); - doCallRealMethod().when(report).getViolations(nullable(Violation.Severity.class)); + doCallRealMethod().when(report).getViolations(nullable(Severity.class)); doCallRealMethod().when(report).toJson(); final PackageId fooId = PackageId.fromString("test:foo:1.0-SNAPSHOT"); final PackageId barId = PackageId.fromString("test:bar:1.0-SNAPSHOT"); - violations.add(new SimpleViolation(Violation.Severity.MINOR, "minor violation")); - violations.add(new SimpleViolation(Violation.Severity.MAJOR, "major violation", fooId)); - violations.add(new SimpleViolation(Violation.Severity.SEVERE, "severe violation", fooId, barId)); + violations.add(new SimpleViolation(Severity.MINOR, "minor violation")); + violations.add(new SimpleViolation(Severity.MAJOR, "major violation", fooId)); + violations.add(new SimpleViolation(Severity.SEVERE, "severe violation", fooId, barId)); JsonObject json = report.toJson(); assertNotNull("json is not null", json); - assertEquals("checkName should be", "mock", json.getString(ReportMapper.KEY_CHECK_NAME)); - JsonArray violationArray = json.getJsonArray(ReportMapper.KEY_VIOLATIONS); + assertEquals("checkName should be", "mock", json.getString(ReportMapper.keys().checkName())); + JsonArray violationArray = json.getJsonArray(ReportMapper.keys().violations()); List fromJson = JavaxJson.mapArrayOfObjects(violationArray, SimpleViolation::fromJson); assertEquals("fromJson should be an array of three simple violations", 3, fromJson.size()); assertEquals("foo package is reported twice", 2, diff --git a/core/src/test/java/net/adamcin/oakpal/core/CheckSpecTest.java b/core/src/test/java/net/adamcin/oakpal/core/CheckSpecTest.java index 4830b3b4a..8a24dd4ee 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/CheckSpecTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/CheckSpecTest.java @@ -16,22 +16,24 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import org.junit.Test; +import javax.json.JsonObject; +import javax.json.JsonValue; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.json.JsonObject; -import javax.json.JsonValue; -import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class CheckSpecTest { @@ -313,7 +315,7 @@ public void testInlineScript() throws Exception { CheckSpec inline = CheckSpec.fromJson(key("inlineScript", "function importedPath(packageId, path) { print(path); }").get()); List checks = Locator.loadFromCheckSpecs(Collections.singletonList(inline)); - checks.get(0).importedPath(null, "/foo", null); + checks.get(0).importedPath(null, "/foo", null, PathAction.ADDED); } @Test @@ -348,21 +350,22 @@ public void testGetters() { @Test public void testFromJson() { + final CheckSpec.JsonKeys keys = CheckSpec.keys(); final JsonObject specJson = obj() - .key(CheckSpec.KEY_NAME, CheckSpec.KEY_NAME) - .key(CheckSpec.KEY_IMPL, CheckSpec.KEY_IMPL) - .key(CheckSpec.KEY_INLINE_SCRIPT, CheckSpec.KEY_INLINE_SCRIPT) - .key(CheckSpec.KEY_INLINE_ENGINE, CheckSpec.KEY_INLINE_ENGINE) - .key(CheckSpec.KEY_CONFIG, key("foo", "bar")) - .key(CheckSpec.KEY_TEMPLATE, CheckSpec.KEY_TEMPLATE) - .key(CheckSpec.KEY_SKIP, true) + .key(keys.name(), keys.name()) + .key(keys.impl(), keys.impl()) + .key(keys.inlineScript(), keys.inlineScript()) + .key(keys.inlineEngine(), keys.inlineEngine()) + .key(keys.config(), key("foo", "bar")) + .key(keys.template(), keys.template()) + .key(keys.skip(), true) .get(); final CheckSpec spec = CheckSpec.fromJson(specJson); - assertEquals("fromJson getName same", CheckSpec.KEY_NAME, spec.getName()); - assertEquals("fromJson getImpl same", CheckSpec.KEY_IMPL, spec.getImpl()); - assertEquals("fromJson getTemplate same", CheckSpec.KEY_TEMPLATE, spec.getTemplate()); - assertEquals("fromJson getInlineScript same", CheckSpec.KEY_INLINE_SCRIPT, spec.getInlineScript()); - assertEquals("fromJson getInlineEngine same", CheckSpec.KEY_INLINE_ENGINE, spec.getInlineEngine()); + assertEquals("fromJson getName same", keys.name(), spec.getName()); + assertEquals("fromJson getImpl same", keys.impl(), spec.getImpl()); + assertEquals("fromJson getTemplate same", keys.template(), spec.getTemplate()); + assertEquals("fromJson getInlineScript same", keys.inlineScript(), spec.getInlineScript()); + assertEquals("fromJson getInlineEngine same", keys.inlineEngine(), spec.getInlineEngine()); assertEquals("fromJson getConfig same", key("foo", "bar").get(), spec.getConfig()); assertTrue("fromJson isSkip true", spec.isSkip()); assertEquals("json should be equal", specJson, spec.toJson()); @@ -372,14 +375,15 @@ public void testFromJson() { @Test public void testCopyOf() { + final CheckSpec.JsonKeys keys = CheckSpec.keys(); final JsonObject specJson = obj() - .key(CheckSpec.KEY_NAME, CheckSpec.KEY_NAME) - .key(CheckSpec.KEY_IMPL, CheckSpec.KEY_IMPL) - .key(CheckSpec.KEY_INLINE_SCRIPT, CheckSpec.KEY_INLINE_SCRIPT) - .key(CheckSpec.KEY_INLINE_ENGINE, CheckSpec.KEY_INLINE_ENGINE) - .key(CheckSpec.KEY_CONFIG, key("foo", "bar")) - .key(CheckSpec.KEY_TEMPLATE, CheckSpec.KEY_TEMPLATE) - .key(CheckSpec.KEY_SKIP, true) + .key(keys.name(), keys.name()) + .key(keys.impl(), keys.impl()) + .key(keys.inlineScript(), keys.inlineScript()) + .key(keys.inlineEngine(), keys.inlineEngine()) + .key(keys.config(), key("foo", "bar")) + .key(keys.template(), keys.template()) + .key(keys.skip(), true) .get(); final CheckSpec spec = CheckSpec.fromJson(specJson); assertEquals("copy should equal copied", spec, CheckSpec.copyOf(spec)); diff --git a/core/src/test/java/net/adamcin/oakpal/core/ChecklistPlannerTest.java b/core/src/test/java/net/adamcin/oakpal/core/ChecklistPlannerTest.java index 363f9ff31..61c2b05e9 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ChecklistPlannerTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ChecklistPlannerTest.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.key; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -38,6 +38,7 @@ import java.util.stream.Collectors; import javax.json.JsonObject; +import net.adamcin.oakpal.api.Fun; import org.junit.Before; import org.junit.Test; diff --git a/core/src/test/java/net/adamcin/oakpal/core/ChecklistTest.java b/core/src/test/java/net/adamcin/oakpal/core/ChecklistTest.java index 5d73fe0a9..de7019c28 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ChecklistTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ChecklistTest.java @@ -16,9 +16,9 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,6 +37,7 @@ import javax.json.JsonObject; import javax.json.JsonReader; +import net.adamcin.oakpal.api.JavaxJson; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; @@ -76,20 +77,21 @@ public void testFromJSON() throws Exception { @Test public void testChecklistKeyComparator() { - final String[] inputValues1 = Checklist.KEY_ORDER.toArray(new String[0]); + final Checklist.JsonKeys keys = Checklist.keys(); + final String[] inputValues1 = keys.orderedKeys().toArray(new String[0]); Arrays.sort(inputValues1, Checklist.checklistKeyComparator.reversed()); Arrays.sort(inputValues1, Checklist.checklistKeyComparator); assertArrayEquals("sort reverse sort should be stable", - Checklist.KEY_ORDER.toArray(new String[0]), inputValues1); + keys.orderedKeys().toArray(new String[0]), inputValues1); - final String[] inputValues2 = new String[]{"foo", Checklist.KEY_NAME}; - final String[] expectValues2 = new String[]{Checklist.KEY_NAME, "foo"}; + final String[] inputValues2 = new String[]{"foo", keys.name()}; + final String[] expectValues2 = new String[]{keys.name(), "foo"}; Arrays.sort(inputValues2, Checklist.checklistKeyComparator); assertArrayEquals("sort unknown keys after known keys", expectValues2, inputValues2); - final String[] inputValues2b = new String[]{Checklist.KEY_NAME, "foo"}; - final String[] expectValues2b = new String[]{Checklist.KEY_NAME, "foo"}; + final String[] inputValues2b = new String[]{keys.name(), "foo"}; + final String[] expectValues2b = new String[]{keys.name(), "foo"}; Arrays.sort(inputValues2b, Checklist.checklistKeyComparator); assertArrayEquals("sort unknown keys after known keys", expectValues2b, inputValues2b); @@ -103,9 +105,10 @@ public void testChecklistKeyComparator() { @Test public void testComparingJsonKeys() { - final JsonObject obj1 = key("id", Checklist.KEY_CHECKS).get(); - final JsonObject obj2 = key("id", Checklist.KEY_CND_NAMES).get(); - final JsonObject obj3 = key("id", Checklist.KEY_CND_URLS).get(); + final Checklist.JsonKeys keys = Checklist.keys(); + final JsonObject obj1 = key("id", keys.checks()).get(); + final JsonObject obj2 = key("id", keys.cndNames()).get(); + final JsonObject obj3 = key("id", keys.cndUrls()).get(); final JsonObject[] inputValues1 = new JsonObject[]{obj2, obj3, obj1}; final JsonObject[] expectValues1 = new JsonObject[]{obj1, obj2, obj3}; Arrays.sort(inputValues1, Checklist.comparingJsonKeys(obj -> obj.getString("id"))); @@ -189,12 +192,12 @@ public void testBuilderWithForcedRoots() { Checklist.Builder builderWithForcedRoots = new Checklist.Builder("test"); final List forcedRoots = Arrays.asList( ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/foo") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "foo:primaryType") + .key(ForcedRoot.keys().path(), "/test/foo") + .key(ForcedRoot.keys().primaryType(), "foo:primaryType") .get()), ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/bar") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "bar:primaryType") + .key(ForcedRoot.keys().path(), "/test/bar") + .key(ForcedRoot.keys().primaryType(), "bar:primaryType") .get())); builderWithForcedRoots.withForcedRoots(forcedRoots); assertEquals("forced roots pass thru", @@ -204,23 +207,23 @@ public void testBuilderWithForcedRoots() { @Test public void testBuilderIsValidCheckSpec() { Checklist.Builder builder = new Checklist.Builder("test"); - CheckSpec validSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid") - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec validSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid") + .key(CheckSpec.keys().impl(), "impl").get()); assertTrue("valid spec isValid: " + validSpec.getName(), builder.isValidCheckspec(validSpec)); - CheckSpec abstractSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid").get()); + CheckSpec abstractSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid").get()); assertFalse("abstract spec not isValid: " + abstractSpec.getName(), builder.isValidCheckspec(abstractSpec)); - CheckSpec nullNameSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, null) - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec nullNameSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), null) + .key(CheckSpec.keys().impl(), "impl").get()); assertFalse("nullName spec not isValid: " + nullNameSpec.getName(), builder.isValidCheckspec(nullNameSpec)); - CheckSpec emptyNameSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "") - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec emptyNameSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), "") + .key(CheckSpec.keys().impl(), "impl").get()); assertFalse("emptyName spec not isValid: " + emptyNameSpec.getName(), builder.isValidCheckspec(emptyNameSpec)); - CheckSpec slashNameSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid/notValid") - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec slashNameSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid/notValid") + .key(CheckSpec.keys().impl(), "impl").get()); assertFalse("slashName spec not isValid: " + slashNameSpec.getName(), builder.isValidCheckspec(slashNameSpec)); } @@ -228,12 +231,12 @@ public void testBuilderIsValidCheckSpec() { @Test public void testBuilderWithChecks() { final Checklist.Builder builder = new Checklist.Builder("test"); - CheckSpec validSpec1 = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid1") - .key(CheckSpec.KEY_IMPL, "impl").get()); - CheckSpec validSpec2 = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid2") - .key(CheckSpec.KEY_IMPL, "impl").get()); - CheckSpec slashNameSpec = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid/notValid") - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec validSpec1 = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid1") + .key(CheckSpec.keys().impl(), "impl").get()); + CheckSpec validSpec2 = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid2") + .key(CheckSpec.keys().impl(), "impl").get()); + CheckSpec slashNameSpec = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid/notValid") + .key(CheckSpec.keys().impl(), "impl").get()); assertFalse("slashName spec not isValid: " + slashNameSpec.getName(), builder.isValidCheckspec(slashNameSpec)); final String prefix = "test/"; @@ -275,14 +278,14 @@ public void testAsInitStage() throws Exception { builder.withJcrPrivileges(privileges); final List forcedRoots = Arrays.asList( ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/foo") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "foo:primaryType") - .key(ForcedRoot.KEY_MIXIN_TYPES, arr().val("foo:mixinType")) + .key(ForcedRoot.keys().path(), "/test/foo") + .key(ForcedRoot.keys().primaryType(), "foo:primaryType") + .key(ForcedRoot.keys().mixinTypes(), arr().val("foo:mixinType")) .get()), ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/bar") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "bar:primaryType") - .key(ForcedRoot.KEY_MIXIN_TYPES, arr().val("bar:mixinType")) + .key(ForcedRoot.keys().path(), "/test/bar") + .key(ForcedRoot.keys().primaryType(), "bar:primaryType") + .key(ForcedRoot.keys().mixinTypes(), arr().val("bar:mixinType")) .get())); builder.withForcedRoots(forcedRoots); InitStage initStage = builder.build().asInitStage(); @@ -337,33 +340,34 @@ public void testToJsonFromBuilder() { builder.withJcrPrivileges(privileges); final List forcedRoots = Arrays.asList( ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/foo") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "foo:primaryType") - .key(ForcedRoot.KEY_MIXIN_TYPES, arr().val("foo:mixinType")) + .key(ForcedRoot.keys().path(), "/test/foo") + .key(ForcedRoot.keys().primaryType(), "foo:primaryType") + .key(ForcedRoot.keys().mixinTypes(), arr().val("foo:mixinType")) .get()), ForcedRoot.fromJson(obj() - .key(ForcedRoot.KEY_PATH, "/test/bar") - .key(ForcedRoot.KEY_PRIMARY_TYPE, "bar:primaryType") - .key(ForcedRoot.KEY_MIXIN_TYPES, arr().val("bar:mixinType")) + .key(ForcedRoot.keys().path(), "/test/bar") + .key(ForcedRoot.keys().primaryType(), "bar:primaryType") + .key(ForcedRoot.keys().mixinTypes(), arr().val("bar:mixinType")) .get())); final JsonArray forcedRootsJson = JavaxJson.wrap(forcedRoots).asJsonArray(); builder.withForcedRoots(forcedRoots); - CheckSpec validSpec1 = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid1") - .key(CheckSpec.KEY_IMPL, "impl").get()); - CheckSpec validSpec2 = CheckSpec.fromJson(key(CheckSpec.KEY_NAME, "valid2") - .key(CheckSpec.KEY_IMPL, "impl").get()); + CheckSpec validSpec1 = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid1") + .key(CheckSpec.keys().impl(), "impl").get()); + CheckSpec validSpec2 = CheckSpec.fromJson(key(CheckSpec.keys().name(), "valid2") + .key(CheckSpec.keys().impl(), "impl").get()); final List checks = Arrays.asList(validSpec1, validSpec2); final JsonArray checksJson = JavaxJson.wrap(checks).asJsonArray(); builder.withChecks(checks); final Checklist checklist = builder.build(); + final Checklist.JsonKeys keys = Checklist.keys(); final JsonObject expectJson = obj() - .key(Checklist.KEY_NAME, "name") - .key(Checklist.KEY_CHECKS, checksJson) - .key(Checklist.KEY_FORCED_ROOTS, forcedRootsJson) - .key(Checklist.KEY_CND_URLS, cndUrlsJson) - .key(Checklist.KEY_JCR_NODETYPES, nodetypesJson) - .key(Checklist.KEY_JCR_PRIVILEGES, privilegesJson) - .key(Checklist.KEY_JCR_NAMESPACES, jcrNamespacesJson) + .key(keys.name(), "name") + .key(keys.checks(), checksJson) + .key(keys.forcedRoots(), forcedRootsJson) + .key(keys.cndUrls(), cndUrlsJson) + .key(keys.jcrNodetypes(), nodetypesJson) + .key(keys.jcrPrivileges(), privilegesJson) + .key(keys.jcrNamespaces(), jcrNamespacesJson) .get(); final JsonObject json = checklist.toJson(); assertEquals("toJson should match", expectJson, json); diff --git a/core/src/test/java/net/adamcin/oakpal/core/DefaultErrorListenerTest.java b/core/src/test/java/net/adamcin/oakpal/core/DefaultErrorListenerTest.java index 6c38feaa5..f18fde6f9 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/DefaultErrorListenerTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/DefaultErrorListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,52 @@ package net.adamcin.oakpal.core; -import static org.junit.Assert.assertEquals; - +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; import org.junit.Test; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Locale; +import java.util.ResourceBundle; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + public class DefaultErrorListenerTest { final Exception simpleCause = new IllegalStateException(new IllegalArgumentException()); + @Test + public void testSetResourceBundle() { + final DefaultErrorListener listener = new DefaultErrorListener(); + + ResourceBundle originalBundle = listener.getResourceBundle(); + assertSame("same object returned twice", originalBundle, listener.getResourceBundle()); + ResourceBundle newBundle = ResourceBundle.getBundle(listener.getResourceBundleBaseName(), + Locale.getDefault(), new URLClassLoader(new URL[0], getClass().getClassLoader())); + assertNotSame("not same object as created externally", newBundle, listener.getResourceBundle()); + listener.setResourceBundle(newBundle); + assertSame("same object as set", newBundle, listener.getResourceBundle()); + assertSame("same object as set, again", newBundle, listener.getResourceBundle()); + } + + @Test + public void testGetString() { + final DefaultErrorListener listener = new DefaultErrorListener(); + assertEquals("expect passthrough", "testKey", listener.getString("testKey")); + ResourceBundle newBundle = ResourceBundle.getBundle(getClass().getName()); + listener.setResourceBundle(newBundle); + assertEquals("expect from bundle", "yeKtset", listener.getString("testKey")); + } + @Test public void testGetReportedViolations() { final DefaultErrorListener errorListener = new DefaultErrorListener(); - errorListener.reportViolation(new SimpleViolation(Violation.Severity.MINOR, "minor")); - errorListener.reportViolation(new SimpleViolation(Violation.Severity.MAJOR, "major")); - errorListener.reportViolation(new SimpleViolation(Violation.Severity.SEVERE, "severe")); + errorListener.reportViolation(new SimpleViolation(Severity.MINOR, "minor")); + errorListener.reportViolation(new SimpleViolation(Severity.MAJOR, "major")); + errorListener.reportViolation(new SimpleViolation(Severity.SEVERE, "severe")); assertEquals("should have reported", 3, errorListener.getReportedViolations().size()); } diff --git a/core/src/test/java/net/adamcin/oakpal/core/DefaultPackagingServiceTest.java b/core/src/test/java/net/adamcin/oakpal/core/DefaultPackagingServiceTest.java index 1db11096d..f25e285da 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/DefaultPackagingServiceTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/DefaultPackagingServiceTest.java @@ -45,6 +45,7 @@ import org.apache.jackrabbit.vault.packaging.VaultPackage; import org.apache.jackrabbit.vault.packaging.impl.JcrPackageDefinitionImpl; import org.apache.jackrabbit.vault.packaging.impl.JcrPackageManagerImpl; +import org.jetbrains.annotations.NotNull; import org.junit.Test; public class DefaultPackagingServiceTest { @@ -292,5 +293,15 @@ public VaultPackage rewrap(final ExportOptions opts, final VaultPackage src, fin public void rewrap(final ExportOptions opts, final VaultPackage src, final OutputStream out) throws IOException { } + + @Override + public @NotNull VaultPackage open(@NotNull final Archive archive) throws IOException { + return null; + } + + @Override + public @NotNull VaultPackage open(@NotNull final Archive archive, final boolean strict) throws IOException { + return null; + } } } \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/ErrorListenerTest.java b/core/src/test/java/net/adamcin/oakpal/core/ErrorListenerTest.java index 799bfe75c..74655418b 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ErrorListenerTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ErrorListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,12 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Violation; +import org.junit.Test; + import java.util.Collection; import java.util.Collections; -import org.junit.Test; - public class ErrorListenerTest { @Test diff --git a/core/src/test/java/net/adamcin/oakpal/core/ForcedRootTest.java b/core/src/test/java/net/adamcin/oakpal/core/ForcedRootTest.java index bac93f100..9450a4e2b 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ForcedRootTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ForcedRootTest.java @@ -16,15 +16,20 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.*; +import org.junit.Test; +import javax.json.JsonValue; import java.util.Collections; import java.util.List; -import javax.json.JsonValue; -import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class ForcedRootTest { @@ -92,6 +97,7 @@ public void testGetNamespacePrefixes() { @Test public void testFromJson() { + final ForcedRoot.JsonKeys keys = ForcedRoot.keys(); final String path = "/correct/path"; final String primaryType = "primaryType"; final List mixinTypes = Collections.singletonList("mixinType"); @@ -101,18 +107,18 @@ public void testFromJson() { assertTrue("empty root has empty mixinTypes", rootEmpty.getMixinTypes().isEmpty()); final ForcedRoot rootPath = ForcedRoot.fromJson( - obj().key(ForcedRoot.KEY_PATH, path).get()); + obj().key(keys.path(), path).get()); assertEquals("path root has correct path", path, rootPath.getPath()); final ForcedRoot rootPrimaryType = ForcedRoot.fromJson( - obj().key(ForcedRoot.KEY_PRIMARY_TYPE, primaryType).get()); + obj().key(keys.primaryType(), primaryType).get()); assertEquals("primaryType root has correct primaryType", primaryType, rootPrimaryType.getPrimaryType()); final ForcedRoot rootMixinTypes = ForcedRoot.fromJson( - obj().key(ForcedRoot.KEY_MIXIN_TYPES, mixinTypes).get()); + obj().key(keys.mixinTypes(), mixinTypes).get()); assertEquals("mixinTypes root has correct mixinTypes", mixinTypes, rootMixinTypes.getMixinTypes()); @@ -120,6 +126,7 @@ public void testFromJson() { @Test public void testToJson() { + final ForcedRoot.JsonKeys keys = ForcedRoot.keys(); final String path = "/correct/path"; final String primaryType = "primaryType"; final List mixinTypes = Collections.singletonList("mixinType"); @@ -130,15 +137,15 @@ public void testToJson() { final ForcedRoot pathRoot = new ForcedRoot().withPath(path); assertEquals("path root toJson should have path", - key(ForcedRoot.KEY_PATH, path).get(), pathRoot.toJson()); + key(keys.path(), path).get(), pathRoot.toJson()); final ForcedRoot primaryTypeRoot = new ForcedRoot().withPrimaryType(primaryType); assertEquals("primaryType root toJson should have primaryType", - key(ForcedRoot.KEY_PRIMARY_TYPE, primaryType).get(), primaryTypeRoot.toJson()); + key(keys.primaryType(), primaryType).get(), primaryTypeRoot.toJson()); final ForcedRoot mixinTypeRoot = new ForcedRoot().withMixinTypes(mixinTypes.get(0)); assertEquals("mixinTypes root toJson should have mixinTypes", - key(ForcedRoot.KEY_MIXIN_TYPES, mixinTypes).get(), mixinTypeRoot.toJson()); + key(keys.mixinTypes(), mixinTypes).get(), mixinTypeRoot.toJson()); } @Test @@ -151,6 +158,7 @@ public void testCompareTo() { @Test public void testToString() { + final ForcedRoot.JsonKeys keys = ForcedRoot.keys(); final String path = "/correct/path"; final String primaryType = "primaryType"; final List mixinTypes = Collections.singletonList("mixinType"); @@ -159,9 +167,9 @@ public void testToString() { .withPrimaryType(primaryType) .withMixinTypes(mixinTypes.get(0)); final String jsonString = root.toString(); - final String expectString = key(ForcedRoot.KEY_PATH, path) - .key(ForcedRoot.KEY_PRIMARY_TYPE, primaryType) - .key(ForcedRoot.KEY_MIXIN_TYPES, mixinTypes) + final String expectString = key(keys.path(), path) + .key(keys.primaryType(), primaryType) + .key(keys.mixinTypes(), mixinTypes) .get().toString(); assertEquals("expect json string for toString", expectString, jsonString); } diff --git a/core/src/test/java/net/adamcin/oakpal/core/InitStageTest.java b/core/src/test/java/net/adamcin/oakpal/core/InitStageTest.java index 5f8eea4a2..82d9ebe51 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/InitStageTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/InitStageTest.java @@ -16,12 +16,11 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheck2; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.uncheck2; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -34,7 +33,6 @@ import java.net.URL; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/core/src/test/java/net/adamcin/oakpal/core/JcrNsTest.java b/core/src/test/java/net/adamcin/oakpal/core/JcrNsTest.java index 27482d10c..80ab6ba20 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/JcrNsTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/JcrNsTest.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.key; import static org.junit.Assert.*; import javax.json.JsonObject; @@ -27,16 +27,17 @@ public class JcrNsTest { final String prefix = "foo"; final String uri = "http://foo.com"; + private static final JcrNs.JsonKeys KEYS = JcrNs.keys(); @Test public void testFromJson() { assertNull("should be null if either prefix and uri are missing", JcrNs.fromJson(JsonValue.EMPTY_JSON_OBJECT)); assertNull("should be null prefix is missing", - JcrNs.fromJson(key(JcrNs.KEY_PREFIX, prefix).get())); + JcrNs.fromJson(key(KEYS.prefix(), prefix).get())); assertNull("should be null if uri is missing", - JcrNs.fromJson(key(JcrNs.KEY_URI, uri).get())); - JcrNs complete = JcrNs.fromJson(key(JcrNs.KEY_PREFIX, prefix).key(JcrNs.KEY_URI, uri).get()); + JcrNs.fromJson(key(KEYS.uri(), uri).get())); + JcrNs complete = JcrNs.fromJson(key(KEYS.prefix(), prefix).key(KEYS.uri(), uri).get()); assertNotNull("complete should not be null", complete); assertEquals("complete prefix should be", prefix, complete.getPrefix()); assertEquals("complete prefix should be", uri, complete.getUri()); @@ -52,14 +53,14 @@ public void testCreate() { @Test public void testToJson() { - JsonObject expect = key(JcrNs.KEY_PREFIX, prefix).key(JcrNs.KEY_URI, uri).get(); + JsonObject expect = key(KEYS.prefix(), prefix).key(KEYS.uri(), uri).get(); JcrNs complete = JcrNs.create(prefix, uri); assertEquals("expect json", expect, complete.toJson()); } @Test public void testToString() { - String expect = key(JcrNs.KEY_PREFIX, prefix).key(JcrNs.KEY_URI, uri).get().toString(); + String expect = key(KEYS.prefix(), prefix).key(KEYS.uri(), uri).get().toString(); JcrNs complete = JcrNs.create(prefix, uri); assertEquals("expect json string", expect, complete.toString()); } diff --git a/core/src/test/java/net/adamcin/oakpal/core/JsonCndTest.java b/core/src/test/java/net/adamcin/oakpal/core/JsonCndTest.java index 5f2a0607c..89567afe8 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/JsonCndTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/JsonCndTest.java @@ -16,8 +16,8 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.Fun.*; -import static net.adamcin.oakpal.core.JavaxJson.*; +import static net.adamcin.oakpal.api.Fun.*; +import static net.adamcin.oakpal.api.JavaxJson.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -41,6 +41,9 @@ import javax.json.JsonValue; import javax.json.stream.JsonCollectors; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.commons.cnd.Lexer; import org.apache.jackrabbit.spi.*; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; diff --git a/core/src/test/java/net/adamcin/oakpal/core/LocatorTest.java b/core/src/test/java/net/adamcin/oakpal/core/LocatorTest.java index 5c8d199a3..19d5f46b6 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/LocatorTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/LocatorTest.java @@ -16,12 +16,8 @@ package net.adamcin.oakpal.core; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Violation; import net.adamcin.oakpal.core.checks.Echo; import net.adamcin.oakpal.core.checks.Paths; import org.junit.Test; @@ -30,6 +26,12 @@ import java.util.Collection; import java.util.Collections; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + public class LocatorTest { public static class NotACheck { @@ -61,7 +63,7 @@ public void testLoadProgressCheck() throws Exception { assertNotNull("echo should load by class name, with config, with cl", Locator.loadProgressCheck(Echo.class.getName(), obj().get(), getClass().getClassLoader())); - assertNotNull("paths should load by class name, with config, with cl", + assertNotNull("paths should load by factory class name, with config, with cl", Locator.loadProgressCheck(Paths.class.getName(), obj().get(), getClass().getClassLoader())); assertNotNull("simpleHandler.js should load by resource name, with config, with cl", diff --git a/core/src/test/java/net/adamcin/oakpal/core/NamespaceMappingRequestTest.java b/core/src/test/java/net/adamcin/oakpal/core/NamespaceMappingRequestTest.java index bf126e480..c1b264435 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/NamespaceMappingRequestTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/NamespaceMappingRequestTest.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.core; -import org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; @@ -26,7 +26,7 @@ import java.util.Arrays; import java.util.List; -import static net.adamcin.oakpal.core.Fun.inferTest1; +import static net.adamcin.oakpal.api.Fun.inferTest1; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/core/src/test/java/net/adamcin/oakpal/core/OakMachineTest.java b/core/src/test/java/net/adamcin/oakpal/core/OakMachineTest.java index 6d65acff0..97d3c8695 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/OakMachineTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/OakMachineTest.java @@ -17,6 +17,9 @@ package net.adamcin.oakpal.core; import junitx.util.PrivateAccessor; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.SimpleProgressCheck; import net.adamcin.oakpal.testing.TestPackageUtil; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; @@ -69,10 +72,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -82,10 +85,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockitoSession; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; @@ -683,7 +684,7 @@ public void testImporterListenerAdapter_onMessage_importedPathException() throws final File testPackage = TestPackageUtil.prepareTestPackage("tmp_foo_bar.zip"); final ProgressCheck check = mock(ProgressCheck.class); doThrow(RepositoryException.class).when(check) - .importedPath(any(PackageId.class), anyString(), any(Node.class)); + .importedPath(any(PackageId.class), anyString(), any(Node.class), any(PathAction.class)); final CompletableFuture eLatch = new CompletableFuture<>(); final CompletableFuture handlerLatch = new CompletableFuture<>(); diff --git a/core/src/test/java/net/adamcin/oakpal/core/OakpalPlanTest.java b/core/src/test/java/net/adamcin/oakpal/core/OakpalPlanTest.java index 6d8bd6618..0f9a04b61 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/OakpalPlanTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/OakpalPlanTest.java @@ -16,6 +16,8 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.api.JackrabbitWorkspace; import org.apache.jackrabbit.api.security.authorization.PrivilegeManager; import org.apache.jackrabbit.spi.PrivilegeDefinition; @@ -31,7 +33,11 @@ import org.slf4j.LoggerFactory; import javax.jcr.nodetype.NodeTypeManager; -import javax.json.*; +import javax.json.Json; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonValue; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -41,13 +47,26 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; - -import static net.adamcin.oakpal.core.JavaxJson.*; -import static org.junit.Assert.*; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.concurrent.CompletableFuture; + +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.wrap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class OakpalPlanTest { private static final Logger LOGGER = LoggerFactory.getLogger(OakpalPlanTest.class); @@ -512,4 +531,21 @@ public void testRelativizeToBaseParent() throws Exception { assertEquals("return relative uri when base is not json", fooRel, OakpalPlan.relativizeToBaseParent(fooRoot, fooAbs)); } + + @Test + public void testInitResourceBundle() throws Exception { + CompletableFuture callback = new CompletableFuture<>(); + OakpalPlan plan = builder().build(); + ProgressCheck check = mock(ProgressCheck.class); + doAnswer(call -> callback.complete(call.getArgument(0))) + .when(check).setResourceBundle(nullable(ResourceBundle.class)); + when(check.getResourceBundleBaseName()).thenReturn(null); + plan.initResourceBundle(check, Locale.getDefault(), getClass().getClassLoader()); + assertFalse("expect callback not done", callback.isDone()); + when(check.getResourceBundleBaseName()).thenReturn(getClass().getName()); + plan.initResourceBundle(check, Locale.getDefault(), getClass().getClassLoader()); + assertSame("expect callback complete with", ResourceBundle.getBundle(getClass().getName()), + callback.getNow(null)); + + } } \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.java index e41f14428..56267f9c4 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.java @@ -16,6 +16,9 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; @@ -29,11 +32,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.ResourceBundle; import java.util.concurrent.CompletableFuture; import java.util.jar.Manifest; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -225,15 +230,17 @@ public void testImportedPath_throws() throws Exception { final PackageId arg0 = PackageId.fromString("my_packages:example:1.0"); final String arg1 = "/correct/path"; final Node arg2 = mock(Node.class); + final PathAction arg3 = PathAction.MODIFIED; final ProgressCheck delegate = mock(ProgressCheck.class); doThrow(RepositoryException.class).when(delegate).importedPath( any(PackageId.class), any(String.class), - any(Node.class)); + any(Node.class), + any(PathAction.class)); final ProgressCheckAliasFacade alias = new ProgressCheckAliasFacade(delegate, null); - alias.importedPath(arg0, arg1, arg2); + alias.importedPath(arg0, arg1, arg2, arg3); } @Test @@ -241,10 +248,12 @@ public void testImportedPath() throws Exception { final PackageId arg0 = PackageId.fromString("my_packages:example:1.0"); final String arg1 = "/correct/path"; final Node arg2 = mock(Node.class); + final PathAction arg3 = PathAction.MODIFIED; final CompletableFuture slot0 = new CompletableFuture<>(); final CompletableFuture slot1 = new CompletableFuture<>(); final CompletableFuture slot2 = new CompletableFuture<>(); + final CompletableFuture slot3 = new CompletableFuture<>(); final ProgressCheck delegate = mock(ProgressCheck.class); @@ -252,18 +261,21 @@ public void testImportedPath() throws Exception { slot0.complete(call.getArgument(0, PackageId.class)); slot1.complete(call.getArgument(1, String.class)); slot2.complete(call.getArgument(2, Node.class)); + slot3.complete(call.getArgument(3, PathAction.class)); return true; }).when(delegate).importedPath( any(PackageId.class), any(String.class), - any(Node.class)); + any(Node.class), + any(PathAction.class)); final ProgressCheckAliasFacade alias = new ProgressCheckAliasFacade(delegate, null); - alias.importedPath(arg0, arg1, arg2); + alias.importedPath(arg0, arg1, arg2, arg3); assertSame("same arg0", arg0, slot0.getNow(null)); assertSame("same arg1", arg1, slot1.getNow(null)); assertSame("same arg2", arg2, slot2.getNow(null)); + assertSame("same arg3", arg3, slot3.getNow(null)); } @Test(expected = RepositoryException.class) @@ -350,4 +362,18 @@ public void testAfterExtract() throws Exception { assertSame("same arg0", arg0, slot0.getNow(null)); assertSame("same arg1", arg1, slot1.getNow(null)); } + + @Test + public void testSetResourceBundle() { + final ProgressCheck wrappedCheck = mock(ProgressCheck.class); + when(wrappedCheck.getResourceBundleBaseName()).thenReturn(getClass().getName()); + final CompletableFuture slot = new CompletableFuture<>(); + doAnswer(call -> slot.complete(call.getArgument(0))) + .when(wrappedCheck).setResourceBundle(nullable(ResourceBundle.class)); + + final ProgressCheckAliasFacade facade = new ProgressCheckAliasFacade(wrappedCheck, "wrapper"); + final ResourceBundle expected = ResourceBundle.getBundle(getClass().getName()); + facade.setResourceBundle(ResourceBundle.getBundle(facade.getResourceBundleBaseName())); + assertSame("expect same resource bundle", expected, slot.getNow(null)); + } } \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/ReportMapperTest.java b/core/src/test/java/net/adamcin/oakpal/core/ReportMapperTest.java index ef62a7130..32a8df5c6 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ReportMapperTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ReportMapperTest.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; import org.apache.jackrabbit.vault.packaging.PackageId; import org.junit.Before; import org.junit.Test; @@ -44,15 +46,15 @@ public void testReportsToJson() throws Exception { final List originalReports = asList( new SimpleReport("test/first", singletonList( - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "one", PackageId.fromString("test:first"))) ), new SimpleReport("test/second", asList( - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "one", PackageId.fromString("test:first")), - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "two", PackageId.fromString("test:first"), PackageId.fromString("test:second")) @@ -73,15 +75,15 @@ public void testWriteThenRead() throws Exception { final List originalReports = asList( new SimpleReport("test/first", singletonList( - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "one", PackageId.fromString("test:first"))) ), new SimpleReport("test/second", asList( - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "one", PackageId.fromString("test:first")), - new SimpleViolation(Violation.Severity.MINOR, + new SimpleViolation(Severity.MINOR, "two", PackageId.fromString("test:first"), PackageId.fromString("test:second")) diff --git a/core/src/test/java/net/adamcin/oakpal/core/ScanTest.java b/core/src/test/java/net/adamcin/oakpal/core/ScanTest.java index ecff555ae..7f597334f 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ScanTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ScanTest.java @@ -30,6 +30,8 @@ import javax.jcr.query.QueryResult; import javax.json.JsonValue; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.SimpleProgressCheck; import net.adamcin.oakpal.testing.TestPackageUtil; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.user.User; diff --git a/core/src/test/java/net/adamcin/oakpal/core/ScriptProgressCheckTest.java b/core/src/test/java/net/adamcin/oakpal/core/ScriptProgressCheckTest.java index 1b58d462c..e4b959c9f 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/ScriptProgressCheckTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/ScriptProgressCheckTest.java @@ -16,6 +16,11 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; @@ -28,18 +33,24 @@ import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; import java.util.jar.Manifest; -import static net.adamcin.oakpal.core.Fun.toEntry; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.tryOrDefault1; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; @@ -97,11 +108,11 @@ public void testScriptHelper() { assertEquals("violations size", 3, violations.size()); assertTrue("test_0 minor violation", violations.stream() - .anyMatch(viol -> viol.getSeverity() == Violation.Severity.MINOR && viol.getPackages().contains(id0))); + .anyMatch(viol -> viol.getSeverity() == Severity.MINOR && viol.getPackages().contains(id0))); assertTrue("test_1 major violation", violations.stream() - .anyMatch(viol -> viol.getSeverity() == Violation.Severity.MAJOR && viol.getPackages().contains(id1))); + .anyMatch(viol -> viol.getSeverity() == Severity.MAJOR && viol.getPackages().contains(id1))); assertTrue("test_2 severe violation", violations.stream() - .anyMatch(viol -> viol.getSeverity() == Violation.Severity.SEVERE && viol.getPackages().contains(id2))); + .anyMatch(viol -> viol.getSeverity() == Severity.SEVERE && viol.getPackages().contains(id2))); check.startedScan(); @@ -338,16 +349,18 @@ public void testImportedPath() throws Exception { final PackageId arg1 = PackageId.fromString("my_packages:example:1.0"); final String arg2 = "/correct/path"; final Node arg3 = mock(Node.class); + final PathAction arg4 = PathAction.MODIFIED; - check.importedPath(arg1, arg2, arg3); + check.importedPath(arg1, arg2, arg3, arg4); Map.Entry call = argRecord.stream() - .filter(entry -> "importedPath".equals(entry.getKey()) && entry.getValue().length == 4).findFirst() + .filter(entry -> "importedPath".equals(entry.getKey()) && entry.getValue().length == 5).findFirst() .orElse(null); assertNotNull("expect call for importedPath", call); assertSame("same arg1", arg1, call.getValue()[1]); assertSame("same arg2", arg2, call.getValue()[2]); assertSame("same arg3", arg3, call.getValue()[3]); + assertSame("same arg4", arg4, call.getValue()[4]); } @Test @@ -443,4 +456,28 @@ public void testGuardSessionHandler_cacheMissing() throws Exception { doThrow(ScriptException.class).when(delegate).invokeFunction("missingFunction", "test"); check.guardSessionHandler("missingFunction", handle -> handle.apply("test")); } + + PropertyResourceBundle fromPropertiesUrl(URL propertiesUrl) { + try (InputStream propsStream = propertiesUrl.openStream()) { + return new PropertyResourceBundle(propsStream); + } catch (IOException e) { + return null; + } + } + + @Test + public void testGetResourceBundleBaseName() throws Exception { + final ScriptProgressCheck check = (ScriptProgressCheck) ScriptProgressCheck + .createScriptCheckFactory(testScriptUrl("checkWithResources.js")).newInstance(null); + + assertEquals("expect testKey=testKey", "testKey", check.getHelper().getString("testKey")); + + check.setResourceBundle(fromPropertiesUrl(testScriptUrl("checkWithResources.properties"))); + + assertEquals("expect testKey=yeKtset", "yeKtset", check.getHelper().getString("testKey")); + + check.setResourceBundle(fromPropertiesUrl(testScriptUrl("overrideResourceBundle.properties"))); + + assertEquals("expect testKey=yeKtsettestKey", "yeKtsettestKey", check.getHelper().getString("testKey")); + } } \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/SimpleReportTest.java b/core/src/test/java/net/adamcin/oakpal/core/SimpleReportTest.java index 07360bf5d..c46e4e417 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/SimpleReportTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/SimpleReportTest.java @@ -16,15 +16,23 @@ package net.adamcin.oakpal.core; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheck; +import net.adamcin.oakpal.api.SimpleViolation; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.packaging.PackageId; import org.junit.Test; import java.util.Arrays; import java.util.Collections; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.*; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class SimpleReportTest { @@ -35,8 +43,8 @@ public void testConstruct() { assertTrue("violations are empty", report.getViolations().isEmpty()); final PackageId id = PackageId.fromString("my_packages:test:1.0"); SimpleReport reportMore = new SimpleReport("moreReport", Arrays.asList( - new SimpleViolation(Violation.Severity.SEVERE, "severe", id), - new SimpleViolation(Violation.Severity.MINOR, "minor", id))); + new SimpleViolation(Severity.SEVERE, "severe", id), + new SimpleViolation(Severity.MINOR, "minor", id))); assertEquals("check name is", "moreReport", reportMore.getCheckName()); assertEquals("violations are 2", 2, reportMore.getViolations().size()); @@ -46,8 +54,8 @@ public void testConstruct() { public void testEqualsAndHash() { final PackageId id = PackageId.fromString("my_packages:test:1.0"); SimpleReport original = new SimpleReport("moreReport", Arrays.asList( - new SimpleViolation(Violation.Severity.SEVERE, "severe", id), - new SimpleViolation(Violation.Severity.MINOR, "minor", id))); + new SimpleViolation(Severity.SEVERE, "severe", id), + new SimpleViolation(Severity.MINOR, "minor", id))); assertFalse("null not equal", original.equals(null)); assertNotEquals("other not equal", original, new Object()); @@ -58,7 +66,7 @@ public void testEqualsAndHash() { assertEquals("copy hash is equal", original.hashCode(), copy.hashCode()); SimpleReport different = new SimpleReport("different", Arrays.asList( - new SimpleViolation(Violation.Severity.MAJOR, "major", id))); + new SimpleViolation(Severity.MAJOR, "major", id))); assertNotEquals("different is not equal", original, different); assertNotEquals("different hash is not equal", original.hashCode(), different.hashCode()); } @@ -70,25 +78,25 @@ public void testToString() { @Test public void testGenerateReportFromProgressCheck() { - SimpleProgressCheck check = new SimpleProgressCheck(); - check.minorViolation("minor"); + TestProgressCheck check = new TestProgressCheck(); + check.minor("minor"); SimpleReport report = SimpleReport.generateReport(check); assertNotNull("report not null", report); - assertEquals("report name", SimpleProgressCheck.class.getSimpleName(), report.getCheckName()); + assertEquals("report name", check.getClass().getSimpleName(), report.getCheckName()); assertEquals("violations are", - Collections.singletonList(new SimpleViolation(Violation.Severity.MINOR, "minor")), + Collections.singletonList(new SimpleViolation(Severity.MINOR, "minor")), report.getViolations()); } @Test public void testGenerateReportFromErrorListener() { - DefaultErrorListener errorListener = new DefaultErrorListener(); - errorListener.reportViolation(new SimpleViolation(Violation.Severity.MINOR, "minor")); + TestErrorListener errorListener = new TestErrorListener(); + errorListener.reportViolation(new SimpleViolation(Severity.MINOR, "minor")); SimpleReport report = SimpleReport.generateReport(errorListener); assertNotNull("report not null", report); - assertEquals("report name", DefaultErrorListener.class.getSimpleName(), report.getCheckName()); + assertEquals("report name", errorListener.getClass().getSimpleName(), report.getCheckName()); assertEquals("violations are", - Collections.singletonList(new SimpleViolation(Violation.Severity.MINOR, "minor")), + Collections.singletonList(new SimpleViolation(Severity.MINOR, "minor")), report.getViolations()); } @@ -98,14 +106,41 @@ public void testFromJson() { assertEquals("empty report name is empty", "", emptyReport.getCheckName()); assertTrue("empty report violations are empty", emptyReport.getViolations().isEmpty()); - SimpleViolation violation = new SimpleViolation(Violation.Severity.MINOR, "minor"); + SimpleViolation violation = new SimpleViolation(Severity.MINOR, "minor"); SimpleReport moreReport = SimpleReport.fromJson(obj() - .key(ReportMapper.KEY_CHECK_NAME, "more") - .key(ReportMapper.KEY_VIOLATIONS, arr().val(violation)) + .key(ReportMapper.keys().checkName(), "more") + .key(ReportMapper.keys().violations(), arr().val(violation)) .get()); assertEquals("more report name is more", "more", moreReport.getCheckName()); assertEquals("more report violations are ", Collections.singletonList(violation), moreReport.getViolations()); } + + static class TestProgressCheck extends SimpleProgressCheck { + @Override + public void reportViolation(final Violation violation) { + super.reportViolation(violation); + } + + public final void minor(final String description, final PackageId... packages) { + this.reportViolation(new SimpleViolation(Severity.MINOR, description, packages)); + } + + public final void major(final String description, final PackageId... packages) { + this.reportViolation(new SimpleViolation(Severity.MAJOR, description, packages)); + } + + public final void severe(final String description, final PackageId... packages) { + this.reportViolation(new SimpleViolation(Severity.SEVERE, description, packages)); + } + } + + static class TestErrorListener extends DefaultErrorListener { + @Override + public void reportViolation(final Violation violation) { + super.reportViolation(violation); + } + } + } diff --git a/core/src/test/java/net/adamcin/oakpal/core/SlingNodetypesScannerTest.java b/core/src/test/java/net/adamcin/oakpal/core/SlingNodetypesScannerTest.java index 890bde22e..101e91888 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/SlingNodetypesScannerTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/SlingNodetypesScannerTest.java @@ -34,9 +34,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; import static org.junit.Assert.assertEquals; public class SlingNodetypesScannerTest { diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/AcHandlingTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/AcHandlingTest.java index cbbb890a3..918e90846 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/AcHandlingTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/AcHandlingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,17 @@ package net.adamcin.oakpal.core.checks; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static net.adamcin.oakpal.core.checks.AcHandling.DEFAULT_LEVEL_SET; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - +import net.adamcin.oakpal.api.ProgressCheck; import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + public class AcHandlingTest extends ProgressCheckTestBase { @Test @@ -36,7 +35,7 @@ public void testLevelSet() throws Exception { ProgressCheck check = new AcHandling().newInstance(obj().key("levelSet", "no_unsafe").get()); CheckReport report = scanWithCheck(check, "test_childnodeorder.zip"); logViolations("level_set:no_unsafe", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -44,7 +43,7 @@ public void testLevelSet() throws Exception { ProgressCheck check = new AcHandling().newInstance(obj().key("levelSet", "only_ignore").get()); CheckReport report = scanWithCheck(check, "test_childnodeorder.zip"); logViolations("level_set:only_ignore", report); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -57,7 +56,7 @@ public void testAllowedModes() throws Exception { new AcHandling().newInstance(obj().key("allowedModes", arr("merge_preserve")).get()); CheckReport report = scanWithCheck(check, "test_childnodeorder.zip"); logViolations("allowedModes:merge_preserve", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -66,7 +65,7 @@ public void testAllowedModes() throws Exception { new AcHandling().newInstance(obj().key("allowedModes", arr("ignore")).get()); CheckReport report = scanWithCheck(check, "test_childnodeorder.zip"); logViolations("allowedModes:ignore", report); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -76,8 +75,8 @@ public void testAllowedModes() throws Exception { public void testEmptyConfig() throws Exception { AcHandling.Check check = (AcHandling.Check) new AcHandling().newInstance(obj().get()); assertNotNull("check should not be null", check); - assertEquals("default levelSet should be " + DEFAULT_LEVEL_SET, - check.levelSet, DEFAULT_LEVEL_SET); + Assert.assertEquals("default levelSet should be " + AcHandling.DEFAULT_LEVEL_SET, + check.levelSet, AcHandling.DEFAULT_LEVEL_SET); assertTrue("default allowedModes should be empty: " + check.allowedModes, check.allowedModes.isEmpty()); } diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignmentTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignmentTest.java new file mode 100644 index 000000000..00def84b8 --- /dev/null +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/CompositeStoreAlignmentTest.java @@ -0,0 +1,289 @@ +/* + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2013 - 2018 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package net.adamcin.oakpal.core.checks; + +import net.adamcin.oakpal.api.PathAction; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.Violation; +import net.adamcin.oakpal.core.CheckReport; +import net.adamcin.oakpal.testing.TestPackageUtil; +import org.apache.jackrabbit.vault.fs.config.MetaInf; +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.apache.jackrabbit.vault.packaging.PackageProperties; +import org.apache.jackrabbit.vault.packaging.registry.impl.JcrPackageRegistry; +import org.junit.Before; +import org.junit.Test; + +import javax.jcr.Node; +import javax.jcr.Session; +import javax.json.JsonObject; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +public class CompositeStoreAlignmentTest extends ProgressCheckTestBase { + + private File simpleContent; + private File simpleLibs; + private File simpleMixed; + + @Before + public void setUp() throws Exception { + simpleContent = TestPackageUtil.prepareTestPackageFromFolder("simple-content.zip", + new File("src/test/resources/simple-content")); + simpleLibs = TestPackageUtil.prepareTestPackageFromFolder("simple-libs.zip", + new File("src/test/resources/simple-libs")); + simpleMixed = TestPackageUtil.prepareTestPackageFromFolder("simple-mixed.zip", + new File("src/test/resources/simple-mixed")); + } + + @Test + public void testSimpleContent() throws Exception { + ProgressCheck check = new CompositeStoreAlignment().newInstance(obj().get()); + CheckReport reportValid = scanWithCheck(check, simpleContent); + assertEquals("No violations with simple content.", 0, reportValid.getViolations().size()); + } + + @Test + public void testSimpleLibs() throws Exception { + ProgressCheck check = new CompositeStoreAlignment().newInstance(obj().get()); + CheckReport reportValid = scanWithCheck(check, simpleLibs); + assertEquals("No violations with simple libs.", 0, reportValid.getViolations().size()); + } + + @Test + public void testSimpleMixed() throws Exception { + ProgressCheck check = new CompositeStoreAlignment().newInstance(obj().get()); + CheckReport reportValid = scanWithCheck(check, simpleMixed); + assertEquals("One violation with simple mixed. " + reportValid.getViolations().iterator().next(), + 1, reportValid.getViolations().size()); + } + + private static String getInstallationPath(final PackageId packageId) { + return JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH_PREFIX + packageId.getDownloadName(); + } + + @Test + public void testGetCheckName() throws Exception { + final ProgressCheck check = new CompositeStoreAlignment().newInstance(obj().get()); + assertEquals("check name should be", CompositeStoreAlignment.class.getSimpleName(), check.getCheckName()); + } + + final PackageId root = PackageId.fromString("my_packages:simple-mixed:1.0"); + final PackageId subAlpha = PackageId.fromString("my_packages:simple-mixed-sub-a:1.0"); + final PackageId subAlphaAlpha = PackageId.fromString("my_packages:simple-mixed-sub-a-a:1.0"); + final PackageId subBravo = PackageId.fromString("my_packages:simple-mixed-sub-b:1.0"); + final PackageId subCharlie = PackageId.fromString("my_packages:simple-mixed-sub-c:1.0"); + final PackageId subCharlieCharlie = PackageId.fromString("my_packages:simple-mixed-sub-c-c:1.0"); + final PackageId subDelta = PackageId.fromString("my_packages:simple-mixed-old-d:1.0"); + + private List virtualSubpackageScan(final JsonObject checkConfig) throws Exception { + final ProgressCheck check = new CompositeStoreAlignment().newInstance(checkConfig); + final Session session = mock(Session.class); + final Node node = mock(Node.class); + final PackageProperties rootProps = mock(PackageProperties.class); + final MetaInf rootMeta = mock(MetaInf.class); + final PathAction action = PathAction.MODIFIED; + + check.startedScan(); + check.identifyPackage(root, simpleMixed); + check.beforeExtract(root, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(root, "/", node, action); + check.importedPath(root, "/etc", node, action); + check.importedPath(root, JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH, node, action); + check.importedPath(root, getInstallationPath(subAlpha), node, action); + check.importedPath(root, getInstallationPath(subBravo), node, action); + check.importedPath(root, getInstallationPath(subCharlie), node, action); + check.deletedPath(root, getInstallationPath(subDelta), session); + check.afterExtract(root, session); + check.identifySubpackage(subAlpha, root); + check.beforeExtract(subAlpha, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(subAlpha, "/", node, action); + check.importedPath(subAlpha, "/etc", node, action); + check.importedPath(subAlpha, JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH, node, action); + check.importedPath(subAlpha, getInstallationPath(subAlphaAlpha), node, action); + check.importedPath(subAlpha, "/apps", node, action); + check.importedPath(subAlpha, "/apps/example-a", node, action); + check.afterExtract(subAlpha, session); + check.identifySubpackage(subAlphaAlpha, subAlpha); + check.beforeExtract(subAlphaAlpha, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(subAlphaAlpha, "/", node, action); + check.importedPath(subAlphaAlpha, "/apps", node, action); + check.importedPath(subAlphaAlpha, "/apps/example-a-a", node, action); + check.afterExtract(subAlphaAlpha, session); + check.identifySubpackage(subBravo, root); + check.beforeExtract(subBravo, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(subBravo, "/", node, action); + check.importedPath(subBravo, "/etc", node, action); + check.importedPath(subBravo, "/etc/clientlibs", node, action); + check.importedPath(subBravo, "/etc/clientlibs/example-b", node, action); + check.importedPath(subBravo, "/apps", node, action); + check.importedPath(subBravo, "/apps/example-b", node, action); + check.afterExtract(subBravo, session); + check.identifySubpackage(subCharlie, root); + check.beforeExtract(subCharlie, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(subCharlie, "/", node, action); + check.importedPath(subCharlie, "/etc", node, action); + check.importedPath(subCharlie, JcrPackageRegistry.DEFAULT_PACKAGE_ROOT_PATH, node, action); + check.importedPath(subCharlie, getInstallationPath(subCharlieCharlie), node, action); + check.importedPath(subCharlie, "/apps", node, action); + check.importedPath(subCharlie, "/apps/example-c", node, action); + check.afterExtract(subCharlie, session); + check.identifySubpackage(subCharlieCharlie, subCharlie); + check.beforeExtract(subCharlieCharlie, session, rootProps, rootMeta, Collections.emptyList()); + check.importedPath(subCharlieCharlie, "/", node, action); + check.importedPath(subCharlieCharlie, "/content", node, action); + check.importedPath(subCharlieCharlie, "/content/example-c-c", node, action); + check.afterExtract(subCharlieCharlie, session); + check.finishedScan(); + + return new ArrayList<>(check.getReportedViolations()); + } + + @Test + public void testSubpackageDefaults() throws Exception { + final List reports = virtualSubpackageScan(obj().get()); + assertFalse("reports not contains root (container): " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(root))); + assertFalse("reports not contains subA: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subAlpha))); + assertFalse("reports not contains subAA: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subAlphaAlpha))); + assertTrue("reports contains subB: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subBravo))); + assertTrue("reports contains subC: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subCharlie) + && violation.getDescription().startsWith("recursive"))); + assertFalse("reports not contains subCC: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subCharlieCharlie))); + } + + @Test + public void testIgnoredSubpackages() throws Exception { + final List reports = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().scopePackageIds(), arr() + .val(new Rule(Rule.RuleType.EXCLUDE, Pattern.compile(subBravo.toString())))) + .get()); + assertFalse("reports not contains root (container): " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(root))); + assertFalse("reports not contains subA: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subAlpha))); + assertFalse("reports not contains subAA: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subAlphaAlpha))); + assertFalse("reports not contains subB (ignored): " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subBravo))); + assertTrue("reports contains subC: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subCharlie) + && violation.getDescription().startsWith("recursive"))); + assertFalse("reports not contains subCC: " + reports, + reports.stream().anyMatch(violation -> violation.getPackages().contains(subCharlieCharlie))); + + } + + @Test + public void testConfigSeverity() throws Exception { + final List reportsDefault = virtualSubpackageScan(obj() + .get()); + assertEquals("reportsDefault has this many violations", 2, reportsDefault.size()); + assertFalse("reportsDefault has no severity minor", + reportsDefault.stream().anyMatch(violation -> violation.getSeverity() == Severity.MINOR)); + assertTrue("reportsDefault has severity major", + reportsDefault.stream().anyMatch(violation -> violation.getSeverity() == Severity.MAJOR)); + assertFalse("reportsDefault has no severity severe", + reportsDefault.stream().anyMatch(violation -> violation.getSeverity() == Severity.SEVERE)); + + final List reportsMinor = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().severity(), "minor") + .get()); + assertEquals("reportsMinor has this many violations", 2, reportsMinor.size()); + assertTrue("reportsMinor has severity minor", + reportsMinor.stream().anyMatch(violation -> violation.getSeverity() == Severity.MINOR)); + assertFalse("reportsMinor has no severity major", + reportsMinor.stream().anyMatch(violation -> violation.getSeverity() == Severity.MAJOR)); + assertFalse("reportsMinor has no severity severe", + reportsMinor.stream().anyMatch(violation -> violation.getSeverity() == Severity.SEVERE)); + + final List reportsMajor = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().severity(), "major") + .get()); + assertEquals("reportsMajor has this many violations", 2, reportsMajor.size()); + assertFalse("reportsMajor has no severity minor", + reportsMajor.stream().anyMatch(violation -> violation.getSeverity() == Severity.MINOR)); + assertTrue("reportsMajor has severity major", + reportsMajor.stream().anyMatch(violation -> violation.getSeverity() == Severity.MAJOR)); + assertFalse("reportsMajor has no severity severe", + reportsMajor.stream().anyMatch(violation -> violation.getSeverity() == Severity.SEVERE)); + + final List reportsSevere = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().severity(), "severe") + .get()); + assertEquals("reportsSevere has this many violations", 2, reportsSevere.size()); + assertFalse("reportsSevere has no severity minor", + reportsSevere.stream().anyMatch(violation -> violation.getSeverity() == Severity.MINOR)); + assertFalse("reportsSevere has no severity major", + reportsSevere.stream().anyMatch(violation -> violation.getSeverity() == Severity.MAJOR)); + assertTrue("reportsSevere has severity severe", + reportsSevere.stream().anyMatch(violation -> violation.getSeverity() == Severity.SEVERE)); + } + + @Test + public void testConfigMounts() throws Exception { + final List reportsDefault = virtualSubpackageScan(obj() + .get()); + assertEquals("reportsDefault has this many violations", 2, reportsDefault.size()); + + final List reportsNoMounts = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().mounts(), obj()) + .get()); + assertEquals("reportsNoMounts has this many violations", 0, reportsNoMounts.size()); + + final List reportsWithDefaultMount = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().mounts(), obj() + .key("", "/apps")) + + .get()); + assertEquals("reportsWithDefaultMount has this many violations", 0, reportsWithDefaultMount.size()); + + final List reportsJustApps = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().mounts(), obj() + .key("justApps", "/apps") + .get()) + .get()); + assertEquals("reportsJustApps has this many violations", 2, reportsJustApps.size()); + + final List reportsClientlibs = virtualSubpackageScan(obj() + .key(CompositeStoreAlignment.keys().mounts(), obj() + .key("clientlibs", arr("/etc/clientlibs", "/var/clientlibs")) + .get()) + .get()); + assertEquals("reportsClientlibs has this many violations", 1, reportsClientlibs.size()); + } +} \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/EchoTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/EchoTest.java index 8da6fab3e..b1403c62e 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/EchoTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/EchoTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ package net.adamcin.oakpal.core.checks; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.junit.Test; import javax.jcr.RepositoryException; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class EchoTest { @@ -67,6 +67,7 @@ public void testBeforeExtract() throws RepositoryException { @Test public void testImportedPath() throws RepositoryException { new Echo().importedPath(null, null, null); + new Echo().importedPath(null, null, null, null); } @Test diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectAcesTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectAcesTest.java index 2df2f00d7..64e5df16c 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectAcesTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectAcesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,11 @@ package net.adamcin.oakpal.core.checks; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.Violation; import org.apache.jackrabbit.api.JackrabbitWorkspace; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; @@ -33,6 +34,7 @@ import org.apache.jackrabbit.value.ValueFactoryImpl; import org.apache.jackrabbit.vault.packaging.PackageId; import org.jetbrains.annotations.NotNull; +import org.junit.Assert; import org.junit.Test; import javax.jcr.Node; @@ -53,15 +55,12 @@ import java.util.regex.Pattern; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -101,17 +100,17 @@ public void testNewInstance_multiPrincipal() throws Exception { .key("principal", " ").key("principals", arr(" ", "foo")) .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect 1 ace", 1, svCheck.expectedAces.size()); + Assert.assertEquals("expect 1 ace", 1, svCheck.expectedAces.size()); ExpectAces.Check mvCheck = checkFor(obj() .key("principal", " ").key("principals", arr("bar", "foo")) .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect 2 ace", 2, mvCheck.expectedAces.size()); + Assert.assertEquals("expect 2 ace", 2, mvCheck.expectedAces.size()); ExpectAces.Check jpCheck = checkFor(obj() .key("principal", "coo").key("principals", arr("bar", "foo")) .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect 1 ace", 1, jpCheck.expectedAces.size()); + Assert.assertEquals("expect 1 ace", 1, jpCheck.expectedAces.size()); } @Test @@ -120,22 +119,22 @@ public void testNewInstance_severity() throws Exception { .key("principal", "foo") .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect major (default)", Violation.Severity.MAJOR, defaultMajorCheck.severity); + Assert.assertEquals("expect major (default)", Severity.MAJOR, defaultMajorCheck.severity); ExpectAces.Check minorCheck = checkFor(obj() .key("principal", "foo").key("severity", "minor") .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect minor", Violation.Severity.MINOR, minorCheck.severity); + Assert.assertEquals("expect minor", Severity.MINOR, minorCheck.severity); ExpectAces.Check majorCheck = checkFor(obj() .key("principal", "foo").key("severity", "major") .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect major", Violation.Severity.MAJOR, majorCheck.severity); + Assert.assertEquals("expect major", Severity.MAJOR, majorCheck.severity); ExpectAces.Check severeCheck = checkFor(obj() .key("principal", "foo").key("severity", "severe") .key("expectedAces", arr("type=allow;path=/foo;privileges=jcr:read")) .get()); - assertEquals("expect severe", Violation.Severity.SEVERE, severeCheck.severity); + Assert.assertEquals("expect severe", Severity.SEVERE, severeCheck.severity); } @Test @@ -144,7 +143,7 @@ public void testNewInstance_empty() throws Exception { assertTrue("empty expectedAces", emptyCheck.expectedAces.isEmpty()); assertTrue("empty notExpectedPaths", emptyCheck.notExpectedAces.isEmpty()); assertTrue("empty afterPackageIdRules", emptyCheck.afterPackageIdRules.isEmpty()); - assertEquals("expect name", ExpectAces.class.getSimpleName(), emptyCheck.getCheckName()); + Assert.assertEquals("expect name", ExpectAces.class.getSimpleName(), emptyCheck.getCheckName()); emptyCheck.afterExtract(PackageId.fromString("hey"), mock(Session.class)); assertTrue("empty violations", emptyCheck.getReportedViolations().isEmpty()); } @@ -157,7 +156,7 @@ public void testNewInstance_afterPackageIdRules() throws Exception { ExpectAces.Check check1 = checkFor(key("principal", "nouser") .key(ExpectPaths.CONFIG_AFTER_PACKAGE_ID_RULES, expectedRules).get()); - assertEquals("expect afterPackageIdRules", expectedRules, check1.afterPackageIdRules); + Assert.assertEquals("expect afterPackageIdRules", expectedRules, check1.afterPackageIdRules); } @Test @@ -168,8 +167,8 @@ public void testNewInstance_expectedAces() throws Exception { ExpectAces.AceCriteria.parse(principal, spec).getOrDefault(null) ); ExpectAces.Check check1 = checkFor(key("principal", principal) - .key(ExpectAces.CONFIG_EXPECTED_ACES, arr(spec)).get()); - assertEquals("expect expectedAces", expectCriterias, check1.expectedAces); + .key(ExpectAces.keys().expectedAces(), arr(spec)).get()); + Assert.assertEquals("expect expectedAces", expectCriterias, check1.expectedAces); } @Test @@ -180,8 +179,8 @@ public void testNewInstance_notExpectedAces() throws Exception { ExpectAces.AceCriteria.parse(principal, spec).getOrDefault(null) ); ExpectAces.Check check1 = checkFor(key("principal", principal) - .key(ExpectAces.CONFIG_NOT_EXPECTED_ACES, arr(spec)).get()); - assertEquals("expect notExpectedAces", expectCriterias, check1.notExpectedAces); + .key(ExpectAces.keys().notExpectedAces(), arr(spec)).get()); + Assert.assertEquals("expect notExpectedAces", expectCriterias, check1.notExpectedAces); } @Test(expected = Exception.class) @@ -192,12 +191,12 @@ public void testParseAceCriteria_throws() throws Exception { @Test public void testCheck_startedScan() throws Exception { ExpectAces.Check check = checkFor(obj() - .key(ExpectAces.CONFIG_PRINCIPAL, "nouser") - .key(ExpectAces.CONFIG_EXPECTED_ACES, arr() + .key(ExpectAces.keys().principal(), "nouser") + .key(ExpectAces.keys().expectedAces(), arr() .val("type=allow;path=/foo1;privileges=jcr:read") .val("type=allow;path=/foo1;privileges=rep:write") ) - .key(ExpectAces.CONFIG_NOT_EXPECTED_ACES, arr() + .key(ExpectAces.keys().notExpectedAces(), arr() .val("type=allow;path=/foo2;privileges=jcr:read") .val("type=allow;path=/foo2;privileges=rep:write") // foo3 is not created. a non-existent path should satisfy not-expected aces @@ -236,13 +235,13 @@ public void testCheck_startedScan() throws Exception { check.finishedScan(); }); - assertEquals("expected violation count", 1, check.getReportedViolations().stream().filter(viol -> viol.getDescription().startsWith("expected: ")).count()); - assertEquals("expected violated spec ends with rep:write", 1, + Assert.assertEquals("expected violation count", 1, check.getReportedViolations().stream().filter(viol -> viol.getDescription().startsWith("expected: ")).count()); + Assert.assertEquals("expected violated spec ends with rep:write", 1, check.getReportedViolations().stream() .filter(viol -> viol.getDescription().startsWith("expected: ") && viol.getDescription().endsWith("rep:write")).count()); - assertEquals("unexpected violation count", 1, check.getReportedViolations().stream().filter(viol -> viol.getDescription().startsWith("unexpected: ")).count()); - assertEquals("unexpected violated spec ends with jcr:read", 1, + Assert.assertEquals("unexpected violation count", 1, check.getReportedViolations().stream().filter(viol -> viol.getDescription().startsWith("unexpected: ")).count()); + Assert.assertEquals("unexpected violated spec ends with jcr:read", 1, check.getReportedViolations().stream() .filter(viol -> viol.getDescription().startsWith("unexpected: ") && viol.getDescription().endsWith("jcr:read")).count()); @@ -263,7 +262,7 @@ public void testShouldExpectAfterExtract() throws Exception { assertTrue("expect true for empty", check1.shouldExpectAfterExtract(PackageId.fromString("foo"))); ExpectAces.Check check2 = checkFor(key("principal", "nouser") - .key(ExpectAces.CONFIG_AFTER_PACKAGE_ID_RULES, + .key(ExpectAces.keys().afterPackageIdRules(), arr().val(key("type", "include").key("pattern", "^my_packages:.*"))).get()); assertFalse("expect false", check2.shouldExpectAfterExtract(PackageId.fromString("adamcin:test:1.0"))); assertTrue("expect true", check2.shouldExpectAfterExtract(PackageId.fromString("my_packages:test:1.0"))); @@ -298,9 +297,9 @@ public void testAceCriteria_parse() throws Exception { Result noRestrictionsResult = ExpectAces.AceCriteria.parse("nouser", "type=allow;path=/foo;privileges=jcr:read"); ExpectAces.AceCriteria noRestrictionsCriteria = noRestrictionsResult.getOrDefault(null); assertNotNull("expect success with no restrictions", noRestrictionsCriteria); - assertEquals("expect path", "/foo", noRestrictionsCriteria.path); + Assert.assertEquals("expect path", "/foo", noRestrictionsCriteria.path); assertTrue("expect isAllow", noRestrictionsCriteria.isAllow); - assertArrayEquals("expect privileges", new String[]{"jcr:read"}, noRestrictionsCriteria.privileges); + Assert.assertArrayEquals("expect privileges", new String[]{"jcr:read"}, noRestrictionsCriteria.privileges); } @Test @@ -312,9 +311,9 @@ public void testAceCriteria_getSpec() { new ExpectAces.RestrictionCriteria("myRestriction", null) }, null); - assertEquals("expect spec", "type=deny;path=/foo;privileges=jcr:read,jcr:all;rep:glob=*;myRestriction", + Assert.assertEquals("expect spec", "type=deny;path=/foo;privileges=jcr:read,jcr:all;rep:glob=*;myRestriction", criteriaNoSpec.getSpec()); - assertEquals("expect toString", "principal:nouser ace:type=deny;path=/foo;privileges=jcr:read,jcr:all;rep:glob=*;myRestriction", + Assert.assertEquals("expect toString", "principal:nouser ace:type=deny;path=/foo;privileges=jcr:read,jcr:all;rep:glob=*;myRestriction", criteriaNoSpec.toString()); ExpectAces.AceCriteria criteriaWithSpec = new ExpectAces.AceCriteria("nouser", false, "/foo", new String[]{"jcr:read", "jcr:all"}, @@ -323,9 +322,9 @@ public void testAceCriteria_getSpec() { new ExpectAces.RestrictionCriteria("myRestriction", null) }, "whuheay!"); - assertEquals("expect whatever spec", "whuheay!", + Assert.assertEquals("expect whatever spec", "whuheay!", criteriaWithSpec.getSpec()); - assertEquals("expect whatever toString", "principal:nouser ace:whuheay!", + Assert.assertEquals("expect whatever toString", "principal:nouser ace:whuheay!", criteriaWithSpec.toString()); } @@ -491,14 +490,14 @@ public void testAceCriteria_satisfiedBy() throws Exception { @Test public void testRestrictionCriteria_equalsHashCode() { final ExpectAces.RestrictionCriteria left = new ExpectAces.RestrictionCriteria("left", null); - assertEquals("expect same", left, left); - assertEquals("expect same hash", left.hashCode(), left.hashCode()); - assertNotEquals("expect null not equal", left, null); + Assert.assertEquals("expect same", left, left); + Assert.assertEquals("expect same hash", left.hashCode(), left.hashCode()); + Assert.assertNotEquals("expect null not equal", left, null); final ExpectAces.RestrictionCriteria leftAgain = new ExpectAces.RestrictionCriteria("left", null); - assertEquals("expect same", left, leftAgain); - assertEquals("expect same hash", left.hashCode(), leftAgain.hashCode()); + Assert.assertEquals("expect same", left, leftAgain); + Assert.assertEquals("expect same hash", left.hashCode(), leftAgain.hashCode()); final ExpectAces.RestrictionCriteria right = new ExpectAces.RestrictionCriteria("right", null); - assertNotEquals("expect not same", left, right); - assertNotEquals("expect not same hash", left.hashCode(), right.hashCode()); + Assert.assertNotEquals("expect not same", left, right); + Assert.assertNotEquals("expect not same hash", left.hashCode(), right.hashCode()); } } \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectPathsTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectPathsTest.java index 030d9a19d..04c2d8a47 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectPathsTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/ExpectPathsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,11 @@ package net.adamcin.oakpal.core.checks; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.packaging.PackageId; +import org.junit.Assert; import org.junit.Test; import javax.jcr.Session; @@ -27,9 +30,9 @@ import java.util.List; import java.util.regex.Pattern; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -48,25 +51,25 @@ public void testNewInstance_empty() throws Exception { assertTrue("empty expectedPaths", emptyCheck.expectedPaths.isEmpty()); assertTrue("empty notExpectedPaths", emptyCheck.notExpectedPaths.isEmpty()); assertTrue("empty afterPackageIdRules", emptyCheck.afterPackageIdRules.isEmpty()); - assertEquals("expect name", ExpectPaths.class.getSimpleName(), emptyCheck.getCheckName()); + Assert.assertEquals("expect name", ExpectPaths.class.getSimpleName(), emptyCheck.getCheckName()); emptyCheck.afterExtract(PackageId.fromString("hey"), mock(Session.class)); assertTrue("empty violations", emptyCheck.getReportedViolations().isEmpty()); } @Test public void testNewInstance_expectedPaths() { - ExpectPaths.Check check1 = checkFor(key(ExpectPaths.CONFIG_EXPECTED_PATHS, arr("/foo1", "/foo2")).get()); - assertEquals("expect expectedPaths", Arrays.asList("/foo1", "/foo2"), check1.expectedPaths); - ExpectPaths.Check check2 = checkFor(key(ExpectPaths.CONFIG_EXPECTED_PATHS, arr("/foo2", "/foo1")).get()); - assertEquals("expect expectedPaths", Arrays.asList("/foo2", "/foo1"), check2.expectedPaths); + ExpectPaths.Check check1 = checkFor(key(ExpectPaths.keys().expectedPaths(), arr("/foo1", "/foo2")).get()); + Assert.assertEquals("expect expectedPaths", Arrays.asList("/foo1", "/foo2"), check1.expectedPaths); + ExpectPaths.Check check2 = checkFor(key(ExpectPaths.keys().expectedPaths(), arr("/foo2", "/foo1")).get()); + Assert.assertEquals("expect expectedPaths", Arrays.asList("/foo2", "/foo1"), check2.expectedPaths); } @Test public void testNewInstance_notExpectedPaths() { - ExpectPaths.Check check1 = checkFor(key(ExpectPaths.CONFIG_NOT_EXPECTED_PATHS, arr("/foo1", "/foo2")).get()); - assertEquals("expect notExpectedPaths", Arrays.asList("/foo1", "/foo2"), check1.notExpectedPaths); - ExpectPaths.Check check2 = checkFor(key(ExpectPaths.CONFIG_NOT_EXPECTED_PATHS, arr("/foo2", "/foo1")).get()); - assertEquals("expect notExpectedPaths", Arrays.asList("/foo2", "/foo1"), check2.notExpectedPaths); + ExpectPaths.Check check1 = checkFor(key(ExpectPaths.keys().notExpectedPaths(), arr("/foo1", "/foo2")).get()); + Assert.assertEquals("expect notExpectedPaths", Arrays.asList("/foo1", "/foo2"), check1.notExpectedPaths); + ExpectPaths.Check check2 = checkFor(key(ExpectPaths.keys().notExpectedPaths(), arr("/foo2", "/foo1")).get()); + Assert.assertEquals("expect notExpectedPaths", Arrays.asList("/foo2", "/foo1"), check2.notExpectedPaths); } @Test @@ -75,8 +78,8 @@ public void testNewInstance_afterPackageIdRules() { new Rule(Rule.RuleType.INCLUDE, Pattern.compile("whua")), new Rule(Rule.RuleType.EXCLUDE, Pattern.compile("heyy"))); - ExpectPaths.Check check1 = checkFor(key(ExpectPaths.CONFIG_AFTER_PACKAGE_ID_RULES, expectedRules).get()); - assertEquals("expect afterPackageIdRules", expectedRules, check1.afterPackageIdRules); + ExpectPaths.Check check1 = checkFor(key(ExpectPaths.keys().afterPackageIdRules(), expectedRules).get()); + Assert.assertEquals("expect afterPackageIdRules", expectedRules, check1.afterPackageIdRules); } @Test @@ -84,22 +87,22 @@ public void testNewInstance_severity() { ExpectPaths.Check defaultMajorCheck = checkFor(obj() .key("expectedPaths", arr("/foo")) .get()); - assertEquals("expect major (default)", Violation.Severity.MAJOR, defaultMajorCheck.severity); + Assert.assertEquals("expect major (default)", Severity.MAJOR, defaultMajorCheck.severity); ExpectPaths.Check minorCheck = checkFor(obj() .key("severity", "minor") .key("expectedPaths", arr("/foo")) .get()); - assertEquals("expect minor", Violation.Severity.MINOR, minorCheck.severity); + Assert.assertEquals("expect minor", Severity.MINOR, minorCheck.severity); ExpectPaths.Check majorCheck = checkFor(obj() .key("severity", "major") .key("expectedPaths", arr("/foo")) .get()); - assertEquals("expect major", Violation.Severity.MAJOR, majorCheck.severity); + Assert.assertEquals("expect major", Severity.MAJOR, majorCheck.severity); ExpectPaths.Check severeCheck = checkFor(obj() .key("severity", "severe") .key("expectedPaths", arr("/foo")) .get()); - assertEquals("expect severe", Violation.Severity.SEVERE, severeCheck.severity); + Assert.assertEquals("expect severe", Severity.SEVERE, severeCheck.severity); } @Test @@ -107,7 +110,7 @@ public void testShouldExpectAfterExtract() { ExpectPaths.Check check1 = checkFor(obj().get()); assertTrue("expect true for empty", check1.shouldExpectAfterExtract(PackageId.fromString("foo"))); - ExpectPaths.Check check2 = checkFor(key(ExpectPaths.CONFIG_AFTER_PACKAGE_ID_RULES, arr() + ExpectPaths.Check check2 = checkFor(key(ExpectPaths.keys().afterPackageIdRules(), arr() .val(key("type", "include").key("pattern", "^my_packages:.*"))).get()); assertFalse("expect false", check2.shouldExpectAfterExtract(PackageId.fromString("adamcin:test:1.0"))); assertTrue("expect true", check2.shouldExpectAfterExtract(PackageId.fromString("my_packages:test:1.0"))); @@ -142,7 +145,7 @@ public void testAfterExtract() throws Exception { @Test public void testStartedScan() throws Exception { - ExpectPaths.Check check = checkFor(key(ExpectPaths.CONFIG_EXPECTED_PATHS, arr("/foo")).get()); + ExpectPaths.Check check = checkFor(key(ExpectPaths.keys().expectedPaths(), arr("/foo")).get()); final PackageId pid = PackageId.fromString("foo"); final Session session = mock(Session.class); check.afterExtract(pid, session); diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/FilterSetsTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/FilterSetsTest.java index 79d606705..ccb0e6042 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/FilterSetsTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/FilterSetsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,17 @@ package net.adamcin.oakpal.core.checks; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Severity; import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertTrue; + public class FilterSetsTest extends ProgressCheckTestBase { @Test @@ -34,7 +34,7 @@ public void testRootImport() throws Exception { TestUtil.testBlock(() -> { ProgressCheck handler = new FilterSets().newInstance(obj().get()); CheckReport report = scanWithCheck(handler, "testrootimport.zip"); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -43,7 +43,7 @@ public void testRootImport() throws Exception { ProgressCheck handler = new FilterSets().newInstance(key("allowRootFilter", true).get()); CheckReport report = scanWithCheck(handler, "testrootimport.zip"); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -52,46 +52,46 @@ public void testRootImport() throws Exception { @Test public void testModeMerge() throws Exception { TestUtil.testBlock(() -> { - ProgressCheck handler = new FilterSets().newInstance(obj().get()); - CheckReport report = scanWithCheck(handler, "tmp_mode_merge.zip"); + ProgressCheck handler = new FilterSets().newInstance(obj().get()); + CheckReport report = scanWithCheck(handler, "tmp_mode_merge.zip"); - assertEquals("one violation", 1, report.getViolations().size()); - assertEquals("is severity", Violation.Severity.MINOR, - report.getViolations().iterator().next().getSeverity()); - assertTrue("all violations have packageIds", report.getViolations().stream() - .allMatch(viol -> !viol.getPackages().isEmpty())); + Assert.assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("is severity", Severity.MINOR, + report.getViolations().iterator().next().getSeverity()); + assertTrue("all violations have packageIds", report.getViolations().stream() + .allMatch(viol -> !viol.getPackages().isEmpty())); }); TestUtil.testBlock(() -> { - ProgressCheck handler = new FilterSets().newInstance(key("importModeSeverity", "severe").get()); - CheckReport report = scanWithCheck(handler, "tmp_mode_merge.zip"); + ProgressCheck handler = new FilterSets().newInstance(key("importModeSeverity", "severe").get()); + CheckReport report = scanWithCheck(handler, "tmp_mode_merge.zip"); - assertEquals("one violation", 1, report.getViolations().size()); - assertEquals("is severity", Violation.Severity.SEVERE, - report.getViolations().iterator().next().getSeverity()); - assertTrue("all violations have packageIds", report.getViolations().stream() - .allMatch(viol -> !viol.getPackages().isEmpty())); + Assert.assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("is severity", Severity.SEVERE, + report.getViolations().iterator().next().getSeverity()); + assertTrue("all violations have packageIds", report.getViolations().stream() + .allMatch(viol -> !viol.getPackages().isEmpty())); }); } @Test public void testEmptyFilter() throws Exception { TestUtil.testBlock(() -> { - ProgressCheck handler = new FilterSets().newInstance(obj().get()); - CheckReport report = scanWithCheck(handler, "tmp_foo_bar_test_nofilter.zip"); + ProgressCheck handler = new FilterSets().newInstance(obj().get()); + CheckReport report = scanWithCheck(handler, "tmp_foo_bar_test_nofilter.zip"); - assertEquals("one violation", 1, report.getViolations().size()); - assertEquals("is severity", Violation.Severity.MAJOR, - report.getViolations().iterator().next().getSeverity()); - assertTrue("all violations have packageIds", report.getViolations().stream() - .allMatch(viol -> !viol.getPackages().isEmpty())); + Assert.assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("is severity", Severity.MAJOR, + report.getViolations().iterator().next().getSeverity()); + assertTrue("all violations have packageIds", report.getViolations().stream() + .allMatch(viol -> !viol.getPackages().isEmpty())); }); TestUtil.testBlock(() -> { - ProgressCheck handler = new FilterSets().newInstance(key("allowEmptyFilter", true).get()); - CheckReport report = scanWithCheck(handler, "tmp_foo_bar_test_nofilter.zip"); + ProgressCheck handler = new FilterSets().newInstance(key("allowEmptyFilter", true).get()); + CheckReport report = scanWithCheck(handler, "tmp_foo_bar_test_nofilter.zip"); - assertEquals("no violations", 0, report.getViolations().size()); - assertTrue("all violations have packageIds", report.getViolations().stream() - .allMatch(viol -> !viol.getPackages().isEmpty())); + Assert.assertEquals("no violations", 0, report.getViolations().size()); + assertTrue("all violations have packageIds", report.getViolations().stream() + .allMatch(viol -> !viol.getPackages().isEmpty())); }); } } diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/JcrPropertiesTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/JcrPropertiesTest.java index af62f75dd..bc15c49c0 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/JcrPropertiesTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/JcrPropertiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,26 @@ package net.adamcin.oakpal.core.checks; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.core.CheckReport; +import net.adamcin.oakpal.testing.TestPackageUtil; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; +import org.junit.Test; import java.io.File; +import java.util.Collections; +import java.util.ResourceBundle; import java.util.regex.Pattern; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; -import net.adamcin.oakpal.testing.TestPackageUtil; -import org.junit.Test; +import static java.util.Collections.emptyList; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class JcrPropertiesTest extends ProgressCheckTestBase { @@ -42,7 +48,7 @@ public void testDenyIfAbsent() throws Exception { .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); logViolations("double_man, no scope", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -54,7 +60,7 @@ public void testDenyIfAbsent() throws Exception { .and(key("pattern", "/tmp/jcr:content").key("type", "allow"))) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -67,7 +73,7 @@ public void testDenyIfAbsent() throws Exception { .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); logViolations("double_man, with scope", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -82,7 +88,7 @@ public void testDenyIfPresent() throws Exception { .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); logViolations("double_nan, no scope", report); - assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); + Assert.assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -94,7 +100,7 @@ public void testDenyIfPresent() throws Exception { .and(key("pattern", "/tmp/jcr:content").key("type", "allow"))) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); + Assert.assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -107,7 +113,7 @@ public void testDenyIfPresent() throws Exception { .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); logViolations("double_nan, with scope", report); - assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); + Assert.assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -123,7 +129,7 @@ public void testRequireType() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); + Assert.assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -135,7 +141,7 @@ public void testRequireType() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); + Assert.assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -148,7 +154,7 @@ public void testDenyNodeTypes() throws Exception { .key("denyNodeTypes", arr("sling:Folder")) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); + Assert.assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -157,7 +163,7 @@ public void testDenyNodeTypes() throws Exception { .key("denyNodeTypes", arr("nt:unstructured")) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); + Assert.assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -173,7 +179,7 @@ public void testScopeNodeTypes() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); + Assert.assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -186,7 +192,7 @@ public void testScopeNodeTypes() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); + Assert.assertEquals("two violations: " + report.getViolations(), 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -199,7 +205,7 @@ public void testScopeNodeTypes() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, "double_properties.zip"); - assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); + Assert.assertEquals("no violations: " + report.getViolations(), 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -222,7 +228,7 @@ public void testPropertyValueDeny() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, playground); - assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); + Assert.assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); }); TestUtil.testBlock(() -> { ProgressCheck check = new JcrProperties().newInstance(obj() @@ -234,7 +240,7 @@ public void testPropertyValueDeny() throws Exception { ) .get()); CheckReport report = scanWithCheck(check, playground); - assertEquals("two violation: " + report.getViolations(), 2, report.getViolations().size()); + Assert.assertEquals("two violation: " + report.getViolations(), 2, report.getViolations().size()); }); } @@ -253,7 +259,32 @@ public void testDenyIfMultivalued() throws Exception { .get()); CheckReport report = scanWithCheck(check, playground); // only /apps/acme has the incorrect singleString property - assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); + Assert.assertEquals("one violation: " + report.getViolations(), 1, report.getViolations().size()); }); } + + @Test + public void testSetResourceBundle() { + final JcrProperties.ResourceBundleHolder resourceBundleHolder = + new JcrProperties.ResourceBundleHolder(); + JcrPropertyConstraints constraints = new JcrPropertyConstraints("prop", false, + false, false, "nt:base", emptyList(), Severity.MAJOR, + resourceBundleHolder::getResourceBundle); + JcrProperties.Check check = new JcrProperties.Check(emptyList(), emptyList(), emptyList(), + Collections.singletonList(constraints), resourceBundleHolder); + + final ResourceBundle fromHolder = resourceBundleHolder.getResourceBundle(); + final ResourceBundle fromCheck = check.getResourceBundle(); + assertSame("expect same resource bundle due to internal caching", fromHolder, fromCheck); + + final ResourceBundle toSetForTest = ResourceBundle.getBundle(getClass().getName()); + check.setResourceBundle(toSetForTest); + final ResourceBundle fromHolderAfterSet = resourceBundleHolder.getResourceBundle(); + final ResourceBundle fromCheckAfterSet = check.getResourceBundle(); + assertSame("expect same resource bundle after set", fromHolderAfterSet, fromCheckAfterSet); + + final String milliString = String.valueOf(System.currentTimeMillis()); + assertSame("same key returned from constraints when not in bundle", + milliString, constraints.getString(milliString)); + } } diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/OverlapsTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/OverlapsTest.java index 0bc59519a..c031f6a96 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/OverlapsTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/OverlapsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,15 @@ package net.adamcin.oakpal.core.checks; -import static net.adamcin.oakpal.core.JavaxJson.obj; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - +import net.adamcin.oakpal.api.ProgressCheck; import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.obj; +import static org.junit.Assert.assertTrue; + public class OverlapsTest extends ProgressCheckTestBase { @Test public void testOverlaps() throws Exception { @@ -32,7 +32,7 @@ public void testOverlaps() throws Exception { ProgressCheck check = new Overlaps().newInstance(obj().get()); CheckReport report = scanWithCheck(check, "test_a-1.0.zip", "test_b-1.0.zip"); logViolations("testOverlaps:none", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -40,7 +40,7 @@ public void testOverlaps() throws Exception { ProgressCheck check = new Overlaps().newInstance(obj().get()); CheckReport report = scanWithCheck(check, "tmp_foo.zip", "tmp_foo_bar.zip", "tmp_foo_bar_test.zip"); logViolations("testOverlaps:[foo, foo_bar, foo_bar_test]", report); - assertEquals("two violations", 2, report.getViolations().size()); + Assert.assertEquals("two violations", 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -48,15 +48,15 @@ public void testOverlaps() throws Exception { ProgressCheck check = new Overlaps().newInstance(obj().get()); CheckReport report = scanWithCheck(check, "tmp_foo_bar_test.zip", "tmp_foo_bar.zip", "tmp_foo.zip"); logViolations("testOverlaps:[foo_bar_test, foo_bar, foo]", report); - assertEquals("two violations", 2, report.getViolations().size()); + Assert.assertEquals("two violations", 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); TestUtil.testBlock(() -> { - ProgressCheck check = new Overlaps().newInstance(obj().key(Overlaps.CONFIG_REPORT_ALL_OVERLAPS, true).get()); + ProgressCheck check = new Overlaps().newInstance(obj().key(Overlaps.keys().reportAllOverlaps(), true).get()); CheckReport report = scanWithCheck(check, "tmp_foo_bar_test.zip", "tmp_foo_bar.zip", "tmp_foo.zip"); logViolations("testOverlaps:[foo_bar_test, foo_bar, foo]:reportAllOverlaps", report); - assertEquals("three violations", 3, report.getViolations().size()); + Assert.assertEquals("three violations", 3, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/PathsTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/PathsTest.java index db8baa912..2b010409a 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/PathsTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/PathsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,25 @@ package net.adamcin.oakpal.core.checks; -import static java.util.Collections.singletonList; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.Rule; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.Violation; +import net.adamcin.oakpal.core.CheckReport; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; +import org.junit.Test; import java.util.Collections; +import java.util.Locale; +import java.util.ResourceBundle; import java.util.regex.Pattern; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; -import net.adamcin.oakpal.core.Violation; -import org.junit.Test; +import static java.util.Collections.singletonList; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class PathsTest extends ProgressCheckTestBase { @@ -42,9 +46,9 @@ public void testDefaultSeverity() throws Exception { ProgressCheck check = new Paths().newInstance(key("rules", arr(denyEtc)).get()); CheckReport report = scanWithCheck(check, "test-package-with-etc.zip"); logViolations("level_set:no_unsafe", report); - assertEquals("violations", 6, report.getViolations().size()); + Assert.assertEquals("violations", 6, report.getViolations().size()); assertTrue("all violations are MAJOR", report.getViolations().stream() - .allMatch(viol -> viol.getSeverity().equals(Violation.Severity.MAJOR))); + .allMatch(viol -> viol.getSeverity().equals(Severity.MAJOR))); }); } @@ -54,9 +58,9 @@ public void testCustomSeverity() throws Exception { ProgressCheck check = new Paths().newInstance(key("severity", "SEVERE").key("rules", arr(denyEtc)).get()); CheckReport report = scanWithCheck(check, "test-package-with-etc.zip"); logViolations("level_set:no_unsafe", report); - assertEquals("violations", 6, report.getViolations().size()); + Assert.assertEquals("violations", 6, report.getViolations().size()); assertTrue("all violations are SEVERE", report.getViolations().stream() - .allMatch(viol -> viol.getSeverity().equals(Violation.Severity.SEVERE))); + .allMatch(viol -> viol.getSeverity().equals(Severity.SEVERE))); }); } @@ -66,9 +70,9 @@ public void testCustomSeverityMixedCase() throws Exception { ProgressCheck check = new Paths().newInstance(key("severity", "minor").key("rules", arr(denyEtc)).get()); CheckReport report = scanWithCheck(check, "test-package-with-etc.zip"); logViolations("level_set:no_unsafe", report); - assertEquals("violations", 6, report.getViolations().size()); + Assert.assertEquals("violations", 6, report.getViolations().size()); assertTrue("all violations are MINOR", report.getViolations().stream() - .allMatch(viol -> viol.getSeverity().equals(Violation.Severity.MINOR))); + .allMatch(viol -> viol.getSeverity().equals(Severity.MINOR))); }); } @@ -93,7 +97,8 @@ public void testDeletedPath() throws Exception { allDeletesCheck.deletedPath(null, "/foo", null); assertFalse("reported violations should not be empty after deletedPath", allDeletesCheck.getReportedViolations().isEmpty()); - Paths.Check allDeletesCheckByConfig = (Paths.Check) new Paths().newInstance(key(Paths.CONFIG_DENY_ALL_DELETES, true).get()); + Paths.Check allDeletesCheckByConfig = (Paths.Check) new Paths() + .newInstance(key(Paths.keys().denyAllDeletes(), true).get()); assertTrue("reported violations should be empty before deletedPath", allDeletesCheckByConfig.getReportedViolations().isEmpty()); allDeletesCheckByConfig.deletedPath(null, "/foo", null); @@ -119,4 +124,18 @@ public void testDeletedPath() throws Exception { assertFalse("reported violations should not be empty after deletedPath for /bar", deletesByRuleCheck.getReportedViolations().isEmpty()); } + + @Test + public void testDeletedPath_de() throws Exception { + Paths.Check allDeletesCheck = new Paths.Check(Collections.emptyList(), true, Paths.DEFAULT_SEVERITY); + allDeletesCheck.setResourceBundle(ResourceBundle.getBundle(allDeletesCheck.getResourceBundleBaseName(), Locale.GERMAN)); + + allDeletesCheck.deletedPath(null, "/foo", null); + assertFalse("reported violations should not be empty after deletedPath", + allDeletesCheck.getReportedViolations().isEmpty()); + Violation violation = allDeletesCheck.getReportedViolations().iterator().next(); + assertTrue("violation description should be in german: " + violation.getDescription(), + violation.getDescription().contains("gelöschte")); + + } } diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/ProgressCheckTestBase.java b/core/src/test/java/net/adamcin/oakpal/core/checks/ProgressCheckTestBase.java index aa2da9b1f..4fd7dece0 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/ProgressCheckTestBase.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/ProgressCheckTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,12 @@ package net.adamcin.oakpal.core.checks; -import static org.junit.Assert.assertTrue; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.core.CheckReport; +import net.adamcin.oakpal.core.OakMachine; +import net.adamcin.oakpal.testing.TestPackageUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.util.ArrayList; @@ -24,12 +29,7 @@ import java.util.List; import java.util.Optional; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.testing.TestPackageUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertTrue; public class ProgressCheckTestBase { protected final Logger LOGGER = LoggerFactory.getLogger(getClass()); diff --git a/core/src/test/java/net/adamcin/oakpal/core/checks/SubpackagesTest.java b/core/src/test/java/net/adamcin/oakpal/core/checks/SubpackagesTest.java index bc9ab2384..e9f86a8f6 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/checks/SubpackagesTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/checks/SubpackagesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,16 @@ package net.adamcin.oakpal.core.checks; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - +import net.adamcin.oakpal.api.ProgressCheck; import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.ProgressCheck; -import net.adamcin.oakpal.core.TestUtil; +import net.adamcin.oakpal.testing.TestUtil; +import org.junit.Assert; import org.junit.Test; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static org.junit.Assert.assertTrue; + public class SubpackagesTest extends ProgressCheckTestBase { @Test @@ -34,7 +34,7 @@ public void testDenyAll() throws Exception { ProgressCheck check = new Subpackages().newInstance(key("denyAll", false).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("denyAll:false", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -42,7 +42,7 @@ public void testDenyAll() throws Exception { ProgressCheck check = new Subpackages().newInstance(key("denyAll", true).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("denyAll:false", report); - assertEquals("two violations", 2, report.getViolations().size()); + Assert.assertEquals("two violations", 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -54,7 +54,7 @@ public void testPatterns() throws Exception { ProgressCheck check = new Subpackages().newInstance(key("rules", arr()).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:[]", report); - assertEquals("no violations", 0, report.getViolations().size()); + Assert.assertEquals("no violations", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -63,7 +63,7 @@ public void testPatterns() throws Exception { key("rules", arr(key("type", "deny").key("pattern", "my_packages:sub_.*"))).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:sub_.*", report); - assertEquals("two violations", 2, report.getViolations().size()); + Assert.assertEquals("two violations", 2, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -75,7 +75,7 @@ public void testPatterns() throws Exception { ).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:sub_.* - sub_a", report); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -84,7 +84,7 @@ public void testPatterns() throws Exception { key("rules", arr(key("type", "deny").key("pattern", "my_packages:sub_a"))).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:sub_a", report); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -93,7 +93,7 @@ public void testPatterns() throws Exception { key("rules", arr(key("type", "deny").key("pattern", "my_packages:sub_b"))).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:sub_b", report); - assertEquals("one violation", 1, report.getViolations().size()); + Assert.assertEquals("one violation", 1, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); @@ -102,7 +102,7 @@ public void testPatterns() throws Exception { key("rules", arr(key("type", "deny").key("pattern", "my_packages:sub_c"))).get()); CheckReport report = scanWithCheck(check, "subtest_with_content.zip"); logViolations("testPatterns:sub_c", report); - assertEquals("one violation", 0, report.getViolations().size()); + Assert.assertEquals("one violation", 0, report.getViolations().size()); assertTrue("all violations have packageIds", report.getViolations().stream() .allMatch(viol -> !viol.getPackages().isEmpty())); }); diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/FacadeGetterMapping.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/FacadeGetterMapping.java index 8696c80c2..acaf47d82 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/FacadeGetterMapping.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/FacadeGetterMapping.java @@ -25,7 +25,7 @@ import java.util.Iterator; import java.util.function.Function; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import org.jetbrains.annotations.NotNull; public class FacadeGetterMapping { diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/ItemFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/ItemFacadeTest.java index 20dc3c008..5179787fc 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/ItemFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/ItemFacadeTest.java @@ -36,11 +36,9 @@ import javax.jcr.version.Version; import javax.jcr.version.VersionHistory; -import net.adamcin.oakpal.core.Fun; import net.adamcin.oakpal.core.ListenerReadOnlyException; import net.adamcin.oakpal.core.jcrfacade.version.VersionFacade; import net.adamcin.oakpal.core.jcrfacade.version.VersionHistoryFacade; -import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/JackrabbitSessionFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/JackrabbitSessionFacadeTest.java index 61bfe43ee..9c77588c7 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/JackrabbitSessionFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/JackrabbitSessionFacadeTest.java @@ -26,7 +26,6 @@ import javax.jcr.Session; import net.adamcin.oakpal.core.AbortedScanException; -import net.adamcin.oakpal.core.Fun; import net.adamcin.oakpal.core.OakMachine; import net.adamcin.oakpal.core.jcrfacade.security.user.UserManagerFacade; import org.apache.jackrabbit.JcrConstants; diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/NodeFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/NodeFacadeTest.java index ba8db297c..af94f751d 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/NodeFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/NodeFacadeTest.java @@ -41,7 +41,6 @@ import javax.jcr.version.Version; import javax.jcr.version.VersionHistory; -import net.adamcin.oakpal.core.Fun; import net.adamcin.oakpal.core.ListenerReadOnlyException; import net.adamcin.oakpal.core.jcrfacade.lock.LockFacade; import net.adamcin.oakpal.core.jcrfacade.version.VersionFacade; diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/WorkspaceFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/WorkspaceFacadeTest.java index ee8847146..cbf26a740 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/WorkspaceFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/WorkspaceFacadeTest.java @@ -33,7 +33,6 @@ import javax.jcr.version.Version; import javax.jcr.version.VersionManager; -import net.adamcin.oakpal.core.Fun; import net.adamcin.oakpal.core.ListenerReadOnlyException; import net.adamcin.oakpal.core.jcrfacade.lock.LockManagerFacade; import net.adamcin.oakpal.core.jcrfacade.nodetype.NodeTypeManagerFacade; @@ -42,7 +41,6 @@ import net.adamcin.oakpal.core.jcrfacade.version.VersionManagerFacade; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.JackrabbitWorkspace; -import org.jetbrains.annotations.NotNull; import org.junit.Test; public class WorkspaceFacadeTest { diff --git a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/query/QueryFacadeTest.java b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/query/QueryFacadeTest.java index d0664f856..96a81bd45 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/query/QueryFacadeTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/jcrfacade/query/QueryFacadeTest.java @@ -32,7 +32,7 @@ import javax.jcr.query.Query; import javax.jcr.query.QueryResult; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.ListenerReadOnlyException; import net.adamcin.oakpal.core.jcrfacade.FacadeGetterMapping; import net.adamcin.oakpal.core.jcrfacade.JcrSessionFacade; diff --git a/core/src/test/java/net/adamcin/oakpal/core/opear/AdhocOpearTest.java b/core/src/test/java/net/adamcin/oakpal/core/opear/AdhocOpearTest.java new file mode 100644 index 000000000..e59a5da2e --- /dev/null +++ b/core/src/test/java/net/adamcin/oakpal/core/opear/AdhocOpearTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core.opear; + +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.Result; +import org.junit.Test; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +import static org.junit.Assert.*; + +public class AdhocOpearTest { + + @Test + public void testFromPlanFile() { + final File planFile = new File("src/test/resources/OpearFileTest/folders_on_classpath/plan.json"); + Result opearResult = AdhocOpear.fromPlanFile(planFile, null); + + assertFalse("opear should not be a failure", opearResult.isFailure()); + AdhocOpear opear = opearResult.getOrDefault(null); + assertNotNull("opear is not null", opear); + + final ClassLoader parent = getClass().getClassLoader(); + final ClassLoader planCl = opear.getPlanClassLoader(parent); + assertNotSame("not same classloader", parent, planCl); + assertSame("expect same parent", parent, planCl.getParent()); + + assertTrue("expect instance of URLClassLoader", planCl instanceof URLClassLoader); + final URL firstUrl = ((URLClassLoader) planCl).getURLs()[0]; + + assertEquals("expect planFile parent is referenced by first url", + planFile.getParentFile().getAbsolutePath(), + Fun.tryOrOptional0(() -> new File(firstUrl.toURI()).getAbsolutePath()).get().orElse("")); + } + + @Test + public void testFromPlanFileWithBaseDir() { + final File targetDir = new File("target"); + final File planFile = new File("src/test/resources/OpearFileTest/folders_on_classpath/plan.json"); + Result opearResult = AdhocOpear.fromPlanFile(planFile, targetDir); + + assertFalse("opear should not be a failure", opearResult.isFailure()); + AdhocOpear opear = opearResult.getOrDefault(null); + assertNotNull("opear is not null", opear); + + final ClassLoader parent = getClass().getClassLoader(); + final ClassLoader planCl = opear.getPlanClassLoader(parent); + assertNotSame("not same classloader", parent, planCl); + assertSame("expect same parent", parent, planCl.getParent()); + + assertTrue("expect instance of URLClassLoader", planCl instanceof URLClassLoader); + final URL firstUrl = ((URLClassLoader) planCl).getURLs()[0]; + + assertEquals("expect planFile parent is referenced by first url", + targetDir.getAbsolutePath(), + Fun.tryOrOptional0(() -> new File(firstUrl.toURI()).getAbsolutePath()).get().orElse("")); + } +} \ No newline at end of file diff --git a/core/src/test/java/net/adamcin/oakpal/core/OpearFileTest.java b/core/src/test/java/net/adamcin/oakpal/core/opear/OpearFileTest.java similarity index 93% rename from core/src/test/java/net/adamcin/oakpal/core/OpearFileTest.java rename to core/src/test/java/net/adamcin/oakpal/core/opear/OpearFileTest.java index fbf5533b8..9dbeadcd8 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/OpearFileTest.java +++ b/core/src/test/java/net/adamcin/oakpal/core/opear/OpearFileTest.java @@ -1,5 +1,23 @@ -package net.adamcin.oakpal.core; - +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.core.opear; + +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.core.OakpalPlan; import net.adamcin.oakpal.testing.TestPackageUtil; import org.apache.commons.io.FileUtils; import org.junit.Test; @@ -25,8 +43,8 @@ import java.util.jar.JarOutputStream; import java.util.jar.Manifest; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.OpearFile.NAME_CLASS_PATH; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.core.opear.OpearFile.NAME_CLASS_PATH; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/core/src/test/java/net/adamcin/oakpal/it/ChecklistIT.java b/core/src/test/java/net/adamcin/oakpal/it/ChecklistIT.java index 17bd0911e..17c77018b 100644 --- a/core/src/test/java/net/adamcin/oakpal/it/ChecklistIT.java +++ b/core/src/test/java/net/adamcin/oakpal/it/ChecklistIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,8 @@ public void testLoadChecklists() throws Exception { "subpackages", "acHandling", "filterSets", - "overlaps" + "overlaps", + "composite-store-alignment" ).map(name -> OAKPAL_MODULE_NAME + "/" + OAKPAL_CHECKLIST_BASIC + "/" + name) .collect(Collectors.toList()); diff --git a/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.js b/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.js new file mode 100644 index 000000000..6ddbacfbb --- /dev/null +++ b/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.js @@ -0,0 +1,19 @@ +/* + * Copyright 2019 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function getCheckName() { + return "some check"; +} \ No newline at end of file diff --git a/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.properties b/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.properties new file mode 100644 index 000000000..c10f9295e --- /dev/null +++ b/core/src/test/resources/ScriptProgressCheckTest/checkWithResources.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +testKey=yeKtset \ No newline at end of file diff --git a/core/src/test/resources/ScriptProgressCheckTest/overrideResourceBundle.properties b/core/src/test/resources/ScriptProgressCheckTest/overrideResourceBundle.properties new file mode 100644 index 000000000..c1a714764 --- /dev/null +++ b/core/src/test/resources/ScriptProgressCheckTest/overrideResourceBundle.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +testKey=yeKtsettestKey \ No newline at end of file diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml index 2e4368e3c..9aae311b0 100644 --- a/core/src/test/resources/logback-test.xml +++ b/core/src/test/resources/logback-test.xml @@ -28,7 +28,5 @@ - - \ No newline at end of file diff --git a/core/src/test/resources/net/adamcin/oakpal/core/DefaultErrorListenerTest.properties b/core/src/test/resources/net/adamcin/oakpal/core/DefaultErrorListenerTest.properties new file mode 100644 index 000000000..b7707f204 --- /dev/null +++ b/core/src/test/resources/net/adamcin/oakpal/core/DefaultErrorListenerTest.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +testKey=yeKtset diff --git a/core/src/test/resources/net/adamcin/oakpal/core/OakpalPlanTest.properties b/core/src/test/resources/net/adamcin/oakpal/core/OakpalPlanTest.properties new file mode 100644 index 000000000..b7707f204 --- /dev/null +++ b/core/src/test/resources/net/adamcin/oakpal/core/OakpalPlanTest.properties @@ -0,0 +1,17 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +testKey=yeKtset diff --git a/core/src/test/resources/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.properties b/core/src/test/resources/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.properties new file mode 100644 index 000000000..b8553e3c2 --- /dev/null +++ b/core/src/test/resources/net/adamcin/oakpal/core/ProgressCheckAliasFacadeTest.properties @@ -0,0 +1,16 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/core/src/test/resources/net/adamcin/oakpal/core/checks/JcrPropertiesTest.properties b/core/src/test/resources/net/adamcin/oakpal/core/checks/JcrPropertiesTest.properties new file mode 100644 index 000000000..b8553e3c2 --- /dev/null +++ b/core/src/test/resources/net/adamcin/oakpal/core/checks/JcrPropertiesTest.properties @@ -0,0 +1,16 @@ +# +# Copyright 2020 Mark Adamcin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/core/src/test/resources/simple-content/.gitkeep b/core/src/test/resources/simple-content/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/resources/simple-content/META-INF/vault/filter.xml b/core/src/test/resources/simple-content/META-INF/vault/filter.xml new file mode 100644 index 000000000..2c3a6af00 --- /dev/null +++ b/core/src/test/resources/simple-content/META-INF/vault/filter.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/core/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd b/core/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd new file mode 100644 index 000000000..001ecf462 --- /dev/null +++ b/core/src/test/resources/simple-content/META-INF/vault/nodetypes.cnd @@ -0,0 +1,7 @@ +<'sling'='http://sling.apache.org/jcr/sling/1.0'> + +[sling:Folder] > nt:folder + - * (undefined) + - * (undefined) multiple + + * (nt:base) = sling:Folder version + diff --git a/core/src/test/resources/simple-content/META-INF/vault/properties.xml b/core/src/test/resources/simple-content/META-INF/vault/properties.xml new file mode 100644 index 000000000..2cc942719 --- /dev/null +++ b/core/src/test/resources/simple-content/META-INF/vault/properties.xml @@ -0,0 +1,18 @@ + + + + FileVault Package Properties + admin + simple-content + 2012-04-24T10:17:21.641+05:30 + admin + 2012-04-24T10:17:21.969+05:30 + 1 + 1.0 + + 2 + my_packages + 2012-04-24T10:17:21.641+05:30 + + admin + \ No newline at end of file diff --git a/core/src/test/resources/simple-content/jcr_root/content/example/.content.xml b/core/src/test/resources/simple-content/jcr_root/content/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-content/jcr_root/content/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/src/test/resources/simple-content/jcr_root/etc/example/.content.xml b/core/src/test/resources/simple-content/jcr_root/etc/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-content/jcr_root/etc/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/src/test/resources/simple-libs/.gitkeep b/core/src/test/resources/simple-libs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/resources/simple-libs/META-INF/vault/filter.xml b/core/src/test/resources/simple-libs/META-INF/vault/filter.xml new file mode 100644 index 000000000..302502066 --- /dev/null +++ b/core/src/test/resources/simple-libs/META-INF/vault/filter.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/src/test/resources/simple-libs/META-INF/vault/nodetypes.cnd b/core/src/test/resources/simple-libs/META-INF/vault/nodetypes.cnd new file mode 100644 index 000000000..001ecf462 --- /dev/null +++ b/core/src/test/resources/simple-libs/META-INF/vault/nodetypes.cnd @@ -0,0 +1,7 @@ +<'sling'='http://sling.apache.org/jcr/sling/1.0'> + +[sling:Folder] > nt:folder + - * (undefined) + - * (undefined) multiple + + * (nt:base) = sling:Folder version + diff --git a/core/src/test/resources/simple-libs/META-INF/vault/properties.xml b/core/src/test/resources/simple-libs/META-INF/vault/properties.xml new file mode 100644 index 000000000..7ef8dc6e4 --- /dev/null +++ b/core/src/test/resources/simple-libs/META-INF/vault/properties.xml @@ -0,0 +1,18 @@ + + + + FileVault Package Properties + admin + simple-libs + 2012-04-24T10:17:21.641+05:30 + admin + 2012-04-24T10:17:21.969+05:30 + 1 + 1.0 + + 2 + my_packages + 2012-04-24T10:17:21.641+05:30 + + admin + \ No newline at end of file diff --git a/core/src/test/resources/simple-libs/jcr_root/apps/example/.content.xml b/core/src/test/resources/simple-libs/jcr_root/apps/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-libs/jcr_root/apps/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/src/test/resources/simple-mixed/.gitkeep b/core/src/test/resources/simple-mixed/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/resources/simple-mixed/META-INF/vault/filter.xml b/core/src/test/resources/simple-mixed/META-INF/vault/filter.xml new file mode 100644 index 000000000..04a7ae433 --- /dev/null +++ b/core/src/test/resources/simple-mixed/META-INF/vault/filter.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/core/src/test/resources/simple-mixed/META-INF/vault/nodetypes.cnd b/core/src/test/resources/simple-mixed/META-INF/vault/nodetypes.cnd new file mode 100644 index 000000000..001ecf462 --- /dev/null +++ b/core/src/test/resources/simple-mixed/META-INF/vault/nodetypes.cnd @@ -0,0 +1,7 @@ +<'sling'='http://sling.apache.org/jcr/sling/1.0'> + +[sling:Folder] > nt:folder + - * (undefined) + - * (undefined) multiple + + * (nt:base) = sling:Folder version + diff --git a/core/src/test/resources/simple-mixed/META-INF/vault/properties.xml b/core/src/test/resources/simple-mixed/META-INF/vault/properties.xml new file mode 100644 index 000000000..0c79b1f15 --- /dev/null +++ b/core/src/test/resources/simple-mixed/META-INF/vault/properties.xml @@ -0,0 +1,18 @@ + + + + FileVault Package Properties + admin + simple-mixed + 2012-04-24T10:17:21.641+05:30 + admin + 2012-04-24T10:17:21.969+05:30 + 1 + 1.0 + + 2 + my_packages + 2012-04-24T10:17:21.641+05:30 + + admin + \ No newline at end of file diff --git a/core/src/test/resources/simple-mixed/jcr_root/apps/example/.content.xml b/core/src/test/resources/simple-mixed/jcr_root/apps/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-mixed/jcr_root/apps/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/src/test/resources/simple-mixed/jcr_root/content/example/.content.xml b/core/src/test/resources/simple-mixed/jcr_root/content/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-mixed/jcr_root/content/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/core/src/test/resources/simple-mixed/jcr_root/etc/example/.content.xml b/core/src/test/resources/simple-mixed/jcr_root/etc/example/.content.xml new file mode 100644 index 000000000..23f448703 --- /dev/null +++ b/core/src/test/resources/simple-mixed/jcr_root/etc/example/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/maven/pom.xml b/maven/pom.xml index 5c4c9c51b..d491f62a9 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -21,7 +21,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT .. @@ -169,7 +169,14 @@ - + + org.jetbrains + annotations + + + net.adamcin.oakpal + oakpal-api + net.adamcin.oakpal oakpal-core diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/component/JsonConverter.java b/maven/src/main/java/net/adamcin/oakpal/maven/component/JsonConverter.java index c82fb994b..f1e53eb9b 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/component/JsonConverter.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/component/JsonConverter.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.maven.component; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.JavaxJson; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; import org.codehaus.plexus.component.configurator.ConfigurationListener; import org.codehaus.plexus.component.configurator.converters.AbstractConfigurationConverter; diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojo.java index bc26c472c..d412d5db2 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojo.java @@ -17,12 +17,17 @@ package net.adamcin.oakpal.maven.mojo; import java.io.File; +import java.text.MessageFormat; import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; import java.util.Set; import java.util.stream.Collectors; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Violation; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -54,9 +59,9 @@ abstract class AbstractITestMojo extends AbstractCommonMojo { /** * Specify the minimum violation severity level that will trigger plugin execution failure. Valid options are - * {@link net.adamcin.oakpal.core.Violation.Severity#MINOR}, - * {@link net.adamcin.oakpal.core.Violation.Severity#MAJOR}, and - * {@link net.adamcin.oakpal.core.Violation.Severity#SEVERE}. + * {@link Severity#MINOR}, + * {@link Severity#MAJOR}, and + * {@link Severity#SEVERE}. *

* FYI: FileVault Importer errors are reported as MAJOR by default. *

@@ -64,7 +69,7 @@ abstract class AbstractITestMojo extends AbstractCommonMojo { * @since 0.1.0 */ @Parameter(defaultValue = "MAJOR") - protected Violation.Severity failOnSeverity = Violation.Severity.MAJOR; + protected Severity failOnSeverity = Severity.MAJOR; protected abstract boolean isIndividuallySkipped(); diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojo.java index e6891567c..dacd34139 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojo.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.List; +import net.adamcin.oakpal.api.ProgressCheck; +import net.adamcin.oakpal.api.ProgressCheckFactory; import net.adamcin.oakpal.core.AbortedScanException; import net.adamcin.oakpal.core.CheckReport; import net.adamcin.oakpal.core.CheckSpec; @@ -154,11 +156,11 @@ abstract class AbstractITestWithPlanMojo extends AbstractITestMojo implements Pl protected List forcedRoots = new ArrayList<>(); /** - * Specify a list of Checks to locate and load as {@link net.adamcin.oakpal.core.ProgressCheck}s. + * Specify a list of Checks to locate and load as {@link ProgressCheck}s. *

* Minimally, there are two ways to define a {@link CheckSpec}. To load a check from the project's test output * directory, you must specify the {@code impl} value as a classPath-relative resource name for script checks, or as - * a fully-qualified class name for Java {@link net.adamcin.oakpal.core.ProgressCheck} implementations. + * a fully-qualified class name for Java {@link ProgressCheck} implementations. *

* For example, if your script check source is located at {@code src/test/resources/OAKPAL-INF/scripts/acme-vault-enforcer.js}: *

@@ -169,7 +171,7 @@ abstract class AbstractITestWithPlanMojo extends AbstractITestMojo implements Pl
      * </checks>
      * 
*

- * If your {@code impl} represents a script check or a {@link net.adamcin.oakpal.core.ProgressCheckFactory}, you can + * If your {@code impl} represents a script check or a {@link ProgressCheckFactory}, you can * also provide a {@code config} element that will be translated to a {@link javax.json.JsonObject} by the * {@link JsonConverter} via the * {@link net.adamcin.oakpal.maven.component.OakpalComponentConfigurator}: diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParams.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParams.java index 4ec3f8a74..304c5a86d 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParams.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParams.java @@ -1,25 +1,23 @@ package net.adamcin.oakpal.maven.mojo; import net.adamcin.oakpal.core.ForcedRoot; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.NamespaceMappingRequest; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Result; import net.adamcin.oakpal.core.SlingNodetypesScanner; import org.apache.jackrabbit.spi.QNodeTypeDefinition; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; import org.apache.jackrabbit.vault.fs.spi.NodeTypeSet; import org.apache.maven.model.Dependency; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.ArrayList; @@ -28,13 +26,12 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.JavaxJson.wrap; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.JavaxJson.wrap; interface MojoWithPlanParams extends MojoWithCommonParams, MojoWithRepositoryParams { diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParams.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParams.java index 80674dede..71734905b 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParams.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParams.java @@ -12,7 +12,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.DefaultRepositoryRequest; import org.apache.maven.artifact.repository.RepositoryRequest; diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojo.java index 6b00321af..c1ed889ef 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojo.java @@ -1,19 +1,18 @@ package net.adamcin.oakpal.maven.mojo; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.composeTest; -import static net.adamcin.oakpal.core.Fun.entriesToMap; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.testValue; -import static net.adamcin.oakpal.core.Fun.uncheck0; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; -import static net.adamcin.oakpal.core.Fun.zipValuesWithKeyFunc; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.composeTest; +import static net.adamcin.oakpal.api.Fun.entriesToMap; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.testValue; +import static net.adamcin.oakpal.api.Fun.uncheck0; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.Fun.zipValuesWithKeyFunc; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.URI; @@ -37,8 +36,8 @@ import javax.json.stream.JsonGenerator; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Opear; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.core.opear.Opear; +import net.adamcin.oakpal.api.Result; import net.adamcin.oakpal.core.Util; import net.adamcin.oakpal.maven.component.OakpalComponentConfigurator; import org.apache.jackrabbit.oak.commons.FileIOUtils; @@ -57,7 +56,6 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; import org.apache.maven.shared.utils.io.FileUtils; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.util.DefaultFileSet; diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojo.java index beb686257..f6d50dc1e 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojo.java @@ -4,7 +4,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; -import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Optional; @@ -13,7 +12,6 @@ import javax.json.JsonWriterFactory; import javax.json.stream.JsonGenerator; -import net.adamcin.oakpal.core.Fun; import net.adamcin.oakpal.core.OakpalPlan; import net.adamcin.oakpal.maven.component.OakpalComponentConfigurator; import org.apache.maven.plugin.MojoFailureException; @@ -21,7 +19,6 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; -import org.jetbrains.annotations.NotNull; /** * Exports a plan builder configuration for inclusion in an opear file, using the {@link OpearPackageMojo}. diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanArtifactMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanArtifactMojo.java index 3b2b50ad4..7f4c8a20f 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanArtifactMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanArtifactMojo.java @@ -17,18 +17,10 @@ package net.adamcin.oakpal.maven.mojo; import java.io.File; -import java.io.IOException; import java.util.Collections; -import java.util.List; import java.util.Optional; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.DefaultErrorListener; -import net.adamcin.oakpal.core.FileBlobMemoryNodeStore; -import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.ReportMapper; import net.adamcin.oakpal.maven.component.OakpalComponentConfigurator; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanManyArtifactsMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanManyArtifactsMojo.java index 6a3ed1fb3..2cf1515f6 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanManyArtifactsMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/ScanManyArtifactsMojo.java @@ -17,25 +17,12 @@ package net.adamcin.oakpal.maven.mojo; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import net.adamcin.oakpal.core.AbortedScanException; -import net.adamcin.oakpal.core.CheckReport; -import net.adamcin.oakpal.core.DefaultErrorListener; -import net.adamcin.oakpal.core.FileBlobMemoryNodeStore; -import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.ReportMapper; +import net.adamcin.oakpal.api.ProgressCheck; import net.adamcin.oakpal.maven.component.OakpalComponentConfigurator; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.repository.DefaultRepositoryRequest; -import org.apache.maven.artifact.repository.RepositoryRequest; import org.apache.maven.model.Dependency; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; @@ -44,7 +31,7 @@ /** * Scans a list of artifacts by simulating package installation and listening for violations reported by the - * configured {@code checks}. This goal supports the use of {@link net.adamcin.oakpal.core.ProgressCheck}s that must + * configured {@code checks}. This goal supports the use of {@link ProgressCheck}s that must * evaluate the side effects of installing packages in combination, such as for detection of workspace filter overlap. * More simply, this goal can be used in a sidecar project to scan multiple artifacts produced by previous builds using * a common library of checklists. diff --git a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/WebsterMojo.java b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/WebsterMojo.java index bfc845664..766acbbf2 100644 --- a/maven/src/main/java/net/adamcin/oakpal/maven/mojo/WebsterMojo.java +++ b/maven/src/main/java/net/adamcin/oakpal/maven/mojo/WebsterMojo.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.maven.mojo; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.maven.component.OakpalComponentConfigurator; import net.adamcin.oakpal.webster.CliArgParser; import net.adamcin.oakpal.webster.JcrFactory; diff --git a/maven/src/site/resources/exampleCheck.js b/maven/src/site/resources/exampleCheck.js index 8a614205e..2bc9347b9 100644 --- a/maven/src/site/resources/exampleCheck.js +++ b/maven/src/site/resources/exampleCheck.js @@ -39,11 +39,13 @@ * import java.util.jar.Manifest * import javax.jcr.Node; * import javax.jcr.Session; + * import net.adamcin.oakpal.api.PathAction; */ /*** * Optionally implement this function to return a label for your check. */ + /* function getCheckName() { return "ACME Example Check"; @@ -103,8 +105,9 @@ function beforeExtract(packageId /* PackageId */, inspectSession /* Session */, * @param packageId the current package * @param path the imported path * @param node the imported JCR node + * @param action the import action (NOOP, ADDED, MODIFIED, or REPLACED) */ -function importedPath(packageId /* PackageId */, path /* String */, node /* Node */) { +function importedPath(packageId /* PackageId */, path /* String */, node /* Node */, action /* PathAction */) { } diff --git a/maven/src/site/xdoc/usage.xml.vm b/maven/src/site/xdoc/usage.xml.vm index fb922c9d5..d7e583acb 100644 --- a/maven/src/site/xdoc/usage.xml.vm +++ b/maven/src/site/xdoc/usage.xml.vm @@ -108,7 +108,7 @@

- The basic checklist implements some sane default package + The basic checklist implements some sane default package acceptance rules, like disallowing subpackages, deletion of existing paths, unsafe acHandling modes, and preventing workspace filter overlaps between multiple packages.

diff --git a/maven/src/site/xdoc/writing-a-script-check.xml.vm b/maven/src/site/xdoc/writing-a-script-check.xml.vm index ab5d8ff4f..60ef4359d 100644 --- a/maven/src/site/xdoc/writing-a-script-check.xml.vm +++ b/maven/src/site/xdoc/writing-a-script-check.xml.vm @@ -53,6 +53,7 @@ * import java.util.jar.Manifest; * import javax.jcr.Node; * import javax.jcr.Session; + * import net.adamcin.oakpal.api.PathAction; */ /*** @@ -117,8 +118,9 @@ function beforeExtract(packageId /* PackageId */, inspectSession /* Session */, * @param packageId the current package * @param path the imported path * @param node the imported JCR node + * @param action the import action (NOOP, ADDED, MODIFIED, or REPLACED) */ -function importedPath(packageId /* PackageId */, path /* String */, node /* Node */) { +function importedPath(packageId /* PackageId */, path /* String */, node /* Node */, action /* PathAction */) { } diff --git a/maven/src/test/filtered-resources/test-packages.properties b/maven/src/test/filtered-resources/test-packages.properties index 1c6d7919e..eae2b1bad 100644 --- a/maven/src/test/filtered-resources/test-packages.properties +++ b/maven/src/test/filtered-resources/test-packages.properties @@ -15,4 +15,4 @@ # test-packages.root=${project.build.directory} -test-packages.src=/jackrabbit-filevault-${vault-api.version}/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/ \ No newline at end of file +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/checks/SimpleJavaCheck.java b/maven/src/test/java/net/adamcin/oakpal/maven/checks/SimpleJavaCheck.java index 7e1c5fb9c..0e7b52fe7 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/checks/SimpleJavaCheck.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/checks/SimpleJavaCheck.java @@ -20,9 +20,9 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; -import net.adamcin.oakpal.core.SimpleProgressCheck; -import net.adamcin.oakpal.core.SimpleViolation; -import net.adamcin.oakpal.core.Violation; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleProgressCheck; +import net.adamcin.oakpal.api.SimpleViolation; import org.apache.jackrabbit.vault.fs.config.MetaInf; import org.apache.jackrabbit.vault.packaging.PackageId; import org.apache.jackrabbit.vault.packaging.PackageProperties; @@ -34,7 +34,7 @@ public void beforeExtract(final PackageId packageId, final Session inspectSessio final PackageProperties packageProperties, final MetaInf metaInf, final List subpackages) throws RepositoryException { - reportViolation(new SimpleViolation(Violation.Severity.MINOR, + reportViolation(new SimpleViolation(Severity.MINOR, packageProperties.getACHandling().toString(), packageId)); } } diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/component/JsonConverterTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/component/JsonConverterTest.java index ec27f7c66..4754babfc 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/component/JsonConverterTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/component/JsonConverterTest.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.maven.component; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.JavaxJson; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; import org.codehaus.plexus.component.configurator.ConfigurationListener; import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup; @@ -40,10 +40,10 @@ import java.util.Map; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.uncheck0; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.wrap; +import static net.adamcin.oakpal.api.Fun.uncheck0; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.wrap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojoTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojoTest.java index c8b28870d..3434ed826 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojoTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestMojoTest.java @@ -16,19 +16,14 @@ package net.adamcin.oakpal.maven.mojo; +import net.adamcin.oakpal.api.Severity; +import net.adamcin.oakpal.api.SimpleViolation; import net.adamcin.oakpal.core.CheckReport; import net.adamcin.oakpal.core.SimpleReport; -import net.adamcin.oakpal.core.SimpleViolation; -import net.adamcin.oakpal.core.Violation; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.vault.packaging.PackageId; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.project.MavenProject; -import org.apache.maven.repository.RepositorySystem; -import org.apache.maven.settings.Settings; import org.junit.Test; import java.io.File; @@ -43,8 +38,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; public class AbstractITestMojoTest { private final File testOutBaseDir = new File("target/test-out/AbstractITestMojoTest"); @@ -97,10 +90,10 @@ public void testReactToReports_nonEmptyReports_noFail() throws Exception { mojo.setLog(log); mojo.reactToReports(Arrays.asList( new SimpleReport("one", Collections - .singletonList(new SimpleViolation(Violation.Severity.MINOR, "one", + .singletonList(new SimpleViolation(Severity.MINOR, "one", packageId))), new SimpleReport("two", Collections - .singletonList(new SimpleViolation(Violation.Severity.MINOR, "two"))))); + .singletonList(new SimpleViolation(Severity.MINOR, "two"))))); assertFalse("no error messages", log.any(MockMojoLog.MockMojoLogEntry::isError)); assertTrue("expect log header", log.any(entry -> "OakPAL Check Reports".equals(entry.message))); assertTrue("expect report one", log.any(entry -> " one".equals(entry.message))); @@ -115,14 +108,14 @@ public void testReactToReports_nonEmptyReports_fail() { final PackageId packageId = PackageId.fromString("my_packages:example-one:1.0"); MockMojoLog log = new MockMojoLog(); ConcreteMojo mojo = new ConcreteMojo(); - mojo.setFailOnSeverity(Violation.Severity.MINOR); + mojo.setFailOnSeverity(Severity.MINOR); mojo.setLog(log); final List reports = Arrays.asList( new SimpleReport("one", Collections - .singletonList(new SimpleViolation(Violation.Severity.MINOR, "one", + .singletonList(new SimpleViolation(Severity.MINOR, "one", packageId))), new SimpleReport("two", Collections - .singletonList(new SimpleViolation(Violation.Severity.MINOR, "two")))); + .singletonList(new SimpleViolation(Severity.MINOR, "two")))); boolean failed = false; try { mojo.reactToReports(reports); @@ -210,7 +203,7 @@ public void setSummaryFile(File summaryFile) { this.summaryFile = summaryFile; } - public void setFailOnSeverity(Violation.Severity failOnSeverity) { + public void setFailOnSeverity(Severity failOnSeverity) { this.failOnSeverity = failOnSeverity; } diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojoTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojoTest.java index c2c2918dd..e632f6dcf 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojoTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/AbstractITestWithPlanMojoTest.java @@ -22,7 +22,7 @@ import net.adamcin.oakpal.core.ForcedRoot; import net.adamcin.oakpal.core.InstallHookPolicy; import net.adamcin.oakpal.core.JcrNs; -import net.adamcin.oakpal.core.Nothing; +import net.adamcin.oakpal.api.Nothing; import net.adamcin.oakpal.core.ReportMapper; import net.adamcin.oakpal.testing.TestPackageUtil; import org.apache.commons.io.FileUtils; @@ -36,10 +36,10 @@ import java.util.List; import java.util.Optional; -import static net.adamcin.oakpal.core.Fun.result0; +import static net.adamcin.oakpal.api.Fun.result0; import static net.adamcin.oakpal.core.InstallHookPolicy.PROHIBIT; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParamsTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParamsTest.java index 12177428e..2774aa94b 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParamsTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithPlanParamsTest.java @@ -18,12 +18,11 @@ import net.adamcin.oakpal.core.CheckSpec; import net.adamcin.oakpal.core.ForcedRoot; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.InstallHookPolicy; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.OakpalPlan; import net.adamcin.oakpal.core.SlingNodetypesScanner; -import net.adamcin.oakpal.maven.component.JsonConverter; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; @@ -41,7 +40,6 @@ import java.net.URL; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -49,9 +47,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.entriesToMap; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.Fun.entriesToMap; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParamsTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParamsTest.java index cc0b19de4..c0ebc3283 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParamsTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/MojoWithRepositoryParamsTest.java @@ -45,7 +45,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.toEntry; +import static net.adamcin.oakpal.api.Fun.toEntry; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojoTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojoTest.java index b1e8caba9..ea2febd0b 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojoTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPackageMojoTest.java @@ -16,9 +16,9 @@ package net.adamcin.oakpal.maven.mojo; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Result; import net.adamcin.oakpal.core.Util; import net.adamcin.oakpal.testing.TestPackageUtil; import net.adamcin.oakpal.testing.oakpaltest.Handler; @@ -39,7 +39,6 @@ import org.junit.Test; import java.io.File; -import java.io.FilenameFilter; import java.net.URI; import java.net.URL; import java.util.ArrayList; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojoTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojoTest.java index ab8dcdd0a..2010cec9a 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojoTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/OpearPlanMojoTest.java @@ -36,8 +36,8 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/WebsterMojoTest.java b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/WebsterMojoTest.java index 1cc640107..b443e43fd 100644 --- a/maven/src/test/java/net/adamcin/oakpal/maven/mojo/WebsterMojoTest.java +++ b/maven/src/test/java/net/adamcin/oakpal/maven/mojo/WebsterMojoTest.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.maven.mojo; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.webster.JcrFactory; import net.adamcin.oakpal.webster.WebsterPlan; import net.adamcin.oakpal.webster.WebsterTarget; @@ -43,7 +43,7 @@ import java.util.Properties; import java.util.concurrent.CompletableFuture; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; diff --git a/pom.xml b/pom.xml index 82355dd06..72a746cc1 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT pom 2017 @@ -58,6 +58,7 @@ + api core cli webster @@ -71,14 +72,16 @@ 1.8 1.8 1.7.21 - 3.4.0 - 2.18.3 + + + 3.4.4 + 2.20.0 - 1.18.0 - 3.3.0 + 1.26.0 + vault-core/src/test/resources/test-packages @@ -148,7 +151,7 @@ test-packages ${project.build.testOutputDirectory} - */vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/* + */${vault.test-packages.src}/* @@ -178,6 +181,9 @@ biz.aQute.bnd bnd-baseline-maven-plugin 4.1.0 + + false + org.apache.maven.plugins @@ -468,24 +474,34 @@ net.adamcin.oakpal oakpal-testing - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT + + + net.adamcin.oakpal + oakpal-api + 2.0.0-SNAPSHOT net.adamcin.oakpal oakpal-core - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT net.adamcin.oakpal oakpal-webster - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT org.jetbrains annotations - 16.0.2 + 18.0.0 provided + + org.osgi + org.osgi.annotation.versioning + 1.1.0 + org.apache.sling org.apache.sling.commons.johnzon @@ -501,6 +517,21 @@ org.apache.jackrabbit.vault ${vault-api.version} + + org.apache.jackrabbit + jackrabbit-jcr-commons + ${jackrabbit.version} + + + org.apache.jackrabbit + jackrabbit-spi-commons + ${jackrabbit.version} + + + org.apache.jackrabbit + oak-jackrabbit-api + ${oak.version} + org.apache.jackrabbit oak-core @@ -559,7 +590,7 @@ org.mockito mockito-core - 2.28.2 + 3.3.3 test diff --git a/src/site/xdoc/about.xml b/src/site/xdoc/about.xml index 6cfb1e60b..fc162b9d6 100644 --- a/src/site/xdoc/about.xml +++ b/src/site/xdoc/about.xml @@ -81,7 +81,7 @@
  • A pluggable listener API with classpath discovery of third-party - Checklists, + Checklists, ProgressChecks, and ScriptProgressChecks, which receive progress tracker events along with read-only access to diff --git a/testing/pom.xml b/testing/pom.xml index c60f40617..1d35ce123 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -21,7 +21,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT .. @@ -73,33 +73,10 @@ - - - org.apache.jackrabbit.vault - org.apache.jackrabbit.vault - - - - javax.jcr - jcr - - - org.apache.jackrabbit - jackrabbit-spi-commons - ${jackrabbit.version} + org.jetbrains + annotations - - - org.apache.jackrabbit - oak-core - - - - org.apache.jackrabbit - oak-jcr - - org.slf4j slf4j-api diff --git a/core/src/test/java/net/adamcin/oakpal/core/TestUtil.java b/testing/src/main/java/net/adamcin/oakpal/testing/TestUtil.java similarity index 87% rename from core/src/test/java/net/adamcin/oakpal/core/TestUtil.java rename to testing/src/main/java/net/adamcin/oakpal/testing/TestUtil.java index db216d4e2..aab2862e3 100644 --- a/core/src/test/java/net/adamcin/oakpal/core/TestUtil.java +++ b/testing/src/main/java/net/adamcin/oakpal/testing/TestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Mark Adamcin + * Copyright 2020 Mark Adamcin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,16 @@ * limitations under the License. */ -package net.adamcin.oakpal.core; +package net.adamcin.oakpal.testing; /** * Some simple utilities for scoped subtests. */ public class TestUtil { + private TestUtil() { + /* no constructor */ + } + public static void testBlock(final TestBody testBody) throws Exception { testBody.apply(); } diff --git a/testing/src/test/filtered-resources/test-packages.properties b/testing/src/test/filtered-resources/test-packages.properties index 1c6d7919e..eae2b1bad 100644 --- a/testing/src/test/filtered-resources/test-packages.properties +++ b/testing/src/test/filtered-resources/test-packages.properties @@ -15,4 +15,4 @@ # test-packages.root=${project.build.directory} -test-packages.src=/jackrabbit-filevault-${vault-api.version}/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/ \ No newline at end of file +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/testing/src/test/java/net/adamcin/oakpal/testing/TestPackageUtilTest.java b/testing/src/test/java/net/adamcin/oakpal/testing/TestPackageUtilTest.java index b3b85d194..8553d8935 100644 --- a/testing/src/test/java/net/adamcin/oakpal/testing/TestPackageUtilTest.java +++ b/testing/src/test/java/net/adamcin/oakpal/testing/TestPackageUtilTest.java @@ -16,17 +16,16 @@ package net.adamcin.oakpal.testing; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.junit.Before; +import org.junit.Test; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -35,12 +34,10 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; -import org.apache.commons.io.FileUtils; -import org.jetbrains.annotations.NotNull; -import org.junit.Before; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TestPackageUtilTest { final File baseDir = new File("target/test-out/TestPackageUtilTest"); diff --git a/testing/src/test/java/net/adamcin/oakpal/testing/TestUtilTest.java b/testing/src/test/java/net/adamcin/oakpal/testing/TestUtilTest.java new file mode 100644 index 000000000..9252564c3 --- /dev/null +++ b/testing/src/test/java/net/adamcin/oakpal/testing/TestUtilTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Mark Adamcin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.adamcin.oakpal.testing; + +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; + +import static org.junit.Assert.assertTrue; + +public class TestUtilTest { + + @Test + public void testTestBlock() throws Exception { + CompletableFuture latch = new CompletableFuture<>(); + TestUtil.testBlock(() -> { + latch.complete(true); + }); + assertTrue("expect completed true", latch.getNow(false)); + } + + @Test(expected = Exception.class) + public void testTestBlock_throws() throws Exception { + TestUtil.testBlock(() -> { + throw new Exception("Expected"); + }); + } +} \ No newline at end of file diff --git a/webster/pom.xml b/webster/pom.xml index c2170a548..b914e82c2 100644 --- a/webster/pom.xml +++ b/webster/pom.xml @@ -21,7 +21,7 @@ net.adamcin.oakpal oakpal - 1.5.3-SNAPSHOT + 2.0.0-SNAPSHOT .. @@ -40,10 +40,6 @@ HEAD - - 1.0.0 - - @@ -147,7 +143,14 @@ - + + org.jetbrains + annotations + + + org.osgi + org.osgi.annotation.versioning + net.adamcin.oakpal oakpal-core diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/ArchiveAware.java b/webster/src/main/java/net/adamcin/oakpal/webster/ArchiveAware.java index 3792bad14..62207c285 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/ArchiveAware.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/ArchiveAware.java @@ -19,11 +19,13 @@ import java.io.File; import org.apache.jackrabbit.vault.fs.io.Archive; +import org.osgi.annotation.versioning.ProviderType; /** * Interface that allows the {@link WebsterPlan} to provide a {@link WebsterTarget} with a fresh FileVault * {@link Archive} to traverse. */ +@ProviderType public interface ArchiveAware { /** diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/ChecklistExporter.java b/webster/src/main/java/net/adamcin/oakpal/webster/ChecklistExporter.java index 9de55187c..616250f9d 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/ChecklistExporter.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/ChecklistExporter.java @@ -18,13 +18,13 @@ import net.adamcin.oakpal.core.Checklist; import net.adamcin.oakpal.core.ForcedRoot; -import net.adamcin.oakpal.core.Fun; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.Fun; +import net.adamcin.oakpal.api.JavaxJson; import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.NamespaceMappingRequest; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Rule; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; @@ -76,13 +76,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.inferTest1; -import static net.adamcin.oakpal.core.Fun.mapValue; -import static net.adamcin.oakpal.core.Fun.testKey; -import static net.adamcin.oakpal.core.Fun.testValue; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.mapValue; +import static net.adamcin.oakpal.api.Fun.testKey; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; /** * Exports namespaces, node types, and {@link ForcedRoot}s from a JCR session to assist with project checklist management. @@ -93,7 +91,7 @@ public final class ChecklistExporter { /** * Builder for a {@link ChecklistExporter}, which is otherwise immutable. */ - public static class Builder { + public static final class Builder { private List operations = new ArrayList<>(); private List exportTypeDefs = new ArrayList<>(); private List pathScopes = new ArrayList<>(); @@ -225,7 +223,7 @@ public static SelectorType byName(final String name) { /** * Represents an atomic export operation. */ - static class Op { + static final class Op { private final SelectorType selectorType; private final List args; @@ -451,7 +449,7 @@ public void updateChecklist(final WriterOpener writerOpener, //final Set finalPrefixes = new HashSet<>(); final NamespaceMappingRequest.Builder request = new NamespaceMappingRequest.Builder(); if (!privileges.isEmpty()) { - builder.add(Checklist.KEY_JCR_PRIVILEGES, JsonCnd.privilegesToJson(privileges, origMapping)); + builder.add(Checklist.keys().jcrPrivileges(), JsonCnd.privilegesToJson(privileges, origMapping)); privileges.stream().flatMap(JsonCnd::namedBy).forEach(request::withQName); } @@ -464,7 +462,7 @@ public void updateChecklist(final WriterOpener writerOpener, .map(ForcedRoot::toJson) .collect(JsonCollectors.toJsonArray()); - builder.add(Checklist.KEY_FORCED_ROOTS, forcedRootsJson); + builder.add(Checklist.keys().forcedRoots(), forcedRootsJson); // begin nodetype handling @@ -506,7 +504,7 @@ public void updateChecklist(final WriterOpener writerOpener, final JsonObject jcrNodetypes = JsonCnd.toJson(qNodeTypes, new NamespaceMapping(new SessionNamespaceResolver(session))); - builder.add(Checklist.KEY_JCR_NODETYPES, jcrNodetypes); + builder.add(Checklist.keys().jcrNodetypes(), jcrNodetypes); } // begin namespace handling @@ -521,7 +519,7 @@ public void updateChecklist(final WriterOpener writerOpener, .flatMap(Result::stream) .collect(Collectors.toList()); if (!exportNamespaces.isEmpty()) { - builder.add(Checklist.KEY_JCR_NAMESPACES, JavaxJson.wrap(exportNamespaces)); + builder.add(Checklist.keys().jcrNamespaces(), JavaxJson.wrap(exportNamespaces)); } final JsonObject sorted = builder.build().entrySet().stream() @@ -669,5 +667,4 @@ Optional nodeToRoot(final Node node, final NamespaceMapping mapping) forcedRoot.setMixinTypes(mixinTypes); return Optional.of(forcedRoot); } - } diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/CliArgParser.java b/webster/src/main/java/net/adamcin/oakpal/webster/CliArgParser.java index 1448480cc..dc73a3318 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/CliArgParser.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/CliArgParser.java @@ -60,7 +60,7 @@ static Pattern tokenPattern() { } - static class Token { + static final class Token { final TokenType type; final String data; @@ -93,7 +93,7 @@ static List lex(final String input) { return tokens; } - static class Parser { + static final class Parser { final List args = new ArrayList<>(); StringBuilder arg = new StringBuilder(); boolean escaped; diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/CndExporter.java b/webster/src/main/java/net/adamcin/oakpal/webster/CndExporter.java index 096af3978..d4b28898e 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/CndExporter.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/CndExporter.java @@ -16,10 +16,10 @@ package net.adamcin.oakpal.webster; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.JsonCnd; -import net.adamcin.oakpal.core.Result; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Result; +import net.adamcin.oakpal.api.Rule; import org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader; import org.apache.jackrabbit.commons.cnd.ParseException; import org.apache.jackrabbit.commons.cnd.TemplateBuilderFactory; @@ -66,10 +66,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.Fun.inSet; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheck1; /** * Interface independent logic for exporting .cnd files from a JCR session. diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/FileVaultNameFinder.java b/webster/src/main/java/net/adamcin/oakpal/webster/FileVaultNameFinder.java index d0e658c96..f654c8c5d 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/FileVaultNameFinder.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/FileVaultNameFinder.java @@ -16,9 +16,9 @@ package net.adamcin.oakpal.webster; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.JsonCnd; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.PrivilegeDefinition; import org.apache.jackrabbit.spi.QNodeTypeDefinition; @@ -65,8 +65,8 @@ import static javax.jcr.PropertyType.NAME; import static javax.jcr.PropertyType.STRING; import static javax.jcr.PropertyType.UNDEFINED; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.uncheckVoid1; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheckVoid1; import static net.adamcin.oakpal.core.JsonCnd.BUILTIN_MAPPINGS; public final class FileVaultNameFinder { @@ -195,7 +195,7 @@ void handleDocView(final @NotNull VaultInputSource source) throws ParserConfigur parser.parse(source, handler); } - static class NsStack { + static final class NsStack { /** * Next NameSpace mapping for this prefix on the stack. @@ -220,7 +220,7 @@ static class NsStack { } } - class Handler extends RejectingEntityDefaultHandler implements NamespaceResolver { + final class Handler extends RejectingEntityDefaultHandler implements NamespaceResolver { boolean expectStartElement = false; boolean expectEndPrefixMapping = false; NamespaceMapping mapping = new NamespaceMapping(); diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/JcrFactory.java b/webster/src/main/java/net/adamcin/oakpal/webster/JcrFactory.java index 1acfc273d..7ce206e88 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/JcrFactory.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/JcrFactory.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.webster; -import static net.adamcin.oakpal.core.Fun.inSet; +import static net.adamcin.oakpal.api.Fun.inSet; import java.io.File; import java.io.IOException; @@ -134,5 +134,4 @@ public static NodeStoreFixture getReadOnlyFixture(final File segmentStore, final public static NodeStoreFixture getReadWriteFixture(final File segmentStore, final String... args) throws Exception { return getNodeStoreFixture(false, segmentStore, args); } - } diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/PrivilegeXmlExporter.java b/webster/src/main/java/net/adamcin/oakpal/webster/PrivilegeXmlExporter.java index b3a19e8e8..504f6edf8 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/PrivilegeXmlExporter.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/PrivilegeXmlExporter.java @@ -35,7 +35,7 @@ import javax.jcr.Workspace; import javax.jcr.security.Privilege; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.JsonCnd; import org.apache.jackrabbit.api.JackrabbitWorkspace; import org.apache.jackrabbit.api.security.authorization.PrivilegeManager; diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/QName.java b/webster/src/main/java/net/adamcin/oakpal/webster/QName.java index 13c5a3dde..36afcd7b9 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/QName.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/QName.java @@ -23,7 +23,7 @@ import java.util.Objects; -import static net.adamcin.oakpal.core.Fun.result1; +import static net.adamcin.oakpal.api.Fun.result1; /** * Representation of a Qualified Name. diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTarget.java b/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTarget.java index 7c5381020..5290d7517 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTarget.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTarget.java @@ -16,11 +16,14 @@ package net.adamcin.oakpal.webster; +import org.osgi.annotation.versioning.ProviderType; + import javax.jcr.Session; /** * Simple interface for executing a Webster action that generates one source files. */ +@ProviderType public interface WebsterTarget { /** diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTargetFactory.java b/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTargetFactory.java index a44853384..5f8b11a25 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTargetFactory.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/WebsterTargetFactory.java @@ -16,9 +16,12 @@ package net.adamcin.oakpal.webster; +import org.osgi.annotation.versioning.ProviderType; + import java.io.File; import javax.json.JsonObject; +@ProviderType @FunctionalInterface public interface WebsterTargetFactory { diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/targets/JsonTargetFactory.java b/webster/src/main/java/net/adamcin/oakpal/webster/targets/JsonTargetFactory.java index d4a9829de..d42b49c1e 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/targets/JsonTargetFactory.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/targets/JsonTargetFactory.java @@ -16,11 +16,11 @@ package net.adamcin.oakpal.webster.targets; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.uncheck2; -import static net.adamcin.oakpal.core.JavaxJson.mapArrayOfObjects; -import static net.adamcin.oakpal.core.JavaxJson.mapObjectValues; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.uncheck2; +import static net.adamcin.oakpal.api.JavaxJson.mapArrayOfObjects; +import static net.adamcin.oakpal.api.JavaxJson.mapObjectValues; +import static net.adamcin.oakpal.api.JavaxJson.obj; import java.io.File; import java.util.ArrayList; diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTarget.java b/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTarget.java index 9dd9d8368..534696ac1 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTarget.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTarget.java @@ -16,7 +16,7 @@ package net.adamcin.oakpal.webster.targets; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.obj; import java.io.File; import java.io.FileInputStream; @@ -35,9 +35,9 @@ import javax.json.JsonReader; import net.adamcin.oakpal.core.Checklist; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.JavaxJson; import net.adamcin.oakpal.core.JcrNs; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Rule; import net.adamcin.oakpal.webster.ChecklistExporter; import net.adamcin.oakpal.webster.WebsterTarget; import org.slf4j.Logger; @@ -58,7 +58,7 @@ final class WebsterChecklistTarget implements WebsterTarget { static final String KEY_UPDATE_POLICY = "updatePolicy"; static final String KEY_EXPORT_NODETYPES = "exportNodeTypes"; - static class Selector { + static final class Selector { private final ChecklistExporter.SelectorType type; private final String[] args; diff --git a/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTarget.java b/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTarget.java index 967fcfcb2..8e00e1d1a 100644 --- a/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTarget.java +++ b/webster/src/main/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTarget.java @@ -23,7 +23,7 @@ import javax.jcr.Session; import javax.json.JsonObject; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Rule; import net.adamcin.oakpal.webster.ArchiveAware; import net.adamcin.oakpal.webster.CndExporter; import net.adamcin.oakpal.webster.FileVaultNameFinder; diff --git a/webster/src/test/filtered-resources/test-packages.properties b/webster/src/test/filtered-resources/test-packages.properties index 41f1c6fe3..20aba2171 100644 --- a/webster/src/test/filtered-resources/test-packages.properties +++ b/webster/src/test/filtered-resources/test-packages.properties @@ -15,4 +15,4 @@ # test-packages.root=${project.build.directory} -test-packages.src=/jackrabbit-filevault-${vault-api.version}/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/integration/testpackages/ \ No newline at end of file +test-packages.src=/jackrabbit-filevault-${vault-api.version}/${vault.test-packages.src}/ diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/ChecklistExporterTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/ChecklistExporterTest.java index bf435570a..d120d72d4 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/ChecklistExporterTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/ChecklistExporterTest.java @@ -16,9 +16,9 @@ package net.adamcin.oakpal.webster; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -49,13 +49,13 @@ import net.adamcin.oakpal.core.Checklist; import net.adamcin.oakpal.core.ForcedRoot; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.InitStage; -import net.adamcin.oakpal.core.JavaxJson; +import net.adamcin.oakpal.api.JavaxJson; import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Rule; import org.apache.jackrabbit.commons.JcrUtils; import org.apache.jackrabbit.commons.NamespaceHelper; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; @@ -187,10 +187,10 @@ public void testUpdateChecklist() throws Exception { pathExporter.updateChecklist(() -> new OutputStreamWriter( new FileOutputStream(pass1Checklist), StandardCharsets.UTF_8), session, Checklist.fromJson("", null, obj() - .key(Checklist.KEY_JCR_NAMESPACES, Collections + .key(Checklist.keys().jcrNamespaces(), Collections .singletonList(JcrNs.create("sling", "http://sling.apache.org/jcr/sling/1.0"))) - .key(Checklist.KEY_JCR_PRIVILEGES, obj() + .key(Checklist.keys().jcrPrivileges(), obj() .key("sling:doesAll", obj() .key("contains", arr("sling:doOne", "sling:doTwo"))) .get()) @@ -199,11 +199,11 @@ public void testUpdateChecklist() throws Exception { try (JsonReader reader = Json.createReader(new FileInputStream(pass1Checklist))) { JsonObject checklist = reader.readObject(); assertTrue("checklist object should contain the forcedRoots key", - checklist.containsKey(Checklist.KEY_FORCED_ROOTS)); + checklist.containsKey(Checklist.keys().forcedRoots())); assertTrue("checklist object should contain the privileges key", - checklist.containsKey(Checklist.KEY_JCR_PRIVILEGES)); + checklist.containsKey(Checklist.keys().jcrPrivileges())); - JsonArray forcedRoots = checklist.getJsonArray(Checklist.KEY_FORCED_ROOTS); + JsonArray forcedRoots = checklist.getJsonArray(Checklist.keys().forcedRoots()); assertEquals("forcedRoots should be array with expected number of elements", allPaths.size(), forcedRoots.size()); @@ -237,11 +237,11 @@ public void testUpdateChecklist() throws Exception { try (JsonReader reader = Json.createReader(new FileInputStream(fullPassChecklist))) { JsonObject checklist = reader.readObject(); assertTrue("checklist object should contain the forcedRoots key", - checklist.containsKey(Checklist.KEY_FORCED_ROOTS)); + checklist.containsKey(Checklist.keys().forcedRoots())); assertTrue("checklist object should contain the privileges key", - checklist.containsKey(Checklist.KEY_JCR_PRIVILEGES)); + checklist.containsKey(Checklist.keys().jcrPrivileges())); - JsonArray forcedRoots = checklist.getJsonArray(Checklist.KEY_FORCED_ROOTS); + JsonArray forcedRoots = checklist.getJsonArray(Checklist.keys().forcedRoots()); assertEquals("forcedRoots should be array with expected number of elements", allPaths.size(), forcedRoots.size()); @@ -291,9 +291,9 @@ public void testUpdateChecklist() throws Exception { try (JsonReader reader = Json.createReader(new FileInputStream(mergePassChecklist))) { JsonObject checklist = reader.readObject(); assertTrue("checklist object should contain the forcedRoots key", - checklist.containsKey(Checklist.KEY_FORCED_ROOTS)); + checklist.containsKey(Checklist.keys().forcedRoots())); - JsonArray forcedRoots = checklist.getJsonArray(Checklist.KEY_FORCED_ROOTS); + JsonArray forcedRoots = checklist.getJsonArray(Checklist.keys().forcedRoots()); assertEquals("[mergePass] forcedRoots should be array with expected number of elements", allPaths.size(), forcedRoots.size()); @@ -323,9 +323,9 @@ public void testUpdateChecklist() throws Exception { try (JsonReader reader = Json.createReader(new FileInputStream(replacePassChecklist))) { JsonObject checklist = reader.readObject(); assertTrue("checklist object should contain the forcedRoots key", - checklist.containsKey(Checklist.KEY_FORCED_ROOTS)); + checklist.containsKey(Checklist.keys().forcedRoots())); - JsonArray forcedRoots = checklist.getJsonArray(Checklist.KEY_FORCED_ROOTS); + JsonArray forcedRoots = checklist.getJsonArray(Checklist.keys().forcedRoots()); assertEquals("[replacePass] forcedRoots should be array with expected number of elements", 1 + unorderedPaths.size() + orderedPaths.stream().filter(path -> path.matches(".*[02468]$")).count(), forcedRoots.size()); @@ -358,9 +358,9 @@ public void testUpdateChecklist() throws Exception { try (JsonReader reader = Json.createReader(new FileInputStream(truncatePassChecklist))) { JsonObject checklist = reader.readObject(); assertTrue("checklist object should contain the forcedRoots key", - checklist.containsKey(Checklist.KEY_FORCED_ROOTS)); + checklist.containsKey(Checklist.keys().forcedRoots())); - JsonArray forcedRoots = checklist.getJsonArray(Checklist.KEY_FORCED_ROOTS); + JsonArray forcedRoots = checklist.getJsonArray(Checklist.keys().forcedRoots()); assertEquals("[truncatePass] forcedRoots should be array with expected number of elements", orderedPaths.stream().filter(path -> path.matches(".*[02468]$")).count(), forcedRoots.size()); diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/CndExporterTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/CndExporterTest.java index 71ac89486..ab89ba437 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/CndExporterTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/CndExporterTest.java @@ -23,8 +23,6 @@ import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -37,7 +35,7 @@ import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.JsonCnd; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Result; import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping; import org.junit.Test; diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/PrivilegeXmlExporterTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/PrivilegeXmlExporterTest.java index eb3aa5d0b..03875c5e0 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/PrivilegeXmlExporterTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/PrivilegeXmlExporterTest.java @@ -16,7 +16,6 @@ package net.adamcin.oakpal.webster; -import net.adamcin.oakpal.core.Nothing; import org.apache.jackrabbit.api.JackrabbitWorkspace; import org.apache.jackrabbit.api.security.authorization.PrivilegeManager; import org.junit.Test; @@ -32,10 +31,7 @@ import java.util.Arrays; import java.util.Collections; -import static net.adamcin.oakpal.core.Fun.result0; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.resultNothing1; -import static net.adamcin.oakpal.core.Fun.throwingVoidToNothing1; +import static net.adamcin.oakpal.api.Fun.resultNothing1; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/TestUtil.java b/webster/src/test/java/net/adamcin/oakpal/webster/TestUtil.java index f46cb41fb..ccb0edf36 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/TestUtil.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/TestUtil.java @@ -18,10 +18,10 @@ import joptsimple.OptionParser; import joptsimple.OptionSet; -import net.adamcin.oakpal.core.Fun; +import net.adamcin.oakpal.api.Fun; import net.adamcin.oakpal.core.OakMachine; import net.adamcin.oakpal.core.OakpalPlan; -import net.adamcin.oakpal.core.Result; +import net.adamcin.oakpal.api.Result; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.api.JackrabbitRepository; import org.apache.jackrabbit.api.JackrabbitWorkspace; @@ -70,10 +70,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.result1; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.Fun.zipKeysWithValueFunc; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.result1; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.Fun.zipKeysWithValueFunc; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertTrue; public final class TestUtil { diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/WebsterPlanTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/WebsterPlanTest.java index 98163c04c..ce8e60acc 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/WebsterPlanTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/WebsterPlanTest.java @@ -33,8 +33,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/targets/JsonTargetFactoryTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/targets/JsonTargetFactoryTest.java index 45eb7c046..40c83fac8 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/targets/JsonTargetFactoryTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/targets/JsonTargetFactoryTest.java @@ -16,16 +16,15 @@ package net.adamcin.oakpal.webster.targets; -import net.adamcin.oakpal.core.JavaxJson; import org.junit.Test; import java.io.File; import java.util.stream.Stream; -import static net.adamcin.oakpal.core.Fun.compose; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.compose; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static net.adamcin.oakpal.webster.targets.JsonTargetFactory.CHECKLIST; import static net.adamcin.oakpal.webster.targets.JsonTargetFactory.HINT_KEY_MORE_TARGETS; import static net.adamcin.oakpal.webster.targets.JsonTargetFactory.KEY_TYPE; diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTargetTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTargetTest.java index f2d596ba9..efa953b94 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTargetTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterChecklistTargetTest.java @@ -21,7 +21,7 @@ import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.JsonCnd; import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Rule; import net.adamcin.oakpal.webster.TestUtil; import net.adamcin.oakpal.webster.WebsterTarget; import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; @@ -43,10 +43,10 @@ import java.util.function.Consumer; import java.util.regex.Pattern; -import static net.adamcin.oakpal.core.Fun.uncheck1; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.Fun.uncheck1; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static net.adamcin.oakpal.webster.ChecklistExporter.SelectorType.NODETYPE; import static net.adamcin.oakpal.webster.ChecklistExporter.SelectorType.PATH; import static net.adamcin.oakpal.webster.ChecklistExporter.SelectorType.QUERY; diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTargetTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTargetTest.java index 0a7f3d0c2..739d4df2b 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTargetTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterNodetypesTargetTest.java @@ -18,7 +18,7 @@ import net.adamcin.oakpal.core.JcrNs; import net.adamcin.oakpal.core.OakMachine; -import net.adamcin.oakpal.core.checks.Rule; +import net.adamcin.oakpal.api.Rule; import net.adamcin.oakpal.webster.TestUtil; import net.adamcin.oakpal.webster.WebsterTarget; import org.apache.commons.io.FileUtils; @@ -32,9 +32,9 @@ import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static org.junit.Assert.*; public class WebsterNodetypesTargetTest { diff --git a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterPrivilegesTargetTest.java b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterPrivilegesTargetTest.java index e1991b848..00c4cfa52 100644 --- a/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterPrivilegesTargetTest.java +++ b/webster/src/test/java/net/adamcin/oakpal/webster/targets/WebsterPrivilegesTargetTest.java @@ -27,9 +27,9 @@ import java.io.File; -import static net.adamcin.oakpal.core.JavaxJson.arr; -import static net.adamcin.oakpal.core.JavaxJson.key; -import static net.adamcin.oakpal.core.JavaxJson.obj; +import static net.adamcin.oakpal.api.JavaxJson.arr; +import static net.adamcin.oakpal.api.JavaxJson.key; +import static net.adamcin.oakpal.api.JavaxJson.obj; import static net.adamcin.oakpal.webster.TestUtil.assertFileContains; import static net.adamcin.oakpal.webster.targets.WebsterNodetypesTargetTest.assertFileNotContains; import static org.junit.Assert.*;