From a1049df80959fa560b901cc3eaae00e9a242041e Mon Sep 17 00:00:00 2001 From: ac74475 Date: Thu, 20 Aug 2020 09:21:48 +0100 Subject: [PATCH 01/27] Updated versions to 0.5.0-SNAPSHOT --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0740d7b2..a6becf10 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ 4.0.0 uk.gov.gchq.palisade common - 0.4.0 + 0.5.0-${revision} Palisade common code https://github.com/gchq/Palisade-common @@ -40,7 +40,7 @@ 11 - RELEASE + SNAPSHOT ${java.version} ${java.version} UTF-8 From 126eb6bba949226f6272328ad6e996eb359e39c7 Mon Sep 17 00:00:00 2001 From: ac74475 Date: Thu, 20 Aug 2020 10:49:46 +0100 Subject: [PATCH 02/27] Updated dependancy tree --- licenses/mit_license.html | 8 ++++---- mvn_dependency_tree.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/licenses/mit_license.html b/licenses/mit_license.html index 69e05ee3..ef598503 100644 --- a/licenses/mit_license.html +++ b/licenses/mit_license.html @@ -15,10 +15,10 @@ - + @@ -56,7 +56,7 @@ @@ -109,7 +109,7 @@

Search form

-
+
diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index 69f3c80b..78ea3d32 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -1,4 +1,4 @@ -uk.gov.gchq.palisade:common:jar:0.4.0 +uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT +- com.fasterxml.jackson.core:jackson-core:jar:2.10.0:compile +- com.fasterxml.jackson.core:jackson-annotations:jar:2.10.0:compile +- com.fasterxml.jackson.core:jackson-databind:jar:2.10.0:compile From eeb37297f75b40875d80f9faf0d09d0c9d2344b1 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Tue, 8 Sep 2020 12:16:42 +0100 Subject: [PATCH 03/27] Pal 649 policy service redis (#73) --- src/main/java/uk/gov/gchq/palisade/Util.java | 5 ++- .../resource/AbstractLeafResource.java | 37 ++++++++++++----- .../palisade/resource/AbstractResource.java | 4 +- .../gov/gchq/palisade/resource/Resource.java | 4 +- .../resource/impl/DirectoryResource.java | 1 + .../palisade/resource/impl/FileResource.java | 6 ++- .../resource/impl/StreamResource.java | 7 +++- .../resource/impl/SystemResource.java | 2 + .../gov/gchq/palisade/rule/PredicateRule.java | 6 ++- .../java/uk/gov/gchq/palisade/rule/Rule.java | 6 ++- .../java/uk/gov/gchq/palisade/rule/Rules.java | 40 +++++++++---------- .../palisade/rule/SerializablePredicate.java | 28 +++++++++++++ .../rule/SerializableUnaryOperator.java | 28 +++++++++++++ .../gov/gchq/palisade/rule/WrappedRule.java | 23 ++++++----- .../palisade/service/ConnectionDetail.java | 4 +- .../service/SimpleConnectionDetail.java | 1 + .../gchq/palisade/service/request/Policy.java | 17 ++++---- .../palisade/policy/HasSensitiveAuthRule.java | 2 +- .../palisade/policy/HasTestingPurpose.java | 2 +- .../gchq/palisade/policy/PassThroughRule.java | 2 +- .../gchq/palisade/policy/WrappedRuleTest.java | 5 +-- 21 files changed, 164 insertions(+), 66 deletions(-) create mode 100644 src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java create mode 100644 src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java diff --git a/src/main/java/uk/gov/gchq/palisade/Util.java b/src/main/java/uk/gov/gchq/palisade/Util.java index a89b04ac..980423ae 100644 --- a/src/main/java/uk/gov/gchq/palisade/Util.java +++ b/src/main/java/uk/gov/gchq/palisade/Util.java @@ -22,6 +22,7 @@ import uk.gov.gchq.palisade.rule.Rule; import uk.gov.gchq.palisade.rule.Rules; +import java.io.Serializable; import java.net.URL; import java.security.CodeSource; import java.time.Duration; @@ -61,7 +62,7 @@ private Util() { * @param recordsReturned a counter for the number of records being returned * @return filtered stream */ - public static Stream applyRulesToStream(final Stream records, final User user, final Context context, final Rules rules, final AtomicLong recordsProcessed, final AtomicLong recordsReturned) { + public static Stream applyRulesToStream(final Stream records, final User user, final Context context, final Rules rules, final AtomicLong recordsProcessed, final AtomicLong recordsReturned) { Objects.requireNonNull(records); if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { return records; @@ -84,7 +85,7 @@ public static Stream applyRulesToStream(final Stream records, final Us * @param record type * @return item with rules applied */ - public static T applyRulesToItem(final T item, final User user, final Context context, final Rules rules) { + public static T applyRulesToItem(final T item, final User user, final Context context, final Rules rules) { if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { return item; } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java index edb863d8..54c05814 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java @@ -20,6 +20,7 @@ import uk.gov.gchq.palisade.resource.impl.FileResource; import uk.gov.gchq.palisade.service.ConnectionDetail; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -33,13 +34,13 @@ * See {@link FileResource} for a concrete implementation with an id. * This class is mostly used when deserialisation to a LeafResource is required, but the interface can't be used. */ -public abstract class AbstractLeafResource extends AbstractResource implements LeafResource, ChildResource { +public abstract class AbstractLeafResource extends AbstractResource implements LeafResource { private String type; private String serialisedFormat; private ConnectionDetail connectionDetail; private ParentResource parent; - private Map attributes = new HashMap<>(); + private HashMap attributes = new HashMap<>(); public AbstractLeafResource() { } @@ -62,14 +63,27 @@ public AbstractLeafResource connectionDetail(final ConnectionDetail connectionDe return this; } + /** + * Sets the attributes for the {@link AbstractLeafResource} + * + * @param attributes a {@link Map} of {@link String} and {@link Serializable}. + * @return a {@link AbstractLeafResource} object. + */ @Generated - public AbstractLeafResource attributes(final Map attributes) { + public AbstractLeafResource attributes(final Map attributes) { this.setAttributes(attributes); return this; } + /** + * Sets the attributes for the {@link AbstractLeafResource} + * + * @param attributeKey a {@link String} value for the key. + * @param attributeValue a {@link Serializable} value + * @return the {@link AbstractLeafResource} object + */ @Generated - public AbstractLeafResource attribute(final String attributeKey, final Object attributeValue) { + public AbstractLeafResource attribute(final String attributeKey, final Serializable attributeValue) { this.setAttribute(attributeKey, attributeValue); return this; } @@ -133,14 +147,14 @@ public void setParent(final ParentResource parent) { } @Generated - public Map getAttributes() { + public Map getAttributes() { return attributes; } @Generated - public void setAttributes(final Map attributes) { + public void setAttributes(final Map attributes) { requireNonNull(attributes); - this.attributes = attributes; + this.attributes = new HashMap<>(attributes); } @Generated @@ -153,9 +167,14 @@ public Boolean isAttributeSet(final String attributeKey) { return this.attributes.containsKey(attributeKey); } - + /** + * Sets the key and value of the attributes {@link Map} for the {@link AbstractLeafResource} + * + * @param attributeKey a {@link String} value for the attribute key. + * @param attributeValue a {@link Serializable} value for the attribute value. + */ @Generated - public void setAttribute(final String attributeKey, final Object attributeValue) { + public void setAttribute(final String attributeKey, final Serializable attributeValue) { requireNonNull(attributeKey, "The attributeKey cannot be set to null."); requireNonNull(attributeKey, "The attributeValue cannot be set to null."); this.attributes.put(attributeKey, attributeValue); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java index c5992727..cd380dd8 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java @@ -30,8 +30,8 @@ * This class is mostly used when deserialisation to a Resource is required, but the interface can't be used. */ public abstract class AbstractResource implements Resource { + private static final Comparator COMP = Comparator.comparing(Resource::getId); - private static Comparator comp = Comparator.comparing(Resource::getId); protected String id; public AbstractResource() { @@ -86,7 +86,7 @@ public String toString() { @Override public int compareTo(final Resource o) { - return comp.compare(this, o); + return COMP.compare(this, o); } } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java index 4de61531..4ba32cb1 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java @@ -24,6 +24,8 @@ import uk.gov.gchq.palisade.resource.impl.FileResource; +import java.io.Serializable; + /** * A high level API to define a resource, where a resource could be a system, directory, file, stream, etc. * A resource is expected to have a unique identifier. @@ -35,7 +37,7 @@ property = "class", defaultImpl = FileResource.class ) -public interface Resource extends Comparable { +public interface Resource extends Comparable, Serializable { Resource id(String id); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java index 9dbbfa08..c72c66d5 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java @@ -27,6 +27,7 @@ import static java.util.Objects.requireNonNull; public class DirectoryResource extends AbstractResource implements ChildResource, ParentResource { + private static final long serialVersionUID = 1L; private ParentResource parent; diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java index 612be685..18a84906 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java @@ -20,9 +20,11 @@ import uk.gov.gchq.palisade.resource.ParentResource; import uk.gov.gchq.palisade.service.ConnectionDetail; +import java.io.Serializable; import java.util.Map; public class FileResource extends AbstractLeafResource { + private static final long serialVersionUID = 1L; public FileResource() { //no-args constructor needed for serialization only @@ -49,12 +51,12 @@ public FileResource connectionDetail(final ConnectionDetail connectionDetail) { } @Override - public FileResource attributes(final Map attributes) { + public FileResource attributes(final Map attributes) { return (FileResource) super.attributes(attributes); } @Override - public FileResource attribute(final String attributeKey, final Object attributeValue) { + public FileResource attribute(final String attributeKey, final Serializable attributeValue) { return (FileResource) super.attribute(attributeKey, attributeValue); } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java index 6403f8a3..4e5cdddb 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java @@ -21,11 +21,14 @@ import uk.gov.gchq.palisade.resource.ParentResource; import uk.gov.gchq.palisade.service.ConnectionDetail; +import java.io.Serializable; import java.util.Map; import java.util.Objects; import java.util.StringJoiner; public class StreamResource extends AbstractLeafResource { + private static final long serialVersionUID = 1L; + protected long start; protected long end; @@ -54,12 +57,12 @@ public StreamResource connectionDetail(final ConnectionDetail connectionDetail) } @Override - public StreamResource attributes(final Map attributes) { + public StreamResource attributes(final Map attributes) { return (StreamResource) super.attributes(attributes); } @Override - public StreamResource attribute(final String attributeKey, final Object attributeValue) { + public StreamResource attribute(final String attributeKey, final Serializable attributeValue) { return (StreamResource) super.attribute(attributeKey, attributeValue); } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java index 4b9a8679..4e9b0ade 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java @@ -20,6 +20,8 @@ import uk.gov.gchq.palisade.resource.ParentResource; public class SystemResource extends AbstractResource implements ParentResource { + private static final long serialVersionUID = 1L; + public SystemResource() { //no-args constructor needed for serialization only } diff --git a/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java b/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java index a685dd5e..22d98ac3 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java @@ -19,14 +19,16 @@ import uk.gov.gchq.palisade.Context; import uk.gov.gchq.palisade.User; +import java.io.Serializable; + /** * A {@link PredicateRule} is a simplified implementation of {@link Rule} that simply * tests whether a record should be fully redacted or not. * * @param The type of the record. In normal cases the raw data will be deserialised - * by the record reader before being passed to the {@link PredicateRule#apply(Object, User, Context)}. + * by the record reader before being passed to the apply(T, User, Context) method. */ -public interface PredicateRule extends Rule { +public interface PredicateRule extends Rule { /** * Applies the rule logic to test whether a record should be redacted based on the user and context. * diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java index be040fb2..16df3199 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java @@ -25,6 +25,8 @@ import uk.gov.gchq.palisade.Context; import uk.gov.gchq.palisade.User; +import java.io.Serializable; + /** *

* A {@code Rule} is the fundamental interface for applying policy criteria. @@ -40,7 +42,7 @@ *

* * @param The type of the record. In normal cases the raw data will be deserialised - * by the record reader before being passed to the {@link Rule#apply(Object, User, Context)}. + * by the record reader before being passed to the apply(T, User, Context) method. */ @FunctionalInterface @JsonPropertyOrder(value = {"class"}, alphabetic = true) @@ -49,7 +51,7 @@ include = As.EXISTING_PROPERTY, property = "class" ) -public interface Rule { +public interface Rule extends Serializable { /** * Applies the rule logic to redact or modify the record based on the user and context. * diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java index 5ef4a477..1a15be6d 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java @@ -22,12 +22,11 @@ import uk.gov.gchq.palisade.Generated; import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; +import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.StringJoiner; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; import static java.util.Objects.requireNonNull; @@ -38,19 +37,20 @@ * @param The type of data records that the rules will be applied to. */ @JsonPropertyOrder(value = {"message", "rules"}, alphabetic = true) -public class Rules { +public class Rules implements Serializable { + private static final long serialVersionUID = 1L; private static final String ID_CANNOT_BE_NULL = "The id field can not be null."; private static final String RULE_CANNOT_BE_NULL = "The rule can not be null."; public static final String NO_RULES_SET = "no rules set"; private String message; - private Map> rulesHashMap; + private LinkedHashMap> rulesMap; /** * Constructs an empty instance of {@link Rules}. */ public Rules() { - rulesHashMap = new LinkedHashMap<>(); + rulesMap = new LinkedHashMap<>(); message = NO_RULES_SET; } @@ -70,7 +70,7 @@ public Rules rules(final Map> rules) { @Generated public Rules addRules(final Map> rules) { requireNonNull(rules, "Cannot add null to the existing rules."); - this.rulesHashMap.putAll(rules); + this.rulesMap.putAll(rules); return this; } @@ -97,7 +97,7 @@ public Rules message(final String message) { public Rules addRule(final String id, final Rule rule) { requireNonNull(id, ID_CANNOT_BE_NULL); requireNonNull(rule, RULE_CANNOT_BE_NULL); - rulesHashMap.put(id, rule); + rulesMap.put(id, rule); return this; } @@ -123,7 +123,7 @@ public Rules addPredicateRule(final String id, final PredicateRule rule) { * @return this Rules instance */ @Generated - public Rules addSimplePredicateRule(final String id, final Predicate rule) { + public Rules addSimplePredicateRule(final String id, final SerializablePredicate rule) { this.addRule(id, new WrappedRule<>(rule)); return this; } @@ -137,7 +137,7 @@ public Rules addSimplePredicateRule(final String id, final Predicate rule) * @return this Rules instance */ @Generated - public Rules addSimpleFunctionRule(final String id, final UnaryOperator rule) { + public Rules addSimpleFunctionRule(final String id, final SerializableUnaryOperator rule) { this.addRule(id, new WrappedRule<>(rule)); return this; } @@ -155,13 +155,13 @@ public void setMessage(final String message) { @Generated public Map> getRules() { - return rulesHashMap; + return rulesMap; } @Generated - public void setRules(final Map> rulesHashMap) { - requireNonNull(rulesHashMap); - this.rulesHashMap = rulesHashMap; + public void setRules(final Map> rulesMap) { + requireNonNull(rulesMap); + this.rulesMap = new LinkedHashMap<>(rulesMap); } /** @@ -170,7 +170,7 @@ public void setRules(final Map> rulesHashMap) { * @return {@code true} if this rule set contains at least one rule */ public boolean containsRules() { - return !rulesHashMap.isEmpty(); + return !rulesMap.isEmpty(); } @Override @@ -182,13 +182,13 @@ public boolean equals(final Object o) { final EqualsBuilder builder = new EqualsBuilder() .append(message, that.message) - .append(this.rulesHashMap.keySet(), that.getRules().keySet()); + .append(this.rulesMap.keySet(), that.getRules().keySet()); if (builder.isEquals()) { - for (final Map.Entry> entry : this.rulesHashMap.entrySet()) { + for (final Map.Entry> entry : this.rulesMap.entrySet()) { final String ruleName = entry.getKey(); - final Rule thisRule = entry.getValue(); - final Rule thatRule = that.getRules().get(ruleName); + final Rule thisRule = entry.getValue(); + final Rule thatRule = that.getRules().get(ruleName); builder.append(thisRule.getClass(), thatRule.getClass()); if (builder.isEquals()) { @@ -209,7 +209,7 @@ public boolean equals(final Object o) { @Override @Generated public int hashCode() { - return Objects.hash(message, rulesHashMap); + return Objects.hash(message, rulesMap); } @Override @@ -217,7 +217,7 @@ public int hashCode() { public String toString() { return new StringJoiner(", ", Rules.class.getSimpleName() + "[", "]") .add("message='" + message + "'") - .add("rulesHashMap=" + rulesHashMap) + .add("rulesHashMap=" + rulesMap) .add(super.toString()) .toString(); } diff --git a/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java b/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java new file mode 100644 index 00000000..7fd2749c --- /dev/null +++ b/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Crown Copyright + * + * 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 uk.gov.gchq.palisade.rule; + +import java.io.Serializable; +import java.util.function.Predicate; + +/** + * A serializable interface for a {@link Predicate} + * + * @param the type of the input to the predicate + */ +public interface SerializablePredicate extends Predicate, Serializable { +} diff --git a/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java b/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java new file mode 100644 index 00000000..8031b9c1 --- /dev/null +++ b/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Crown Copyright + * + * 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 uk.gov.gchq.palisade.rule; + +import java.io.Serializable; +import java.util.function.UnaryOperator; + +/** + * A serializable interface for a {@link UnaryOperator} + * + * @param the type of the operand and result of the operator + */ +public interface SerializableUnaryOperator extends UnaryOperator, Serializable { +} diff --git a/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java b/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java index 7dc02b69..b170a469 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java @@ -25,6 +25,7 @@ import uk.gov.gchq.palisade.Generated; import uk.gov.gchq.palisade.User; +import java.io.Serializable; import java.util.Objects; import java.util.StringJoiner; import java.util.function.Predicate; @@ -33,22 +34,24 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; - -@JsonPropertyOrder(value = {"class", "rule", "function", "predicate"}, alphabetic = true) /** * A {@link WrappedRule} is helper implementation of {@link Rule}. It is useful * when you need to set simple rules that don't require the {@link User} or {@link Context}. + * * @param The type of the record. In normal cases the raw data will be deserialised - * by the record reader before being passed to the {@link Rule#apply(Object, User, Context)}. + * by the record reader before being passed to the {@link Rule#apply(Serializable, User, Context)}. */ -public class WrappedRule implements Rule { +@JsonPropertyOrder(value = {"class", "rule", "function", "predicate"}, alphabetic = true) +public class WrappedRule implements Rule { + public static final String WRAPPED_RULE_WAS_INITIALISED_WITH_NULL = "WrappedRule was initialised with null."; public static final String RULE_STRING = "rule"; public static final String FUNCTION_STRING = "function"; public static final String PREDICATE_STRING = "predicate"; + private static final long serialVersionUID = 1L; private Rule rule; - private UnaryOperator function; - private Predicate predicate; + private SerializableUnaryOperator function; + private SerializablePredicate predicate; /** * Constructs a {@link WrappedRule} with a null rule. @@ -72,7 +75,7 @@ public WrappedRule(final Rule rule) { * * @param function the simple {@link UnaryOperator} rule to wrap. */ - public WrappedRule(final UnaryOperator function) { + public WrappedRule(final SerializableUnaryOperator function) { requireNonNull(function, WRAPPED_RULE_WAS_INITIALISED_WITH_NULL + FUNCTION_STRING); this.function = function; } @@ -83,15 +86,15 @@ public WrappedRule(final UnaryOperator function) { * * @param predicate the simple {@link Predicate} rule to wrap. */ - public WrappedRule(final Predicate predicate) { + public WrappedRule(final SerializablePredicate predicate) { requireNonNull(predicate, WRAPPED_RULE_WAS_INITIALISED_WITH_NULL + PREDICATE_STRING); this.predicate = predicate; } @JsonCreator public WrappedRule(@JsonProperty("rule") final Rule rule, - @JsonProperty("function") final UnaryOperator function, - @JsonProperty("predicate") final Predicate predicate) { + @JsonProperty("function") final SerializableUnaryOperator function, + @JsonProperty("predicate") final SerializablePredicate predicate) { checkNullCount(rule, function, predicate); this.rule = rule; this.function = function; diff --git a/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java index d4018853..09261786 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java @@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import java.io.Serializable; + /** * A High level API for passing details of how to connect to a resource */ @@ -32,7 +34,7 @@ property = "class", defaultImpl = SimpleConnectionDetail.class ) -public interface ConnectionDetail { +public interface ConnectionDetail extends Serializable { String createConnection(); diff --git a/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java index 34277762..7795da0a 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java @@ -27,6 +27,7 @@ * A simple implementation of the {@link ConnectionDetail} that holds a reference to the {@link Service} */ public class SimpleConnectionDetail implements ConnectionDetail { + private static final long serialVersionUID = 1L; private String serviceName; diff --git a/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java b/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java index cb3c8728..7e9b5a4d 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java +++ b/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java @@ -25,12 +25,13 @@ import uk.gov.gchq.palisade.rule.PredicateRule; import uk.gov.gchq.palisade.rule.Rule; import uk.gov.gchq.palisade.rule.Rules; +import uk.gov.gchq.palisade.rule.SerializablePredicate; +import uk.gov.gchq.palisade.rule.SerializableUnaryOperator; +import java.io.Serializable; import java.util.Objects; import java.util.StringJoiner; import java.util.UUID; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; import static java.util.Objects.requireNonNull; @@ -43,7 +44,9 @@ * the format of, e.g. if T was String then a policy has Rules of type Resource for coarse filtering * and Rules of type String for fine grain filtering of files where each record is a String */ -public class Policy { +public class Policy implements Serializable { + private static final long serialVersionUID = 1L; + private Rules recordRules; private Rules resourceRules; private User owner; @@ -129,7 +132,7 @@ public Policy recordLevelPredicateRule(final String message, final PredicateR * @return the policy */ @Generated - public Policy recordLevelSimplePredicateRule(final String message, final Predicate rule) { + public Policy recordLevelSimplePredicateRule(final String message, final SerializablePredicate rule) { Rules recordLevelRules = getRecordRules(); recordLevelRules.addSimplePredicateRule(generateUUID(), rule); addMessage(message, recordLevelRules); @@ -144,7 +147,7 @@ public Policy recordLevelSimplePredicateRule(final String message, final Pred * @return the policy */ @Generated - public Policy recordLevelSimpleFunctionRule(final String message, final UnaryOperator rule) { + public Policy recordLevelSimpleFunctionRule(final String message, final SerializableUnaryOperator rule) { Rules recordLevelRules = getRecordRules(); recordLevelRules.addSimpleFunctionRule(generateUUID(), rule); addMessage(message, recordLevelRules); @@ -189,7 +192,7 @@ public Policy resourceLevelPredicateRule(final String message, final Predicat * @return the policy */ @Generated - public Policy resourceLevelSimplePredicateRule(final String message, final Predicate rule) { + public Policy resourceLevelSimplePredicateRule(final String message, final SerializablePredicate rule) { Rules resourceLevelRules = getResourceRules(); resourceLevelRules.addSimplePredicateRule(generateUUID(), rule); addMessage(message, resourceLevelRules); @@ -204,7 +207,7 @@ public Policy resourceLevelSimplePredicateRule(final String message, final Pr * @return the policy */ @Generated - public Policy resourceLevelSimpleFunctionRule(final String message, final UnaryOperator rule) { + public Policy resourceLevelSimpleFunctionRule(final String message, final SerializableUnaryOperator rule) { Rules resourceLevelRules = getResourceRules(); resourceLevelRules.addSimpleFunctionRule(generateUUID(), rule); addMessage(message, resourceLevelRules); diff --git a/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java b/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java index 3bd39674..c93a351f 100644 --- a/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java +++ b/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java @@ -22,7 +22,7 @@ import java.io.Serializable; -public class HasSensitiveAuthRule implements Serializable, Rule { +public class HasSensitiveAuthRule implements Serializable, Rule { @Override public T apply(final T record, final User user, final Context context) { if (user.getAuths().contains("Sensitive")) { diff --git a/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java b/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java index 77088a03..c56d2209 100644 --- a/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java +++ b/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java @@ -22,7 +22,7 @@ import java.io.Serializable; -public class HasTestingPurpose implements Serializable, Rule { +public class HasTestingPurpose implements Serializable, Rule { @Override public T apply(final T record, final User user, final Context context) { if (context.getPurpose().equalsIgnoreCase("testing")) { diff --git a/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java b/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java index ece5b7d8..98759e1d 100644 --- a/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java +++ b/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java @@ -22,7 +22,7 @@ import java.io.Serializable; -public class PassThroughRule implements Serializable, Rule { +public class PassThroughRule implements Serializable, Rule { @Override public T apply(final T record, final User user, final Context context) { return record; diff --git a/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java b/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java index 262945a9..781eb7c6 100644 --- a/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java +++ b/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java @@ -18,10 +18,9 @@ import org.junit.Assert; import org.junit.Test; +import uk.gov.gchq.palisade.rule.SerializableUnaryOperator; import uk.gov.gchq.palisade.rule.WrappedRule; -import java.util.function.UnaryOperator; - public class WrappedRuleTest { @Test @@ -61,7 +60,7 @@ public void shouldNotConstructNullFunction() { @Test(expected = IllegalArgumentException.class) public void shouldNotConstructNullRule() { //When - new WrappedRule<>(null, (UnaryOperator) Object::toString, o -> true); + new WrappedRule<>(null, (SerializableUnaryOperator) String::toString, o -> true); //Then it should throw an IllegalArgumentException } From 8944e4fd29c1493904f9143e31624713b0907441 Mon Sep 17 00:00:00 2001 From: ac74475 Date: Thu, 10 Sep 2020 15:33:38 +0100 Subject: [PATCH 04/27] Pal 710 ecr push (#74) * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - Updated Jenkinsfile * PAL-710 - updated jenkinsfile * PAL-710 - updated JenkinsFile to make the revision start with BRANCH-SNAPSHOT rather than ending with that and include an IS_PR variable to reduce duplicate processing * PAL-710 - updated JenkinsFile to make the revision work for both nexus and ECR lifecycle policies * PAL-710-ECR-Push: Re-inserted a missing Maven deploy segment of the Jenkins file * PAL-710-ECR-Push: Removed the "Maven deploy" stage from the Jenkins file. * PAL-710-ECR-Push: added quick profile to deploy Co-authored-by: m78233 <61839550+m78233@users.noreply.github.com> Co-authored-by: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> --- Jenkinsfile | 59 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e3f89ff5..ac79eb37 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -68,21 +68,38 @@ timestamps { node(POD_LABEL) { def GIT_BRANCH_NAME + def COMMON_REVISION + def IS_PR stage('Bootstrap') { if (env.CHANGE_BRANCH) { GIT_BRANCH_NAME = env.CHANGE_BRANCH + IS_PR="true" } else { GIT_BRANCH_NAME = env.BRANCH_NAME + IS_PR="false" + } + def GIT_BRANCH_NAME_LOWER = GIT_BRANCH_NAME.toLowerCase().take(7) + COMMON_REVISION = "BRANCH-${GIT_BRANCH_NAME_LOWER}-SNAPSHOT" + if ("${env.BRANCH_NAME}" == "develop") { + COMMON_REVISION = "SNAPSHOT" + } + if ("${env.BRANCH_NAME}" == "main") { + COMMON_REVISION = "RELEASE" } echo sh(script: 'env | sort', returnStdout: true) } + stage('Install, Unit Tests, Checkstyle') { dir('Palisade-common') { git branch: GIT_BRANCH_NAME, url: 'https://github.com/gchq/Palisade-common.git' container('docker-cmds') { configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - sh 'mvn -s $MAVEN_SETTINGS install' + if (IS_PR == "true") { + sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} -P quick deploy" + } else { + sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} install" + } } } } @@ -96,41 +113,23 @@ timestamps { file(credentialsId: "${env.SQ_KEY_STORE}", variable: 'KEYSTORE')]) { configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { withSonarQubeEnv(installationName: 'sonar') { - if (env.CHANGE_BRANCH) { - sh 'mvn -s $MAVEN_SETTINGS org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar -Dsonar.projectKey="Palisade-Common-${CHANGE_BRANCH}" -Dsonar.projectName="Palisade-Common-${CHANGE_BRANCH}" -Dsonar.webhooks.project=$SONARQUBE_WEBHOOK -Djavax.net.ssl.trustStore=$KEYSTORE -Djavax.net.ssl.trustStorePassword=$KEYSTORE_PASS' - } else { - sh 'mvn -s $MAVEN_SETTINGS org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar -Dsonar.projectKey="Palisade-Common-${BRANCH_NAME}" -Dsonar.projectName="Palisade-Common-${BRANCH_NAME}" -Dsonar.webhooks.project=$SONARQUBE_WEBHOOK -Djavax.net.ssl.trustStore=$KEYSTORE -Djavax.net.ssl.trustStorePassword=$KEYSTORE_PASS' - } + sh "mvn -s ${MAVEN_SETTINGS} org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar -Dsonar.projectKey=Palisade-Common-${GIT_BRANCH_NAME} -Dsonar.projectName=Palisade-Common-${GIT_BRANCH_NAME} -Dsonar.webhooks.project=$SONARQUBE_WEBHOOK -Djavax.net.ssl.trustStore=$KEYSTORE -Djavax.net.ssl.trustStorePassword=$KEYSTORE_PASS" } } } } } + } - stage("SonarQube Quality Gate") { - // Wait for SonarQube to prepare the report - sleep(time: 10, unit: 'SECONDS') - // Just in case something goes wrong, pipeline will be killed after a timeout - timeout(time: 5, unit: 'MINUTES') { - // Reuse taskId previously collected by withSonarQubeEnv - def qg = waitForQualityGate() - if (qg.status != 'OK') { - error "Pipeline aborted due to SonarQube quality gate failure: ${qg.status}" - } - } - } - stage('Maven deploy') { - dir('Palisade-common') { - container('docker-cmds') { - configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - if (("${env.BRANCH_NAME}" == "develop") || - ("${env.BRANCH_NAME}" == "master")) { - sh 'mvn -s $MAVEN_SETTINGS deploy -P default,quick,avro' - } else { - sh "echo - no deploy" - } - } - } + stage("SonarQube Quality Gate") { + // Wait for SonarQube to prepare the report + sleep(time: 10, unit: 'SECONDS') + // Just in case something goes wrong, pipeline will be killed after a timeout + timeout(time: 5, unit: 'MINUTES') { + // Reuse taskId previously collected by withSonarQubeEnv + def qg = waitForQualityGate() + if (qg.status != 'OK') { + error "Pipeline aborted due to SonarQube quality gate failure: ${qg.status}" } } } From 52d0e97ffaf03d8999f9357b11a4bc630c510ae3 Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Thu, 10 Sep 2020 16:26:26 +0100 Subject: [PATCH 05/27] Pal 710 maven deploy (#75) * Updated Jenkinsfile to readd the Maven Deploy phase * added testing of deploy * updated maven deploy for debugging * removed debugging steps in Jenkinsfile, now ready for merge --- Jenkinsfile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index ac79eb37..acce025a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -133,7 +133,20 @@ timestamps { } } } + + stage('Maven deploy') { + dir('Palisade-common') { + container('docker-cmds') { + configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { + if (("${env.BRANCH_NAME}" == "develop") || ("${env.BRANCH_NAME}" == "main")) { + sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} -P quick deploy" + } else { + sh "echo - no deploy" + } + } + } + } + } } } - } From f8f1baf0b8d58d1ff9903961a4abb5cfd2f53d7f Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Tue, 15 Sep 2020 11:58:27 +0100 Subject: [PATCH 06/27] Pal 710 fix pr testing (#76) --- Jenkinsfile | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index acce025a..8bd65a00 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -70,22 +70,26 @@ timestamps { def GIT_BRANCH_NAME def COMMON_REVISION def IS_PR + def FEATURE_BRANCH stage('Bootstrap') { if (env.CHANGE_BRANCH) { GIT_BRANCH_NAME = env.CHANGE_BRANCH - IS_PR="true" + IS_PR = "true" } else { GIT_BRANCH_NAME = env.BRANCH_NAME - IS_PR="false" + IS_PR = "false" } def GIT_BRANCH_NAME_LOWER = GIT_BRANCH_NAME.toLowerCase().take(7) COMMON_REVISION = "BRANCH-${GIT_BRANCH_NAME_LOWER}-SNAPSHOT" + FEATURE_BRANCH = "true" if ("${env.BRANCH_NAME}" == "develop") { COMMON_REVISION = "SNAPSHOT" + FEATURE_BRANCH = "false" } if ("${env.BRANCH_NAME}" == "main") { COMMON_REVISION = "RELEASE" + FEATURE_BRANCH = "false" } echo sh(script: 'env | sort', returnStdout: true) } @@ -95,8 +99,8 @@ timestamps { git branch: GIT_BRANCH_NAME, url: 'https://github.com/gchq/Palisade-common.git' container('docker-cmds') { configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - if (IS_PR == "true") { - sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} -P quick deploy" + if (IS_PR == "true" || FEATURE_BRANCH == "false") { + sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} deploy" } else { sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} install" } @@ -133,20 +137,6 @@ timestamps { } } } - - stage('Maven deploy') { - dir('Palisade-common') { - container('docker-cmds') { - configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - if (("${env.BRANCH_NAME}" == "develop") || ("${env.BRANCH_NAME}" == "main")) { - sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} -P quick deploy" - } else { - sh "echo - no deploy" - } - } - } - } - } } } } From 33d2226cd4c04443c36fbe50eb6ff47c32ba7e6d Mon Sep 17 00:00:00 2001 From: ac74475 Date: Tue, 20 Oct 2020 10:57:18 +0100 Subject: [PATCH 07/27] updated the junit version from 4.12 to 4.13.1 (#78) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a6becf10..b9d9165e 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 1.8.2 - 4.12 + 4.13.1 2.22.1 2.22.1 3.1.0 From af1f3fe91a318ec66580ff47973b9baaccb484ca Mon Sep 17 00:00:00 2001 From: kg981167224 <68223404+kg981167224@users.noreply.github.com> Date: Thu, 22 Oct 2020 14:45:57 +0100 Subject: [PATCH 08/27] PAL-398: Call froovy library from Jenkinsfile (#79) --- Jenkinsfile | 126 +--------------------------------------------------- 1 file changed, 2 insertions(+), 124 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8bd65a00..7da8dd52 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,130 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -//node-affinity -//nodes 1..3 are reserved for Jenkins slave pods. -//node 0 is used for the Jenkins master +@Library('jenkinsfile-lib')_ timestamps { - - podTemplate(yaml: ''' - apiVersion: v1 - kind: Pod - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: palisade-node-name - operator: In - values: - - node1 - - node2 - - node3 - containers: - - name: jnlp - image: jenkins/jnlp-slave - imagePullPolicy: Always - args: - - $(JENKINS_SECRET) - - $(JENKINS_NAME) - resources: - requests: - ephemeral-storage: "4Gi" - limits: - ephemeral-storage: "8Gi" - - - name: docker-cmds - image: 779921734503.dkr.ecr.eu-west-1.amazonaws.com/jnlp-did:200608 - imagePullPolicy: IfNotPresent - command: - - sleep - args: - - 99d - env: - - name: DOCKER_HOST - value: tcp://localhost:2375 - resources: - requests: - ephemeral-storage: "4Gi" - limits: - ephemeral-storage: "8Gi" - - ''') { - - node(POD_LABEL) { - def GIT_BRANCH_NAME - def COMMON_REVISION - def IS_PR - def FEATURE_BRANCH - - stage('Bootstrap') { - if (env.CHANGE_BRANCH) { - GIT_BRANCH_NAME = env.CHANGE_BRANCH - IS_PR = "true" - } else { - GIT_BRANCH_NAME = env.BRANCH_NAME - IS_PR = "false" - } - def GIT_BRANCH_NAME_LOWER = GIT_BRANCH_NAME.toLowerCase().take(7) - COMMON_REVISION = "BRANCH-${GIT_BRANCH_NAME_LOWER}-SNAPSHOT" - FEATURE_BRANCH = "true" - if ("${env.BRANCH_NAME}" == "develop") { - COMMON_REVISION = "SNAPSHOT" - FEATURE_BRANCH = "false" - } - if ("${env.BRANCH_NAME}" == "main") { - COMMON_REVISION = "RELEASE" - FEATURE_BRANCH = "false" - } - echo sh(script: 'env | sort', returnStdout: true) - } - - stage('Install, Unit Tests, Checkstyle') { - dir('Palisade-common') { - git branch: GIT_BRANCH_NAME, url: 'https://github.com/gchq/Palisade-common.git' - container('docker-cmds') { - configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - if (IS_PR == "true" || FEATURE_BRANCH == "false") { - sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} deploy" - } else { - sh "mvn -s ${MAVEN_SETTINGS} -D revision=${COMMON_REVISION} install" - } - } - } - } - } - - stage('SonarQube analysis') { - dir('Palisade-common') { - container('docker-cmds') { - withCredentials([string(credentialsId: "${env.SQ_WEB_HOOK}", variable: 'SONARQUBE_WEBHOOK'), - string(credentialsId: "${env.SQ_KEY_STORE_PASS}", variable: 'KEYSTORE_PASS'), - file(credentialsId: "${env.SQ_KEY_STORE}", variable: 'KEYSTORE')]) { - configFileProvider([configFile(fileId: "${env.CONFIG_FILE}", variable: 'MAVEN_SETTINGS')]) { - withSonarQubeEnv(installationName: 'sonar') { - sh "mvn -s ${MAVEN_SETTINGS} org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar -Dsonar.projectKey=Palisade-Common-${GIT_BRANCH_NAME} -Dsonar.projectName=Palisade-Common-${GIT_BRANCH_NAME} -Dsonar.webhooks.project=$SONARQUBE_WEBHOOK -Djavax.net.ssl.trustStore=$KEYSTORE -Djavax.net.ssl.trustStorePassword=$KEYSTORE_PASS" - } - } - } - } - } - } - - stage("SonarQube Quality Gate") { - // Wait for SonarQube to prepare the report - sleep(time: 10, unit: 'SECONDS') - // Just in case something goes wrong, pipeline will be killed after a timeout - timeout(time: 5, unit: 'MINUTES') { - // Reuse taskId previously collected by withSonarQubeEnv - def qg = waitForQualityGate() - if (qg.status != 'OK') { - error "Pipeline aborted due to SonarQube quality gate failure: ${qg.status}" - } - } - } - } - } + common() } From 9fd1bec3b311fb52a3beb6f8aef97c8c3b7e9e97 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Tue, 27 Oct 2020 12:22:16 +0000 Subject: [PATCH 09/27] Pal 414 refactored ResourceService interface --- .../palisade/service/ResourceService.java | 58 +++---------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java index 65948284..c3bd78cb 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java @@ -17,68 +17,26 @@ package uk.gov.gchq.palisade.service; import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; - -import java.util.stream.Stream; /** * The resource service is the Palisade component that determines what resources are available that meet a specific - * (type of) request and how they should be accessed. This interface details several methods for obtaining a list of - * resources, e.g. by type or by data format. The methods of this service all return {@link Stream}s which link a valid - * {@link LeafResource} with a {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain - * information on how to set up a connection to retrieve a particular resource. Implementations of this service do not - * deal with the filtering or application of security policy to the resources. Therefore, a result returned from a - * method call on this interface doesn't guarantee that the user will be allowed to access it by policy. Other - * components of the Palisade system will enforce the necessary policy controls to prevent access to resources by users - * without the necessary access rights. - * Implementation note: None of the ${@code getResourcesByXXX} methods in this class will return in error if there - * don't happen to be any resources that do not match a request, instead they will simply return empty ${@link Stream} - * instances. + * request and how they should be accessed. The get method of this service returns a {@link LeafResource} with a + * {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain information on how to set up a connection to + * retrieve a particular resource. Implementations of this service do not deal with the filtering or application of security policy + * to the resources. Therefore, a result returned from a method call on this interface doesn't guarantee that the user will be + * allowed to access it by policy. Other components of the Palisade system will enforce the necessary policy controls to + * prevent access to resources by users without the necessary access rights. */ public interface ResourceService extends Service { - /** - * Get a list of resources based on a specific resource. This allows for the retrieval of the appropriate {@link - * ConnectionDetail}s for a given resource. It may also be used to retrieve the details all the resources that are - * notionally children of another resource. For example, in a standard hierarchical filing system the files in a - * directory could be considered child resources and calling this method on the directory resource would fetch the - * details on the contained files. - * - * @param resource the resource to request - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} - */ - Stream getResourcesByResource(final Resource resource); - /** * Retrieve resource and connection details by resource ID. The request object allows the client to specify the * resource ID and obtain the connection details once the returned future has completed. * * @param resourceId the ID to request - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} - */ - Stream getResourcesById(final String resourceId); - - /** - * Obtain a list of resources that match a specific resource type. This method allows a client to obtain potentially - * large collections of resources by requesting all the resources of one particular type. For example, a client may - * request all "employee contact card" records. Please note the warning in the class documentation above, that just - * because a resource is available does not guarantee that the requesting client has the right to access it. - * - * @param type the type of resource to retrieve. - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} - */ - Stream getResourcesByType(final String type); - - /** - * Find all resources that match a particular data format. Resources of a particular data format may not share a - * type, e.g. not all CSV format records will contain employee contact details. This method allows clients to - * retrieve all the resources Palisade knows about that conform to one particular format. Note that this method can - * potentially return large ${@code Map}s with many mappings. - * - * @param serialisedFormat the specific format for retrieval - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + * @return {@link LeafResource}, each with an appropriate {@link ConnectionDetail} */ - Stream getResourcesBySerialisedFormat(final String serialisedFormat); + LeafResource getResourcesById(final String resourceId); /** * Informs Palisade about a specific resource that it may return to users. This lets Palisade clients request access From 1525aafbe79049c69032017a4c50115ce34ef3c4 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Wed, 28 Oct 2020 12:19:42 +0000 Subject: [PATCH 10/27] Pal 414 reverted ResourceService changes --- .../palisade/service/ResourceService.java | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java index c3bd78cb..8817a3c2 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java @@ -17,26 +17,68 @@ package uk.gov.gchq.palisade.service; import uk.gov.gchq.palisade.resource.LeafResource; +import uk.gov.gchq.palisade.resource.Resource; + +import java.util.stream.Stream; /** * The resource service is the Palisade component that determines what resources are available that meet a specific - * request and how they should be accessed. The get method of this service returns a {@link LeafResource} with a - * {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain information on how to set up a connection to - * retrieve a particular resource. Implementations of this service do not deal with the filtering or application of security policy - * to the resources. Therefore, a result returned from a method call on this interface doesn't guarantee that the user will be - * allowed to access it by policy. Other components of the Palisade system will enforce the necessary policy controls to - * prevent access to resources by users without the necessary access rights. + * (type of) request and how they should be accessed. This interface details several methods for obtaining a list of + * resources, e.g. by type or by data format. The methods of this service all return {@link Stream}s which link a valid + * {@link LeafResource} with a {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain + * information on how to set up a connection to retrieve a particular resource. Implementations of this service do not + * deal with the filtering or application of security policy to the resources. Therefore, a result returned from a + * method call on this interface doesn't guarantee that the user will be allowed to access it by policy. Other + * components of the Palisade system will enforce the necessary policy controls to prevent access to resources by users + * without the necessary access rights. + * Implementation note: None of the ${@code getResourcesByXXX} methods in this class will return in error if there + * don't happen to be any resources that do not match a request, instead they will simply return empty ${@link Stream} + * instances. */ public interface ResourceService extends Service { + /** + * Get a list of resources based on a specific resource. This allows for the retrieval of the appropriate {@link + * ConnectionDetail}s for a given resource. It may also be used to retrieve the details all the resources that are + * notionally children of another resource. For example, in a standard hierarchical filing system the files in a + * directory could be considered child resources and calling this method on the directory resource would fetch the + * details on the contained files. + * + * @param resource the resource to request + * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + */ + Stream getResourcesByResource(final Resource resource); + /** * Retrieve resource and connection details by resource ID. The request object allows the client to specify the * resource ID and obtain the connection details once the returned future has completed. * * @param resourceId the ID to request - * @return {@link LeafResource}, each with an appropriate {@link ConnectionDetail} + * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + */ + Stream getResourcesById(final String resourceId); + + /** + * Obtain a list of resources that match a specific resource type. This method allows a client to obtain potentially + * large collections of resources by requesting all the resources of one particular type. For example, a client may + * request all "employee contact card" records. Please note the warning in the class documentation above, that just + * because a resource is available does not guarantee that the requesting client has the right to access it. + * + * @param type the type of resource to retrieve. + * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + */ + Stream getResourcesByType(final String type); + + /** + * Find all resources that match a particular data format. Resources of a particular data format may not share a + * type, e.g. not all CSV format records will contain employee contact details. This method allows clients to + * retrieve all the resources Palisade knows about that conform to one particular format. Note that this method can + * potentially return large ${@code Map}s with many mappings. + * + * @param serialisedFormat the specific format for retrieval + * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} */ - LeafResource getResourcesById(final String resourceId); + Stream getResourcesBySerialisedFormat(final String serialisedFormat); /** * Informs Palisade about a specific resource that it may return to users. This lets Palisade clients request access @@ -48,4 +90,4 @@ public interface ResourceService extends Service { */ Boolean addResource(final LeafResource resource); -} +} \ No newline at end of file From a2a6d9f16c98ba06f7e43bd6707300a8e03a29af Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Thu, 29 Oct 2020 11:39:50 +0000 Subject: [PATCH 11/27] Pal 414 refactored ResourceService interface --- mvn_dependency_tree.txt | 2 +- .../gchq/palisade/service/ResourceService.java | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index 78ea3d32..cfaa9a8f 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -14,7 +14,7 @@ uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT | +- org.xerial.snappy:snappy-java:jar:1.1.1.3:compile | +- org.apache.commons:commons-compress:jar:1.8.1:compile | \- org.tukaani:xz:jar:1.5:compile -+- junit:junit:jar:4.12:test ++- junit:junit:jar:4.13.1:test | \- org.hamcrest:hamcrest-core:jar:1.3:test +- org.mockito:mockito-core:jar:3.1.0:test | +- net.bytebuddy:byte-buddy:jar:1.9.10:test diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java index 8817a3c2..2146d546 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java @@ -19,6 +19,7 @@ import uk.gov.gchq.palisade.resource.LeafResource; import uk.gov.gchq.palisade.resource.Resource; +import java.util.Iterator; import java.util.stream.Stream; /** @@ -45,18 +46,18 @@ public interface ResourceService extends Service { * details on the contained files. * * @param resource the resource to request - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} */ - Stream getResourcesByResource(final Resource resource); + Iterator getResourcesByResource(final Resource resource); /** * Retrieve resource and connection details by resource ID. The request object allows the client to specify the * resource ID and obtain the connection details once the returned future has completed. * * @param resourceId the ID to request - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} */ - Stream getResourcesById(final String resourceId); + Iterator getResourcesById(final String resourceId); /** * Obtain a list of resources that match a specific resource type. This method allows a client to obtain potentially @@ -65,9 +66,9 @@ public interface ResourceService extends Service { * because a resource is available does not guarantee that the requesting client has the right to access it. * * @param type the type of resource to retrieve. - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} */ - Stream getResourcesByType(final String type); + Iterator getResourcesByType(final String type); /** * Find all resources that match a particular data format. Resources of a particular data format may not share a @@ -76,9 +77,9 @@ public interface ResourceService extends Service { * potentially return large ${@code Map}s with many mappings. * * @param serialisedFormat the specific format for retrieval - * @return a {@link Stream} of resources, each with an appropriate {@link ConnectionDetail} + * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} */ - Stream getResourcesBySerialisedFormat(final String serialisedFormat); + Iterator getResourcesBySerialisedFormat(final String serialisedFormat); /** * Informs Palisade about a specific resource that it may return to users. This lets Palisade clients request access From d78ee53d91d68c6056cf542c8916287c68b525af Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Tue, 10 Nov 2020 09:34:25 +0000 Subject: [PATCH 12/27] PAL-412, removed policy object and updated prePopulation and IsTextResourceRule to use LeafResources over Resources (#80) --- mvn_dependency_tree.txt | 2 +- .../service/PolicyPrepopulationFactory.java | 20 +- .../gchq/palisade/service/request/Policy.java | 345 ------------------ .../palisade/policy/IsTextResourceRule.java | 11 +- 4 files changed, 20 insertions(+), 358 deletions(-) delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/request/Policy.java diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index 78ea3d32..cfaa9a8f 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -14,7 +14,7 @@ uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT | +- org.xerial.snappy:snappy-java:jar:1.1.1.3:compile | +- org.apache.commons:commons-compress:jar:1.8.1:compile | \- org.tukaani:xz:jar:1.5:compile -+- junit:junit:jar:4.12:test ++- junit:junit:jar:4.13.1:test | \- org.hamcrest:hamcrest-core:jar:1.3:test +- org.mockito:mockito-core:jar:3.1.0:test | +- net.bytebuddy:byte-buddy:jar:1.9.10:test diff --git a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java b/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java index 79ab5d48..61acb9f3 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java +++ b/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java @@ -24,9 +24,11 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import uk.gov.gchq.palisade.resource.LeafResource; import uk.gov.gchq.palisade.resource.Resource; -import uk.gov.gchq.palisade.service.request.Policy; +import uk.gov.gchq.palisade.rule.Rules; +import java.io.Serializable; import java.util.List; import java.util.Map.Entry; @@ -45,13 +47,21 @@ public interface PolicyPrepopulationFactory { /** - * Creates a {@link Policy} that is associated to a {@link Resource} using the data within an implementation of the {@link PolicyPrepopulationFactory}. + * Creates a {@link Rules} of type {@link LeafResource} that is associated + * to a {@link Resource} using the data within an implementation of the {@link PolicyPrepopulationFactory} * - * @param users a {@link List} of {@link UserPrepopulationFactory} implementations * @param resources a {@link List} of {@link ResourcePrepopulationFactory} implementations - * @return an {@link Entry} value that consists of a {@link Resource} and the created {@link Policy}. + * @return an {@link Entry} value that consists of a {@link Resource} and the created {@link Rules} of type {@link LeafResource}. */ - Entry build(List users, List resources); + Entry> buildResourceRules(List resources); + + /** + * Creates a {@link Rules} of type {@link Serializable} that is associated to a {@link Resource} using the data within an implementation of the {@link PolicyPrepopulationFactory}. + * + * @param resources a {@link List} of {@link ResourcePrepopulationFactory} implementations + * @return an {@link Entry} value that consists of a {@link Resource} and the created {@link Rules} of type {@link Serializable}. + */ + Entry> buildRecordRules(List resources); @JsonGetter("class") default String getClassName() { diff --git a/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java b/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java deleted file mode 100644 index 7e9b5a4d..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/request/Policy.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright 2020 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service.request; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.resource.Resource; -import uk.gov.gchq.palisade.rule.PredicateRule; -import uk.gov.gchq.palisade.rule.Rule; -import uk.gov.gchq.palisade.rule.Rules; -import uk.gov.gchq.palisade.rule.SerializablePredicate; -import uk.gov.gchq.palisade.rule.SerializableUnaryOperator; - -import java.io.Serializable; -import java.util.Objects; -import java.util.StringJoiner; -import java.util.UUID; - -import static java.util.Objects.requireNonNull; - -/** - * This class is used to store the information that is required by the policy - * service but not needed by the rest of the palisade services. That includes - * separating the rules that need to be applied at the resource level or the record level. - * - * @param The Java class that the rules expect the records of data to be in - * the format of, e.g. if T was String then a policy has Rules of type Resource for coarse filtering - * and Rules of type String for fine grain filtering of files where each record is a String - */ -public class Policy implements Serializable { - private static final long serialVersionUID = 1L; - - private Rules recordRules; - private Rules resourceRules; - private User owner; - - /** - * Instantiates a new Policy. - */ - // no-args constructor required - public Policy() { - recordRules = new Rules<>(); - resourceRules = new Rules<>(); - } - - /** - * Generates UUID - * - * @return UUID.randomUUID - */ - @Generated - private static String generateUUID() { - return UUID.randomUUID().toString(); - } - - /** - * Record rules policy. - * - * @param recordRules the record rules - * @return the policy - */ - @Generated - public Policy recordRules(final Rules recordRules) { - this.setRecordRules(recordRules); - return this; - } - - /** - * Resource rules policy. - * - * @param resourceRules the resource rules - * @return the policy - */ - @Generated - public Policy resourceRules(final Rules resourceRules) { - this.setResourceRules(resourceRules); - return this; - } - - /** - * Record level rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy recordLevelRule(final String message, final Rule rule) { - Rules recordLevelRules = getRecordRules(); - recordLevelRules.addRule(generateUUID(), rule); - addMessage(message, recordLevelRules); - return this; - } - - /** - * Record level predicate rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy recordLevelPredicateRule(final String message, final PredicateRule rule) { - Rules recordLevelRules = getRecordRules(); - recordLevelRules.addPredicateRule(generateUUID(), rule); - addMessage(message, recordLevelRules); - return this; - } - - /** - * Record level simple predicate rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy recordLevelSimplePredicateRule(final String message, final SerializablePredicate rule) { - Rules recordLevelRules = getRecordRules(); - recordLevelRules.addSimplePredicateRule(generateUUID(), rule); - addMessage(message, recordLevelRules); - return this; - } - - /** - * Record level simple function rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy recordLevelSimpleFunctionRule(final String message, final SerializableUnaryOperator rule) { - Rules recordLevelRules = getRecordRules(); - recordLevelRules.addSimpleFunctionRule(generateUUID(), rule); - addMessage(message, recordLevelRules); - return this; - } - - /** - * Resource level rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy resourceLevelRule(final String message, final Rule rule) { - Rules resourceLevelRules = getResourceRules(); - resourceLevelRules.addRule(generateUUID(), rule); - addMessage(message, resourceLevelRules); - return this; - } - - /** - * Resource level predicate rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy resourceLevelPredicateRule(final String message, final PredicateRule rule) { - Rules resourceLevelRules = getResourceRules(); - resourceLevelRules.addRule(generateUUID(), rule); - addMessage(message, resourceLevelRules); - return this; - } - - /** - * Resource level simple predicate rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy resourceLevelSimplePredicateRule(final String message, final SerializablePredicate rule) { - Rules resourceLevelRules = getResourceRules(); - resourceLevelRules.addSimplePredicateRule(generateUUID(), rule); - addMessage(message, resourceLevelRules); - return this; - } - - /** - * Resource level simple function rule policy. - * - * @param message the message - * @param rule the rule - * @return the policy - */ - @Generated - public Policy resourceLevelSimpleFunctionRule(final String message, final SerializableUnaryOperator rule) { - Rules resourceLevelRules = getResourceRules(); - resourceLevelRules.addSimpleFunctionRule(generateUUID(), rule); - addMessage(message, resourceLevelRules); - return this; - } - - - /** - * Gets message. - * - * @return the message - */ - @JsonIgnore - public String getMessage() { - return "Resource level rules: " + getResourceRules().getMessage() + ", record level rules: " + getRecordRules().getMessage(); - } - - public Policy owner(final User owner) { - setOwner(owner); - return this; - } - - /** - * Gets owner. - * - * @return the owner - */ - @Generated - public User getOwner() { - return owner; - } - - /** - * Sets owner. - * - * @param owner the owner - */ - @Generated - public void setOwner(final User owner) { - requireNonNull(owner); - this.owner = owner; - } - - - @Generated - public Rules getRecordRules() { - return recordRules; - } - - /** - * Sets record rules. - * - * @param recordRules the record rules - */ - @Generated - public void setRecordRules(final Rules recordRules) { - requireNonNull(recordRules); - this.recordRules = recordRules; - } - - /** - * Gets resource rules. - * - * @return the resource rules - */ - @Generated - public Rules getResourceRules() { - return resourceRules; - } - - /** - * Sets resource rules. - * - * @param resourceRules the resource rules - */ - @Generated - public void setResourceRules(final Rules resourceRules) { - requireNonNull(resourceRules); - this.resourceRules = resourceRules; - } - - private void addMessage(final String newMessage, final Rules rules) { - requireNonNull(newMessage, "Cannot add a null message."); - requireNonNull(rules, "Cannot add a message to a null set of rules."); - String currentMessage = rules.getMessage(); - if (currentMessage.equals(Rules.NO_RULES_SET)) { - rules.message(newMessage); - } else { - rules.message(currentMessage + ", " + newMessage); - } - } - - - - /** - * Gets nullable owner. - * - * @return the nullable owner - */ - @JsonGetter("owner") - User getNullableOwner() { - return owner; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Policy)) { - return false; - } - Policy policy = (Policy) o; - return recordRules.equals(policy.recordRules) && - resourceRules.equals(policy.resourceRules) && - owner.equals(policy.owner); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(recordRules, resourceRules, owner); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", Policy.class.getSimpleName() + "[", "]") - .add("recordRules=" + recordRules) - .add("resourceRules=" + resourceRules) - .add("owner=" + owner) - .toString(); - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java b/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java index 79fba6b1..05998ae6 100644 --- a/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java +++ b/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java @@ -19,18 +19,15 @@ import uk.gov.gchq.palisade.Context; import uk.gov.gchq.palisade.User; import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; import uk.gov.gchq.palisade.rule.Rule; import java.io.Serializable; -public class IsTextResourceRule implements Serializable, Rule { +public class IsTextResourceRule implements Serializable, Rule { @Override - public Resource apply(final Resource record, final User user, final Context context) { - if (record instanceof LeafResource) { - if (((LeafResource) record).getSerialisedFormat().equalsIgnoreCase("txt")) { - return record; - } + public LeafResource apply(final LeafResource record, final User user, final Context context) { + if (record.getSerialisedFormat().equalsIgnoreCase("txt")) { + return record; } return null; } From b1a889f8a58a7b50631ad17b4e8f2eceaf300f3c Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Tue, 10 Nov 2020 15:09:40 +0000 Subject: [PATCH 13/27] Pal 414 resolved ResourceService issue --- src/main/java/uk/gov/gchq/palisade/service/ResourceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java index 2146d546..4cbd598b 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java @@ -91,4 +91,4 @@ public interface ResourceService extends Service { */ Boolean addResource(final LeafResource resource); -} \ No newline at end of file +} From c666344b45664fd5752886ab3e298098a3f19671 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Mon, 16 Nov 2020 08:50:29 +0000 Subject: [PATCH 14/27] Pal 414 removed Stream from ResourceService JavaDoc --- .../java/uk/gov/gchq/palisade/service/ResourceService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java index 4cbd598b..88fc50e1 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java @@ -20,12 +20,11 @@ import uk.gov.gchq.palisade.resource.Resource; import java.util.Iterator; -import java.util.stream.Stream; /** * The resource service is the Palisade component that determines what resources are available that meet a specific * (type of) request and how they should be accessed. This interface details several methods for obtaining a list of - * resources, e.g. by type or by data format. The methods of this service all return {@link Stream}s which link a valid + * resources, e.g. by type or by data format. The methods of this service all return {@link Iterator}s which link a valid * {@link LeafResource} with a {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain * information on how to set up a connection to retrieve a particular resource. Implementations of this service do not * deal with the filtering or application of security policy to the resources. Therefore, a result returned from a @@ -33,7 +32,7 @@ * components of the Palisade system will enforce the necessary policy controls to prevent access to resources by users * without the necessary access rights. * Implementation note: None of the ${@code getResourcesByXXX} methods in this class will return in error if there - * don't happen to be any resources that do not match a request, instead they will simply return empty ${@link Stream} + * don't happen to be any resources that do not match a request, instead they will simply return empty ${@link Iterator} * instances. */ public interface ResourceService extends Service { From 8fe0e11d19a60dd77a13a6a785cdc4524a35204f Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Thu, 21 Jan 2021 15:09:29 +0000 Subject: [PATCH 15/27] Pal 612 removed resource references from Policy Prepopulation Factory (#82) * Pal 867 removed resource references from Policy Prepopulation Factory * Pal 612 removed property value from annotation as per review comment --- .../service/PolicyPrepopulationFactory.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java b/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java index 61acb9f3..96ebb561 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java +++ b/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java @@ -25,11 +25,9 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerators; import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; import uk.gov.gchq.palisade.rule.Rules; import java.io.Serializable; -import java.util.List; import java.util.Map.Entry; /** @@ -43,25 +41,24 @@ include = As.EXISTING_PROPERTY, property = "class" ) -@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id") +@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) public interface PolicyPrepopulationFactory { /** - * Creates a {@link Rules} of type {@link LeafResource} that is associated - * to a {@link Resource} using the data within an implementation of the {@link PolicyPrepopulationFactory} + * Creates a {@link Rules} of type {@link LeafResource} that is associated to a resourceId using the + * data within an implementation of the {@link PolicyPrepopulationFactory} * - * @param resources a {@link List} of {@link ResourcePrepopulationFactory} implementations - * @return an {@link Entry} value that consists of a {@link Resource} and the created {@link Rules} of type {@link LeafResource}. + * @return an {@link Entry} value that consists of a resourceId and the created {@link Rules} of type {@link LeafResource}. */ - Entry> buildResourceRules(List resources); + Entry> buildResourceRules(); /** - * Creates a {@link Rules} of type {@link Serializable} that is associated to a {@link Resource} using the data within an implementation of the {@link PolicyPrepopulationFactory}. + * Creates a {@link Rules} of type {@link Serializable} that is associated to a resourceId using the + * data within an implementation of the {@link PolicyPrepopulationFactory}. * - * @param resources a {@link List} of {@link ResourcePrepopulationFactory} implementations - * @return an {@link Entry} value that consists of a {@link Resource} and the created {@link Rules} of type {@link Serializable}. + * @return an {@link Entry} value that consists of a resourceId and the created {@link Rules} of type {@link Serializable}. */ - Entry> buildRecordRules(List resources); + Entry> buildRecordRules(); @JsonGetter("class") default String getClassName() { From 17a5b3c3082282453b220c24aa1be2dc5ea486ae Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Wed, 27 Jan 2021 14:07:14 +0000 Subject: [PATCH 16/27] Pal 569 update copyright (#83) * PAL-956-Updated Copyright to be a range of years * PAL-956-Updated Copyright to be a range of years --- .gitignore | 2 +- Jenkinsfile | 2 +- README.md | 2 +- code-style/checkstyle-suppressions.xml | 2 +- code-style/checkstyle.xml | 2 +- pom.xml | 2 +- src/main/java/uk/gov/gchq/palisade/Context.java | 2 +- src/main/java/uk/gov/gchq/palisade/Generated.java | 2 +- src/main/java/uk/gov/gchq/palisade/RequestId.java | 2 +- src/main/java/uk/gov/gchq/palisade/User.java | 2 +- src/main/java/uk/gov/gchq/palisade/UserId.java | 2 +- src/main/java/uk/gov/gchq/palisade/Util.java | 2 +- .../uk/gov/gchq/palisade/data/serialise/AvroSerialiser.java | 2 +- .../uk/gov/gchq/palisade/data/serialise/LineSerialiser.java | 2 +- .../java/uk/gov/gchq/palisade/data/serialise/Serialiser.java | 2 +- .../gchq/palisade/data/serialise/SimpleStringSerialiser.java | 2 +- src/main/java/uk/gov/gchq/palisade/exception/Error.java | 2 +- src/main/java/uk/gov/gchq/palisade/exception/ErrorFactory.java | 2 +- .../java/uk/gov/gchq/palisade/exception/ForbiddenException.java | 2 +- .../gov/gchq/palisade/exception/PalisadeRuntimeException.java | 2 +- .../uk/gov/gchq/palisade/exception/RequestFailedException.java | 2 +- src/main/java/uk/gov/gchq/palisade/exception/Status.java | 2 +- .../uk/gov/gchq/palisade/jsonserialisation/JSONSerialiser.java | 2 +- .../gchq/palisade/jsonserialisation/JSONSerialiserModules.java | 2 +- .../palisade/jsonserialisation/ResourceKeyDeserialiser.java | 2 +- .../gchq/palisade/jsonserialisation/ResourceKeySerialiser.java | 2 +- .../gchq/palisade/jsonserialisation/UserIdKeyDeserialiser.java | 2 +- .../gchq/palisade/jsonserialisation/UserIdKeySerialiser.java | 2 +- .../uk/gov/gchq/palisade/jsonserialisation/package-info.java | 2 +- .../uk/gov/gchq/palisade/resource/AbstractLeafResource.java | 2 +- .../java/uk/gov/gchq/palisade/resource/AbstractResource.java | 2 +- src/main/java/uk/gov/gchq/palisade/resource/ChildResource.java | 2 +- src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java | 2 +- src/main/java/uk/gov/gchq/palisade/resource/ParentResource.java | 2 +- src/main/java/uk/gov/gchq/palisade/resource/Resource.java | 2 +- .../uk/gov/gchq/palisade/resource/impl/DirectoryResource.java | 2 +- .../java/uk/gov/gchq/palisade/resource/impl/FileResource.java | 2 +- .../java/uk/gov/gchq/palisade/resource/impl/StreamResource.java | 2 +- .../java/uk/gov/gchq/palisade/resource/impl/SystemResource.java | 2 +- src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java | 2 +- src/main/java/uk/gov/gchq/palisade/rule/Rule.java | 2 +- src/main/java/uk/gov/gchq/palisade/rule/Rules.java | 2 +- .../java/uk/gov/gchq/palisade/rule/SerializablePredicate.java | 2 +- .../uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java | 2 +- src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java | 2 +- .../java/uk/gov/gchq/palisade/service/ConnectionDetail.java | 2 +- .../java/uk/gov/gchq/palisade/service/PolicyConfiguration.java | 2 +- .../gov/gchq/palisade/service/PolicyPrepopulationFactory.java | 2 +- .../uk/gov/gchq/palisade/service/ResourceConfiguration.java | 2 +- .../gov/gchq/palisade/service/ResourcePrepopulationFactory.java | 2 +- src/main/java/uk/gov/gchq/palisade/service/ResourceService.java | 2 +- src/main/java/uk/gov/gchq/palisade/service/Service.java | 2 +- .../uk/gov/gchq/palisade/service/SimpleConnectionDetail.java | 2 +- .../java/uk/gov/gchq/palisade/service/UserConfiguration.java | 2 +- .../uk/gov/gchq/palisade/service/UserPrepopulationFactory.java | 2 +- .../uk/gov/gchq/palisade/service/request/DataRequestConfig.java | 2 +- .../gov/gchq/palisade/service/request/DataRequestResponse.java | 2 +- src/main/java/uk/gov/gchq/palisade/service/request/Request.java | 2 +- src/main/java/uk/gov/gchq/palisade/util/DebugUtil.java | 2 +- src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java | 2 +- src/main/java/uk/gov/gchq/palisade/util/StreamUtil.java | 2 +- src/main/java/uk/gov/gchq/palisade/util/UriBuilder.java | 2 +- src/test/java/uk/gov/gchq/palisade/ContextTest.java | 2 +- src/test/java/uk/gov/gchq/palisade/UtilTest.java | 2 +- .../uk/gov/gchq/palisade/data/serialise/AvroSerialiserTest.java | 2 +- .../palisade/data/serialise/SimpleStringSerialiserTest.java | 2 +- .../java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java | 2 +- .../java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java | 2 +- .../java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java | 2 +- src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java | 2 +- src/test/java/uk/gov/gchq/palisade/policy/RulesTest.java | 2 +- src/test/java/uk/gov/gchq/palisade/policy/TestRule.java | 2 +- src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java | 2 +- src/test/java/uk/gov/gchq/palisade/resource/StubResource.java | 2 +- src/test/java/uk/gov/gchq/palisade/util/JsonAssert.java | 2 +- .../java/uk/gov/gchq/palisade/util/ResourceBuilderTest.java | 2 +- src/test/java/uk/gov/gchq/palisade/util/TestUtil.java | 2 +- 77 files changed, 77 insertions(+), 77 deletions(-) diff --git a/.gitignore b/.gitignore index 791e84eb..f6c6cbf1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# Copyright 2019 Crown Copyright +# Copyright 2018-2021 Crown Copyright # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/Jenkinsfile b/Jenkinsfile index 7da8dd52..c090e943 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ /* - * Copyright 2020 Crown Copyright + * Copyright 2018-2021 Crown Copyright * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 7aa23123..09252206 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ + 11 SNAPSHOT ${java.version} @@ -47,29 +47,24 @@ ${encoding} ${encoding} - - 3.1.1 - 3.2.0 + + 3.1.0 + 3.1.2 + 3.1.0 3.8.0 - 3.1.1 - - 1.7.25 - 1.7.25 2.10.0 2.6 3.8.1 1.8.2 - + 4.13.1 2.22.1 2.22.1 3.1.0 - 3.1.0 - 3.1.1 - + jacoco reuseReports ${project.basedir}/../target/site/jacoco/jacoco.xml @@ -93,70 +88,71 @@ - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - + + com.fasterxml.jackson.core jackson-databind ${jackson.version} + com.fasterxml.jackson.datatype jackson-datatype-jdk8 ${jackson.version} + com.fasterxml.jackson.datatype jackson-datatype-jsr310 ${jackson.version} + + + commons-io commons-io ${commons-io.version} + org.apache.commons commons-lang3 ${commons-lang3.version} + + + org.slf4j slf4j-api ${slf4j.api.version} + + org.apache.avro avro ${avro.version} + + + junit junit ${junit.version} test - - org.mockito - mockito-core - ${mockito.core} - test - - - org.apache.commons - commons-math3 - ${common-maths} - test - @@ -173,6 +169,7 @@ + avro @@ -189,8 +186,7 @@ ${project.basedir}/src/main/resources/avro ${project.basedir}/src/main/java - ${project.basedir}/src/test/resources/avro - + ${project.basedir}/src/test/resources/avro ${project.basedir}/src/test/java @@ -226,6 +222,7 @@ + org.apache.maven.plugins maven-javadoc-plugin ${javadoc.plugin.version} @@ -274,11 +271,13 @@ + org.apache.maven.plugins maven-surefire-plugin ${surefire.plugin.version} + org.apache.maven.plugins maven-dependency-plugin ${depends.maven.version} @@ -296,6 +295,7 @@ + org.codehaus.mojo license-maven-plugin 2.0.0 @@ -377,6 +377,7 @@ + org.jacoco jacoco-maven-plugin 0.8.5 @@ -402,6 +403,7 @@ + org.apache.maven.plugins maven-jar-plugin ${jar.plugin.version} @@ -414,6 +416,7 @@ + org.apache.maven.plugins maven-checkstyle-plugin ${checkstyle.plugin.version} From ddf75f476fe49bd16a94f3209c3cddbc05cbd7f6 Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Tue, 20 Apr 2021 11:40:36 +0100 Subject: [PATCH 19/27] PAL-999-keep-common-library initial commit (#86) --- licenses/mit_license.html | 10 +- .../java/uk/gov/gchq/palisade/Context.java | 63 ++- .../java/uk/gov/gchq/palisade/Generated.java | 40 +- .../java/uk/gov/gchq/palisade/RequestId.java | 79 ---- src/main/java/uk/gov/gchq/palisade/Util.java | 227 --------- .../palisade/data/serialise/Serialiser.java | 11 - .../uk/gov/gchq/palisade/exception/Error.java | 212 --------- .../gchq/palisade/exception/ErrorFactory.java | 89 ---- .../exception/ForbiddenException.java | 34 -- .../exception/PalisadeRuntimeException.java | 72 --- .../exception/RequestFailedException.java | 37 -- .../gov/gchq/palisade/exception/Status.java | 126 ----- .../jsonserialisation/JSONSerialiser.java | 416 ----------------- .../JSONSerialiserModules.java | 30 -- .../ResourceKeyDeserialiser.java | 32 -- .../ResourceKeySerialiser.java | 48 -- .../UserIdKeyDeserialiser.java | 32 -- .../UserIdKeySerialiser.java | 47 -- .../jsonserialisation/package-info.java | 20 - .../resource/AbstractLeafResource.java | 15 +- .../ConnectionDetail.java | 4 +- .../gchq/palisade/resource/LeafResource.java | 2 - .../resource/impl/DirectoryResource.java | 4 + .../palisade/resource/impl/FileResource.java | 6 +- .../impl}/SimpleConnectionDetail.java | 5 +- .../resource/impl/StreamResource.java | 105 ----- .../resource/impl/SystemResource.java | 4 + .../gov/gchq/palisade/rule/PredicateRule.java | 51 --- .../java/uk/gov/gchq/palisade/rule/Rule.java | 2 +- .../java/uk/gov/gchq/palisade/rule/Rules.java | 90 +--- .../palisade/rule/SerializablePredicate.java | 28 -- .../rule/SerializableUnaryOperator.java | 28 -- .../gov/gchq/palisade/rule/WrappedRule.java | 189 -------- .../palisade/service/PolicyConfiguration.java | 31 -- .../service/PolicyPrepopulationFactory.java | 72 --- .../service/ResourceConfiguration.java | 33 -- .../service/ResourcePrepopulationFactory.java | 64 --- .../palisade/service/ResourceService.java | 93 ---- .../uk/gov/gchq/palisade/service/Service.java | 50 -- .../palisade/service/UserConfiguration.java | 31 -- .../service/UserPrepopulationFactory.java | 59 --- .../service/request/DataRequestConfig.java | 131 ------ .../service/request/DataRequestResponse.java | 125 ----- .../palisade/service/request/Request.java | 92 ---- .../uk/gov/gchq/palisade/{ => user}/User.java | 4 +- .../gov/gchq/palisade/{ => user}/UserId.java | 4 +- .../uk/gov/gchq/palisade/util/DebugUtil.java | 62 --- .../gchq/palisade/util/ResourceBuilder.java | 96 ++-- .../uk/gov/gchq/palisade/util/RulesUtil.java | 87 ++++ .../uk/gov/gchq/palisade/util/StreamUtil.java | 77 ---- .../uk/gov/gchq/palisade/util/UriBuilder.java | 150 +++++- .../uk/gov/gchq/palisade/ContextTest.java | 59 --- .../java/uk/gov/gchq/palisade/UtilTest.java | 83 ---- .../data/serialise/AvroSerialiserTest.java | 160 ------- .../serialise/SimpleStringSerialiserTest.java | 60 --- .../gchq/palisade/data/serialise/TestObj.java | 431 ----------------- .../data/service/impl/serialiser/TestObj.java | 433 ------------------ .../palisade/policy/HasSensitiveAuthRule.java | 34 -- .../palisade/policy/HasTestingPurpose.java | 34 -- .../palisade/policy/IsTextResourceRule.java | 34 -- .../gchq/palisade/policy/PassThroughRule.java | 30 -- .../gov/gchq/palisade/policy/RulesTest.java | 104 ----- .../uk/gov/gchq/palisade/policy/TestRule.java | 28 -- .../gchq/palisade/policy/WrappedRuleTest.java | 74 --- .../gchq/palisade/resource/StubResource.java | 51 --- .../uk/gov/gchq/palisade/util/JsonAssert.java | 91 ---- .../palisade/util/ResourceBuilderTest.java | 153 ------- .../uk/gov/gchq/palisade/util/TestUtil.java | 59 --- src/test/resources/avro/testObj.avsc | 25 - 69 files changed, 438 insertions(+), 4924 deletions(-) delete mode 100644 src/main/java/uk/gov/gchq/palisade/RequestId.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/Util.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/Error.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/ErrorFactory.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/ForbiddenException.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/PalisadeRuntimeException.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/RequestFailedException.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/exception/Status.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiser.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiserModules.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeyDeserialiser.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeySerialiser.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeyDeserialiser.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeySerialiser.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/jsonserialisation/package-info.java rename src/main/java/uk/gov/gchq/palisade/{service => resource}/ConnectionDetail.java (93%) rename src/main/java/uk/gov/gchq/palisade/{service => resource/impl}/SimpleConnectionDetail.java (94%) delete mode 100644 src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/PolicyConfiguration.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/ResourceConfiguration.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/ResourcePrepopulationFactory.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/ResourceService.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/Service.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/UserConfiguration.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/UserPrepopulationFactory.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/request/DataRequestConfig.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/request/DataRequestResponse.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/service/request/Request.java rename src/main/java/uk/gov/gchq/palisade/{ => user}/User.java (98%) rename src/main/java/uk/gov/gchq/palisade/{ => user}/UserId.java (96%) delete mode 100644 src/main/java/uk/gov/gchq/palisade/util/DebugUtil.java create mode 100644 src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java delete mode 100644 src/main/java/uk/gov/gchq/palisade/util/StreamUtil.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/ContextTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/UtilTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiserTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiserTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/data/serialise/TestObj.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/data/service/impl/serialiser/TestObj.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/RulesTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/TestRule.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/resource/StubResource.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/util/JsonAssert.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/util/ResourceBuilderTest.java delete mode 100644 src/test/java/uk/gov/gchq/palisade/util/TestUtil.java delete mode 100644 src/test/resources/avro/testObj.avsc diff --git a/licenses/mit_license.html b/licenses/mit_license.html index ef598503..77f6583b 100644 --- a/licenses/mit_license.html +++ b/licenses/mit_license.html @@ -18,7 +18,7 @@ @@ -32,7 +32,7 @@ window.jQuery || document.write(" - + @@ -109,7 +109,7 @@

Search form

-
+
@@ -175,7 +175,7 @@

Search form

+ diff --git a/src/main/java/uk/gov/gchq/palisade/Context.java b/src/main/java/uk/gov/gchq/palisade/Context.java index 6c17dda4..a06e0ed4 100644 --- a/src/main/java/uk/gov/gchq/palisade/Context.java +++ b/src/main/java/uk/gov/gchq/palisade/Context.java @@ -48,39 +48,74 @@ public class Context { private static final String PURPOSE = "purpose"; private Map contents; + /** + * Create a new context object with an empty hashMap of attributes + */ public Context() { this(new HashMap<>()); } + /** + * Create a new context object, passing in a map of attributes + * + * @param contents a map of attributes, containing a purpose + */ @JsonCreator + @SuppressWarnings("java:S1699") public Context(@JsonProperty("contents") final Map contents) { this.setContents(contents); } + /** + * Sets the contents of this Context object + * + * @param contents a map of contents that will be added to this class + * @return the {@link Context} object + */ @Generated public Context contents(final Map contents) { this.setContents(contents); return this; } - + /** + * Get the contents map of the {@link Context} + * + * @return a map of {@link String} and {@link Object} + */ @Generated public Map getContents() { return contents; } + /** + * Set the contents map of the {@link Context} + * + * @param contents the map to be added to the Context + */ @Generated public void setContents(final Map contents) { requireNonNull(contents); this.contents = contents; } + /** + * Get a copy of the contents map of the {@link Context} + * + * @return an unmodifiable map of the contents + */ @JsonIgnore @Generated public Map getContentsCopy() { return Collections.unmodifiableMap(contents); } + /** + * Adds a purpose, or reason for requesting data to the Context object. + * + * @param purpose a String containing why the User wants access to the data + * @return the Context object with the purpose added to the contents map + */ @JsonIgnore @Generated public Context purpose(final String purpose) { @@ -88,6 +123,12 @@ public Context purpose(final String purpose) { return this; } + /** + * Get the purpose from the contents map of the {@link Context} + * + * @return a string value of the purpose + */ + @SuppressWarnings({"java:S112", "java:S1166"}) @JsonIgnore public String getPurpose() { try { @@ -97,11 +138,24 @@ public String getPurpose() { } } + /** + * Get the purpose by the key value in the map + * + * @param key the key value associated with the purpose + * @return the Object purpose associated with to the String key + */ @Generated public Object get(final String key) { return contents.get(key); } + /** + * Put the provided key and value into the contents map + * + * @param key the key value + * @param value the value object + * @return the {@link Context} object + */ @Generated public Context put(final String key, final Object value) { requireNonNull(key, "The key cannot be null."); @@ -110,6 +164,13 @@ public Context put(final String key, final Object value) { return this; } + /** + * Put the provided key and value into the contents map if it does not already exist + * + * @param key the key value + * @param value the value object + * @return the {@link Context} object + */ @Generated public Context putIfAbsent(final String key, final Object value) { requireNonNull(key, "The key cannot be null."); diff --git a/src/main/java/uk/gov/gchq/palisade/Generated.java b/src/main/java/uk/gov/gchq/palisade/Generated.java index 8827fa73..6fa499f5 100644 --- a/src/main/java/uk/gov/gchq/palisade/Generated.java +++ b/src/main/java/uk/gov/gchq/palisade/Generated.java @@ -21,27 +21,27 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/* + * Used by JaCoCo and SonarQube, any method annotated with an annotation with + * a simple name of "Generated' is ignored from code coverage reports. For best + * results, this should be integrated into one's IDE. In IntelliJ, this is done + * through: Code -> + * Generate -> + * [method] -> + * ... -> + * Prepend "@uk.gov.gchq.palisade.Generated" to the velocity template. + * + * Alternatively, xml files representing these code generation templates can + * be found under ~/.IntelliJIdea${year.version}/config/options: + * - equalsHashCodeTemplates.xml + * - getterTemplates.xml + * - setterTemplates.xml + * - toStringTemplates.xml + * + * It is recommended to include this in all code generation methods used, such + * as: equals, hashCode, toString, getters, setters + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Generated { - /** - * Used by JaCoCo and SonarQube, any method annotated with an annotation with - * a simple name of "Generated' is ignored from code coverage reports. For best - * results, this should be integrated into one's IDE. In IntelliJ, this is done - * through: Code -> - * Generate -> - * [method] -> - * ... -> - * Prepend "@uk.gov.gchq.palisade.Generated" to the velocity template. - * - * Alternatively, xml files representing these code generation templates can - * be found under ~/.IntelliJIdea${year.version}/config/options: - * - equalsHashCodeTemplates.xml - * - getterTemplates.xml - * - setterTemplates.xml - * - toStringTemplates.xml - * - * It is recommended to include this in all code generation methods used, such - * as: equals, hashCode, toString, getters, setters - */ } diff --git a/src/main/java/uk/gov/gchq/palisade/RequestId.java b/src/main/java/uk/gov/gchq/palisade/RequestId.java deleted file mode 100644 index 1ce97974..00000000 --- a/src/main/java/uk/gov/gchq/palisade/RequestId.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade; - - -import java.util.Objects; -import java.util.StringJoiner; - -import static java.util.Objects.requireNonNull; - -/** - * This class contains the information that makes a request unique. - */ -public class RequestId { - - private String id; - - public RequestId() { - //no-args constructor needed for serialization only - } - - @Generated - public RequestId id(final String id) { - this.setId(id); - return this; - } - - @Generated - public String getId() { - return id; - } - - @Generated - public void setId(final String id) { - requireNonNull(id); - this.id = id; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof RequestId)) { - return false; - } - RequestId requestId = (RequestId) o; - return id.equals(requestId.id); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(id); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", RequestId.class.getSimpleName() + "[", "]") - .add("id='" + id + "'") - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/Util.java b/src/main/java/uk/gov/gchq/palisade/Util.java deleted file mode 100644 index 802d9032..00000000 --- a/src/main/java/uk/gov/gchq/palisade/Util.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import uk.gov.gchq.palisade.rule.Rule; -import uk.gov.gchq.palisade.rule.Rules; - -import java.io.Serializable; -import java.net.URL; -import java.security.CodeSource; -import java.time.Duration; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Stream; - -import static java.util.Objects.isNull; -import static java.util.Objects.requireNonNull; - -/** - * Common utility methods. - */ -public final class Util { - private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); - - /** - * Minimum retry time to wait between attempts. - */ - public static final Duration MIN_DURATION_PAUSE = Duration.ofSeconds(1); - - private Util() { - } - - /** - * Applies a collection of rules to a record stream. - * - * @param records record stream - * @param user user the records are being processed for - * @param context the additional context - * @param rules rules collection - * @param record type - * @param recordsProcessed a counter for the number of records being processed - * @param recordsReturned a counter for the number of records being returned - * @return filtered stream - */ - public static Stream applyRulesToStream(final Stream records, final User user, final Context context, final Rules rules, final AtomicLong recordsProcessed, final AtomicLong recordsReturned) { - Objects.requireNonNull(records); - if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { - return records; - } - - return records - .peek(processed -> recordsProcessed.incrementAndGet()) - .map(record -> applyRulesToItem(record, user, context, rules)) - .filter(Objects::nonNull) - .peek(returned -> recordsReturned.incrementAndGet()); - } - - /** - * Applies a collection of rules to an item (record/resource). - * - * @param item resource or record to filter - * @param user user the record is being processed for - * @param context the additional context - * @param rules rules collection - * @param record type - * @return item with rules applied - */ - public static T applyRulesToItem(final T item, final User user, final Context context, final Rules rules) { - if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { - return item; - } - T updateItem = item; - for (final Rule rule : rules.getRules().values()) { - updateItem = rule.apply(updateItem, user, context); - if (null == updateItem) { - break; - } - } - return updateItem; - } - - /** - * Create a {@link ThreadFactory} that creates daemon threads that don't prevent JVM exit. - * - * @return a daemon thread factory - */ - public static ThreadFactory createDaemonThreadFactory() { - //set up a thread to watch this - final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); - //ensure thread is daemon - return runnable -> { - Thread t = defaultFactory.newThread(runnable); - t.setDaemon(true); - return t; - }; - } - - /** - * Carries out the given operation a set number of times in case of failure. The given function will be called and the - * result returned if successful. If the function throws an exception, then the function is tried again after a pause. - * If {@code retryCount} attempts fail, then a {@link RuntimeException} is thrown with the most recent cause. - * - * @param function the function to run - * @param retryCount the maximum number of attempts - * @param pause the pause time between failure - * @param the return type - * @return object of type {@code R} from {@code function} - * @throws IllegalArgumentException if {@code retryCount} less than 1 or {@code pause} is negative or less than the - * minimum required time - * @throws RuntimeException if all attempts to execute {@code function} fail - */ - public static R retryThunker(final Callable function, final int retryCount, final Duration pause) { - requireNonNull(function, "function"); - requireNonNull(pause, "pause"); - if (retryCount < 1) { - throw new IllegalArgumentException("retryCount must be >=1"); - } - if (pause.isNegative()) { - throw new IllegalArgumentException("pause time cannot be negative"); - } - if (MIN_DURATION_PAUSE.compareTo(pause) > 0) { - throw new IllegalArgumentException("pause time must be at least " + MIN_DURATION_PAUSE.toMillis() + " ms"); - } - - - RuntimeException lastCause = null; - int count = 0; - long wait = pause.toMillis(); - - //loop with a count - while (count < retryCount) { - try { - return function.call(); - } catch (RuntimeException r) { - lastCause = r; - } catch (Exception e) { - lastCause = new RuntimeException(e); - } - - //failed so increment count and retry - count++; - try { - if (count < retryCount) { - Thread.sleep(wait); - } - } catch (InterruptedException e) { - //ignore - } - } - - //wrap the exception if it is checked - if (isNull(lastCause)) { - //shouldn't happen - throw new RuntimeException("root cause unknown"); - } - throw lastCause; - } - - /** - * Captures an exception thrown while running the given function and writes the cause to the log. This is especially - * useful during connection refused errors as they do not give the host information in their stack trace. - * - * @param func the function to protect - * @param address the address being contacted - * @param the return type of the function - * @return dependent on {@code func} - * @throws Exception any exception thrown by {@code func} - */ - public static V logConnectionFailed(final Callable func, final URL address) throws Exception { - try { - return func.call(); - } catch (Exception e) { - throw new Exception("Call to " + address.toString() + "failed due to ", e); - } - } - - /** - * Debug utility for finding which JAR file (if any) a class was loaded from. This is useful in debugging classpath - * conflicts. - * - * @param clazz the fully qualified class name to search for - * @return where the class was loaded from, or {@code null} - */ - public static String locateJarFile(final String clazz) { - requireNonNull(clazz, "clazz"); - try { - - Class c = Class.forName(clazz); - CodeSource codeSource = c.getProtectionDomain().getCodeSource(); - - if (codeSource != null) { - LOGGER.info("{} was loaded from {}", clazz, codeSource.getLocation()); - - } else { - LOGGER.info("Can't determine where {} was loaded from", clazz); - } - if (codeSource != null) { - return codeSource.getLocation().toString(); - } else { - return null; - } - } catch (ClassNotFoundException e) { - LOGGER.error("LocateJarFile threw an exception ", e); - return e.getMessage(); - } - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java index f4057fad..bf59ee9f 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java @@ -15,8 +15,6 @@ */ package uk.gov.gchq.palisade.data.serialise; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; @@ -57,13 +55,4 @@ public interface Serialiser extends Serializable { */ Stream deserialise(final InputStream stream) throws IOException; - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } } diff --git a/src/main/java/uk/gov/gchq/palisade/exception/Error.java b/src/main/java/uk/gov/gchq/palisade/exception/Error.java deleted file mode 100644 index f08b3197..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/Error.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - - -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.util.DebugUtil; - -import java.util.Objects; -import java.util.StringJoiner; - -import static java.util.Objects.requireNonNull; - -/** - * Simple serialisable POJO for containing details of errors. - * An {@link uk.gov.gchq.palisade.exception.Error} object is typically - * created automatically by a Jersey ExceptionMapper and should not be created - * manually. - */ -public final class Error { - - private int statusCode; - private Status status; - private String simpleMessage; - private String detailMessage; - private Class exceptionClass; - - public Error() { - } - - private Error(final ErrorBuilder builder) { - this.setStatusCode(builder.statusCode); - this.setStatus(builder.status); - this.setSimpleMessage(builder.simpleMessage); - this.setDetailMessage(builder.detailMessage); - this.setExceptionClass(builder.exceptionClass); - } - - @Generated - public int getStatusCode() { - return statusCode; - } - - @Generated - public void setStatusCode(final int statusCode) { - requireNonNull(statusCode); - this.statusCode = statusCode; - } - - @Generated - public Status getStatus() { - return status; - } - - @Generated - public void setStatus(final Status status) { - requireNonNull(status); - this.status = status; - } - - @Generated - public String getSimpleMessage() { - return simpleMessage; - } - - @Generated - public void setSimpleMessage(final String simpleMessage) { - requireNonNull(simpleMessage); - this.simpleMessage = simpleMessage; - } - - @Generated - public String getDetailMessage() { - return detailMessage; - } - - @Generated - public void setDetailMessage(final String detailMessage) { - requireNonNull(detailMessage); - this.detailMessage = detailMessage; - } - - @Generated - public Class getExceptionClass() { - return exceptionClass; - } - - @Generated - public void setExceptionClass(final Class exceptionClass) { - requireNonNull(exceptionClass); - this.exceptionClass = exceptionClass; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Error)) { - return false; - } - Error error = (Error) o; - return statusCode == error.statusCode && - status == error.status && - simpleMessage.equals(error.simpleMessage) && - detailMessage.equals(error.detailMessage) && - exceptionClass.equals(error.exceptionClass); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(statusCode, status, simpleMessage, detailMessage, exceptionClass); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", Error.class.getSimpleName() + "[", "]") - .add("statusCode=" + statusCode) - .add("status=" + status) - .add("simpleMessage='" + simpleMessage + "'") - .add("detailMessage='" + detailMessage + "'") - .add("exceptionClass=" + exceptionClass) - .toString(); - } - - - public static final class ErrorBuilder { - private int statusCode; - private Status status; - private String simpleMessage; - private String detailMessage; - private Class exceptionClass; - - public ErrorBuilder() { - // Empty - } - - public ErrorBuilder statusCode(final int statusCode) { - this.statusCode = statusCode; - this.status = Status.fromStatusCode(statusCode); - return this; - } - - public ErrorBuilder status(final Status status) { - this.status = status; - this.statusCode = status.getStatusCode(); - return this; - } - - public ErrorBuilder simpleMessage(final String simpleMessage) { - this.simpleMessage = simpleMessage; - return this; - } - - public ErrorBuilder detailMessage(final String detailMessage) { - this.detailMessage = detailMessage; - return this; - } - - public ErrorBuilder exceptionClass(final RuntimeException exception) { - requireNonNull(exception, "exception is required"); - this.exceptionClass = exception.getClass(); - return this; - } - - public ErrorBuilder exceptionClass(final Exception exception) { - requireNonNull(exception, "exception is required"); - if (exception instanceof RuntimeException) { - this.exceptionClass = exception.getClass().asSubclass(RuntimeException.class); - } else { - this.exceptionClass = PalisadeRuntimeException.class; - } - return this; - } - - public ErrorBuilder exceptionClass(final Class exceptionClass) { - requireNonNull(exceptionClass, "exceptionClass is required"); - if (RuntimeException.class.isAssignableFrom(exceptionClass)) { - this.exceptionClass = exceptionClass.asSubclass(RuntimeException.class); - } else { - this.exceptionClass = PalisadeRuntimeException.class; - } - return this; - } - - public Error build() { - if (DebugUtil.checkDebugMode()) { - return new Error(this); - } else { - return new Error(this.detailMessage(null)); - - } - } - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/exception/ErrorFactory.java b/src/main/java/uk/gov/gchq/palisade/exception/ErrorFactory.java deleted file mode 100644 index 3817b1d6..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/ErrorFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import uk.gov.gchq.palisade.exception.Error.ErrorBuilder; - -import java.util.Arrays; -import java.util.concurrent.CompletionException; -import java.util.stream.Collectors; - -/** - * Static utility class to standardise the instantiation of {@link uk.gov.gchq.palisade.exception.Error} - * objects. - */ -public final class ErrorFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(ErrorFactory.class); - - /** - * Empty, private constructor to prevent instantiation. - */ - private ErrorFactory() { - // Empty - } - - - - /** - * Create an {@link uk.gov.gchq.palisade.exception.Error} object from a - * {@link uk.gov.gchq.palisade.exception.PalisadeRuntimeException}. - * - * @param gex the exception object - * @return a newly constructed {@link uk.gov.gchq.palisade.exception.Error} - */ - public static Error from(final PalisadeRuntimeException gex) { - LOGGER.error("Error: {}", gex.getMessage(), gex); - return new ErrorBuilder() - .status(gex.getStatus()) - .simpleMessage(gex.getMessage()) - .detailMessage(Arrays.asList(gex.getStackTrace()).stream().map(StackTraceElement::toString).collect(Collectors.joining("\n"))) - .exceptionClass(gex) - .build(); - } - - - - /** - * Create an {@link uk.gov.gchq.palisade.exception.Error} object from an - * {@link Exception}. - * - * @param ex the exception object - * @return a newly constructed {@link uk.gov.gchq.palisade.exception.Error} - */ - public static Error from(final Exception ex) { - final Exception unwrappedEx = unwrapCompletionException(ex); - LOGGER.error("Error: {}", ex.getMessage(), ex); - return new ErrorBuilder() - .status(Status.INTERNAL_SERVER_ERROR) - .simpleMessage(unwrappedEx.getMessage()) - .detailMessage(Arrays.asList(unwrappedEx.getStackTrace()).stream().map(StackTraceElement::toString).collect(Collectors.joining("\n"))) - .exceptionClass(unwrappedEx) - .build(); - } - - private static Exception unwrapCompletionException(final Exception ex) { - Exception unwrappedEx = ex; - while (unwrappedEx instanceof CompletionException && unwrappedEx.getCause() instanceof Exception) { - unwrappedEx = (Exception) ex.getCause(); - } - - return unwrappedEx; - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/exception/ForbiddenException.java b/src/main/java/uk/gov/gchq/palisade/exception/ForbiddenException.java deleted file mode 100644 index 56fbbe03..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/ForbiddenException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - -public class ForbiddenException extends RuntimeException { - public ForbiddenException() { - } - - public ForbiddenException(final String e) { - super(e); - } - - public ForbiddenException(final String e, final Throwable t) { - super(e, t); - } - - public ForbiddenException(final Throwable t) { - super(t); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/exception/PalisadeRuntimeException.java b/src/main/java/uk/gov/gchq/palisade/exception/PalisadeRuntimeException.java deleted file mode 100644 index 22902b26..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/PalisadeRuntimeException.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - -import uk.gov.gchq.palisade.Generated; - -import java.util.StringJoiner; - -import static uk.gov.gchq.palisade.exception.Status.INTERNAL_SERVER_ERROR; - -/** - * Subtype of {@link RuntimeException} with additional constructors to support the inclusion of a HTTP error message - * along with the other exception details. - */ -public class PalisadeRuntimeException extends RuntimeException { - - private final Status status; - - public PalisadeRuntimeException(final String message) { - this(message, INTERNAL_SERVER_ERROR); - } - - public PalisadeRuntimeException(final Throwable cause) { - this(cause, INTERNAL_SERVER_ERROR); - } - - public PalisadeRuntimeException(final String message, final Throwable cause) { - this(message, cause, INTERNAL_SERVER_ERROR); - } - - public PalisadeRuntimeException(final String message, final Status status) { - super(message); - this.status = status; - } - - public PalisadeRuntimeException(final Throwable cause, final Status status) { - super(cause); - this.status = status; - } - - public PalisadeRuntimeException(final String message, final Throwable cause, final Status status) { - super(message, cause); - this.status = status; - } - - @Generated - public Status getStatus() { - return status; - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", PalisadeRuntimeException.class.getSimpleName() + "[", "]") - .add("status=" + status) - .add(super.toString()) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/exception/RequestFailedException.java b/src/main/java/uk/gov/gchq/palisade/exception/RequestFailedException.java deleted file mode 100644 index 0a804c21..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/RequestFailedException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - -import uk.gov.gchq.palisade.service.request.Request; - -/** - * An abstract exception thrown by Palisade components when unable to satisfy a particular {@link Request}. - * Exception sub-classes should specify more detailed reasons. - */ -public abstract class RequestFailedException extends PalisadeRuntimeException { - - public RequestFailedException(final String e) { - super(e); - } - - public RequestFailedException(final Throwable cause) { - super(cause); - } - - public RequestFailedException(final String e, final Throwable cause) { - super(e, cause); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/exception/Status.java b/src/main/java/uk/gov/gchq/palisade/exception/Status.java deleted file mode 100644 index 7e42da7b..00000000 --- a/src/main/java/uk/gov/gchq/palisade/exception/Status.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.exception; - -import uk.gov.gchq.palisade.Generated; - -import java.util.Arrays; -import java.util.StringJoiner; - -/** - * Enumerated types for HTTP status codes, loosely based on the JAX-RS 2.0 - * Status class. Also includes HTTP statuses form the WebDAV HTTP extension. - */ -public enum Status { - CONTINUE(100, "Continue"), - SWITCHING_PROTOCOLS(101, "Switching protocols"), - PROCESSING(102, "Processing"), - OK(200, "OK"), - CREATED(201, "Created"), - ACCEPTED(202, "Accepted"), - NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), - NO_CONTENT(204, "No Content"), - RESET_CONTENT(205, "Reset Content"), - PARTIAL_CONTENT(206, "Partial Content"), - MULTI_STATUS(207, "Multi Status"), - ALREADY_REPORTED(208, "Already Reported"), - IM_USED(226, "IM Used"), - MULTIPLE_CHOICES(300, "Multiple Choices"), - MOVED_PERMANENTLY(301, "Moved Permanently"), - FOUND(302, "Found"), - SEE_OTHER(303, "See Other"), - NOT_MODIFIED(304, "Not Modified"), - USE_PROXY(305, "Use Proxy"), - SWITCH_PROXY(306, "Switch Proxy"), - TEMPORARY_REDIRECT(307, "Temporary Redirect"), - PERMANENT_REDIRECT(308, "Permanent Redirect"), - BAD_REQUEST(400, "Bad Request"), - UNAUTHORIZED(401, "Unauthorized"), - PAYMENT_REQUIRED(402, "Payment Required"), - FORBIDDEN(403, "Forbidden"), - NOT_FOUND(404, "Not Found"), - METHOD_NOT_ALLOWED(405, "Method Not Allowed"), - NOT_ACCEPTABLE(406, "Not Acceptable"), - PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), - REQUEST_TIMEOUT(408, "Request Timeout"), - CONFLICT(409, "Conflict"), - GONE(410, "Gone"), - LENGTH_REQUIRED(411, "Length Required"), - PRECONDITION_FAILED(412, "Precondition Failed"), - REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), - REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), - UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), - REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), - EXPECTATION_FAILED(417, "Expectation Failed"), - IM_A_TEAPOT(418, "I'm A Teapot"), - MISDIRECTED_REQUEST(421, "Misdirected Request"), - UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), - LOCKED(423, "Locked"), - FAILED_DEPENDENCY(424, "Failed Dependency"), - UPGRADE_REQUIRED(426, "Upgrade Required"), - PRECONDITION_REQUIRED(428, "Precondition Required"), - TOO_MANY_REQUESTS(429, "Too Many Requests"), - REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), - UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), - INTERNAL_SERVER_ERROR(500, "Internal Server Error"), - NOT_IMPLEMENTED(501, "Not Implemented"), - BAD_GATEWAY(502, "Bad Gateway"), - SERVICE_UNAVAILABLE(503, "Service Unavailable"), - GATEWAY_TIMEOUT(504, "Gateway Timeout"), - HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"); - - private final int statusCode; - private final String reason; - - Status(final int statusCode, final String reasonPhrase) { - this.statusCode = statusCode; - this.reason = reasonPhrase; - } - - /** - * Converts a HTTP status code into the matching object. - * - * @param statusCode the status code to lookup - * @return the corresponding status object - */ - public static Status fromStatusCode(final int statusCode) { - return Arrays.asList(values()) - .stream() - .filter(v -> v.getStatusCode() == statusCode) - .findFirst() - .orElseThrow(RuntimeException::new); - } - - @Generated - public int getStatusCode() { - return statusCode; - } - - @Generated - public String getReason() { - return reason; - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", Status.class.getSimpleName() + "[", "]") - .add("statusCode=" + statusCode) - .add("reason='" + reason + "'") - .add(super.toString()) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiser.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiser.java deleted file mode 100644 index 316fa57d..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiser.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.core.util.ByteArrayBuilder; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.ser.FilterProvider; -import com.fasterxml.jackson.databind.ser.PropertyFilter; -import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; -import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -/** - * A {@code JSONSerialiser} provides the ability to serialise and deserialise to/from JSON. - * The serialisation is set to not include nulls or default values. - *

- * JSONSerialiser is a singleton. The behaviour of the {@link ObjectMapper} - * can be configured by extending this class and configuring the ObjectMapper. - * Child classes must has a default no argument constructor. You will then need - * to set the palisade.serialiser.json.class property in your StoreProperties or - * as a System Property. You can also add modules to the ObjectMapper by writing - * an implementation of {@link JSONSerialiserModules} and registering it using the - * palisade.serialiser.json.modules property in your StoreProperties or - * as a System Property. - *

- *

- * Once the singleton instance has been instantiated it will not be updated, - * unless update() or update(jsonSerialiserClass, jsonSerialiserModules) is called. - * An update will be done automatically in the REST API when it is first initialised and - * also when a Store is initialised. - *

- */ -public class JSONSerialiser { - public static final String JSON_SERIALISER_CLASS_KEY = "palisade.serialiser.json.class"; - /** - * CSV of {@link JSONSerialiserModules} class names. These modules will - * be added to the {@link ObjectMapper}. - */ - public static final String JSON_SERIALISER_MODULES = "palisade.serialiser.json.modules"; - public static final String DEFAULT_SERIALISER_CLASS_NAME = JSONSerialiser.class.getName(); - - public static final String STRICT_JSON = "palisade.serialiser.json.strict"; - public static final boolean STRICT_JSON_DEFAULT = true; - private static final String STRICT_JSON_DEFAULT_STR = Boolean.toString(STRICT_JSON_DEFAULT); - - public static final String FILTER_FIELDS_BY_NAME = "filterFieldsByName"; - - private static final JsonFactory JSON_FACTORY = new JsonFactory(); - private static final Logger LOGGER = LoggerFactory.getLogger(JSONSerialiser.class); - - private static JSONSerialiser instance; - - private final ObjectMapper mapper; - - /** - * Constructs a {@code JSONSerialiser} that skips nulls and default values. - */ - protected JSONSerialiser() { - this(createDefaultMapper()); - } - - /** - * Constructs a {@code JSONSerialiser} with a custom {@link ObjectMapper}. - * To create the custom ObjectMapper it is advised that you start with the - * default mapper provided from JSONSerialiser.createDefaultMapper() then - * add your custom configuration. - * - * @param mapper a custom object mapper - */ - protected JSONSerialiser(final ObjectMapper mapper) { - this.mapper = mapper; - } - - /** - * Child classes of this JSONSerialiser can call this method to register - * custom modules. - * - * @param modules the {@link ObjectMapper} modules to register - */ - protected void registerModules(final Module... modules) { - for (final Module module : modules) { - mapper.registerModule(module); - } - } - - /** - * Child classes of this JSONSerialiser can call this method to register - * custom modules. - * - * @param modules the {@link ObjectMapper} modules to register - */ - protected void registerModules(final Collection modules) { - modules.forEach(mapper::registerModule); - } - - /** - * Update the json serialiser with the provided custom properties. - * - * @param jsonSerialiserClass the json serialiser class to use (or null to use the default) - * @param jsonSerialiserModules any extra json serialiser modules required - * @param strictJson true if strict json conversion should be used - */ - public static void update(final String jsonSerialiserClass, - final String jsonSerialiserModules, - final Boolean strictJson) { - if (null != jsonSerialiserModules && !jsonSerialiserModules.isEmpty()) { - final String modulesCsv = deduplicateConcat( - System.getProperty(JSON_SERIALISER_MODULES), - jsonSerialiserModules - ); - System.setProperty(JSON_SERIALISER_MODULES, modulesCsv); - } - - if (null != jsonSerialiserClass) { - System.setProperty(JSON_SERIALISER_CLASS_KEY, jsonSerialiserClass); - } - - if (null != strictJson) { - System.setProperty(STRICT_JSON, strictJson.toString()); - } - - update(); - } - - public static void update() { - final String jsonSerialiserClass = System.getProperty(JSON_SERIALISER_CLASS_KEY, DEFAULT_SERIALISER_CLASS_NAME); - final JSONSerialiser newInstance; - try { - newInstance = Class.forName(jsonSerialiserClass).asSubclass(JSONSerialiser.class).getDeclaredConstructor().newInstance(); - } catch (final InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException | ClassNotFoundException e) { - throw new IllegalArgumentException("Property " + JSON_SERIALISER_CLASS_KEY + " must be set to a class that is a sub class of " + JSONSerialiser.class.getName() + ". This class is not valid: " + jsonSerialiserClass, e); - } - - final String moduleFactories = System.getProperty(JSON_SERIALISER_MODULES, ""); - final Set factoryClasses = new HashSet<>(Arrays.asList(moduleFactories.split(","))); - factoryClasses.remove(""); - for (final String factoryClass : factoryClasses) { - final JSONSerialiserModules factory; - try { - factory = Class.forName(factoryClass).asSubclass(JSONSerialiserModules.class).getDeclaredConstructor().newInstance(); - } catch (final InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException | ClassNotFoundException e) { - throw new IllegalArgumentException("Property " + JSON_SERIALISER_MODULES + " must be set to a csv of classes that are a sub class of " + JSONSerialiserModules.class.getName() + ". These classes are not valid: " + factoryClass, e); - } - final List modules = factory.getModules(); - if (null != modules) { - newInstance.mapper.registerModules(modules); - } - } - - newInstance.mapper.configure( - DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, - Boolean.parseBoolean(System.getProperty(STRICT_JSON, STRICT_JSON_DEFAULT_STR)) - ); - - instance = newInstance; - LOGGER.debug("Updated json serialiser to use: {}, and modules: {}", jsonSerialiserClass, moduleFactories); - } - - public static ObjectMapper createDefaultMapper() { - final ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - mapper.configure(SerializationFeature.CLOSE_CLOSEABLE, true); - mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); - - // Allow unknown properties. This will help to avoid conflicts between Palisade versions. - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, STRICT_JSON_DEFAULT); - - // Add JDK8 module for Optional - mapper.registerModule(new Jdk8Module()); - // Add JDK8 Time module - mapper.registerModule(new JavaTimeModule()); - - mapper.registerModule(ResourceKeySerialiser.getModule()); - - mapper.registerModule(UserIdKeySerialiser.getModule()); - - // Use the 'setFilters' method so it is compatible with older versions of jackson - mapper.setFilterProvider(getFilterProvider()); - - return mapper; - } - - public static FilterProvider getFilterProvider(final String... fieldsToExclude) { - if (null == fieldsToExclude || fieldsToExclude.length == 0) { - // Use the 'serializeAllExcept' method so it is compatible with older versions of jackson - return new SimpleFilterProvider() - .addFilter(FILTER_FIELDS_BY_NAME, (PropertyFilter) SimpleBeanPropertyFilter.serializeAllExcept()); - } - - return new SimpleFilterProvider() - .addFilter(FILTER_FIELDS_BY_NAME, (PropertyFilter) SimpleBeanPropertyFilter.serializeAllExcept(fieldsToExclude)); - } - - /** - * @param clazz the clazz of the object to be serialised/deserialised - * @return true if the clazz can be serialised/deserialised - */ - public static boolean canHandle(final Class clazz) { - return getInstance().mapper.canSerialize(clazz); - } - - /** - * Serialises an object. - * - * @param object the object to be serialised - * @param fieldsToExclude optional property names to exclude from the json - * @return the provided object serialised into bytes - * @throws RuntimeException if the object fails to be serialised - */ - public static byte[] serialise(final Object object, final String... fieldsToExclude) { - return serialise(object, false, fieldsToExclude); - } - - - /** - * Serialises an object. - * - * @param object the object to be serialised - * @param prettyPrint true if the object should be serialised with pretty printing - * @param fieldsToExclude optional property names to exclude from the json - * @return the provided object serialised (with pretty printing) into bytes - * @throws RuntimeException if the object fails to serialise - */ - public static byte[] serialise(final Object object, final boolean prettyPrint, final String... fieldsToExclude) { - final ByteArrayBuilder byteArrayBuilder = new ByteArrayBuilder(); - try { - serialise(object, JSON_FACTORY.createGenerator(byteArrayBuilder, JsonEncoding.UTF8), prettyPrint, fieldsToExclude); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - - return byteArrayBuilder.toByteArray(); - } - - /** - * Serialises an object using the provided json generator. - * - * @param object the object to be serialised and written to file - * @param jsonGenerator the {@link com.fasterxml.jackson.core.JsonGenerator} to use to write the object to - * @param prettyPrint true if the object should be serialised with pretty printing - * @param fieldsToExclude optional property names to exclude from the json - * @throws RuntimeException if the object fails to serialise - */ - public static void serialise(final Object object, final JsonGenerator jsonGenerator, final boolean prettyPrint, final String... fieldsToExclude) { - if (prettyPrint) { - jsonGenerator.useDefaultPrettyPrinter(); - } - - final ObjectWriter writer = getInstance().mapper.writer(getFilterProvider(fieldsToExclude)); - try { - writer.writeValue(jsonGenerator, object); - } catch (final IOException e) { - throw new RuntimeException("Failed to serialise object to json: " + e.getMessage(), e); - } - } - - /** - * @param json the json of the object to deserialise - * @param clazz the class of the object to deserialise - * @param the type of the object - * @return the deserialised object - * @throws RuntimeException if the json fails to deserialise - */ - public static T deserialise(final String json, final Class clazz) { - try { - return getInstance().mapper.readValue(json, clazz); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param bytes the bytes of the object to deserialise - * @param clazz the class of the object to deserialise - * @param the type of the object - * @return the deserialised object - * @throws RuntimeException if the bytes fail to deserialise - */ - public static T deserialise(final byte[] bytes, final Class clazz) { - try { - return getInstance().mapper.readValue(bytes, clazz); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param stream the {@link java.io.InputStream} containing the bytes of the object to deserialise - * @param clazz the class of the object to deserialise - * @param the type of the object - * @return the deserialised object - * @throws RuntimeException if the bytes fail to deserialise - */ - public static T deserialise(final InputStream stream, final Class clazz) { - try (InputStream stream2 = stream) { - final byte[] bytes = IOUtils.toByteArray(stream2); - return deserialise(bytes, clazz); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param bytes the bytes of the object to deserialise - * @param type the type reference of the object to deserialise - * @param the type of the object - * @return the deserialised object - * @throws RuntimeException if the bytes fail to deserialise - */ - public static T deserialise(final byte[] bytes, final TypeReference type) { - try { - return getInstance().mapper.readValue(bytes, type); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param stream the {@link java.io.InputStream} containing the bytes of the object to deserialise - * @param type the type reference of the object to deserialise - * @param the type of the object - * @return the deserialised object - * @throws RuntimeException if the bytes fail to deserialise - */ - public static T deserialise(final InputStream stream, final TypeReference type) { - try (InputStream stream2 = stream) { - final byte[] bytes = IOUtils.toByteArray(stream2); - return deserialise(bytes, type); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param content the {@link java.lang.String} containing the bytes of the object to deserialise - * @return the deserialised object - * @throws RuntimeException if the bytes fail to deserialise - */ - public static JsonNode getJsonNodeFromString(final String content) { - try { - return getInstance().mapper.readTree(content); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - @JsonIgnore - public static ObjectMapper getMapper() { - return getInstance().mapper; - } - - @JsonIgnore - public static JSONSerialiser getInstance() { - if (null == instance) { - update(); - } - return instance; - } - - private static String deduplicateConcat(final String a, final String b) { - if (null == a) { - return b; - } - if (null == b) { - return a; - } - final LinkedHashSet set = new LinkedHashSet<>(); - Collections.addAll(set, StringUtils.removeStart(a, ",").split(",")); - Collections.addAll(set, StringUtils.removeStart(b, ",").split(",")); - return StringUtils.join(set, ","); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiserModules.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiserModules.java deleted file mode 100644 index edb411e7..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/JSONSerialiserModules.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.databind.Module; - -import java.util.List; - -/** - * A {@code JSONSerialiserModuleFactory} is a simple factory that returns - * a list of {@link Module}s to be uses in an {@link com.fasterxml.jackson.databind.ObjectMapper} - * in {@link uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser}. - */ -public interface JSONSerialiserModules { - List getModules(); -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeyDeserialiser.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeyDeserialiser.java deleted file mode 100644 index d7eebb03..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeyDeserialiser.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.KeyDeserializer; - -import uk.gov.gchq.palisade.resource.LeafResource; - -import java.io.IOException; - -class ResourceKeyDeserialiser extends KeyDeserializer { - @Override - public Object deserializeKey(final String key, final DeserializationContext ctxt) - throws IOException { - return JSONSerialiser.deserialise(key, LeafResource.class); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeySerialiser.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeySerialiser.java deleted file mode 100644 index 5cb16f3d..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/ResourceKeySerialiser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; - -import java.io.IOException; - -class ResourceKeySerialiser extends StdSerializer { - private static final long serialVersionUID = 1L; - - ResourceKeySerialiser() { - super(Resource.class); - } - - public static SimpleModule getModule() { - final SimpleModule module = new SimpleModule(); - module.addKeyDeserializer(LeafResource.class, new ResourceKeyDeserialiser()); - module.addKeySerializer(LeafResource.class, new ResourceKeySerialiser()); - return module; - } - - - @Override - public void serialize(final Resource value, final JsonGenerator g, final SerializerProvider provider) throws IOException { - g.writeFieldName(new String(JSONSerialiser.serialise(value))); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeyDeserialiser.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeyDeserialiser.java deleted file mode 100644 index 70d39891..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeyDeserialiser.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.KeyDeserializer; - -import uk.gov.gchq.palisade.UserId; - -import java.io.IOException; - -class UserIdKeyDeserialiser extends KeyDeserializer { - @Override - public Object deserializeKey(final String key, final DeserializationContext ctxt) - throws IOException { - return JSONSerialiser.deserialise(key, UserId.class); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeySerialiser.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeySerialiser.java deleted file mode 100644 index 91a12b59..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/UserIdKeySerialiser.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.jsonserialisation; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import uk.gov.gchq.palisade.UserId; - -import java.io.IOException; - -public class UserIdKeySerialiser extends StdSerializer { - private static final long serialVersionUID = 1L; - - public UserIdKeySerialiser() { - super(UserId.class); - } - - public static SimpleModule getModule() { - final SimpleModule module = new SimpleModule(); - module.addKeyDeserializer(UserId.class, new UserIdKeyDeserialiser()); - module.addKeySerializer(UserId.class, new UserIdKeySerialiser()); - return module; - } - - - @Override - public void serialize(final UserId value, final JsonGenerator g, final SerializerProvider provider) throws IOException { - g.writeFieldName(new String(JSONSerialiser.serialise(value))); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/package-info.java b/src/main/java/uk/gov/gchq/palisade/jsonserialisation/package-info.java deleted file mode 100644 index b9465570..00000000 --- a/src/main/java/uk/gov/gchq/palisade/jsonserialisation/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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. - */ - -/** - * Classes for handling JSON serialisation and deserialisation. - */ -package uk.gov.gchq.palisade.jsonserialisation; diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java index af4be9b7..fa4df52e 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java @@ -18,7 +18,6 @@ import uk.gov.gchq.palisade.Generated; import uk.gov.gchq.palisade.resource.impl.FileResource; -import uk.gov.gchq.palisade.service.ConnectionDetail; import java.io.Serializable; import java.util.HashMap; @@ -66,8 +65,8 @@ public AbstractLeafResource connectionDetail(final ConnectionDetail connectionDe /** * Sets the attributes for the {@link AbstractLeafResource} * - * @param attributes a {@link Map} of {@link String} and {@link Serializable}. - * @return a {@link AbstractLeafResource} object. + * @param attributes a {@link Map} of {@link String} and {@link Serializable}. + * @return a {@link AbstractLeafResource} object. */ @Generated public AbstractLeafResource attributes(final Map attributes) { @@ -78,9 +77,9 @@ public AbstractLeafResource attributes(final Map attribute /** * Sets the attributes for the {@link AbstractLeafResource} * - * @param attributeKey a {@link String} value for the key. - * @param attributeValue a {@link Serializable} value - * @return the {@link AbstractLeafResource} object + * @param attributeKey a {@link String} value for the key. + * @param attributeValue a {@link Serializable} value + * @return the {@link AbstractLeafResource} object */ @Generated public AbstractLeafResource attribute(final String attributeKey, final Serializable attributeValue) { @@ -170,8 +169,8 @@ public Boolean isAttributeSet(final String attributeKey) { /** * Sets the key and value of the attributes {@link Map} for the {@link AbstractLeafResource} * - * @param attributeKey a {@link String} value for the attribute key. - * @param attributeValue a {@link Serializable} value for the attribute value. + * @param attributeKey a {@link String} value for the attribute key. + * @param attributeValue a {@link Serializable} value for the attribute value. */ @Generated public void setAttribute(final String attributeKey, final Serializable attributeValue) { diff --git a/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java similarity index 93% rename from src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java rename to src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java index 147bfac0..744873b9 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/ConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package uk.gov.gchq.palisade.service; +package uk.gov.gchq.palisade.resource; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import uk.gov.gchq.palisade.resource.impl.SimpleConnectionDetail; + import java.io.Serializable; /** diff --git a/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java index 05eb22ff..267dabca 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java @@ -16,8 +16,6 @@ package uk.gov.gchq.palisade.resource; -import uk.gov.gchq.palisade.service.ConnectionDetail; - /** * A leaf resource is the interface for any resource that can be read for data * and is not just part of the hierarchical resource structure. diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java index 34890bd8..ce55300d 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/DirectoryResource.java @@ -26,6 +26,10 @@ import static java.util.Objects.requireNonNull; +/** + * A DirectoryResource is a Palisade representation of a directory and can have Children and Parent resources + * {@code eg. "file:/dev/Palisade/pom.xml" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade/" -> File "/dev/Palisade/pom.xml" } + */ public class DirectoryResource extends AbstractResource implements ChildResource, ParentResource { private static final long serialVersionUID = 1L; diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java index 5c175f37..4ac3cc80 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java @@ -17,12 +17,16 @@ package uk.gov.gchq.palisade.resource.impl; import uk.gov.gchq.palisade.resource.AbstractLeafResource; +import uk.gov.gchq.palisade.resource.ConnectionDetail; import uk.gov.gchq.palisade.resource.ParentResource; -import uk.gov.gchq.palisade.service.ConnectionDetail; import java.io.Serializable; import java.util.Map; +/** + * A FileResource is the Palisade representation of a file, which extends a LeafResource. + * {@code eg. "file:/dev/Palisade/pom.xml" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade/" -> File "/dev/Palisade/pom.xml" } + */ public class FileResource extends AbstractLeafResource { private static final long serialVersionUID = 1L; diff --git a/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java similarity index 94% rename from src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java rename to src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java index 60f2dd1c..00d484e5 100644 --- a/src/main/java/uk/gov/gchq/palisade/service/SimpleConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package uk.gov.gchq.palisade.service; +package uk.gov.gchq.palisade.resource.impl; import uk.gov.gchq.palisade.Generated; +import uk.gov.gchq.palisade.resource.ConnectionDetail; import java.util.Objects; import java.util.StringJoiner; @@ -24,7 +25,7 @@ import static java.util.Objects.requireNonNull; /** - * A simple implementation of the {@link ConnectionDetail} that holds a reference to the {@link Service} + * A simple implementation of the {@link ConnectionDetail} that holds a reference to the Service */ public class SimpleConnectionDetail implements ConnectionDetail { private static final long serialVersionUID = 1L; diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java deleted file mode 100644 index a1b9d225..00000000 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/StreamResource.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.resource.impl; - -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.resource.AbstractLeafResource; -import uk.gov.gchq.palisade.resource.ParentResource; -import uk.gov.gchq.palisade.service.ConnectionDetail; - -import java.io.Serializable; -import java.util.Map; -import java.util.Objects; -import java.util.StringJoiner; - -public class StreamResource extends AbstractLeafResource { - private static final long serialVersionUID = 1L; - - protected long start; - protected long end; - - public StreamResource() { - //no-args constructor needed for serialization only - } - - @Override - public StreamResource id(final String id) { - return (StreamResource) super.id(id); - } - - @Override - public StreamResource type(final String type) { - return (StreamResource) super.type(type); - } - - @Override - public StreamResource serialisedFormat(final String serialisedFormat) { - return (StreamResource) super.serialisedFormat(serialisedFormat); - } - - @Override - public StreamResource connectionDetail(final ConnectionDetail connectionDetail) { - return (StreamResource) super.connectionDetail(connectionDetail); - } - - @Override - public StreamResource attributes(final Map attributes) { - return (StreamResource) super.attributes(attributes); - } - - @Override - public StreamResource attribute(final String attributeKey, final Serializable attributeValue) { - return (StreamResource) super.attribute(attributeKey, attributeValue); - } - - @Override - public StreamResource parent(final ParentResource parent) { - return (StreamResource) super.parent(parent); - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof StreamResource)) { - return false; - } - if (!super.equals(o)) { - return false; - } - final StreamResource that = (StreamResource) o; - return start == that.start && - end == that.end; - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(super.hashCode(), start, end); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", StreamResource.class.getSimpleName() + "[", "]") - .add("start=" + start) - .add("end=" + end) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java index 1027c8f1..e0eb87c8 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/SystemResource.java @@ -19,6 +19,10 @@ import uk.gov.gchq.palisade.resource.AbstractResource; import uk.gov.gchq.palisade.resource.ParentResource; +/** + * A SystemResource is the Palisade representation of a root directory inside a system + * {@code eg. "file:/dev/Palisade/pom.xml" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade/" -> File "/dev/Palisade/pom.xml" } + */ public class SystemResource extends AbstractResource implements ParentResource { private static final long serialVersionUID = 1L; diff --git a/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java b/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java deleted file mode 100644 index ccb8f6cc..00000000 --- a/src/main/java/uk/gov/gchq/palisade/rule/PredicateRule.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.rule; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; - -import java.io.Serializable; - -/** - * A {@link PredicateRule} is a simplified implementation of {@link Rule} that simply - * tests whether a record should be fully redacted or not. - * - * @param The type of the record. In normal cases the raw data will be deserialised - * by the record reader before being passed to the apply(T, User, Context) method. - */ -public interface PredicateRule extends Rule { - /** - * Applies the rule logic to test whether a record should be redacted based on the user and context. - * - * @param record the record to be checked. - * @param user the user - * @param context the query context - * @return true if the record should be kept, false if the record should be redacted. - */ - boolean test(final T record, final User user, final Context context); - - @Override - default T apply(final T obj, final User user, final Context context) { - if (test(obj, user, context)) { - return obj; - } else { - return null; - - } - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java index 6706769d..bae99d9b 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java @@ -22,7 +22,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; +import uk.gov.gchq.palisade.user.User; import java.io.Serializable; diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java index bae1523a..9754bb86 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java @@ -17,16 +17,17 @@ package uk.gov.gchq.palisade.rule; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import org.apache.commons.lang3.builder.EqualsBuilder; import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; import java.io.Serializable; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.StringJoiner; +import java.util.stream.Collectors; import static java.util.Objects.requireNonNull; @@ -101,47 +102,6 @@ public Rules addRule(final String id, final Rule rule) { return this; } - /** - * Adds a predicate rule. - * - * @param id the unique rule id - * @param rule the predicate rule - * @return this Rules instance - */ - @Generated - public Rules addPredicateRule(final String id, final PredicateRule rule) { - this.addRule(id, rule); - return this; - } - - /** - * Adds a simple predicate rule that just takes the record and returns true or false. Note - using this means your - * rule will not be given the User or Context. - * - * @param id the unique rule id - * @param rule the simple predicate rule - * @return this Rules instance - */ - @Generated - public Rules addSimplePredicateRule(final String id, final SerializablePredicate rule) { - this.addRule(id, new WrappedRule<>(rule)); - return this; - } - - /** - * Adds a simple function rule that just takes the record and returns a modified record or null if the record should - * be fully redacted. Note - using this means your rule will not be given the User or Context. - * - * @param id the unique rule id - * @param rule the simple function rule - * @return this Rules instance - */ - @Generated - public Rules addSimpleFunctionRule(final String id, final SerializableUnaryOperator rule) { - this.addRule(id, new WrappedRule<>(rule)); - return this; - } - @Generated public String getMessage() { return message; @@ -175,35 +135,22 @@ public boolean containsRules() { @Override public boolean equals(final Object o) { - boolean rtn = (this == o); - if (!rtn && (o != null && this.getClass() == o.getClass())) { - - final Rules that = (Rules) o; - - final EqualsBuilder builder = new EqualsBuilder() - .append(message, that.message) - .append(this.rulesMap.keySet(), that.getRules().keySet()); - - if (builder.isEquals()) { - for (final Map.Entry> entry : this.rulesMap.entrySet()) { - final String ruleName = entry.getKey(); - final Rule thisRule = entry.getValue(); - final Rule thatRule = that.getRules().get(ruleName); - - builder.append(thisRule.getClass(), thatRule.getClass()); - if (builder.isEquals()) { - // This is expensive - but we don't have any other way of doing it - builder.append(JSONSerialiser.serialise(thisRule), JSONSerialiser.serialise(thatRule)); - } - - if (!builder.isEquals()) { - return false; - } - } - } - rtn = builder.isEquals(); + if (this == o) { + return true; + } + if (!(o instanceof Rules)) { + return false; } - return rtn; + Rules other = (Rules) o; + List>> thisRuleClasses = this.rulesMap.values().stream() + .map(Optional::ofNullable) + .map(rule -> rule.map(Rule::getClass)) + .collect(Collectors.toList()); + List>> otherRuleClasses = other.rulesMap.values().stream() + .map(Optional::ofNullable) + .map(rule -> rule.map(Rule::getClass)) + .collect(Collectors.toList()); + return thisRuleClasses.equals(otherRuleClasses); } @Override @@ -218,7 +165,6 @@ public String toString() { return new StringJoiner(", ", Rules.class.getSimpleName() + "[", "]") .add("message='" + message + "'") .add("rulesHashMap=" + rulesMap) - .add(super.toString()) .toString(); } } diff --git a/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java b/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java deleted file mode 100644 index 16c098b5..00000000 --- a/src/main/java/uk/gov/gchq/palisade/rule/SerializablePredicate.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.rule; - -import java.io.Serializable; -import java.util.function.Predicate; - -/** - * A serializable interface for a {@link Predicate} - * - * @param the type of the input to the predicate - */ -public interface SerializablePredicate extends Predicate, Serializable { -} diff --git a/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java b/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java deleted file mode 100644 index aa46f851..00000000 --- a/src/main/java/uk/gov/gchq/palisade/rule/SerializableUnaryOperator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.rule; - -import java.io.Serializable; -import java.util.function.UnaryOperator; - -/** - * A serializable interface for a {@link UnaryOperator} - * - * @param the type of the operand and result of the operator - */ -public interface SerializableUnaryOperator extends UnaryOperator, Serializable { -} diff --git a/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java b/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java deleted file mode 100644 index 681db7ef..00000000 --- a/src/main/java/uk/gov/gchq/palisade/rule/WrappedRule.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.rule; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.User; - -import java.io.Serializable; -import java.util.Objects; -import java.util.StringJoiner; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; - -/** - * A {@link WrappedRule} is helper implementation of {@link Rule}. It is useful - * when you need to set simple rules that don't require the {@link User} or {@link Context}. - * - * @param The type of the record. In normal cases the raw data will be deserialised - * by the record reader before being passed to the {@link Rule#apply(Serializable, User, Context)}. - */ -@JsonPropertyOrder(value = {"class", "rule", "function", "predicate"}, alphabetic = true) -public class WrappedRule implements Rule { - - public static final String WRAPPED_RULE_WAS_INITIALISED_WITH_NULL = "WrappedRule was initialised with null."; - public static final String RULE_STRING = "rule"; - public static final String FUNCTION_STRING = "function"; - public static final String PREDICATE_STRING = "predicate"; - private static final long serialVersionUID = 1L; - private Rule rule; - private SerializableUnaryOperator function; - private SerializablePredicate predicate; - - /** - * Constructs a {@link WrappedRule} with a null rule. - */ - public WrappedRule() { - } - - /** - * Constructs a {@link WrappedRule} with a given rule to wrap. - * - * @param rule the {@link Rule} to wrap. - */ - public WrappedRule(final Rule rule) { - requireNonNull(rule, WRAPPED_RULE_WAS_INITIALISED_WITH_NULL + RULE_STRING); - this.rule = rule; - } - - /** - * Constructs a {@link WrappedRule} with a given simple function rule to apply. - * Note - using this means your rule will not be given the User or Context. - * - * @param function the simple {@link UnaryOperator} rule to wrap. - */ - public WrappedRule(final SerializableUnaryOperator function) { - requireNonNull(function, WRAPPED_RULE_WAS_INITIALISED_WITH_NULL + FUNCTION_STRING); - this.function = function; - } - - /** - * Constructs a {@link WrappedRule} with a given simple predicate rule to apply. - * Note - using this means your rule will not be given the User or Context. - * - * @param predicate the simple {@link Predicate} rule to wrap. - */ - public WrappedRule(final SerializablePredicate predicate) { - requireNonNull(predicate, WRAPPED_RULE_WAS_INITIALISED_WITH_NULL + PREDICATE_STRING); - this.predicate = predicate; - } - - @JsonCreator - public WrappedRule(@JsonProperty("rule") final Rule rule, - @JsonProperty("function") final SerializableUnaryOperator function, - @JsonProperty("predicate") final SerializablePredicate predicate) { - checkNullCount(rule, function, predicate); - this.rule = rule; - this.function = function; - this.predicate = predicate; - } - - private void checkNullCount(final Rule rule, final UnaryOperator function, final Predicate predicate) { - //needs improving with Jackson - int nullCount = 0; - if (rule == null) { - nullCount++; - } - if (function == null) { - nullCount++; - } - if (predicate == null) { - nullCount++; - } - if (nullCount != 2) { - throw new IllegalArgumentException("Only one constructor parameter can be non-null"); - } - } - - @Override - public T apply(final T obj, final User user, final Context context) { - final T rtn; - if (nonNull(rule)) { - rtn = rule.apply(obj, user, context); - } else if (nonNull(function)) { - rtn = function.apply(obj); - } else if (nonNull(predicate)) { - final boolean test = predicate.test(obj); - if (test) { - rtn = obj; - } else { - rtn = null; - } - } else { - rtn = obj; - } - return rtn; - } - - - @Generated - public Rule getRule() { - return rule; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") - @Generated - public UnaryOperator getFunction() { - return function; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") - @Generated - public Predicate getPredicate() { - return predicate; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof WrappedRule)) { - return false; - } - final WrappedRule that = (WrappedRule) o; - return Objects.equals(rule, that.rule) && - Objects.equals(function, that.function) && - Objects.equals(predicate, that.predicate); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(rule, function, predicate); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", WrappedRule.class.getSimpleName() + "[", "]") - .add("rule=" + rule) - .add("function=" + function) - .add("predicate=" + predicate) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/PolicyConfiguration.java b/src/main/java/uk/gov/gchq/palisade/service/PolicyConfiguration.java deleted file mode 100644 index 2fdde7b6..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/PolicyConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import java.util.List; - -public interface PolicyConfiguration { - - /** - * Gets a {@link List} of the {@link PolicyPrepopulationFactory} implemented - * objects that have been created from a yaml file. - * - * @return a {@link List} of the objects that have implemented {@link PolicyPrepopulationFactory}. - */ - List getPolicies(); - -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java b/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java deleted file mode 100644 index 647b8d5e..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/PolicyPrepopulationFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; - -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.rule.Rules; - -import java.io.Serializable; -import java.util.Map.Entry; - -/** - * This class defines the top level of the cache prepopulation. - *

- * The only requirement is that there is a build method, used to create the object - */ -@JsonPropertyOrder(value = {"class"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class" -) -@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) -public interface PolicyPrepopulationFactory { - - /** - * Creates a {@link Rules} of type {@link LeafResource} that is associated to a resourceId using the - * data within an implementation of the {@link PolicyPrepopulationFactory} - * - * @return an {@link Entry} value that consists of a resourceId and the created {@link Rules} of type {@link LeafResource}. - */ - Entry> buildResourceRules(); - - /** - * Creates a {@link Rules} of type {@link Serializable} that is associated to a resourceId using the - * data within an implementation of the {@link PolicyPrepopulationFactory}. - * - * @return an {@link Entry} value that consists of a resourceId and the created {@link Rules} of type {@link Serializable}. - */ - Entry> buildRecordRules(); - - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceConfiguration.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceConfiguration.java deleted file mode 100644 index c8a43705..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import java.net.URI; -import java.util.List; - -public interface ResourceConfiguration { - - /** - * Gets a {@link List} of the {@link ResourcePrepopulationFactory} implemented - * objects that have been created from a yaml file, paired with the {@link URI} of the topmost parent - * that resource should be prepopulated up-to. - * - * @return a {@link List} of the objects that have implemented {@link ResourcePrepopulationFactory}. - */ - List getResources(); - -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourcePrepopulationFactory.java b/src/main/java/uk/gov/gchq/palisade/service/ResourcePrepopulationFactory.java deleted file mode 100644 index 2eb3d215..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourcePrepopulationFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; - -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; - -import java.util.Map.Entry; -import java.util.function.Function; - -/** - * This class defines the top level of persistence prepopulation. - *

- * The only requirement is that there is a build method to construct a LeafResource - */ -@JsonPropertyOrder(value = {"class"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class" -) -@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id") -public interface ResourcePrepopulationFactory { - - /** - * Creates a {@link LeafResource} using the data within an implementation of the {@link ResourcePrepopulationFactory}. - * - * @param connectionDetailMapper a function mapping the connection detail {@link String}s to proper {@link ConnectionDetail}s - * @return the {@link LeafResource} that has been created. - */ - Entry build(Function connectionDetailMapper); - - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java b/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java deleted file mode 100644 index 81439947..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/ResourceService.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.Resource; - -import java.util.Iterator; - -/** - * The resource service is the Palisade component that determines what resources are available that meet a specific - * (type of) request and how they should be accessed. This interface details several methods for obtaining a list of - * resources, e.g. by type or by data format. The methods of this service all return {@link Iterator}s which link a valid - * {@link LeafResource} with a {@link ConnectionDetail} object. The ${@link ConnectionDetail} objects contain - * information on how to set up a connection to retrieve a particular resource. Implementations of this service do not - * deal with the filtering or application of security policy to the resources. Therefore, a result returned from a - * method call on this interface doesn't guarantee that the user will be allowed to access it by policy. Other - * components of the Palisade system will enforce the necessary policy controls to prevent access to resources by users - * without the necessary access rights. - * Implementation note: None of the ${@code getResourcesByXXX} methods in this class will return in error if there - * don't happen to be any resources that do not match a request, instead they will simply return empty ${@link Iterator} - * instances. - */ -public interface ResourceService extends Service { - - /** - * Get a list of resources based on a specific resource. This allows for the retrieval of the appropriate {@link - * ConnectionDetail}s for a given resource. It may also be used to retrieve the details all the resources that are - * notionally children of another resource. For example, in a standard hierarchical filing system the files in a - * directory could be considered child resources and calling this method on the directory resource would fetch the - * details on the contained files. - * - * @param resource the resource to request - * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} - */ - Iterator getResourcesByResource(final Resource resource); - - /** - * Retrieve resource and connection details by resource ID. The request object allows the client to specify the - * resource ID and obtain the connection details once the returned future has completed. - * - * @param resourceId the ID to request - * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} - */ - Iterator getResourcesById(final String resourceId); - - /** - * Obtain a list of resources that match a specific resource type. This method allows a client to obtain potentially - * large collections of resources by requesting all the resources of one particular type. For example, a client may - * request all "employee contact card" records. Please note the warning in the class documentation above, that just - * because a resource is available does not guarantee that the requesting client has the right to access it. - * - * @param type the type of resource to retrieve. - * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} - */ - Iterator getResourcesByType(final String type); - - /** - * Find all resources that match a particular data format. Resources of a particular data format may not share a - * type, e.g. not all CSV format records will contain employee contact details. This method allows clients to - * retrieve all the resources Palisade knows about that conform to one particular format. Note that this method can - * potentially return large ${@code Map}s with many mappings. - * - * @param serialisedFormat the specific format for retrieval - * @return an {@link Iterator} of resources, each with an appropriate {@link ConnectionDetail} - */ - Iterator getResourcesBySerialisedFormat(final String serialisedFormat); - - /** - * Informs Palisade about a specific resource that it may return to users. This lets Palisade clients request access - * to that resource and allows Palisade to provide policy controlled access to it via the other methods in this - * interface. - * - * @param resource the resource that Palisade can manage access to - * @return whether or not the addResource call completed successfully - */ - Boolean addResource(final LeafResource resource); - -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/Service.java b/src/main/java/uk/gov/gchq/palisade/service/Service.java deleted file mode 100644 index 921398df..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/Service.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; - -/** - * This class defines the top level services API. - *

- * The only requirement is that there is a process method, used to process all requests that are passed to a service. - */ -@JsonPropertyOrder(value = {"class"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class" -) -@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id") -public interface Service { - - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/UserConfiguration.java b/src/main/java/uk/gov/gchq/palisade/service/UserConfiguration.java deleted file mode 100644 index 5a84ad9b..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/UserConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import java.util.List; - -public interface UserConfiguration { - - /** - * Gets a {@link List} of the {@link UserPrepopulationFactory} implemented - * objects that have been created from a yaml file. - * - * @return a {@link List} of the objects that have implemented {@link UserPrepopulationFactory}. - */ - List getUsers(); - -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/UserPrepopulationFactory.java b/src/main/java/uk/gov/gchq/palisade/service/UserPrepopulationFactory.java deleted file mode 100644 index 6d7a9a29..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/UserPrepopulationFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; - -import uk.gov.gchq.palisade.User; - -/** - * This class defines the top level of the cache prepopulation. - *

- * The only requirement is that there is a warm method, used to create the object - */ -@JsonPropertyOrder(value = {"class"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class" -) -@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id") -public interface UserPrepopulationFactory { - - /** - * Creates a {@link User} using the data within an implementation of the {@link UserPrepopulationFactory}. - * - * @return the {@link User} that has been created. - */ - User build(); - - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestConfig.java b/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestConfig.java deleted file mode 100644 index b0d91f4e..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestConfig.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service.request; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.rule.Rules; - -import java.util.Map; -import java.util.Objects; -import java.util.StringJoiner; - -import static java.util.Objects.requireNonNull; - -/** - * This is the high level API for the object that contains all the information - * that the data service will require from the palisade, for the data service to - * respond to requests for access to data. - */ -public class DataRequestConfig extends Request { - private User user; - private Context context; - private Map rules; - - public DataRequestConfig() { - //no-args constructor needed for serialization only - } - - @Generated - public DataRequestConfig user(final User user) { - this.setUser(user); - return this; - } - - @Generated - public DataRequestConfig context(final Context context) { - this.setContext(context); - return this; - } - - @Generated - public DataRequestConfig rules(final Map rules) { - this.setRules(rules); - return this; - } - - - @Generated - public User getUser() { - return user; - } - - @Generated - public void setUser(final User user) { - requireNonNull(user); - this.user = user; - } - - @Generated - public Context getContext() { - return context; - } - - @Generated - public void setContext(final Context context) { - requireNonNull(context); - this.context = context; - } - - @Generated - public Map getRules() { - return rules; - } - - @Generated - public void setRules(final Map rules) { - requireNonNull(rules); - this.rules = rules; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DataRequestConfig)) { - return false; - } - if (!super.equals(o)) { - return false; - } - DataRequestConfig that = (DataRequestConfig) o; - return user.equals(that.user) && - context.equals(that.context) && - rules.equals(that.rules); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(super.hashCode(), user, context, rules); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", DataRequestConfig.class.getSimpleName() + "[", "]") - .add("user=" + user) - .add("context=" + context) - .add("rules=" + rules) - .add(super.toString()) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestResponse.java b/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestResponse.java deleted file mode 100644 index 4600a058..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/request/DataRequestResponse.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service.request; - -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.resource.LeafResource; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; - -import static java.util.Objects.requireNonNull; - -/** - * This is the high level API object that is used to pass back to the client the information it requires to connect to - * the correct data service implementations and to decide how best to parallelise their job. - *

- * It is also the object that the client then passes to the data service to access the data. When it is passed to the - * data service the resources field might have been changed to be a subset of the resources. - */ -public class DataRequestResponse extends Request { - private String token; - private Set resources; - - public DataRequestResponse() { - //no-args constructor needed for serialization only - } - - @Generated - public DataRequestResponse token(final String token) { - this.setToken(token); - return this; - } - - @Generated - public DataRequestResponse resource(final LeafResource resource) { - this.addResource(resource); - return this; - } - - @Generated - public DataRequestResponse resources(final Set resources) { - this.setResources(resources); - return this; - } - - - public void addResource(final LeafResource resource) { - requireNonNull(resource); - if (resources == null) { - resources = new HashSet<>(); - } - resources.add(resource); - } - - @Generated - public String getToken() { - return token; - } - - @Generated - public void setToken(final String token) { - requireNonNull(token); - this.token = token; - } - - @Generated - public Set getResources() { - return resources; - } - - @Generated - public void setResources(final Set resources) { - requireNonNull(resources); - this.resources = resources; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DataRequestResponse)) { - return false; - } - if (!super.equals(o)) { - return false; - } - DataRequestResponse that = (DataRequestResponse) o; - return token.equals(that.token) && - resources.equals(that.resources); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(super.hashCode(), token, resources); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", DataRequestResponse.class.getSimpleName() + "[", "]") - .add("token='" + token + "'") - .add("resources=" + resources) - .add(super.toString()) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/service/request/Request.java b/src/main/java/uk/gov/gchq/palisade/service/request/Request.java deleted file mode 100644 index af3e5176..00000000 --- a/src/main/java/uk/gov/gchq/palisade/service/request/Request.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.service.request; - - -import uk.gov.gchq.palisade.Generated; -import uk.gov.gchq.palisade.RequestId; - -import java.util.Objects; -import java.util.StringJoiner; -import java.util.UUID; - -import static java.util.Objects.requireNonNull; - -/** - * This is the high level API for any request sent to a service. - * This makes sure each request has a unique identifier. - */ -public abstract class Request { - private RequestId id; //this is a unique ID for each individual request made between the micro-services - private RequestId originalRequestId; //this Id is unique per data access request from a user - - public Request() { - this.id = new RequestId().id(UUID.randomUUID().toString()); - } - - @Generated - public Request originalRequestId(final RequestId originalRequestId) { - this.setOriginalRequestId(originalRequestId); - return this; - } - - @Generated - public RequestId getId() { - return id; - } - - - @Generated - public RequestId getOriginalRequestId() { - return originalRequestId; - } - - @Generated - public void setOriginalRequestId(final RequestId originalRequestId) { - requireNonNull(originalRequestId); - this.originalRequestId = originalRequestId; - } - - @Override - @Generated - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Request)) { - return false; - } - Request request = (Request) o; - return id.equals(request.id) && - originalRequestId.equals(request.originalRequestId); - } - - @Override - @Generated - public int hashCode() { - return Objects.hash(id, originalRequestId); - } - - @Override - @Generated - public String toString() { - return new StringJoiner(", ", Request.class.getSimpleName() + "[", "]") - .add("id=" + id) - .add("originalRequestId=" + originalRequestId) - .toString(); - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/User.java b/src/main/java/uk/gov/gchq/palisade/user/User.java similarity index 98% rename from src/main/java/uk/gov/gchq/palisade/User.java rename to src/main/java/uk/gov/gchq/palisade/user/User.java index 4a96a4f1..bab456d4 100644 --- a/src/main/java/uk/gov/gchq/palisade/User.java +++ b/src/main/java/uk/gov/gchq/palisade/user/User.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package uk.gov.gchq.palisade; +package uk.gov.gchq.palisade.user; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import uk.gov.gchq.palisade.Generated; + import java.io.Serializable; import java.util.Arrays; import java.util.HashSet; diff --git a/src/main/java/uk/gov/gchq/palisade/UserId.java b/src/main/java/uk/gov/gchq/palisade/user/UserId.java similarity index 96% rename from src/main/java/uk/gov/gchq/palisade/UserId.java rename to src/main/java/uk/gov/gchq/palisade/user/UserId.java index cd7e199c..97bbbd97 100644 --- a/src/main/java/uk/gov/gchq/palisade/UserId.java +++ b/src/main/java/uk/gov/gchq/palisade/user/UserId.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package uk.gov.gchq.palisade; +package uk.gov.gchq.palisade.user; + +import uk.gov.gchq.palisade.Generated; import java.io.Serializable; import java.util.Objects; diff --git a/src/main/java/uk/gov/gchq/palisade/util/DebugUtil.java b/src/main/java/uk/gov/gchq/palisade/util/DebugUtil.java deleted file mode 100644 index 9c945db2..00000000 --- a/src/main/java/uk/gov/gchq/palisade/util/DebugUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Util methods for debugging - */ -public final class DebugUtil { - public static final String DEBUG = "palisade.error-mode.debug"; - public static final String DEBUG_DEFAULT = String.valueOf(false); - - private static Boolean isDebug; - private static final Logger LOGGER = LoggerFactory.getLogger(DebugUtil.class); - - private DebugUtil() { - // Private constructor to prevent instantiation. - } - - /** - * Retrieve the value of the debug mode flag. - * - * @return the debug mode status, true if enabled, otherwise false - */ - public static boolean checkDebugMode() { - if (null == isDebug) { - updateDebugMode(); - } - return isDebug; - } - - /** - * Update the debug mode status by reading the system properties. - */ - public static void updateDebugMode() { - try { - isDebug = Boolean.valueOf(System.getProperty(DEBUG, DEBUG_DEFAULT).trim()); - if (Boolean.TRUE.equals(isDebug)) { - LOGGER.debug("Debug has been enabled in SystemProperties"); - } - } catch (final Exception e) { - LOGGER.error("Defaulting Debug flag. Could not assign from System Properties: {}", e.getMessage()); - isDebug = Boolean.valueOf(DEBUG_DEFAULT); - } - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java index ac8d5012..43ec992b 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java @@ -19,13 +19,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import uk.gov.gchq.palisade.resource.ConnectionDetail; import uk.gov.gchq.palisade.resource.LeafResource; import uk.gov.gchq.palisade.resource.ParentResource; import uk.gov.gchq.palisade.resource.Resource; import uk.gov.gchq.palisade.resource.impl.DirectoryResource; import uk.gov.gchq.palisade.resource.impl.FileResource; import uk.gov.gchq.palisade.resource.impl.SystemResource; -import uk.gov.gchq.palisade.service.ConnectionDetail; import java.io.File; import java.io.IOException; @@ -33,6 +33,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.util.Collections; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -47,13 +48,17 @@ * - {@link SystemResource} * Any parents automatically constructed will also be from this collection. * If another method of creating a resource is required (i.e. directly using strings) - * there is no guarantee that this can correctly resolve parents. Instead use the + * there is no guarantee that this can correctly resolve parents. Instead, use the * methods provided by the appropriate resource impl. */ public class ResourceBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBuilder.class); private static final URI ROOT; + public ResourceBuilder() { + // Empty Constructor + } + static { File userDir = new File(System.getProperty("user.dir")); URI root; @@ -66,27 +71,23 @@ public class ResourceBuilder { ROOT = root; } - private ResourceBuilder() { - // empty private constructor - } - private enum Scheme { FILE, HDFS } - public static boolean canCreate(final URI uri) { - try { - Scheme.valueOf(uri.getScheme()); // Or throw - return uri.isAbsolute(); - } catch (IllegalArgumentException ex) { - return false; - } - } - - // Create a leafResource from a uri, connectionDetail, type, serialisedFormat and attribute map - // Throw IllegalArgumentException if unsupported scheme - // Throw ClassCastException if uri did not point to a leaf resource + /** + * Create a leafResource from a uri, connectionDetail, type, serialisedFormat and attribute map + * Throw IllegalArgumentException if unsupported scheme + * Throw ClassCastException if uri did not point to a leaf resource + * + * @param uri the uri location of the resource + * @param connectionDetail the service storing the resource + * @param type the type of resource + * @param serialisedFormat the format of the resource e.g avro, txt + * @param attributes any additional attributes about the resource + * @return a new LeafResource populated with these resources + */ public static LeafResource create(final URI uri, final ConnectionDetail connectionDetail, final String type, final String serialisedFormat, final Map attributes) { return ((LeafResource) create(uri, attributes)) .connectionDetail(connectionDetail) @@ -94,8 +95,14 @@ public static LeafResource create(final URI uri, final ConnectionDetail connecti .serialisedFormat(serialisedFormat); } - // Create a resource from a uri and attribute map - // Throw IllegalArgumentException if unsupported scheme + /** + * Create a resource from a uri and attribute map + * Throw IllegalArgumentException if unsupported scheme + * + * @param uri the id of the resource + * @param attributes any additional attributes about the resource + * @return a new resource created using this uri + */ public static Resource create(final URI uri, final Map attributes) { // If passed relative paths, we can resolve them against the user.dir system property URI absolute; @@ -115,7 +122,7 @@ public static Resource create(final URI uri, final Map attribute // This should be assigning the attributes map to the returned object, once resources support attribute maps - switch (Scheme.valueOf(normal.getScheme().toUpperCase())) { + switch (Scheme.valueOf(normal.getScheme().toUpperCase(Locale.ENGLISH))) { // Both file:/ and hdfs:/ schema produce filesystem-like structures case FILE: case HDFS: @@ -125,15 +132,41 @@ public static Resource create(final URI uri, final Map attribute } } - // Create a resource from a uri - // Default to an empty attribute map - // Throw IllegalArgumentException if unsupported scheme + /** + * Used to validate URIs and check that they can be created + * + * @param uri the uri to validate or attach to a resource + * @return a boolean true/false if the uri scheme is valid. + */ + public static boolean canCreate(final URI uri) { + try { + Scheme.valueOf(uri.getScheme()); // Or throw + return uri.isAbsolute(); + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + * Create a resource from a uri + * Default to an empty attribute map + * Throw IllegalArgumentException if unsupported scheme + * + * @param uri an id of a resource + * @return a newly created resource with an empty map of attributes + */ public static Resource create(final URI uri) { return create(uri, Collections.emptyMap()); } - // Create a resource from a uri string and attribute map - // Throw IllegalArgumentException if invalid uri string or unsupported scheme + /** + * Create a resource from a uri string and attribute map + * Throw IllegalArgumentException if invalid uri string or unsupported scheme + * + * @param uriString a string value of a url used to create a new resource + * @param attributes any additional attributes about the resource + * @return a newly created resource using these parameters + */ public static Resource create(final String uriString, final Map attributes) { try { return create(new URI(uriString), attributes); @@ -142,9 +175,14 @@ public static Resource create(final String uriString, final Map } } - // Create a resource from a uri string - // Default to an empty attribute map - // Throw IllegalArgumentException if invalid uri string or unsupported scheme + /** + * Create a resource from a uri string + * Default to an empty attribute map + * Throw IllegalArgumentException if invalid uri string or unsupported scheme + * + * @param uriString a string value of a url used to create a new resource + * @return a newly created resource with this url and an empty map of attributes + */ public static Resource create(final String uriString) { return create(uriString, Collections.emptyMap()); } diff --git a/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java b/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java new file mode 100644 index 00000000..8c1b486b --- /dev/null +++ b/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java @@ -0,0 +1,87 @@ +/* + * Copyright 2018-2021 Crown Copyright + * + * 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 uk.gov.gchq.palisade.util; + +import uk.gov.gchq.palisade.Context; +import uk.gov.gchq.palisade.rule.Rule; +import uk.gov.gchq.palisade.rule.Rules; +import uk.gov.gchq.palisade.user.User; + +import java.io.Serializable; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; + +/** + * Common utility methods. + */ +public final class RulesUtil { + private RulesUtil() { + } + + /** + * Applies a collection of rules to a record stream. + * + * @param records record stream + * @param user user the records are being processed for + * @param context the additional context + * @param rules rules collection + * @param record type + * @param recordsProcessed a counter for the number of records being processed + * @param recordsReturned a counter for the number of records being returned + * @return filtered stream + */ + public static Stream applyRulesToStream(final Stream records, final User user, final Context context, final Rules rules, final AtomicLong recordsProcessed, final AtomicLong recordsReturned) { + Objects.requireNonNull(records); + if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { + return records; + } + + return records + .peek(processed -> recordsProcessed.incrementAndGet()) + .map(record -> applyRulesToItem(record, user, context, rules)) + .filter(Objects::nonNull) + .peek(returned -> recordsReturned.incrementAndGet()); + } + + /** + * Applies a collection of rules to an item (record/resource). + * + * @param item resource or record to filter + * @param user user the record is being processed for + * @param context the additional context + * @param rules rules collection + * @param record type + * @return item with rules applied + */ + public static T applyRulesToItem(final T item, final User user, final Context context, final Rules rules) { + if (isNull(rules) || isNull(rules.getRules()) || rules.getRules().isEmpty()) { + return item; + } + T updateItem = item; + for (final Rule rule : rules.getRules().values()) { + updateItem = rule.apply(updateItem, user, context); + if (null == updateItem) { + break; + } + } + return updateItem; + } + +} diff --git a/src/main/java/uk/gov/gchq/palisade/util/StreamUtil.java b/src/main/java/uk/gov/gchq/palisade/util/StreamUtil.java deleted file mode 100644 index b54ff84e..00000000 --- a/src/main/java/uk/gov/gchq/palisade/util/StreamUtil.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Objects; - -/** - * Utility methods for opening {@link InputStream}s. - */ -public final class StreamUtil { - private StreamUtil() { - // Private constructor to prevent instantiation. - } - - /** - * Open the file found at the the specified path under the location of the given - * class. - * - * @param clazz the class location - * @param path the path in the class location - * @return an input stream representating the requested file - * @throws IllegalArgumentException if there was an error opening the stream - */ - public static InputStream openStream(final Class clazz, final String path) throws IllegalArgumentException { - Objects.requireNonNull(path, "Path is required"); - if (new File(path).exists()) { - try { - return Files.newInputStream(Paths.get(path)); - } catch (final IOException e) { - throw new IllegalArgumentException("Unable to load file: " + path, e); - } - } else { - final String checkedPath = formatPathForOpenStream(path); - final InputStream resourceAsStream = clazz.getResourceAsStream(checkedPath); - if (null == resourceAsStream) { - throw new IllegalArgumentException("Unable to load file: " + path); - } - return resourceAsStream; - } - } - - /** - * Format a path to ensure that it begins with a '/' character. - * - * @param path the path to format - * @return a correctly formatted path string - */ - public static String formatPathForOpenStream(final String path) { - if (null == path || path.isEmpty()) { - throw new IllegalArgumentException("path is required"); - } - if (path.startsWith("/")) { - return path; - } else { - return "/" + path; - } - } -} diff --git a/src/main/java/uk/gov/gchq/palisade/util/UriBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/UriBuilder.java index b00440f0..4de5e3fe 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/UriBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/UriBuilder.java @@ -27,17 +27,29 @@ */ public class UriBuilder { - private Optional baseUri = Optional.empty(); - + /** + * The starter method for the Authority Builder class. + */ public static class AuthorityBuilder { private Optional baseUri = Optional.empty(); + /** + * The starter method for the Authority Builder class. + * + * @param baseUri the uri of the resource to create + * @return the composed immutable object + */ public static IUserInfo create(final URI baseUri) { AuthorityBuilder builder = new AuthorityBuilder(); builder.baseUri = Optional.of(baseUri); return builder.build(); } + /** + * The starter method for the Builder class without passing in a URI + * + * @return the composed immutable object + */ public static IUserInfo create() { return new AuthorityBuilder().build(); } @@ -51,44 +63,104 @@ private IUserInfo build() { ); } + /** + * Adds the user info to the URI + */ public interface IUserInfo { + /** + * Adds the user info to the URI + * + * @param userInfo information about the user that is requesting access to the data. {@code [userinfo@]host[:port]} + * @return interface {@link IHost} for the next step in the build. + */ IHost withUserInfo(String userInfo); + /** + * By default, add no user information to the URI + * + * @return interface {@link IHost} for the next step in the build. + */ default IHost withoutUserInfo() { return withUserInfo(null); } } + /** + * Adds the host to the URI + */ public interface IHost { + /** + * Adds the host to the URI + * + * @param host the URI host {@code [userinfo@]host[:port]} + * @return interface {@link IPort} for the next step in the build. + */ IPort withHost(String host); + /** + * By default, add no host to the URI + * + * @return interface {@link IPort} for the next step in the build. + */ default IPort withoutHost() { return withHost(null); } } + /** + * Adds the port to the uri + */ public interface IPort { + /** + * Adds the port to the URI + * + * @param port the URI port {@code [userinfo@]host[:port]} + * @return the completed builder and URI object + */ String withPort(Integer port); + /** + * By default, add no port to the URI + * + * @return the completed builder and URI object. + */ default String withoutPort() { return withPort(null); } } - } + private Optional baseUri = Optional.empty(); + + /** + * The starter method for the URI Builder class. + * + * @param baseUri the uri of the resource to create + * @return the composed immutable object + */ public static IScheme create(final URI baseUri) { UriBuilder builder = new UriBuilder(); builder.baseUri = Optional.of(baseUri); return builder.build(); } + /** + * The starter method for the Builder class. + * + * @return the composed immutable object + */ public static IScheme create() { return new UriBuilder().build(); } + /** + * Starter method for the Builder class. This method is called to start the process of creating the + * URI class. + * + * @return interface {@link IScheme} for the next step in the build. + */ private IScheme build() { - return scheme -> authority -> path -> query -> fragment -> { + return scheme -> authority -> path -> query -> (String fragment) -> { String thisScheme = Optional.ofNullable(scheme).or(() -> baseUri.map(URI::getScheme)).orElseThrow(); String thisAuth = Optional.ofNullable(authority).or(() -> baseUri.map(URI::getAuthority)).orElse(null); String thisPath = Optional.ofNullable(path).or(() -> baseUri.map(URI::getPath)).orElseThrow(); @@ -108,41 +180,111 @@ private IScheme build() { }; } + /** + * Adds the scheme to the URI + */ public interface IScheme { + /** + * Adds the {@link URI#getScheme()} to the URI + * + * @param scheme the string scheme for the uri + * @return interface {@link IAuthority} for the next step in the build. + */ IAuthority withScheme(String scheme); + /** + * By default, create the uri without a scheme + * + * @return interface {@link IAuthority} for the next step in the build. + */ default IAuthority withoutScheme() { return withScheme(null); } } + /** + * Adds the authority to the URI + */ public interface IAuthority { + /** + * Adds the {@link URI#getAuthority()} to the URI + * + * @param authority the string authority for the uri + * @return interface {@link IPath} for the next step in the build. + */ IPath withAuthority(String authority); + /** + * By default, create a uri without the authority + * + * @return interface {@link IPath} for the next step in the build + */ default IPath withoutAuthority() { return withAuthority(null); } } + /** + * Adds the path to the URI + */ public interface IPath { + /** + * Adds the {@link URI#getPath()} to the URI + * + * @param path the string path for the uri + * @return interface {@link IQuery} for the next step in the build. + */ IQuery withPath(String path); + /** + * By default, create a uri without the path + * + * @return interface {@link IQuery} for the next step in the build + */ default IQuery withoutPath() { return withPath(null); } } + /** + * Adds the query to the URI + */ public interface IQuery { + /** + * Adds the {@link URI#getQuery()} to the URI + * + * @param query the string query for the uri + * @return interface {@link IFragment} for the next step in the build. + */ IFragment withQuery(String query); + /** + * By default, create a uri without the query + * + * @return interface {@link IFragment} for the next step in the build + */ default IFragment withoutQuery() { return withQuery(null); } } + /** + * Adds the fragment to the URI + */ public interface IFragment { + /** + * Adds the {@link URI#getFragment()} to the URI + * + * @param fragment the string fragment for the uri + * @return a now completed URI object + */ URI withFragment(String fragment); + /** + * By default, create a uri without a fragment + * + * @return a now completed URI object + */ default URI withoutFragment() { return withFragment(null); } diff --git a/src/test/java/uk/gov/gchq/palisade/ContextTest.java b/src/test/java/uk/gov/gchq/palisade/ContextTest.java deleted file mode 100644 index c300caa7..00000000 --- a/src/test/java/uk/gov/gchq/palisade/ContextTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; - -public class ContextTest { - - private Context testObject; - - @Before - public void setUp() { - testObject = new Context().purpose("purpose1"); - } - - @Test - public void shouldJsonSerialise() { - //when - final byte[] serialise = JSONSerialiser.serialise(testObject, true); - final Context deserialise = JSONSerialiser.deserialise(serialise, Context.class); - //then - Assert.assertEquals("testObject deserialised unsuccessfully: ", testObject, deserialise); - } - - @Test - public void shouldHistoricalJsonSerialise() { - //when - final byte[] serialise = JSONSerialiser.serialise(testObject, true); - - String serialised = new String(serialise); - Context deserialise = JSONSerialiser.deserialise( - "{\n" + - " \"contents\" : {\n" + - " \"purpose\" : \"purpose1\"\n" + - " },\n" + - " \"class\" : \"uk.gov.gchq.palisade.Context\"\n" + - "}", Context.class); - - //then - Assert.assertEquals(new String(serialise), testObject, deserialise); - } -} \ No newline at end of file diff --git a/src/test/java/uk/gov/gchq/palisade/UtilTest.java b/src/test/java/uk/gov/gchq/palisade/UtilTest.java deleted file mode 100644 index 54e442a4..00000000 --- a/src/test/java/uk/gov/gchq/palisade/UtilTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade; - -import org.junit.Test; - -import uk.gov.gchq.palisade.rule.Rules; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static uk.gov.gchq.palisade.Util.applyRulesToItem; - -public class UtilTest { - - @Test - public void shouldReturnResourceIfNoRules() { - //when - final String actual1 = applyRulesToItem("String", null, null, null); - final String actual2 = applyRulesToItem("String", null, null, new Rules<>()); - //then - assertEquals("Only 'String' should be returned if there are no rules", "String", actual1); - assertEquals("Only 'String' should be returned if there are no rules", "String", actual2); - } - - @Test - public void shouldUpdateRecord() { - //given - final Rules rules = new Rules().addRule("r1", (record, user, context) -> "fromRule"); - //when - final String actual1 = applyRulesToItem("String", null, null, rules); - assertEquals("'fromRule' should be returned as the record has been updated", "fromRule", actual1); - } - - @Test - public void shouldUpdateRecordFromAllRules() { - //given - final Rules rules = new Rules() - .addRule("r1", (record, user, context) -> "fromRule") - .addRule("r2", (record, user, context) -> record.concat("2ndRule")); - //when - final String actual1 = applyRulesToItem("String", null, null, rules); - //then - assertEquals("'fromRule2ndRule' should be returned as the record has been updated for all rules", "fromRule" + "2ndRule", actual1); - } - - @Test - public void shouldUpdateStreamOfRecordsFromAllRules() { - //given - final AtomicLong recordsProcessed = new AtomicLong(0); - final AtomicLong recordsReturned = new AtomicLong(0); - final Stream stream = Arrays.asList("one", "two").stream(); - final Rules rules = new Rules() - .addRule("r2", (record, user, context) -> record.concat("2ndRule")) - .addRule("r3", (record, user, context) -> record.concat("3rdRule")); - //when - final List result = Util.applyRulesToStream(stream, null, null, rules, recordsProcessed, recordsReturned).collect(Collectors.toList()); - //then - assertTrue("The stream of records and rules should be updated and show one2ndRule3rdRule", result.stream().anyMatch(s -> s.equals("one" + "2ndRule" + "3rdRule"))); - assertTrue("The stream of records and rules should be updated and show two2ndRule3rdRule", result.stream().anyMatch(s -> s.equals("two" + "2ndRule" + "3rdRule"))); - assertEquals("2 records should be processed", 2, recordsProcessed.get()); - assertEquals("2 records should be returned", 2, recordsReturned.get()); - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiserTest.java b/src/test/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiserTest.java deleted file mode 100644 index d4933f02..00000000 --- a/src/test/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiserTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.data.serialise; - -import org.apache.avro.Schema; -import org.apache.avro.file.DataFileStream; -import org.apache.avro.file.DataFileWriter; -import org.apache.avro.io.DatumReader; -import org.apache.avro.io.DatumWriter; -import org.apache.avro.specific.SpecificData; -import org.apache.avro.specific.SpecificDatumReader; -import org.apache.avro.specific.SpecificDatumWriter; -import org.junit.Test; - -import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; -import uk.gov.gchq.palisade.util.JsonAssert; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; - -public class AvroSerialiserTest { - - public static final int INPUT_SIZE = 100; - public static final Integer[] INPUT = IntStream.range(0, INPUT_SIZE).boxed().toArray((a) -> new Integer[INPUT_SIZE]); - - @Test - public void shouldConsistentlyPass() throws IOException { - for (int i = 0; i < 10000; i++) { - testPrimitiveSerialiseAndDeserialise(); - } - } - - @Test - public void testPrimitiveSerialise() throws IOException { - // Given - final AvroSerialiser serialiser = new AvroSerialiser<>(Integer.class); - - // When - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - serialiser.serialise(Stream.of(INPUT), outputStream); - - // Then - final DatumReader datumReader = new SpecificDatumReader<>(Integer.class); - final DataFileStream in = new DataFileStream<>(new ByteArrayInputStream(outputStream.toByteArray()), datumReader); - final List deserialised = new ArrayList<>(); - in.forEachRemaining(deserialised::add); - in.close(); - assertEquals(INPUT_SIZE + " records should be serialised", Arrays.asList(INPUT), deserialised); - } - - @Test - public void testPrimitiveDeserialise() throws IOException { - // Given - final AvroSerialiser serialiser = new AvroSerialiser<>(Integer.class); - - final Schema schema = SpecificData.get().getSchema(Integer.class); - final DatumWriter datumWriter = new SpecificDatumWriter<>(schema); - final DataFileWriter dataFileWriter = new DataFileWriter<>(datumWriter); - - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - dataFileWriter.create(schema, outputStream); - Stream.of(INPUT).forEach(item -> { - try { - dataFileWriter.append(item); - } catch (final IOException e) { - throw new RuntimeException(e); - } - }); - dataFileWriter.flush(); - dataFileWriter.close(); - final InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - - // When - final Stream deserialised = serialiser.deserialise(inputStream); - - // Then - assertEquals(INPUT_SIZE + " records should be serialised", Arrays.asList(INPUT), deserialised.collect(Collectors.toList())); - } - - @Test - public void testPrimitiveSerialiseAndDeserialise() throws IOException { - // Given - final AvroSerialiser serialiser = new AvroSerialiser<>(Integer.class); - - // When - Stream stream = Stream.of(INPUT); - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - serialiser.serialise(stream, outputStream); - final Stream deserialised = serialiser.deserialise(new ByteArrayInputStream(outputStream.toByteArray())); - - // Then - assertEquals(INPUT_SIZE + " records should be serialised and deserialised", Arrays.asList(INPUT), deserialised.collect(Collectors.toList())); - } - - @Test - public void shouldSerialiseAndDeserialiseWithClass() throws IOException { - // Given - final AvroSerialiser serialiser = new AvroSerialiser<>(TestObj.class); - - final List input = Arrays.asList( - new TestObj("str1A", 1, null), - new TestObj("str1B", 2, "str2B"), - TestObj.newBuilder() - .setFieldStr1("str1C") - .setFieldInt(null) - .setFieldStr2("str2C") - .build() - ); - - // When - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - serialiser.serialise(input.stream(), outputStream); - final Stream deserialised = serialiser.deserialise(new ByteArrayInputStream(outputStream.toByteArray())); - - // Then - assertEquals("The serialised and deserialised TestObj list should match the input list", input, deserialised.collect(Collectors.toList())); - } - - @Test - public void shouldJsonSerialiseAndDeserialise() { - // Given - final AvroSerialiser serialiser = new AvroSerialiser<>(Integer.class); - - // When - final byte[] json = JSONSerialiser.serialise(serialiser, true); - final Serialiser deserialised = JSONSerialiser.deserialise(json, Serialiser.class); - - // Then - JsonAssert.assertEquals(String.format("{%n" + - " \"domainClass\" : \"java.lang.Integer\",%n" + - " \"class\" : \"uk.gov.gchq.palisade.data.serialise.AvroSerialiser\"%n" + - "}").getBytes(), json); - - assertEquals("The deserialised class should be AvroSerialiser", AvroSerialiser.class, deserialised.getClass()); - assertEquals("The serialiser domain class should equal the deserialised domain class", serialiser.getDomainClass(), ((AvroSerialiser) deserialised).getDomainClass()); - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiserTest.java b/src/test/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiserTest.java deleted file mode 100644 index fa92cbe6..00000000 --- a/src/test/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiserTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.data.serialise; - -import org.apache.commons.io.IOUtils; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; - -public class SimpleStringSerialiserTest { - @Test - public void shouldDeserialise() { - // Given - final SimpleStringSerialiser serialiser = new SimpleStringSerialiser(); - final byte[] input = "line1\nline2".getBytes(); - - // When - final Stream result = serialiser.deserialise(new ByteArrayInputStream(input)); - - // Then - assertEquals("the deserialised list should be the same as the input byte stream", Arrays.asList("line1", "line2"), result.collect(Collectors.toList())); - } - - @Test - public void shouldSerialise() throws IOException { - // Given - final SimpleStringSerialiser serialiser = new SimpleStringSerialiser(); - final Stream input = Stream.of("line1", "line2"); - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - // When - serialiser.serialise(input, bos); - InputStream inputBytes = new ByteArrayInputStream(bos.toByteArray()); - - // Then - assertEquals("The serialised list should be the same as the input byte stream", Arrays.asList("line1", "line2"), IOUtils.readLines(inputBytes, StandardCharsets.UTF_8)); - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/data/serialise/TestObj.java b/src/test/java/uk/gov/gchq/palisade/data/serialise/TestObj.java deleted file mode 100644 index e9bf4073..00000000 --- a/src/test/java/uk/gov/gchq/palisade/data/serialise/TestObj.java +++ /dev/null @@ -1,431 +0,0 @@ -/** - * Autogenerated by Avro - * DO NOT EDIT DIRECTLY - */ -package uk.gov.gchq.palisade.data.serialise; - -import org.apache.avro.message.BinaryMessageDecoder; -import org.apache.avro.message.BinaryMessageEncoder; -import org.apache.avro.message.SchemaStore; -import org.apache.avro.specific.SpecificData; - -@SuppressWarnings("all") -@org.apache.avro.specific.AvroGenerated -public class TestObj extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { - private static final long serialVersionUID = 7843905661986499514L; - public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"TestObj\",\"namespace\":\"uk.gov.gchq.palisade.data.service.impl.serialiser\",\"fields\":[{\"name\":\"fieldStr1\",\"type\":\"string\"},{\"name\":\"fieldInt\",\"type\":[\"int\",\"null\"]},{\"name\":\"fieldStr2\",\"type\":[\"string\",\"null\"]}]}"); - - public static org.apache.avro.Schema getClassSchema() { - return SCHEMA$; - } - - private static SpecificData MODEL$ = new SpecificData(); - - private static final BinaryMessageEncoder ENCODER = - new BinaryMessageEncoder(MODEL$, SCHEMA$); - - private static final BinaryMessageDecoder DECODER = - new BinaryMessageDecoder(MODEL$, SCHEMA$); - - /** - * Return the BinaryMessageDecoder instance used by this class. - */ - public static BinaryMessageDecoder getDecoder() { - return DECODER; - } - - /** - * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. - * - * @param resolver a {@link SchemaStore} used to find schemas by fingerprint - */ - public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { - return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); - } - - /** - * Serializes this TestObj to a ByteBuffer. - */ - public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { - return ENCODER.encode(this); - } - - /** - * Deserializes a TestObj from a ByteBuffer. - */ - public static TestObj fromByteBuffer( - java.nio.ByteBuffer b) throws java.io.IOException { - return DECODER.decode(b); - } - - @Deprecated - public java.lang.CharSequence fieldStr1; - @Deprecated - public java.lang.Integer fieldInt; - @Deprecated - public java.lang.CharSequence fieldStr2; - - /** - * Default constructor. Note that this does not initialize fields - * to their default values from the schema. If that is desired then - * one should use newBuilder(). - */ - public TestObj() { - } - - /** - * All-args constructor. - * - * @param fieldStr1 The new value for fieldStr1 - * @param fieldInt The new value for fieldInt - * @param fieldStr2 The new value for fieldStr2 - */ - public TestObj(java.lang.CharSequence fieldStr1, java.lang.Integer fieldInt, java.lang.CharSequence fieldStr2) { - this.fieldStr1 = fieldStr1; - this.fieldInt = fieldInt; - this.fieldStr2 = fieldStr2; - } - - public org.apache.avro.Schema getSchema() { - return SCHEMA$; - } - - // Used by DatumWriter. Applications should not call. - public java.lang.Object get(int field$) { - switch (field$) { - case 0: - return fieldStr1; - case 1: - return fieldInt; - case 2: - return fieldStr2; - default: - throw new org.apache.avro.AvroRuntimeException("Bad index"); - } - } - - // Used by DatumReader. Applications should not call. - @SuppressWarnings(value = "unchecked") - public void put(int field$, java.lang.Object value$) { - switch (field$) { - case 0: - fieldStr1 = (java.lang.CharSequence) value$; - break; - case 1: - fieldInt = (java.lang.Integer) value$; - break; - case 2: - fieldStr2 = (java.lang.CharSequence) value$; - break; - default: - throw new org.apache.avro.AvroRuntimeException("Bad index"); - } - } - - /** - * Gets the value of the 'fieldStr1' field. - * - * @return The value of the 'fieldStr1' field. - */ - public java.lang.CharSequence getFieldStr1() { - return fieldStr1; - } - - /** - * Sets the value of the 'fieldStr1' field. - * - * @param value the value to set. - */ - public void setFieldStr1(java.lang.CharSequence value) { - this.fieldStr1 = value; - } - - /** - * Gets the value of the 'fieldInt' field. - * - * @return The value of the 'fieldInt' field. - */ - public java.lang.Integer getFieldInt() { - return fieldInt; - } - - /** - * Sets the value of the 'fieldInt' field. - * - * @param value the value to set. - */ - public void setFieldInt(java.lang.Integer value) { - this.fieldInt = value; - } - - /** - * Gets the value of the 'fieldStr2' field. - * - * @return The value of the 'fieldStr2' field. - */ - public java.lang.CharSequence getFieldStr2() { - return fieldStr2; - } - - /** - * Sets the value of the 'fieldStr2' field. - * - * @param value the value to set. - */ - public void setFieldStr2(java.lang.CharSequence value) { - this.fieldStr2 = value; - } - - /** - * Creates a new TestObj RecordBuilder. - * - * @return A new TestObj RecordBuilder - */ - public static TestObj.Builder newBuilder() { - return new TestObj.Builder(); - } - - /** - * Creates a new TestObj RecordBuilder by copying an existing Builder. - * - * @param other The existing builder to copy. - * @return A new TestObj RecordBuilder - */ - public static TestObj.Builder newBuilder(TestObj.Builder other) { - return new TestObj.Builder(other); - } - - /** - * Creates a new TestObj RecordBuilder by copying an existing TestObj instance. - * - * @param other The existing instance to copy. - * @return A new TestObj RecordBuilder - */ - public static TestObj.Builder newBuilder(TestObj other) { - return new TestObj.Builder(other); - } - - /** - * RecordBuilder for TestObj instances. - */ - public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase - implements org.apache.avro.data.RecordBuilder { - - private java.lang.CharSequence fieldStr1; - private java.lang.Integer fieldInt; - private java.lang.CharSequence fieldStr2; - - /** - * Creates a new Builder - */ - private Builder() { - super(SCHEMA$); - } - - /** - * Creates a Builder by copying an existing Builder. - * - * @param other The existing Builder to copy. - */ - private Builder(TestObj.Builder other) { - super(other); - if (isValidValue(fields()[0], other.fieldStr1)) { - this.fieldStr1 = data().deepCopy(fields()[0].schema(), other.fieldStr1); - fieldSetFlags()[0] = true; - } - if (isValidValue(fields()[1], other.fieldInt)) { - this.fieldInt = data().deepCopy(fields()[1].schema(), other.fieldInt); - fieldSetFlags()[1] = true; - } - if (isValidValue(fields()[2], other.fieldStr2)) { - this.fieldStr2 = data().deepCopy(fields()[2].schema(), other.fieldStr2); - fieldSetFlags()[2] = true; - } - } - - /** - * Creates a Builder by copying an existing TestObj instance - * - * @param other The existing instance to copy. - */ - private Builder(TestObj other) { - super(SCHEMA$); - if (isValidValue(fields()[0], other.fieldStr1)) { - this.fieldStr1 = data().deepCopy(fields()[0].schema(), other.fieldStr1); - fieldSetFlags()[0] = true; - } - if (isValidValue(fields()[1], other.fieldInt)) { - this.fieldInt = data().deepCopy(fields()[1].schema(), other.fieldInt); - fieldSetFlags()[1] = true; - } - if (isValidValue(fields()[2], other.fieldStr2)) { - this.fieldStr2 = data().deepCopy(fields()[2].schema(), other.fieldStr2); - fieldSetFlags()[2] = true; - } - } - - /** - * Gets the value of the 'fieldStr1' field. - * - * @return The value. - */ - public java.lang.CharSequence getFieldStr1() { - return fieldStr1; - } - - /** - * Sets the value of the 'fieldStr1' field. - * - * @param value The value of 'fieldStr1'. - * @return This builder. - */ - public TestObj.Builder setFieldStr1(java.lang.CharSequence value) { - validate(fields()[0], value); - this.fieldStr1 = value; - fieldSetFlags()[0] = true; - return this; - } - - /** - * Checks whether the 'fieldStr1' field has been set. - * - * @return True if the 'fieldStr1' field has been set, false otherwise. - */ - public boolean hasFieldStr1() { - return fieldSetFlags()[0]; - } - - - /** - * Clears the value of the 'fieldStr1' field. - * - * @return This builder. - */ - public TestObj.Builder clearFieldStr1() { - fieldStr1 = null; - fieldSetFlags()[0] = false; - return this; - } - - /** - * Gets the value of the 'fieldInt' field. - * - * @return The value. - */ - public java.lang.Integer getFieldInt() { - return fieldInt; - } - - /** - * Sets the value of the 'fieldInt' field. - * - * @param value The value of 'fieldInt'. - * @return This builder. - */ - public TestObj.Builder setFieldInt(java.lang.Integer value) { - validate(fields()[1], value); - this.fieldInt = value; - fieldSetFlags()[1] = true; - return this; - } - - /** - * Checks whether the 'fieldInt' field has been set. - * - * @return True if the 'fieldInt' field has been set, false otherwise. - */ - public boolean hasFieldInt() { - return fieldSetFlags()[1]; - } - - - /** - * Clears the value of the 'fieldInt' field. - * - * @return This builder. - */ - public TestObj.Builder clearFieldInt() { - fieldInt = null; - fieldSetFlags()[1] = false; - return this; - } - - /** - * Gets the value of the 'fieldStr2' field. - * - * @return The value. - */ - public java.lang.CharSequence getFieldStr2() { - return fieldStr2; - } - - /** - * Sets the value of the 'fieldStr2' field. - * - * @param value The value of 'fieldStr2'. - * @return This builder. - */ - public TestObj.Builder setFieldStr2(java.lang.CharSequence value) { - validate(fields()[2], value); - this.fieldStr2 = value; - fieldSetFlags()[2] = true; - return this; - } - - /** - * Checks whether the 'fieldStr2' field has been set. - * - * @return True if the 'fieldStr2' field has been set, false otherwise. - */ - public boolean hasFieldStr2() { - return fieldSetFlags()[2]; - } - - - /** - * Clears the value of the 'fieldStr2' field. - * - * @return This builder. - */ - public TestObj.Builder clearFieldStr2() { - fieldStr2 = null; - fieldSetFlags()[2] = false; - return this; - } - - @Override - @SuppressWarnings("unchecked") - public TestObj build() { - try { - TestObj record = new TestObj(); - record.fieldStr1 = fieldSetFlags()[0] ? this.fieldStr1 : (java.lang.CharSequence) defaultValue(fields()[0]); - record.fieldInt = fieldSetFlags()[1] ? this.fieldInt : (java.lang.Integer) defaultValue(fields()[1]); - record.fieldStr2 = fieldSetFlags()[2] ? this.fieldStr2 : (java.lang.CharSequence) defaultValue(fields()[2]); - return record; - } catch (java.lang.Exception e) { - throw new org.apache.avro.AvroRuntimeException(e); - } - } - } - - @SuppressWarnings("unchecked") - private static final org.apache.avro.io.DatumWriter - WRITER$ = (org.apache.avro.io.DatumWriter) MODEL$.createDatumWriter(SCHEMA$); - - @Override - public void writeExternal(java.io.ObjectOutput out) - throws java.io.IOException { - WRITER$.write(this, SpecificData.getEncoder(out)); - } - - @SuppressWarnings("unchecked") - private static final org.apache.avro.io.DatumReader - READER$ = (org.apache.avro.io.DatumReader) MODEL$.createDatumReader(SCHEMA$); - - @Override - public void readExternal(java.io.ObjectInput in) - throws java.io.IOException { - READER$.read(this, SpecificData.getDecoder(in)); - } - -} diff --git a/src/test/java/uk/gov/gchq/palisade/data/service/impl/serialiser/TestObj.java b/src/test/java/uk/gov/gchq/palisade/data/service/impl/serialiser/TestObj.java deleted file mode 100644 index 8dba4466..00000000 --- a/src/test/java/uk/gov/gchq/palisade/data/service/impl/serialiser/TestObj.java +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Autogenerated by Avro - *

- * DO NOT EDIT DIRECTLY - */ - -package uk.gov.gchq.palisade.data.service.impl.serialiser; - -import org.apache.avro.message.BinaryMessageDecoder; -import org.apache.avro.message.BinaryMessageEncoder; -import org.apache.avro.message.SchemaStore; -import org.apache.avro.specific.SpecificData; - -@SuppressWarnings("all") -@org.apache.avro.specific.AvroGenerated -public class TestObj extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { - private static final long serialVersionUID = 7843905661986499514L; - public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"TestObj\",\"namespace\":\"uk.gov.gchq.palisade.data.service.impl.serialiser\",\"fields\":[{\"name\":\"fieldStr1\",\"type\":\"string\"},{\"name\":\"fieldInt\",\"type\":[\"int\",\"null\"]},{\"name\":\"fieldStr2\",\"type\":[\"string\",\"null\"]}]}"); - - public static org.apache.avro.Schema getClassSchema() { - return SCHEMA$; - } - - private static SpecificData MODEL$ = new SpecificData(); - - private static final BinaryMessageEncoder ENCODER = - new BinaryMessageEncoder(MODEL$, SCHEMA$); - - private static final BinaryMessageDecoder DECODER = - new BinaryMessageDecoder(MODEL$, SCHEMA$); - - /** - * Return the BinaryMessageDecoder instance used by this class. - */ - public static BinaryMessageDecoder getDecoder() { - return DECODER; - } - - /** - * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. - * - * @param resolver a {@link SchemaStore} used to find schemas by fingerprint - */ - public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { - return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); - } - - /** - * Serializes this TestObj to a ByteBuffer. - */ - public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { - return ENCODER.encode(this); - } - - /** - * Deserializes a TestObj from a ByteBuffer. - */ - public static TestObj fromByteBuffer( - java.nio.ByteBuffer b) throws java.io.IOException { - return DECODER.decode(b); - } - - @Deprecated - public java.lang.CharSequence fieldStr1; - @Deprecated - public java.lang.Integer fieldInt; - @Deprecated - public java.lang.CharSequence fieldStr2; - - /** - * Default constructor. Note that this does not initialize fields - * to their default values from the schema. If that is desired then - * one should use newBuilder(). - */ - public TestObj() { - } - - /** - * All-args constructor. - * - * @param fieldStr1 The new value for fieldStr1 - * @param fieldInt The new value for fieldInt - * @param fieldStr2 The new value for fieldStr2 - */ - public TestObj(java.lang.CharSequence fieldStr1, java.lang.Integer fieldInt, java.lang.CharSequence fieldStr2) { - this.fieldStr1 = fieldStr1; - this.fieldInt = fieldInt; - this.fieldStr2 = fieldStr2; - } - - public org.apache.avro.Schema getSchema() { - return SCHEMA$; - } - - // Used by DatumWriter. Applications should not call. - public java.lang.Object get(int field$) { - switch (field$) { - case 0: - return fieldStr1; - case 1: - return fieldInt; - case 2: - return fieldStr2; - default: - throw new org.apache.avro.AvroRuntimeException("Bad index"); - } - } - - // Used by DatumReader. Applications should not call. - @SuppressWarnings(value = "unchecked") - public void put(int field$, java.lang.Object value$) { - switch (field$) { - case 0: - fieldStr1 = (java.lang.CharSequence) value$; - break; - case 1: - fieldInt = (java.lang.Integer) value$; - break; - case 2: - fieldStr2 = (java.lang.CharSequence) value$; - break; - default: - throw new org.apache.avro.AvroRuntimeException("Bad index"); - } - } - - /** - * Gets the value of the 'fieldStr1' field. - * - * @return The value of the 'fieldStr1' field. - */ - public java.lang.CharSequence getFieldStr1() { - return fieldStr1; - } - - /** - * Sets the value of the 'fieldStr1' field. - * - * @param value the value to set. - */ - public void setFieldStr1(java.lang.CharSequence value) { - this.fieldStr1 = value; - } - - /** - * Gets the value of the 'fieldInt' field. - * - * @return The value of the 'fieldInt' field. - */ - public java.lang.Integer getFieldInt() { - return fieldInt; - } - - /** - * Sets the value of the 'fieldInt' field. - * - * @param value the value to set. - */ - public void setFieldInt(java.lang.Integer value) { - this.fieldInt = value; - } - - /** - * Gets the value of the 'fieldStr2' field. - * - * @return The value of the 'fieldStr2' field. - */ - public java.lang.CharSequence getFieldStr2() { - return fieldStr2; - } - - /** - * Sets the value of the 'fieldStr2' field. - * - * @param value the value to set. - */ - public void setFieldStr2(java.lang.CharSequence value) { - this.fieldStr2 = value; - } - - /** - * Creates a new TestObj RecordBuilder. - * - * @return A new TestObj RecordBuilder - */ - public static uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder newBuilder() { - return new uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder(); - } - - /** - * Creates a new TestObj RecordBuilder by copying an existing Builder. - * - * @param other The existing builder to copy. - * @return A new TestObj RecordBuilder - */ - public static uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder newBuilder(uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder other) { - return new uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder(other); - } - - /** - * Creates a new TestObj RecordBuilder by copying an existing TestObj instance. - * - * @param other The existing instance to copy. - * @return A new TestObj RecordBuilder - */ - public static uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder newBuilder(uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj other) { - return new uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder(other); - } - - /** - * RecordBuilder for TestObj instances. - */ - public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase - implements org.apache.avro.data.RecordBuilder { - - private java.lang.CharSequence fieldStr1; - private java.lang.Integer fieldInt; - private java.lang.CharSequence fieldStr2; - - /** - * Creates a new Builder - */ - private Builder() { - super(SCHEMA$); - } - - /** - * Creates a Builder by copying an existing Builder. - * - * @param other The existing Builder to copy. - */ - private Builder(uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder other) { - super(other); - if (isValidValue(fields()[0], other.fieldStr1)) { - this.fieldStr1 = data().deepCopy(fields()[0].schema(), other.fieldStr1); - fieldSetFlags()[0] = true; - } - if (isValidValue(fields()[1], other.fieldInt)) { - this.fieldInt = data().deepCopy(fields()[1].schema(), other.fieldInt); - fieldSetFlags()[1] = true; - } - if (isValidValue(fields()[2], other.fieldStr2)) { - this.fieldStr2 = data().deepCopy(fields()[2].schema(), other.fieldStr2); - fieldSetFlags()[2] = true; - } - } - - /** - * Creates a Builder by copying an existing TestObj instance - * - * @param other The existing instance to copy. - */ - private Builder(uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj other) { - super(SCHEMA$); - if (isValidValue(fields()[0], other.fieldStr1)) { - this.fieldStr1 = data().deepCopy(fields()[0].schema(), other.fieldStr1); - fieldSetFlags()[0] = true; - } - if (isValidValue(fields()[1], other.fieldInt)) { - this.fieldInt = data().deepCopy(fields()[1].schema(), other.fieldInt); - fieldSetFlags()[1] = true; - } - if (isValidValue(fields()[2], other.fieldStr2)) { - this.fieldStr2 = data().deepCopy(fields()[2].schema(), other.fieldStr2); - fieldSetFlags()[2] = true; - } - } - - /** - * Gets the value of the 'fieldStr1' field. - * - * @return The value. - */ - public java.lang.CharSequence getFieldStr1() { - return fieldStr1; - } - - /** - * Sets the value of the 'fieldStr1' field. - * - * @param value The value of 'fieldStr1'. - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder setFieldStr1(java.lang.CharSequence value) { - validate(fields()[0], value); - this.fieldStr1 = value; - fieldSetFlags()[0] = true; - return this; - } - - /** - * Checks whether the 'fieldStr1' field has been set. - * - * @return True if the 'fieldStr1' field has been set, false otherwise. - */ - public boolean hasFieldStr1() { - return fieldSetFlags()[0]; - } - - - /** - * Clears the value of the 'fieldStr1' field. - * - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder clearFieldStr1() { - fieldStr1 = null; - fieldSetFlags()[0] = false; - return this; - } - - /** - * Gets the value of the 'fieldInt' field. - * - * @return The value. - */ - public java.lang.Integer getFieldInt() { - return fieldInt; - } - - /** - * Sets the value of the 'fieldInt' field. - * - * @param value The value of 'fieldInt'. - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder setFieldInt(java.lang.Integer value) { - validate(fields()[1], value); - this.fieldInt = value; - fieldSetFlags()[1] = true; - return this; - } - - /** - * Checks whether the 'fieldInt' field has been set. - * - * @return True if the 'fieldInt' field has been set, false otherwise. - */ - public boolean hasFieldInt() { - return fieldSetFlags()[1]; - } - - - /** - * Clears the value of the 'fieldInt' field. - * - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder clearFieldInt() { - fieldInt = null; - fieldSetFlags()[1] = false; - return this; - } - - /** - * Gets the value of the 'fieldStr2' field. - * - * @return The value. - */ - public java.lang.CharSequence getFieldStr2() { - return fieldStr2; - } - - /** - * Sets the value of the 'fieldStr2' field. - * - * @param value The value of 'fieldStr2'. - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder setFieldStr2(java.lang.CharSequence value) { - validate(fields()[2], value); - this.fieldStr2 = value; - fieldSetFlags()[2] = true; - return this; - } - - /** - * Checks whether the 'fieldStr2' field has been set. - * - * @return True if the 'fieldStr2' field has been set, false otherwise. - */ - public boolean hasFieldStr2() { - return fieldSetFlags()[2]; - } - - - /** - * Clears the value of the 'fieldStr2' field. - * - * @return This builder. - */ - public uk.gov.gchq.palisade.data.service.impl.serialiser.TestObj.Builder clearFieldStr2() { - fieldStr2 = null; - fieldSetFlags()[2] = false; - return this; - } - - @Override - @SuppressWarnings("unchecked") - public TestObj build() { - try { - TestObj record = new TestObj(); - record.fieldStr1 = fieldSetFlags()[0] ? this.fieldStr1 : (java.lang.CharSequence) defaultValue(fields()[0]); - record.fieldInt = fieldSetFlags()[1] ? this.fieldInt : (java.lang.Integer) defaultValue(fields()[1]); - record.fieldStr2 = fieldSetFlags()[2] ? this.fieldStr2 : (java.lang.CharSequence) defaultValue(fields()[2]); - return record; - } catch (java.lang.Exception e) { - throw new org.apache.avro.AvroRuntimeException(e); - } - } - } - - @SuppressWarnings("unchecked") - private static final org.apache.avro.io.DatumWriter - WRITER$ = (org.apache.avro.io.DatumWriter) MODEL$.createDatumWriter(SCHEMA$); - - @Override - public void writeExternal(java.io.ObjectOutput out) - throws java.io.IOException { - WRITER$.write(this, SpecificData.getEncoder(out)); - } - - @SuppressWarnings("unchecked") - private static final org.apache.avro.io.DatumReader - READER$ = (org.apache.avro.io.DatumReader) MODEL$.createDatumReader(SCHEMA$); - - @Override - public void readExternal(java.io.ObjectInput in) - throws java.io.IOException { - READER$.read(this, SpecificData.getDecoder(in)); - } - -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java b/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java deleted file mode 100644 index dad9cae8..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/HasSensitiveAuthRule.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.rule.Rule; - -import java.io.Serializable; - -public class HasSensitiveAuthRule implements Serializable, Rule { - @Override - public T apply(final T record, final User user, final Context context) { - if (user.getAuths().contains("Sensitive")) { - return record; - } else { - return null; - } - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java b/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java deleted file mode 100644 index 34b2dcef..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/HasTestingPurpose.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.rule.Rule; - -import java.io.Serializable; - -public class HasTestingPurpose implements Serializable, Rule { - @Override - public T apply(final T record, final User user, final Context context) { - if (context.getPurpose().equalsIgnoreCase("testing")) { - return record; - } else { - return null; - } - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java b/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java deleted file mode 100644 index d2d05eb3..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/IsTextResourceRule.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.rule.Rule; - -import java.io.Serializable; - -public class IsTextResourceRule implements Serializable, Rule { - @Override - public LeafResource apply(final LeafResource record, final User user, final Context context) { - if (record.getSerialisedFormat().equalsIgnoreCase("txt")) { - return record; - } - return null; - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java b/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java deleted file mode 100644 index 569a6a1b..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/PassThroughRule.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.rule.Rule; - -import java.io.Serializable; - -public class PassThroughRule implements Serializable, Rule { - @Override - public T apply(final T record, final User user, final Context context) { - return record; - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/RulesTest.java b/src/test/java/uk/gov/gchq/palisade/policy/RulesTest.java deleted file mode 100644 index 6ca75ea5..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/RulesTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import org.junit.Before; -import org.junit.Test; - -import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; -import uk.gov.gchq.palisade.rule.Rules; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -public class RulesTest { - - private Rules rules; - private byte[] json; - - @Before - public void setUp() { - rules = new Rules() - .message("Age off and visibility filtering") - .addRule("ageOffRule", new TestRule() - ); - json = JSONSerialiser.serialise(rules, true); - } - - @Test - public void shouldNotEquals() { - //given - final Rules one = new Rules<>(); - one.addRule("one", new TestRule()); - - final Rules two = new Rules<>(); - two.addRule("two", new TestRule()); - - //then - assertNotEquals("The 2 different rules should not match", one, two); - } - - @Test - public void shouldEquals() { - final Rules one1 = new Rules<>(); - one1.addRule("one", new TestRule()); - - final Rules one2 = new Rules<>(); - one2.addRule("one", new TestRule()); - - assertEquals("The two rules should match", one1, one2); - } - - @Test - public void shouldSerialiseToEqualObject() { - Rules deserialise = JSONSerialiser.deserialise(json, Rules.class); - final String thisSerialise = new String(JSONSerialiser.serialise(this.rules, true)); - final String thatSerialise = new String(JSONSerialiser.serialise(deserialise, true)); - - assertEquals("Using the deserliased object in the JSONSerialiser should mean that the two strings are the same", thisSerialise, thatSerialise); - assertEquals("The rules object that has been serialised and deserialised should match the original object", rules, deserialise); - } - - @Test - public void shouldSerialiseTo() { - final String text = String.format("{%n" + - " \"message\" : \"Age off and visibility filtering\",%n" + - " \"rules\" : {%n" + - " \"ageOffRule\" : {%n" + - " \"class\" : \"uk.gov.gchq.palisade.policy.TestRule\"%n" + - " }%n" + - " }%n" + - "}"); - - assertEquals("The String json rule object should match the json serialised rule object", text, new String(json)); - } - - @Test - public void shouldDeserialiseText() { - final String text = String.format("{%n" + - " \"message\" : \"Age off and visibility filtering\",%n" + - " \"rules\" : {%n" + - " \"ageOffRule\" : {%n" + - " \"class\" : \"uk.gov.gchq.palisade.policy.TestRule\"%n" + - " }%n" + - " }%n" + - "}"); - - final Rules deserialise = JSONSerialiser.deserialise(text, Rules.class); - assertEquals("When the String json object has been deserialised it should match the original rule object", rules, deserialise); - - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/TestRule.java b/src/test/java/uk/gov/gchq/palisade/policy/TestRule.java deleted file mode 100644 index fbcb757d..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/TestRule.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import uk.gov.gchq.palisade.Context; -import uk.gov.gchq.palisade.User; -import uk.gov.gchq.palisade.rule.Rule; - -public class TestRule implements Rule { - @Override - public String apply(final String item, final User user, final Context context) { - return item; - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java b/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java deleted file mode 100644 index 15ab3674..00000000 --- a/src/test/java/uk/gov/gchq/palisade/policy/WrappedRuleTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.policy; - -import org.junit.Assert; -import org.junit.Test; - -import uk.gov.gchq.palisade.rule.SerializableUnaryOperator; -import uk.gov.gchq.palisade.rule.WrappedRule; - -public class WrappedRuleTest { - - @Test - public void shouldConstruct1ArgumentWithNoErrors() { - // Given - WrappedRule rule1 = new WrappedRule<>(new TestRule(), null, null); - WrappedRule rule2 = new WrappedRule<>(null, o -> o, null); - WrappedRule rule3 = new WrappedRule<>(null, null, o -> true); - - // Then - Assert.assertNotNull("rule1 rule should not be null", rule1.getRule()); - Assert.assertNotNull("rule2 function should not be null", rule2.getFunction()); - Assert.assertNotNull("rule3 predicate should not be null", rule3.getPredicate()); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotConstruct0Arguments() { - //When - new WrappedRule<>(null, null, null); - //Then it should throw an IllegalArgumentException - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotConstructNullPredicate() { - //When - new WrappedRule<>(new TestRule(), o -> o, null); - // Then it should throw an IllegalArgumentException - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotConstructNullFunction() { - //When - new WrappedRule<>(new TestRule(), null, o -> true); - //Then it should throw an IllegalArgumentException - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotConstructNullRule() { - //When - new WrappedRule<>(null, (SerializableUnaryOperator) String::toString, o -> true); - //Then it should throw an IllegalArgumentException - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotConstruct3Arguments() { - //When - new WrappedRule<>(new TestRule(), o -> o, o -> true); - //Then it should throw an IllegalArgumentException - } - -} diff --git a/src/test/java/uk/gov/gchq/palisade/resource/StubResource.java b/src/test/java/uk/gov/gchq/palisade/resource/StubResource.java deleted file mode 100644 index c9166bef..00000000 --- a/src/test/java/uk/gov/gchq/palisade/resource/StubResource.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.resource; - -import uk.gov.gchq.palisade.resource.impl.SystemResource; -import uk.gov.gchq.palisade.service.ConnectionDetail; - -import java.util.Comparator; - -public class StubResource extends AbstractLeafResource { - - private static final SystemResource PARENT = new SystemResource().id("file"); - - public StubResource() { - - } - - public StubResource(final String type, final String id, final String format, final ConnectionDetail connectionDetail) { - id(id); - type(type); - serialisedFormat(format); - connectionDetail(connectionDetail); - parent(PARENT); - } - - private static Comparator comp = Comparator.comparing(StubResource::getSerialisedFormat).thenComparing(StubResource::getType).thenComparing(StubResource::getId); - - /** - * {@inheritDoc} - * Implemented to allow this class to be used in TreeMaps in tests. - */ - @Override - public int compareTo(final Resource o) { - return comp.compare(this, (StubResource) o); - } -} - diff --git a/src/test/java/uk/gov/gchq/palisade/util/JsonAssert.java b/src/test/java/uk/gov/gchq/palisade/util/JsonAssert.java deleted file mode 100644 index c06f8df5..00000000 --- a/src/test/java/uk/gov/gchq/palisade/util/JsonAssert.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Assert; - -import uk.gov.gchq.palisade.jsonserialisation.JSONSerialiser; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertNotEquals; - -public class JsonAssert { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - public static void assertEquals(final Object expected, final Object actual) { - final byte[] expectedJson; - if (expected instanceof byte[]) { - expectedJson = (byte[]) expected; - } else if (expected instanceof String) { - expectedJson = ((String) expected).getBytes(); - } else { - expectedJson = JSONSerialiser.serialise(expected); - } - - final byte[] actualJson; - if (actual instanceof byte[]) { - actualJson = (byte[]) actual; - } else if (actual instanceof String) { - actualJson = ((String) actual).getBytes(); - } else { - actualJson = JSONSerialiser.serialise(actual); - } - assertEquals(expectedJson, actualJson); - } - - public static void assertEquals(final String expectedJson, final String actualJson) { - try { - final Map expectedSchemaMap = null != expectedJson ? OBJECT_MAPPER.readValue(expectedJson, Map.class) : Collections.emptyMap(); - final Map actualSchemaMap = null != actualJson ? OBJECT_MAPPER.readValue(actualJson, Map.class) : Collections.emptyMap(); - Assert.assertEquals("The actualSchemaMap should equal the expectedSchemaMap", expectedSchemaMap, actualSchemaMap); - return; - } catch (final IOException e) { - // ignore the error and try using lists instead - } - - try { - final List expectedSchemaList = null != expectedJson ? OBJECT_MAPPER.readValue(expectedJson, List.class) : Collections.emptyList(); - final List actualSchemaList = null != actualJson ? OBJECT_MAPPER.readValue(actualJson, List.class) : Collections.emptyList(); - Assert.assertEquals("The actualSchemaList should equal the expectedSchemaList", expectedSchemaList, actualSchemaList); - } catch (final IOException e) { - throw new AssertionError(expectedJson + " is not equal to " + actualJson, e); - } - } - - public static void assertEquals(final byte[] expectedJson, final byte[] actualJson) { - assertEquals(null != expectedJson ? new String(expectedJson) : null, null != actualJson ? new String(actualJson) : null); - } - - public static void assertNotEqual(final String firstJson, final String secondJson) { - try { - final Map firstSchemaMap = null != firstJson ? OBJECT_MAPPER.readValue(firstJson, Map.class) : Collections.emptyMap(); - final Map secondSchemaMap = null != secondJson ? OBJECT_MAPPER.readValue(secondJson, Map.class) : Collections.emptyMap(); - assertNotEquals("The firstSchemaMap should not equal the secondSchemaMap", firstSchemaMap, secondSchemaMap); - } catch (final IOException e) { - // ignore - } - } - - public static void assertNotEqual(final byte[] firstJson, final byte[] secondJson) { - assertNotEqual(null != firstJson ? new String(firstJson) : null, null != secondJson ? new String(secondJson) : null); - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/util/ResourceBuilderTest.java b/src/test/java/uk/gov/gchq/palisade/util/ResourceBuilderTest.java deleted file mode 100644 index 367e69ea..00000000 --- a/src/test/java/uk/gov/gchq/palisade/util/ResourceBuilderTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import uk.gov.gchq.palisade.resource.ChildResource; -import uk.gov.gchq.palisade.resource.Resource; -import uk.gov.gchq.palisade.resource.impl.DirectoryResource; -import uk.gov.gchq.palisade.resource.impl.FileResource; -import uk.gov.gchq.palisade.resource.impl.SystemResource; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.LinkedList; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -@RunWith(JUnit4.class) -public class ResourceBuilderTest { - - @Test(expected = IllegalArgumentException.class) - public void invalidSchemaThrowsException() throws URISyntaxException { - // Given - final URI invalidSchema = new URI("badschema:/path/to/resource"); - - // Then - assertFalse("The ResourceBuilder shouldn't be able to create a URI with a bad schema", ResourceBuilder.canCreate(invalidSchema)); - - // When - ResourceBuilder.create(invalidSchema); - // Then throw exception - } - - @Test - public void shouldCreateSystemResource() { - // A file schema uri for a system root should return a SystemResource - // eg. "file:/" = System "/" - - // Given - final File root = new File("/"); - - // When - SystemResource systemResource = (SystemResource) ResourceBuilder.create(root.toURI()); - - // Then - LinkedList parents = getAllParents(systemResource); - // System at the 'top' - assertThat(parents.getFirst(), instanceOf(SystemResource.class)); - parents.removeFirst(); - // Nothing else - parents.forEach(resource -> fail("parents should be empty so cannot create a system resource")); - } - - @Test - public void shouldCreateDirectoryResource() { - // A file schema uri for a directory should return a DirectoryResource - // The parents of this DirectoryResource should be zero or more DirectoryResources up-to a root SystemResource - // eg. "file:/dev/Palisade-common/" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade-common/" - - // Given - final File userDir = new File(System.getProperty("user.dir")); - - // When - DirectoryResource directoryResource = (DirectoryResource) ResourceBuilder.create(userDir.toURI()); - - // Then - LinkedList parents = getAllParents(directoryResource); - // System at the 'top' - assertThat(parents.getFirst(), instanceOf(SystemResource.class)); - parents.removeFirst(); - // Directories at the 'bottom' - parents.forEach(resource -> assertThat(resource, instanceOf(DirectoryResource.class))); - } - - @Test - public void shouldCreateFileResource() { - // A file schema uri for a file should return a FileResource - // The parents of this FileResource should be zero or more DirectoryResources up-to a root SystemResource - // eg. "file:/dev/Palisade-common/pom.xml" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade-common/" -> File "/dev/Palisade-common/pom.xml" - - // Given - final File pom = new File(System.getProperty("user.dir") + "/pom.xml"); - - // When - FileResource fileResource = (FileResource) ResourceBuilder.create(pom.toURI()); - - // Then - LinkedList parents = getAllParents(fileResource); - // System at the 'top' - assertThat(parents.getFirst(), instanceOf(SystemResource.class)); - parents.removeFirst(); - // File at the 'bottom' - assertThat(parents.getLast(), instanceOf(FileResource.class)); - parents.removeLast(); - // Directories in the 'middle' - parents.forEach(resource -> assertThat(resource, instanceOf(DirectoryResource.class))); - } - - @Test - public void shouldNormaliseRelativePaths() { - // A file schema uri for a file with a relative path should return a FileResource with an absolute resource id - // The parents of this FileResource should be zero or more DirectoryResources up-to a root SystemResource - // eg. "file:/dev/Palisade-common/pom.xml" = System "/" -> Directory "/dev/" -> Directory "/dev/Palisade-common/" -> File "/dev/Palisade-common/pom.xml" - - // Given - final URI absolutePom = new File(System.getProperty("user.dir") + "/pom.xml").toURI(); - final URI relativePom = UriBuilder.create(absolutePom) - .withoutScheme() - .withoutAuthority() - .withPath(absolutePom.getPath() + "/../pom.xml") - .withoutQuery() - .withoutFragment(); - - // When - FileResource relativeFile = (FileResource) ResourceBuilder.create(relativePom); - FileResource absoluteFile = (FileResource) ResourceBuilder.create(absolutePom); - - // Then - assertThat(relativeFile, equalTo(absoluteFile)); - } - - private LinkedList getAllParents(final Resource resource) { - if (resource instanceof ChildResource) { - final LinkedList parents = getAllParents(((ChildResource) resource).getParent()); - parents.addLast(resource); - return parents; - } else { - return new LinkedList<>(Collections.singleton(resource)); - } - } -} diff --git a/src/test/java/uk/gov/gchq/palisade/util/TestUtil.java b/src/test/java/uk/gov/gchq/palisade/util/TestUtil.java deleted file mode 100644 index 48f8a026..00000000 --- a/src/test/java/uk/gov/gchq/palisade/util/TestUtil.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class TestUtil { - private static final Logger LOGGER = LoggerFactory.getLogger(TestUtil.class); - - public static final File TMP_DIRECTORY; - - static { - final String tmpDirectoryProperty = System.getProperty("java.io.tmpdir"); - - if (null != tmpDirectoryProperty) { - TMP_DIRECTORY = new File(tmpDirectoryProperty); - } else { - LOGGER.warn("Could not determine default temporary directory, using current directory."); - TMP_DIRECTORY = new File("."); - } - } - - /** - * Compare two streams for equality. Each stream must be of the same length and contain the same elements (by - * value). The streams are sorted beforehand. Therefore T must be naturally comparable. - * - * @param expected first stream - * @param actual second stream - * @param type of list element - * @return true if streams are equal - */ - public static boolean streamEqual(final Stream expected, final Stream actual) { - Stream sortExpected = expected.sorted(); - Stream sortActual = actual.sorted(); - List lhs = sortExpected.collect(Collectors.toList()); - List rhs = sortActual.collect(Collectors.toList()); - return lhs.equals(rhs); - } -} diff --git a/src/test/resources/avro/testObj.avsc b/src/test/resources/avro/testObj.avsc deleted file mode 100644 index 51e3ebd5..00000000 --- a/src/test/resources/avro/testObj.avsc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "namespace": "uk.gov.gchq.palisade.data.service.impl.serialiser", - "type": "record", - "name": "TestObj", - "fields": [ - { - "name": "fieldStr1", - "type": "string" - }, - { - "name": "fieldInt", - "type": [ - "int", - "null" - ] - }, - { - "name": "fieldStr2", - "type": [ - "string", - "null" - ] - } - ] -} From 2a4bea740dd224bb626f666e95895b09d3973455 Mon Sep 17 00:00:00 2001 From: dev930018 <56113485+dev930018@users.noreply.github.com> Date: Tue, 20 Apr 2021 14:56:36 +0100 Subject: [PATCH 20/27] Pal 1043 fix common json serialisation (#89) * PAL-1043-fix-common-json-serialisation initial commit * PAL-1043 always use standard @class for json classpath --- mvn_dependency_tree.txt | 22 +++------ pom.xml | 49 ------------------- .../java/uk/gov/gchq/palisade/Context.java | 15 +----- .../palisade/data/serialise/Serialiser.java | 5 -- .../palisade/resource/ConnectionDetail.java | 23 +-------- .../gov/gchq/palisade/resource/Resource.java | 24 +-------- .../java/uk/gov/gchq/palisade/rule/Rule.java | 20 +------- .../java/uk/gov/gchq/palisade/rule/Rules.java | 3 -- .../java/uk/gov/gchq/palisade/user/User.java | 13 +---- 9 files changed, 13 insertions(+), 161 deletions(-) diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index b5f443db..416c60cf 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -2,17 +2,11 @@ uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT +- com.fasterxml.jackson.core:jackson-databind:jar:2.10.0:compile | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.10.0:compile | \- com.fasterxml.jackson.core:jackson-core:jar:2.10.0:compile -+- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.10.0:compile -+- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.10.0:compile -+- commons-io:commons-io:jar:2.6:compile -+- org.apache.commons:commons-lang3:jar:3.8.1:compile -+- org.slf4j:slf4j-api:jar:1.7.25:compile -+- org.apache.avro:avro:jar:1.8.2:compile -| +- org.codehaus.jackson:jackson-core-asl:jar:1.9.13:compile -| +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.13:compile -| +- com.thoughtworks.paranamer:paranamer:jar:2.7:compile -| +- org.xerial.snappy:snappy-java:jar:1.1.1.3:compile -| +- org.apache.commons:commons-compress:jar:1.8.1:compile -| \- org.tukaani:xz:jar:1.5:compile -\- junit:junit:jar:4.13.1:test - \- org.hamcrest:hamcrest-core:jar:1.3:test +\- org.apache.avro:avro:jar:1.8.2:compile + +- org.codehaus.jackson:jackson-core-asl:jar:1.9.13:compile + +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.13:compile + +- com.thoughtworks.paranamer:paranamer:jar:2.7:compile + +- org.xerial.snappy:snappy-java:jar:1.1.1.3:compile + +- org.apache.commons:commons-compress:jar:1.8.1:compile + +- org.tukaani:xz:jar:1.5:compile + \- org.slf4j:slf4j-api:jar:1.7.7:compile diff --git a/pom.xml b/pom.xml index 76c88c36..d1f7cdf1 100644 --- a/pom.xml +++ b/pom.xml @@ -97,44 +97,6 @@ jackson-databind ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - ${jackson.version} - - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - - - - - - commons-io - commons-io - ${commons-io.version} - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - - - - org.slf4j - slf4j-api - ${slf4j.api.version} - @@ -142,17 +104,6 @@ avro ${avro.version} - - - - - junit - junit - ${junit.version} - test - diff --git a/src/main/java/uk/gov/gchq/palisade/Context.java b/src/main/java/uk/gov/gchq/palisade/Context.java index a06e0ed4..233d575f 100644 --- a/src/main/java/uk/gov/gchq/palisade/Context.java +++ b/src/main/java/uk/gov/gchq/palisade/Context.java @@ -17,10 +17,8 @@ package uk.gov.gchq.palisade; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.Collections; @@ -37,12 +35,7 @@ * additional information that can be stored and recovered in this structure and passed along with the request/operation. * i.e. A users purpose for requesting the contents of a file. */ -@JsonPropertyOrder(value = {"class", "contents"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "class" -) +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public class Context { private static final String PURPOSE = "purpose"; @@ -179,12 +172,6 @@ public Context putIfAbsent(final String key, final Object value) { return this; } - @JsonGetter("class") - @Generated - public String getClassName() { - return getClass().getName(); - } - @Override @Generated public boolean equals(final Object o) { diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java index bf59ee9f..63deb6dd 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java @@ -15,20 +15,15 @@ */ package uk.gov.gchq.palisade.data.serialise; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.stream.Stream; -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.EXISTING_PROPERTY, property = "class") /** * Serialisers are the part of Palisade that convert from a particular data serialised format to a stream of objects * and back again. - * * IMPORTANT: All instances of this interface must be thread safe. That is, they must support multiple threads performing * calling either {@link Serialiser#deserialise(InputStream)} or {@link Serialiser#serialise(Stream, OutputStream)} concurrently. * The easiest and recommended way to do this is to make the {@code Serialiser} instance stateless; don't store anything related to diff --git a/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java index 744873b9..d3092477 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java @@ -16,37 +16,16 @@ package uk.gov.gchq.palisade.resource; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; - -import uk.gov.gchq.palisade.resource.impl.SimpleConnectionDetail; import java.io.Serializable; /** * A High level API for passing details of how to connect to a resource */ -@JsonPropertyOrder(value = {"class", "host", "port"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class", - defaultImpl = SimpleConnectionDetail.class -) +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public interface ConnectionDetail extends Serializable { String createConnection(); - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java index befded71..4a3c17af 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java @@ -16,13 +16,7 @@ package uk.gov.gchq.palisade.resource; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; - -import uk.gov.gchq.palisade.resource.impl.FileResource; import java.io.Serializable; @@ -30,13 +24,7 @@ * A high level API to define a resource, where a resource could be a system, directory, file, stream, etc. * A resource is expected to have a unique identifier. */ -@JsonPropertyOrder(value = {"class", "id"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class", - defaultImpl = FileResource.class -) +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public interface Resource extends Comparable, Serializable { Resource id(String id); @@ -45,14 +33,4 @@ public interface Resource extends Comparable, Serializable { void setId(final String id); - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } - } diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java index bae99d9b..4f8d8d55 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rule.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rule.java @@ -15,11 +15,7 @@ */ package uk.gov.gchq.palisade.rule; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import uk.gov.gchq.palisade.Context; import uk.gov.gchq.palisade.user.User; @@ -44,12 +40,7 @@ * by the record reader before being passed to the apply(T, User, Context) method. */ @FunctionalInterface -@JsonPropertyOrder(value = {"class"}, alphabetic = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = As.EXISTING_PROPERTY, - property = "class" -) +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public interface Rule extends Serializable { /** * Applies the rule logic to redact or modify the record based on the user and context. @@ -74,13 +65,4 @@ default boolean isApplicable(final User user, final Context context) { return true; } - @JsonGetter("class") - default String getClassName() { - return getClass().getName(); - } - - @JsonSetter("class") - default void setClassName(final String className) { - // do nothing. - } } diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java index 9754bb86..1ee9d789 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java @@ -16,8 +16,6 @@ package uk.gov.gchq.palisade.rule; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - import uk.gov.gchq.palisade.Generated; import java.io.Serializable; @@ -37,7 +35,6 @@ * * @param The type of data records that the rules will be applied to. */ -@JsonPropertyOrder(value = {"message", "rules"}, alphabetic = true) public class Rules implements Serializable { private static final long serialVersionUID = 1L; private static final String ID_CANNOT_BE_NULL = "The id field can not be null."; diff --git a/src/main/java/uk/gov/gchq/palisade/user/User.java b/src/main/java/uk/gov/gchq/palisade/user/User.java index bab456d4..4061abb2 100644 --- a/src/main/java/uk/gov/gchq/palisade/user/User.java +++ b/src/main/java/uk/gov/gchq/palisade/user/User.java @@ -16,7 +16,6 @@ package uk.gov.gchq.palisade.user; -import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonTypeInfo; import uk.gov.gchq.palisade.Generated; @@ -42,11 +41,7 @@ * The user auths are used specifically to decide what visibilities users can see. *

*/ -@JsonTypeInfo( - use = JsonTypeInfo.Id.CLASS, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "class" -) +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public class User implements Serializable { private static final long serialVersionUID = 1L; @@ -193,12 +188,6 @@ public void setAuths(final Set auths) { this.auths = auths; } - @JsonGetter("class") - @Generated - public String getClassName() { - return getClass().getName(); - } - @Override @Generated public boolean equals(final Object o) { From 23e868149d0149235420ba3d001c68683f819a03 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Tue, 11 May 2021 17:50:11 +0100 Subject: [PATCH 21/27] PAL-1044 updated jackson dependency (#92) --- mvn_dependency_tree.txt | 6 +++--- pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index 416c60cf..00ffaf28 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -1,7 +1,7 @@ uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT -+- com.fasterxml.jackson.core:jackson-databind:jar:2.10.0:compile -| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.10.0:compile -| \- com.fasterxml.jackson.core:jackson-core:jar:2.10.0:compile ++- com.fasterxml.jackson.core:jackson-databind:jar:2.11.0:compile +| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.11.0:compile +| \- com.fasterxml.jackson.core:jackson-core:jar:2.11.0:compile \- org.apache.avro:avro:jar:1.8.2:compile +- org.codehaus.jackson:jackson-core-asl:jar:1.9.13:compile +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.13:compile diff --git a/pom.xml b/pom.xml index d1f7cdf1..a0c7cf95 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 3.1.0 3.8.0 1.7.25 - 2.10.0 + 2.11.0 2.6 3.8.1 1.8.2 From 07d803eba9d85b0c45e4373d3deb5332f0b9c3bc Mon Sep 17 00:00:00 2001 From: dev930018 <56113485+dev930018@users.noreply.github.com> Date: Mon, 17 May 2021 13:33:41 +0100 Subject: [PATCH 22/27] Pal 1040 push to public nexus (#91) --- .gitignore | 2 + NOTICES.md | 16 +- licenses/eclipse_public_license_1.0.html | 261 ---------------- licenses/mit_license.html | 372 ----------------------- pom.xml | 191 +++++++++--- 5 files changed, 147 insertions(+), 695 deletions(-) delete mode 100644 licenses/eclipse_public_license_1.0.html delete mode 100644 licenses/mit_license.html diff --git a/.gitignore b/.gitignore index f6c6cbf1..8a1133f1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ node_modules/ .project .settings *.iml +*.DS_Store +.flattened-pom.xml ##VSCODE default files that dont need to be tracked .vscode/ diff --git a/NOTICES.md b/NOTICES.md index 8c625723..52b0b323 100644 --- a/NOTICES.md +++ b/NOTICES.md @@ -1,19 +1,5 @@ List of third-party dependencies grouped by their license type ### [Apache Software License 2.0](./licenses/apache_software_license_2.0.txt): -* Jackson-annotations ([com.fasterxml.jackson.core:jackson-annotations:2.10.0](http://github.com/FasterXML/jackson)) -* Jackson-core ([com.fasterxml.jackson.core:jackson-core:2.10.0](https://github.com/FasterXML/jackson-core)) -* jackson-databind ([com.fasterxml.jackson.core:jackson-databind:2.10.0](http://github.com/FasterXML/jackson)) -* Jackson datatype: jdk8 ([com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.0](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8)) -* Jackson datatype: JSR310 ([com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.0](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310)) -* Apache Commons IO ([commons-io:commons-io:2.6](http://commons.apache.org/proper/commons-io/)) +* jackson-databind ([com.fasterxml.jackson.core:jackson-databind:2.11.0](http://github.com/FasterXML/jackson)) * Apache Avro ([org.apache.avro:avro:1.8.2](http://avro.apache.org)) -* Apache Commons Lang ([org.apache.commons:commons-lang3:3.8.1](http://commons.apache.org/proper/commons-lang/)) -* Commons Math ([org.apache.commons:commons-math3:3.1.1](http://commons.apache.org/math/)) - -### [Eclipse Public License 1.0](./licenses/eclipse_public_license_1.0.html): -* JUnit ([junit:junit:4.12](http://junit.org)) - -### [MIT License](./licenses/mit_license.html): -* mockito-core ([org.mockito:mockito-core:3.1.0](https://github.com/mockito/mockito)) -* SLF4J API Module ([org.slf4j:slf4j-api:1.7.25](http://www.slf4j.org)) diff --git a/licenses/eclipse_public_license_1.0.html b/licenses/eclipse_public_license_1.0.html deleted file mode 100644 index 3998fceb..00000000 --- a/licenses/eclipse_public_license_1.0.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/licenses/mit_license.html b/licenses/mit_license.html deleted file mode 100644 index 77f6583b..00000000 --- a/licenses/mit_license.html +++ /dev/null @@ -1,372 +0,0 @@ - - - - - - - - - - - - The MIT License | Open Source Initiative - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
-
- - -
-
- -
- -
-
- -
-
- -
- -
- -
-
- -
-
- -
- -
-
- - -
-
- - - - - - - - - - - - -
- - - -
-
- - - - -
- - - -
- - -
- - - - -
- -

The MIT License

- - - -
-
- - - - - -
-
- - -
-
- - -
-
-

OSI Approved License LogoLicense Copyright: Unknown.
-License License: Unknown.
-License Contact: Unknown.

- -

SPDX short identifier: MIT

- -

- -

 

-
- -
-
Begin license text.
- -
-

Copyright <YEAR> <COPYRIGHT HOLDER>

- -

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

- -

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

- -

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-
- -
-
End license text. - -
-
- - - - -
-
-
- -
- - -
- - -
- - -
- -
-
- - - -
- - - -
-
- - -
-
-
- -
- - - - -
- -
-
-
- - -
-
- - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index a0c7cf95..762766d9 100644 --- a/pom.xml +++ b/pom.xml @@ -14,13 +14,17 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 4.0.0 + uk.gov.gchq.palisade common 0.5.0-${revision} - Palisade common code + Palisade common data-types shared between multiple services https://github.com/gchq/Palisade-common + GCHQ Palisade Common Library @@ -33,12 +37,19 @@ Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt + + ${scm.url} + ${scm.connection} + ${scm.developer.connection} + HEAD + + - + 11 SNAPSHOT ${java.version} @@ -48,10 +59,21 @@ ${encoding} - 3.1.0 + 3.8.0 3.1.2 + 3.1.0 3.1.0 3.8.0 + + + 1.5 + 1.6.8 + 2.5.3 + 1.1 + 3.2.1 + + + 2.10.0 1.7.25 2.11.0 2.6 @@ -59,31 +81,35 @@ 1.8.2 - 4.13.1 - 2.22.1 - 2.22.1 3.1.0 + true - + jacoco reuseReports ${project.basedir}/../target/site/jacoco/jacoco.xml java + + + https://github.com/gchq/Palisade-common + scm:git:https://github.com/gchq/Palisade-common.git + scm:git:https://github.com/gchq/Palisade-common.git - - nexus - releases - ${release.url} + ${releases.repository.id} + ${releases.repository.name} + + ${releases.repository.url} - - nexus - snapshots - ${snapshot.url} + ${snapshots.repository.id} + false + ${snapshots.repository.name} + + ${snapshots.repository.url} @@ -114,7 +140,6 @@ true true true - true true true @@ -146,6 +171,78 @@
+ + release + + + ossrh + Sonatype Nexus release repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + ossrh + Sonatype Nexus snapshot repository + https://oss.sonatype.org/content/repositories/snapshots + RELEASE + true + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus.plugin.version} + true + + ${releases.repository.id} + https://oss.sonatype.org/ + false + true + + 10 + + + + org.apache.maven.plugins + maven-source-plugin + ${source.plugin.version} + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${javadoc.plugin.version} + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${gpg.plugin.version} + + + sign-artifacts + verify + + sign + + + + + + +
@@ -221,12 +318,6 @@ - - - org.apache.maven.plugins - maven-surefire-plugin - ${surefire.plugin.version} - org.apache.maven.plugins @@ -245,6 +336,31 @@ + + org.codehaus.mojo + flatten-maven-plugin + 1.1.0 + + true + ossrh + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + org.codehaus.mojo @@ -255,8 +371,8 @@ licenses-report prepare-package - aggregate-download-licenses - aggregate-add-third-party + download-licenses + add-third-party @@ -353,32 +469,18 @@ - - - org.apache.maven.plugins - maven-jar-plugin - ${jar.plugin.version} - - - - test-jar - - - - org.apache.maven.plugins maven-checkstyle-plugin ${checkstyle.plugin.version} - true + false code-style/checkstyle.xml UTF-8 true true - code-style/licenseHeader.txt - + code-style/licenseHeader.txt code-style/checkstyle-suppressions.xml checkstyle.suppressions.file @@ -392,11 +494,6 @@ - - org.apache.maven.plugins - maven-failsafe-plugin - ${failsafe.version} - From dadfa771b51d2a6448c27f07ca4dcefc3e2be4c0 Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Tue, 18 May 2021 14:55:22 +0100 Subject: [PATCH 23/27] Pal 1021 s3 resource service (#93) --- .../resource/AbstractLeafResource.java | 21 +- .../gchq/palisade/resource/LeafResource.java | 31 ++ .../palisade/resource/impl/FileResource.java | 5 +- .../palisade/util/FileResourceBuilder.java | 114 +++++++ .../gchq/palisade/util/ResourceBuilder.java | 317 ++++++------------ .../uk.gov.gchq.palisade.util.ResourceBuilder | 1 + 6 files changed, 259 insertions(+), 230 deletions(-) create mode 100644 src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java create mode 100644 src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java index fa4df52e..399c00ce 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java @@ -19,7 +19,6 @@ import uk.gov.gchq.palisade.Generated; import uk.gov.gchq.palisade.resource.impl.FileResource; -import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -39,9 +38,9 @@ public abstract class AbstractLeafResource extends AbstractResource implements L private String serialisedFormat; private ConnectionDetail connectionDetail; private ParentResource parent; - private HashMap attributes = new HashMap<>(); + private HashMap attributes = new HashMap<>(); - public AbstractLeafResource() { + protected AbstractLeafResource() { } @Generated @@ -65,11 +64,11 @@ public AbstractLeafResource connectionDetail(final ConnectionDetail connectionDe /** * Sets the attributes for the {@link AbstractLeafResource} * - * @param attributes a {@link Map} of {@link String} and {@link Serializable}. + * @param attributes a {@link Map} of {@link String} keys and {@link String} values. * @return a {@link AbstractLeafResource} object. */ @Generated - public AbstractLeafResource attributes(final Map attributes) { + public AbstractLeafResource attributes(final Map attributes) { this.setAttributes(attributes); return this; } @@ -78,11 +77,11 @@ public AbstractLeafResource attributes(final Map attribute * Sets the attributes for the {@link AbstractLeafResource} * * @param attributeKey a {@link String} value for the key. - * @param attributeValue a {@link Serializable} value + * @param attributeValue a {@link String} value * @return the {@link AbstractLeafResource} object */ @Generated - public AbstractLeafResource attribute(final String attributeKey, final Serializable attributeValue) { + public AbstractLeafResource attribute(final String attributeKey, final String attributeValue) { this.setAttribute(attributeKey, attributeValue); return this; } @@ -146,12 +145,12 @@ public void setParent(final ParentResource parent) { } @Generated - public Map getAttributes() { + public Map getAttributes() { return attributes; } @Generated - public void setAttributes(final Map attributes) { + public void setAttributes(final Map attributes) { requireNonNull(attributes); this.attributes = new HashMap<>(attributes); } @@ -170,10 +169,10 @@ public Boolean isAttributeSet(final String attributeKey) { * Sets the key and value of the attributes {@link Map} for the {@link AbstractLeafResource} * * @param attributeKey a {@link String} value for the attribute key. - * @param attributeValue a {@link Serializable} value for the attribute value. + * @param attributeValue a {@link String} value for the attribute value. */ @Generated - public void setAttribute(final String attributeKey, final Serializable attributeValue) { + public void setAttribute(final String attributeKey, final String attributeValue) { requireNonNull(attributeKey, "The attributeKey cannot be set to null."); requireNonNull(attributeKey, "The attributeValue cannot be set to null."); this.attributes.put(attributeKey, attributeValue); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java index 267dabca..dd1d7eb4 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/LeafResource.java @@ -16,6 +16,8 @@ package uk.gov.gchq.palisade.resource; +import java.util.Map; + /** * A leaf resource is the interface for any resource that can be read for data * and is not just part of the hierarchical resource structure. @@ -25,12 +27,38 @@ public interface LeafResource extends ChildResource { + /** + * The type of resource, used for grouping data of the same structure + * + * @param type the type of resource + * @return a LeafResource with type attached + */ LeafResource type(final String type); + /** + * The format of the resource, e.g CSV, txt + * + * @param serialisedFormat the format of resource + * @return a LeafResource with format attached + */ LeafResource serialisedFormat(final String serialisedFormat); + /** + * The service where the LeafResource is stored. Used by the Data Service to connect to the correct service + * + * @param connectionDetail the location of the LeafResource + * @return a LeafResource with connectionDetail attached + */ LeafResource connectionDetail(final ConnectionDetail connectionDetail); + /** + * Any additional Attributes about the LeafResource + * + * @param attributes additional attributes or metadata about the LeafResource + * @return a LeafResource with attributes attached + */ + LeafResource attributes(final Map attributes); + String getType(); String getSerialisedFormat(); @@ -43,4 +71,7 @@ public interface LeafResource extends ChildResource { void setConnectionDetail(final ConnectionDetail connectionDetail); + Map getAttributes(); + + void setAttributes(Map attributes); } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java index 4ac3cc80..5a646b3a 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/FileResource.java @@ -20,7 +20,6 @@ import uk.gov.gchq.palisade.resource.ConnectionDetail; import uk.gov.gchq.palisade.resource.ParentResource; -import java.io.Serializable; import java.util.Map; /** @@ -55,12 +54,12 @@ public FileResource connectionDetail(final ConnectionDetail connectionDetail) { } @Override - public FileResource attributes(final Map attributes) { + public FileResource attributes(final Map attributes) { return (FileResource) super.attributes(attributes); } @Override - public FileResource attribute(final String attributeKey, final Serializable attributeValue) { + public FileResource attribute(final String attributeKey, final String attributeValue) { return (FileResource) super.attribute(attributeKey, attributeValue); } diff --git a/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java new file mode 100644 index 00000000..3f306a3f --- /dev/null +++ b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java @@ -0,0 +1,114 @@ +/* + * Copyright 2018-2021 Crown Copyright + * + * 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 uk.gov.gchq.palisade.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import uk.gov.gchq.palisade.resource.ParentResource; +import uk.gov.gchq.palisade.resource.Resource; +import uk.gov.gchq.palisade.resource.impl.DirectoryResource; +import uk.gov.gchq.palisade.resource.impl.FileResource; +import uk.gov.gchq.palisade.resource.impl.SystemResource; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Set; + +/** + * Provides a common set of utilities for constructing resources with all parents + * automatically constructed recursively. This primarily targets filesystem-like + * resources (Files, Directories etc.) + * Internally, the resourceId is converted to a URI. + * Can produce any of the following output types: + * - {@link FileResource} + * - {@link DirectoryResource} + * - {@link SystemResource} + * Any parents automatically constructed will also be from this collection. + * If another method of creating a resource is required (i.e. directly using strings) + * there is no guarantee that this can correctly resolve parents. Instead, use the + * methods provided by the appropriate resource impl. + */ +public class FileResourceBuilder extends ResourceBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(FileResourceBuilder.class); + private static final URI ROOT; + private static final Set ACCEPTED_SCHEMES = Set.of("hdfs", "file"); + + public FileResourceBuilder() { + // Empty Constructor + } + + static { + File userDir = new File(System.getProperty("user.dir")); + URI root; + try { + root = userDir.getCanonicalFile().toURI(); + } catch (IOException ex) { + LOGGER.error("Failed to get canonical file for {}", userDir, ex); + root = userDir.getAbsoluteFile().toURI(); + } + ROOT = root; + } + + private static FileResource fileResource(final URI uri) { + return new FileResource() + .id(uri.normalize().toString()) + .parent((ParentResource) filesystemScheme(uri.resolve("."))); + } + + private static DirectoryResource directoryResource(final URI uri) { + return new DirectoryResource() + .id(uri.normalize().toString()) + .parent((ParentResource) filesystemScheme(uri.resolve(".."))); + } + + private static SystemResource systemResource(final URI uri) { + return new SystemResource() + .id(uri.normalize().toString()); + } + + private static Resource filesystemScheme(final URI uri) { + // If passed relative paths, we can resolve them against the user.dir system property + URI absolute; + if (uri.isAbsolute()) { + absolute = uri; + } else { + absolute = ROOT.resolve(uri); + } + // Decide whether a File, Directory or System resource + if (!absolute.resolve(".").equals(absolute)) { + return fileResource(uri); + } else if (Objects.nonNull(Path.of(absolute).getParent())) { + return directoryResource(absolute); + } else { + return systemResource(absolute); + } + } + + @Override + public Resource build(final URI resourceUri) { + return filesystemScheme(resourceUri); + } + + @Override + public boolean accepts(final URI resourceUri) { + return ACCEPTED_SCHEMES.contains(resourceUri.getScheme()); + } +} diff --git a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java index 43ec992b..cc36c4c3 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java @@ -1,216 +1,101 @@ -/* - * Copyright 2018-2021 Crown Copyright - * - * 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 uk.gov.gchq.palisade.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import uk.gov.gchq.palisade.resource.ConnectionDetail; -import uk.gov.gchq.palisade.resource.LeafResource; -import uk.gov.gchq.palisade.resource.ParentResource; -import uk.gov.gchq.palisade.resource.Resource; -import uk.gov.gchq.palisade.resource.impl.DirectoryResource; -import uk.gov.gchq.palisade.resource.impl.FileResource; -import uk.gov.gchq.palisade.resource.impl.SystemResource; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -/** - * Provides a common set of utilities for constructing resources with all parents - * automatically constructed recursively. This primarily targets filesystem-like - * resources (Files, Directories etc.) - * Internally, the resourceId is converted to a URI. - * Can produce any of the following output types: - * - {@link FileResource} - * - {@link DirectoryResource} - * - {@link SystemResource} - * Any parents automatically constructed will also be from this collection. - * If another method of creating a resource is required (i.e. directly using strings) - * there is no guarantee that this can correctly resolve parents. Instead, use the - * methods provided by the appropriate resource impl. - */ -public class ResourceBuilder { - private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBuilder.class); - private static final URI ROOT; - - public ResourceBuilder() { - // Empty Constructor - } - - static { - File userDir = new File(System.getProperty("user.dir")); - URI root; - try { - root = userDir.getCanonicalFile().toURI(); - } catch (IOException ex) { - LOGGER.error("ResourceBuilder threw an error when getting the CanonicalFile ", ex); - root = userDir.getAbsoluteFile().toURI(); - } - ROOT = root; - } - - private enum Scheme { - FILE, - HDFS - } - - /** - * Create a leafResource from a uri, connectionDetail, type, serialisedFormat and attribute map - * Throw IllegalArgumentException if unsupported scheme - * Throw ClassCastException if uri did not point to a leaf resource - * - * @param uri the uri location of the resource - * @param connectionDetail the service storing the resource - * @param type the type of resource - * @param serialisedFormat the format of the resource e.g avro, txt - * @param attributes any additional attributes about the resource - * @return a new LeafResource populated with these resources - */ - public static LeafResource create(final URI uri, final ConnectionDetail connectionDetail, final String type, final String serialisedFormat, final Map attributes) { - return ((LeafResource) create(uri, attributes)) - .connectionDetail(connectionDetail) - .type(type) - .serialisedFormat(serialisedFormat); - } - - /** - * Create a resource from a uri and attribute map - * Throw IllegalArgumentException if unsupported scheme - * - * @param uri the id of the resource - * @param attributes any additional attributes about the resource - * @return a new resource created using this uri - */ - public static Resource create(final URI uri, final Map attributes) { - // If passed relative paths, we can resolve them against the user.dir system property - URI absolute; - if (uri.isAbsolute()) { - absolute = uri; - } else { - absolute = ROOT.resolve(uri); - } - // The hostname is all in the connectionDetail, so we never have a case of file://hostname/some/uri - // A lot of this is trying to normalize file:///some/uri (file:///some/uri) to file:/some/uri - URI normal = UriBuilder.create(absolute) - .withoutScheme() - .withoutAuthority() - .withoutPath() - .withoutQuery() - .withoutFragment(); - - // This should be assigning the attributes map to the returned object, once resources support attribute maps - - switch (Scheme.valueOf(normal.getScheme().toUpperCase(Locale.ENGLISH))) { - // Both file:/ and hdfs:/ schema produce filesystem-like structures - case FILE: - case HDFS: - return filesystemSchema(normal); - default: - throw new IllegalArgumentException("No such implementation for uri scheme " + normal.getScheme()); - } - } - - /** - * Used to validate URIs and check that they can be created - * - * @param uri the uri to validate or attach to a resource - * @return a boolean true/false if the uri scheme is valid. - */ - public static boolean canCreate(final URI uri) { - try { - Scheme.valueOf(uri.getScheme()); // Or throw - return uri.isAbsolute(); - } catch (IllegalArgumentException ex) { - return false; - } - } - - /** - * Create a resource from a uri - * Default to an empty attribute map - * Throw IllegalArgumentException if unsupported scheme - * - * @param uri an id of a resource - * @return a newly created resource with an empty map of attributes - */ - public static Resource create(final URI uri) { - return create(uri, Collections.emptyMap()); - } - - /** - * Create a resource from a uri string and attribute map - * Throw IllegalArgumentException if invalid uri string or unsupported scheme - * - * @param uriString a string value of a url used to create a new resource - * @param attributes any additional attributes about the resource - * @return a newly created resource using these parameters - */ - public static Resource create(final String uriString, final Map attributes) { - try { - return create(new URI(uriString), attributes); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException("URISyntaxException converting string '" + uriString + "' to uri", ex); - } - } - - /** - * Create a resource from a uri string - * Default to an empty attribute map - * Throw IllegalArgumentException if invalid uri string or unsupported scheme - * - * @param uriString a string value of a url used to create a new resource - * @return a newly created resource with this url and an empty map of attributes - */ - public static Resource create(final String uriString) { - return create(uriString, Collections.emptyMap()); - } - - private static FileResource fileResource(final URI uri) { - return new FileResource() - .id(uri.normalize().toString()) - .parent((ParentResource) filesystemSchema(uri.resolve("."))); - } - - private static DirectoryResource directoryResource(final URI uri) { - return new DirectoryResource() - .id(uri.normalize().toString()) - .parent((ParentResource) filesystemSchema(uri.resolve(".."))); - } - - private static SystemResource systemResource(final URI uri) { - return new SystemResource() - .id(uri.normalize().toString()); - } - - private static Resource filesystemSchema(final URI uri) { - if (!uri.resolve(".").equals(uri)) { - return fileResource(uri); - } else if (Objects.nonNull(Path.of(uri).getParent())) { - return directoryResource(uri); - } else { - return systemResource(uri); - } - } -} +/* + * Copyright 2018-2021 Crown Copyright + * + * 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 uk.gov.gchq.palisade.util; + +import uk.gov.gchq.palisade.resource.Resource; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ServiceLoader; +import java.util.ServiceLoader.Provider; + +/** + * ResourceBuilder, taking a URI and building a Resource specific to each scheme + */ +public abstract class ResourceBuilder { + private static final ServiceLoader LOADER = ServiceLoader.load(ResourceBuilder.class); + + /** + * Clears this loader's provider cache so that all providers will be reloaded. + */ + public static void refreshProviders() { + LOADER.reload(); + } + + /** + * Taking a resourceUri, create a resource using the ResourceBuilder provided in the LOADER, + * or throw an exception if the resource scheme is not supported, or no builder exists to build that scheme + * + * @param resourceUri the Uri of the resource you want to build. + * @return a newly created resource + */ + public static Resource create(final URI resourceUri) { + ResourceBuilder resourceBuilder = LOADER.stream() + .map(Provider::get) + .filter(builder -> builder.accepts(resourceUri)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("No ResourceBuilder found that accepts " + resourceUri)); + return resourceBuilder.buildNormal(resourceUri); + } + + /** + * Create a resource from a uri string + * Throw IllegalArgumentException if invalid uri string or unsupported scheme + * + * @param uriString a string value of a url used to create a new resource + * @return a newly created resource using these parameters. + */ + public static Resource create(final String uriString) { + try { + return create(new URI(uriString)); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException("URISyntaxException converting string '" + uriString + "' to uri", ex); + } + } + + /** + * Build a resource, using the uri provided by calling {@link UriBuilder} + * + * @param uri the uri of the resource you want built + * @return a newly created resource with the id of the uri. + */ + public Resource buildNormal(final URI uri) { + URI normal = UriBuilder.create(uri) + .withoutScheme() + .withoutAuthority() + .withoutPath() + .withoutQuery() + .withoutFragment(); + return build(normal); + } + + /** + * An abstract method used in building a resource + * + * @param resourceUri the uri of the resource you want built + * @return a newly created Resource with the id of the the resourceUri. + */ + protected abstract Resource build(URI resourceUri); + + /** + * A abstract method used in building a resource, to check if the Builders provided can accept the resourceUri scheme + * + * @param resourceUri the uri of the resource you want built + * @return a true/false value if a builder exists that supports the uri scheme. + */ + public abstract boolean accepts(URI resourceUri); +} diff --git a/src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder b/src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder new file mode 100644 index 00000000..6dba381d --- /dev/null +++ b/src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder @@ -0,0 +1 @@ +uk.gov.gchq.palisade.util.FileResourceBuilder \ No newline at end of file From 34955e7aab8951e78a20ad43689bfae55ac3cbc1 Mon Sep 17 00:00:00 2001 From: pd104923 <54805408+pd104923@users.noreply.github.com> Date: Wed, 26 May 2021 12:16:56 +0100 Subject: [PATCH 24/27] Pal 822 jvm example pipeline (#94) * PAL-822 updated file resource builder * PAL-822 refactored resource and file builder classes * PAL-822 resolved sq smells * PAL-822 removed unused import * PAL-822 added inline comments, corrected minor spelling mistake * Update src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java Minor expansion to the inline comment Co-authored-by: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> * PAL-822 refactored FileResourceBuilder Co-authored-by: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> --- .../palisade/util/FileResourceBuilder.java | 24 ++++++++++++++++++- .../gchq/palisade/util/ResourceBuilder.java | 23 ++++++++++++------ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java index 3f306a3f..13085dd6 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java @@ -104,7 +104,29 @@ private static Resource filesystemScheme(final URI uri) { @Override public Resource build(final URI resourceUri) { - return filesystemScheme(resourceUri); + URI absoluteResourceId = resourceUri; + + // Check if the path is not complete, and therefore needs enriching to locate resources + if (!Path.of(resourceUri.getSchemeSpecificPart()).isAbsolute()) { + File localResource = new File(resourceUri.getSchemeSpecificPart()); + String path; + try { + path = localResource.getCanonicalPath(); + } catch (IOException e) { + LOGGER.warn("Unable to get the Canonical path value", e); + path = localResource.getAbsolutePath(); + } + + // Check if the resource is a directory and the path does not end with a "/" + if (localResource.isDirectory() && !path.endsWith("/")) { + path += "/"; + } + absoluteResourceId = UriBuilder.create(resourceUri) + .withoutScheme().withoutAuthority() + .withPath(path) + .withoutQuery().withoutFragment(); + } + return filesystemScheme(absoluteResourceId); } @Override diff --git a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java index cc36c4c3..a05e1cd5 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java @@ -16,6 +16,9 @@ package uk.gov.gchq.palisade.util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import uk.gov.gchq.palisade.resource.Resource; import java.net.URI; @@ -28,6 +31,7 @@ */ public abstract class ResourceBuilder { private static final ServiceLoader LOADER = ServiceLoader.load(ResourceBuilder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBuilder.class); /** * Clears this loader's provider cache so that all providers will be reloaded. @@ -74,13 +78,18 @@ public static Resource create(final String uriString) { * @return a newly created resource with the id of the uri. */ public Resource buildNormal(final URI uri) { - URI normal = UriBuilder.create(uri) - .withoutScheme() - .withoutAuthority() - .withoutPath() - .withoutQuery() - .withoutFragment(); - return build(normal); + try { + URI normal = UriBuilder.create(uri) + .withoutScheme() + .withoutAuthority() + .withoutPath() + .withoutQuery() + .withoutFragment(); + return build(normal); + } catch (RuntimeException e) { + LOGGER.error("Unable to build a normal URI", e); + return build(uri); + } } /** From fd8fad087775728fb343bd32091e01049e7054fe Mon Sep 17 00:00:00 2001 From: developer01189998819991197253 <52958809+developer01189998819991197253@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:58:01 +0100 Subject: [PATCH 25/27] PAL-1032 updated Palisade common readme (#96) --- README.md | 20 ++++---------------- logos/icon.png | Bin 79760 -> 0 bytes logos/icon.svg | 18 ------------------ logos/logo-without-text.png | Bin 28986 -> 0 bytes logos/logo-without-txt.svg | 30 ------------------------------ logos/logo.png | Bin 35006 -> 0 bytes 6 files changed, 4 insertions(+), 64 deletions(-) delete mode 100644 logos/icon.png delete mode 100644 logos/icon.svg delete mode 100644 logos/logo-without-text.png delete mode 100644 logos/logo-without-txt.svg delete mode 100644 logos/logo.png diff --git a/README.md b/README.md index 09252206..494c349b 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,11 @@ 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. ---> - - # -### Scalable Data Access Policy Management and Enforcement - -## Status - -This is not the main Palisade Repository, this is the Repository for Palisade Common. For more information, please visit Palisade - - -## Documentation - -The documentation for the latest release can be found [here](https://gchq.github.io/Palisade). +## A Tool for Complex and Scalable Data Access Policy Enforcement +# Palisade Common ### Prerequisites 1. [Git](https://git-scm.com/) @@ -134,14 +124,11 @@ cd Palisade-common

- - You are then ready to build with Maven: ```bash mvn install ``` - ## License Palisade-Common is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/copyright-and-re-use/crown-copyright/). @@ -153,4 +140,5 @@ We welcome contributions to the project. Detailed information on our ways of wor ## FAQ -What versions of Java are supported? We are currently using Java 11. +Q: What versions of Java are supported? +A: We are currently using Java 11. diff --git a/logos/icon.png b/logos/icon.png deleted file mode 100644 index a19f7b7010acf9606ff46f4f60d4e280721d424e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79760 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 - - - - - diff --git a/logos/logo-without-text.png b/logos/logo-without-text.png deleted file mode 100644 index ea3d80e61b27ab591218f651dfb134d9187447f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28986 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 diff --git a/logos/logo-without-txt.svg b/logos/logo-without-txt.svg deleted file mode 100644 index 89492443..00000000 --- a/logos/logo-without-txt.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/logos/logo.png b/logos/logo.png deleted file mode 100644 index a492a4985ed5daa477e1256d893b2792bcd2824a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35006 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 Date: Wed, 30 Jun 2021 18:00:17 +0100 Subject: [PATCH 26/27] Pal 684 SQ smell fixes (#95) --- pom.xml | 5 +-- .../java/uk/gov/gchq/palisade/Generated.java | 4 ++ ...liser.java => AbstractLineSerialiser.java} | 38 ++++++++++++++++--- .../data/serialise/AvroSerialiser.java | 26 ++++++------- .../palisade/data/serialise/Serialiser.java | 2 + .../serialise/SimpleStringSerialiser.java | 5 ++- .../resource/AbstractLeafResource.java | 12 ++++++ .../palisade/resource/AbstractResource.java | 2 +- .../gchq/palisade/resource/ChildResource.java | 6 +++ .../palisade/resource/ConnectionDetail.java | 5 +++ .../gov/gchq/palisade/resource/Resource.java | 6 +++ .../resource/impl/SimpleConnectionDetail.java | 6 +++ .../java/uk/gov/gchq/palisade/rule/Rules.java | 6 +++ .../java/uk/gov/gchq/palisade/user/User.java | 32 +++++++++++----- .../uk/gov/gchq/palisade/user/UserId.java | 2 +- ...lder.java => AbstractResourceBuilder.java} | 22 ++++++----- .../palisade/util/FileResourceBuilder.java | 2 +- .../uk/gov/gchq/palisade/util/RulesUtil.java | 13 +++++-- ...chq.palisade.util.AbstractResourceBuilder} | 0 19 files changed, 143 insertions(+), 51 deletions(-) rename src/main/java/uk/gov/gchq/palisade/data/serialise/{LineSerialiser.java => AbstractLineSerialiser.java} (63%) rename src/main/java/uk/gov/gchq/palisade/util/{ResourceBuilder.java => AbstractResourceBuilder.java} (76%) rename src/main/resources/META-INF/services/{uk.gov.gchq.palisade.util.ResourceBuilder => uk.gov.gchq.palisade.util.AbstractResourceBuilder} (100%) diff --git a/pom.xml b/pom.xml index 762766d9..ed281090 100644 --- a/pom.xml +++ b/pom.xml @@ -84,10 +84,10 @@ 3.1.0 true - + jacoco reuseReports - ${project.basedir}/../target/site/jacoco/jacoco.xml + ${project.basedir}/target/site/jacoco-aggregate/jacoco.xml java @@ -464,7 +464,6 @@ prepare-package report - report-aggregate diff --git a/src/main/java/uk/gov/gchq/palisade/Generated.java b/src/main/java/uk/gov/gchq/palisade/Generated.java index 6fa499f5..64def6c5 100644 --- a/src/main/java/uk/gov/gchq/palisade/Generated.java +++ b/src/main/java/uk/gov/gchq/palisade/Generated.java @@ -41,6 +41,10 @@ * It is recommended to include this in all code generation methods used, such * as: equals, hashCode, toString, getters, setters */ + +/** + * An annotation used to mark methods that are to be ignored by any code coverage reports. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Generated { diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/LineSerialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/AbstractLineSerialiser.java similarity index 63% rename from src/main/java/uk/gov/gchq/palisade/data/serialise/LineSerialiser.java rename to src/main/java/uk/gov/gchq/palisade/data/serialise/AbstractLineSerialiser.java index 6cd2ae31..2401041f 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/LineSerialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/AbstractLineSerialiser.java @@ -30,11 +30,28 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; -public abstract class LineSerialiser implements Serialiser { +/** + * Standard serialiser used to serialise and deserialise file contents. + * + * @param the domain object type + */ +public abstract class AbstractLineSerialiser implements Serialiser { public static final Charset CHARSET = StandardCharsets.UTF_8; + /** + * Serialises an object to a string value. + * + * @param obj the object to be serialised + * @return a string value of the object + */ public abstract String serialiseLine(final T obj); + /** + * Deserialises a String value to an object. + * + * @param line the string value to be deserialised + * @return the object value of the string + */ public abstract T deserialiseLine(final String line); @Override @@ -42,19 +59,30 @@ public void serialise(final Stream objects, final OutputStream output) { serialise(objects.iterator(), output); } + /** + * Serialises a {@link Iterator} of objects to an {@link OutputStream}. If {@code objects} is {@code null}, then + * nothing will be written. + * + * @param itr the iterator of objects + * @param output the output stream to write the serialised bytes to + * @return the serialiser object + */ public Serialiser serialise(final Iterator itr, final OutputStream output) { requireNonNull(output, "output"); if (nonNull(itr)) { - PrintWriter printOut = new PrintWriter(new OutputStreamWriter(output, CHARSET)); - try { + try (PrintWriter printOut = new PrintWriter(new OutputStreamWriter(output, CHARSET))) { itr.forEachRemaining(item -> printOut.println(serialiseLine(item))); - } finally { - printOut.flush(); } } return this; } + /** + * Deserialises an {@link InputStream} into a {@link Stream} of objects. + * + * @param stream the input stream to deserialise + * @return the stream of objects + */ @Override public Stream deserialise(final InputStream stream) { if (isNull(stream)) { diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiser.java index 3ed11f7a..bd561dc6 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/AvroSerialiser.java @@ -46,11 +46,16 @@ public class AvroSerialiser implements Serialiser { private static final long serialVersionUID = 1L; private static final Logger LOGGER = LoggerFactory.getLogger(AvroSerialiser.class); - private final ReflectDatumWriter datumWriter; + private final transient ReflectDatumWriter datumWriter; private final Class domainClass; - private final Schema schema; + private final transient Schema schema; + /** + * Constructor for the {@link AvroSerialiser} + * + * @param domainClass the class for the serialiser + */ @JsonCreator public AvroSerialiser(@JsonProperty("domainClass") final Class domainClass) { requireNonNull(domainClass, "domainClass is required"); @@ -75,9 +80,8 @@ public void serialise(final Stream objects, final OutputStream output) throws if (nonNull(objects)) { //create a data file writer around the output stream //since we didn't create the output stream, we shouldn't close it either, someone else might want it afterwards! - final DataFileWriter dataFileWriter = new DataFileWriter<>(datumWriter); - LOGGER.debug("Creating data file writer"); - try { + try (DataFileWriter dataFileWriter = new DataFileWriter<>(datumWriter)) { + LOGGER.debug("Creating data file writer"); dataFileWriter.create(schema, output); //iterate and append items -- we can't use forEach on the stream as the lambda can't throw an IOException Iterator objectIt = objects.iterator(); @@ -87,16 +91,8 @@ public void serialise(final Stream objects, final OutputStream output) throws dataFileWriter.append(next); } - } catch (Exception ex) { - LOGGER.error("Error occurred: {}", ex.getMessage()); - throw new RuntimeException(ex); - } finally { - try { - dataFileWriter.flush(); - } catch (IOException e) { - LOGGER.warn("Unable to flush Avro DataFileWriter", e); - } - dataFileWriter.close(); + } catch (IOException ex) { + throw new IOException("An error occurred during serialisation", ex); } } } diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java index 63deb6dd..9f04ef37 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/Serialiser.java @@ -28,6 +28,8 @@ * calling either {@link Serialiser#deserialise(InputStream)} or {@link Serialiser#serialise(Stream, OutputStream)} concurrently. * The easiest and recommended way to do this is to make the {@code Serialiser} instance stateless; don't store anything related to * a particular de/serialisation operation in class member fields. + * + * @param the domain object type */ public interface Serialiser extends Serializable { diff --git a/src/main/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiser.java b/src/main/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiser.java index 37879700..c742952c 100644 --- a/src/main/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiser.java +++ b/src/main/java/uk/gov/gchq/palisade/data/serialise/SimpleStringSerialiser.java @@ -15,7 +15,10 @@ */ package uk.gov.gchq.palisade.data.serialise; -public class SimpleStringSerialiser extends LineSerialiser { +/** + * A simple implementation of the {@link AbstractLineSerialiser} + */ +public class SimpleStringSerialiser extends AbstractLineSerialiser { private static final long serialVersionUID = 1L; @Override diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java index 399c00ce..0730d486 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractLeafResource.java @@ -155,11 +155,23 @@ public void setAttributes(final Map attributes) { this.attributes = new HashMap<>(attributes); } + /** + * Get the attribute of the {@link Resource} + * + * @param attributeKey the key of the requested attribute + * @return the attribute value requested + */ @Generated public Object getAttribute(final String attributeKey) { return this.attributes.getOrDefault(attributeKey, null); } + /** + * Check to see if an attribite has been set on a {@link Resource} + * + * @param attributeKey the key of the requested attribute + * @return a {@link Boolean} value + */ @Generated public Boolean isAttributeSet(final String attributeKey) { return this.attributes.containsKey(attributeKey); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java b/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java index d9c89ab9..5b52b5f1 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/AbstractResource.java @@ -34,7 +34,7 @@ public abstract class AbstractResource implements Resource { protected String id; - public AbstractResource() { + protected AbstractResource() { } @Generated diff --git a/src/main/java/uk/gov/gchq/palisade/resource/ChildResource.java b/src/main/java/uk/gov/gchq/palisade/resource/ChildResource.java index 03d67e76..6eb168dc 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/ChildResource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/ChildResource.java @@ -22,6 +22,12 @@ */ public interface ChildResource extends Resource { + /** + * Sets the parent resource value on an {@link ChildResource} + * + * @param parent the resource to be set as the parent + * @return the {@link ChildResource} object + */ ChildResource parent(ParentResource parent); ParentResource getParent(); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java index d3092477..51c37369 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/ConnectionDetail.java @@ -26,6 +26,11 @@ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public interface ConnectionDetail extends Serializable { + /** + * Create a connection. + * + * @return a connection string for a service, this may be a full URL, a DNS name or a key to be used for lookup elsewhere. + */ String createConnection(); } diff --git a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java index 4a3c17af..b03c0964 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/Resource.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/Resource.java @@ -27,6 +27,12 @@ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public interface Resource extends Comparable, Serializable { + /** + * Set the id value of the resource. + * + * @param id the id value to be set + * @return the {@link Resource} object. + */ Resource id(String id); String getId(); diff --git a/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java b/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java index 00d484e5..d2172580 100644 --- a/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java +++ b/src/main/java/uk/gov/gchq/palisade/resource/impl/SimpleConnectionDetail.java @@ -36,6 +36,12 @@ public SimpleConnectionDetail() { //no-args constructor needed for serialization only } + /** + * Sets the service name value within a {@link SimpleConnectionDetail} object. + * + * @param serviceName the string value of the service, e.g. a URL, DNS name or simple string to be used as a map lookup key + * @return the {@link SimpleConnectionDetail} object + */ @Generated public SimpleConnectionDetail serviceName(final String serviceName) { this.setServiceName(serviceName); diff --git a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java index 1ee9d789..8756175a 100644 --- a/src/main/java/uk/gov/gchq/palisade/rule/Rules.java +++ b/src/main/java/uk/gov/gchq/palisade/rule/Rules.java @@ -65,6 +65,12 @@ public Rules rules(final Map> rules) { } + /** + * Adds the provided rules to the rule set. + * + * @param rules the map of rules to be added + * @return the {@link Rules} object + */ @Generated public Rules addRules(final Map> rules) { requireNonNull(rules, "Cannot add null to the existing rules."); diff --git a/src/main/java/uk/gov/gchq/palisade/user/User.java b/src/main/java/uk/gov/gchq/palisade/user/User.java index 4061abb2..0cdc208c 100644 --- a/src/main/java/uk/gov/gchq/palisade/user/User.java +++ b/src/main/java/uk/gov/gchq/palisade/user/User.java @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -63,9 +64,9 @@ public User() { */ User(final User user) { requireNonNull(user, "User to be cloned cannot be null"); - this.setUserId(user.getUserId()); - this.setRoles(user.getRoles()); - this.setAuths(user.getAuths()); + this.userId = user.getUserId(); + this.roles = user.getRoles(); + this.auths = user.getAuths(); } /** @@ -80,7 +81,6 @@ public User userId(final UserId userId) { return this; } - /** * Sets the userId to a {@link UserId} with the given userId string. * @@ -100,7 +100,7 @@ public User userId(final String userId) { * @return this User instance. */ @Generated - public User auths(final String... auths) { + public User auths(final String[] auths) { this.setAuths(new HashSet<>(Arrays.asList(auths))); return this; } @@ -117,6 +117,12 @@ public User auths(final Set auths) { return this; } + /** + * Adds the user auths. + * + * @param auths the user auths to add. + * @return the {@link User} instance + */ @Generated public User addAuths(final Set auths) { requireNonNull(auths, "Cannot add null auths."); @@ -131,11 +137,17 @@ public User addAuths(final Set auths) { * @return this User instance. */ @Generated - public User roles(final String... roles) { + public User roles(final String[] roles) { this.setRoles(new HashSet<>(Arrays.asList(roles))); return this; } + /** + * Adds the user roles. + * + * @param roles the user roles to be added + * @return the {@link User} instance + */ @Generated public User addRoles(final Set roles) { requireNonNull(auths, "Cannot add null roles."); @@ -168,24 +180,24 @@ public void setUserId(final UserId userId) { @Generated public Set getRoles() { - return roles; + return Collections.unmodifiableSet(roles); } @Generated public void setRoles(final Set roles) { requireNonNull(roles); - this.roles = roles; + this.roles = Collections.unmodifiableSet(roles); } @Generated public Set getAuths() { - return auths; + return Collections.unmodifiableSet(auths); } @Generated public void setAuths(final Set auths) { requireNonNull(auths); - this.auths = auths; + this.auths = Collections.unmodifiableSet(auths); } @Override diff --git a/src/main/java/uk/gov/gchq/palisade/user/UserId.java b/src/main/java/uk/gov/gchq/palisade/user/UserId.java index 97bbbd97..4964ee32 100644 --- a/src/main/java/uk/gov/gchq/palisade/user/UserId.java +++ b/src/main/java/uk/gov/gchq/palisade/user/UserId.java @@ -46,7 +46,7 @@ public UserId() { */ UserId(final UserId userId) { requireNonNull(userId, "UserId to be cloned cannot be null"); - this.setId(userId.getId()); + this.id = userId.getId(); } /** diff --git a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/AbstractResourceBuilder.java similarity index 76% rename from src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java rename to src/main/java/uk/gov/gchq/palisade/util/AbstractResourceBuilder.java index a05e1cd5..dd032b8b 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/ResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/AbstractResourceBuilder.java @@ -27,11 +27,13 @@ import java.util.ServiceLoader.Provider; /** - * ResourceBuilder, taking a URI and building a Resource specific to each scheme + * The AbstractResourceBuilder is an abstract factory for building {@link Resource}s. Given a URI, + * it will identify the appropriate implementation and use it to produce a {@link Resource}. + * This is done using Java's {@link ServiceLoader} mechanism. */ -public abstract class ResourceBuilder { - private static final ServiceLoader LOADER = ServiceLoader.load(ResourceBuilder.class); - private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBuilder.class); +public abstract class AbstractResourceBuilder { + private static final ServiceLoader LOADER = ServiceLoader.load(AbstractResourceBuilder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractResourceBuilder.class); /** * Clears this loader's provider cache so that all providers will be reloaded. @@ -41,14 +43,14 @@ public static void refreshProviders() { } /** - * Taking a resourceUri, create a resource using the ResourceBuilder provided in the LOADER, + * Taking a resourceUri, create a {@link Resource} using the appropriate implementation of the {@link AbstractResourceBuilder} provided in the LOADER, * or throw an exception if the resource scheme is not supported, or no builder exists to build that scheme * * @param resourceUri the Uri of the resource you want to build. * @return a newly created resource */ public static Resource create(final URI resourceUri) { - ResourceBuilder resourceBuilder = LOADER.stream() + AbstractResourceBuilder resourceBuilder = LOADER.stream() .map(Provider::get) .filter(builder -> builder.accepts(resourceUri)) .findAny() @@ -57,7 +59,7 @@ public static Resource create(final URI resourceUri) { } /** - * Create a resource from a uri string + * Create a {@link Resource} from a uri string * Throw IllegalArgumentException if invalid uri string or unsupported scheme * * @param uriString a string value of a url used to create a new resource @@ -72,7 +74,7 @@ public static Resource create(final String uriString) { } /** - * Build a resource, using the uri provided by calling {@link UriBuilder} + * Build a {@link Resource}, using the uri provided by calling {@link UriBuilder} * * @param uri the uri of the resource you want built * @return a newly created resource with the id of the uri. @@ -93,7 +95,7 @@ public Resource buildNormal(final URI uri) { } /** - * An abstract method used in building a resource + * An abstract method used in building a {@link Resource} * * @param resourceUri the uri of the resource you want built * @return a newly created Resource with the id of the the resourceUri. @@ -101,7 +103,7 @@ public Resource buildNormal(final URI uri) { protected abstract Resource build(URI resourceUri); /** - * A abstract method used in building a resource, to check if the Builders provided can accept the resourceUri scheme + * A abstract method used in building a {@link Resource}, to check if the Builders provided can accept the resourceUri scheme * * @param resourceUri the uri of the resource you want built * @return a true/false value if a builder exists that supports the uri scheme. diff --git a/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java index 13085dd6..05ae8c2e 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java +++ b/src/main/java/uk/gov/gchq/palisade/util/FileResourceBuilder.java @@ -46,7 +46,7 @@ * there is no guarantee that this can correctly resolve parents. Instead, use the * methods provided by the appropriate resource impl. */ -public class FileResourceBuilder extends ResourceBuilder { +public class FileResourceBuilder extends AbstractResourceBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(FileResourceBuilder.class); private static final URI ROOT; private static final Set ACCEPTED_SCHEMES = Set.of("hdfs", "file"); diff --git a/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java b/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java index 8c1b486b..2f95c80b 100644 --- a/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java +++ b/src/main/java/uk/gov/gchq/palisade/util/RulesUtil.java @@ -54,10 +54,15 @@ public static Stream applyRulesToStream(final Stream } return records - .peek(processed -> recordsProcessed.incrementAndGet()) - .map(record -> applyRulesToItem(record, user, context, rules)) - .filter(Objects::nonNull) - .peek(returned -> recordsReturned.incrementAndGet()); + .map((T record) -> { + recordsProcessed.incrementAndGet(); + var returned = applyRulesToItem(record, user, context, rules); + if (Objects.nonNull(returned)) { + recordsReturned.incrementAndGet(); + } + return returned; + }) + .filter(Objects::nonNull); } /** diff --git a/src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder b/src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.AbstractResourceBuilder similarity index 100% rename from src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.ResourceBuilder rename to src/main/resources/META-INF/services/uk.gov.gchq.palisade.util.AbstractResourceBuilder From b68b1f9d407b90cc4d7fa49fd735d3d00ee80117 Mon Sep 17 00:00:00 2001 From: ac74475 Date: Thu, 8 Jul 2021 16:22:20 +0100 Subject: [PATCH 27/27] release 0.5.0 (#98) --- mvn_dependency_tree.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvn_dependency_tree.txt b/mvn_dependency_tree.txt index 00ffaf28..02edb727 100644 --- a/mvn_dependency_tree.txt +++ b/mvn_dependency_tree.txt @@ -1,4 +1,4 @@ -uk.gov.gchq.palisade:common:jar:0.5.0-SNAPSHOT +uk.gov.gchq.palisade:common:jar:0.5.0-RELEASE +- com.fasterxml.jackson.core:jackson-databind:jar:2.11.0:compile | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.11.0:compile | \- com.fasterxml.jackson.core:jackson-core:jar:2.11.0:compile