/atlassian-plugins-webresource/src/main/java/com/atlassian/plugin/webresource/models/RawRequest.java
Java | 345 lines | 171 code | 35 blank | 139 comment | 2 complexity | 109a175c1fe28db16da60d9164452a00 MD5 | raw file
- package com.atlassian.plugin.webresource.models;
- import com.atlassian.json.marshal.Jsonable;
- import com.atlassian.webresource.api.assembler.resource.ResourcePhase;
- import javax.annotation.Nonnull;
- import javax.annotation.Nullable;
- import java.util.ArrayDeque;
- import java.util.Collection;
- import java.util.Deque;
- import java.util.EnumMap;
- import java.util.LinkedHashMap;
- import java.util.LinkedHashSet;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import static com.atlassian.webresource.api.assembler.resource.ResourcePhase.defaultPhase;
- import static com.atlassian.webresource.api.assembler.resource.ResourcePhase.values;
- import static java.util.Arrays.stream;
- import static java.util.Objects.requireNonNull;
- import static java.util.Optional.ofNullable;
- import static java.util.stream.Collectors.toCollection;
- import static java.util.stream.Collectors.toMap;
- import static org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString;
- /**
- * <p>
- * Represents the specific {@link Requestable} keys a developer has explicitly
- * included or excluded for a given request, along with the phases they should be loaded in.
- * </p>
- * <p>
- * The class is "raw" because it is unaware of the inter-relationships between
- * {@link Requestable} items, such as whether or where they are included in a graph.
- * No processing is done, nor should be done, on the inputs.
- * Other classes may perform analysis and optimisations on the raw request.
- * </p>
- * <p>
- * To keep the data in this class "raw", it must only ever reference "entry points"
- * in to a data structure (e.g., a complete {@link com.atlassian.plugin.webresource.graph.DependencyGraph}).
- * Any operations that need to work with a complete, resolved list of {@link Requestable} items
- * (e.g., resolved excluded dependencies) should use a more appropriate class to
- * represent the result of that operation.
- * </p>
- */
- public final class RawRequest {
- private static final String RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE = "The resource resourcePhase type is mandatory for the resource inclusion.";
- /**
- * Unique keys to include in the request by phase.
- */
- private final Map<ResourcePhase, Deque<Requestable>> includedResourcesByType;
- private final Map<ResourcePhase, LinkedHashMap<String, Jsonable>> includedDataByType;
- /**
- * Unique keys to ignore if found when resolving the request.
- */
- private final LinkedHashSet<Requestable> excludedResources;
- private final Set<String> excludedData;
- public RawRequest() {
- excludedResources = new LinkedHashSet<>();
- excludedData = new LinkedHashSet<>();
- includedResourcesByType = new EnumMap<>(ResourcePhase.class);
- includedDataByType = new EnumMap<>(ResourcePhase.class);
- stream(values()).forEach(type -> includedResourcesByType.put(type, new ArrayDeque<>()));
- stream(values()).forEach(type -> includedDataByType.put(type, new LinkedHashMap<>()));
- }
- private RawRequest(@Nonnull final RawRequest other) {
- requireNonNull(other, "The raw request is mandatory for the cloning.");
- excludedResources = new LinkedHashSet<>(other.excludedResources);
- excludedData = new LinkedHashSet<>(other.excludedData);
- includedResourcesByType = other.includedResourcesByType.entrySet()
- .stream()
- .collect(toMap(Entry::getKey, entry -> new ArrayDeque<>(entry.getValue())));
- includedDataByType = other.includedDataByType.entrySet()
- .stream()
- .collect(toMap(Entry::getKey, entry -> new LinkedHashMap<>(entry.getValue())));
- }
- /**
- * Check if anything has been requested in a given phase.
- * @param phase the phase to check for presence of requestables.
- * @return true if any requestables exist in the given resource phase, false if it is empty.
- */
- public boolean hasAny(final ResourcePhase phase) {
- return !(includedResourcesByType.get(phase).isEmpty() && includedDataByType.get(phase).isEmpty());
- }
- /**
- * Clear all the resources from the excluded list.
- */
- public void clearExcluded() {
- excludedResources.clear();
- }
- /**
- * <p>
- * Removes all resources and data requested in {@link ResourcePhase} and
- * adds them to the excluded list.
- *</p>
- * <p>
- * <b>API note:</b> this method mutates the object state in a non-recoverable way.
- * if you need to know what the request was at a particular point in time,
- * the object would need to be cloned at that point in time.
- *</p>
- * <p>
- * <b>Implementation note:</b> since phases are sequential, this method need not exist;
- * it could be replaced with a `getExcluded(ResourcePhase)` method, which
- * would return all developer-excluded resources along with all included
- * resources from all previous phases. This would allow us to make this class
- * immutable.
- * </p>
- */
- public void setPhaseCompleted(@Nonnull final ResourcePhase resourcePhase) {
- final Deque<Requestable> alreadyIncludedResources = includedResourcesByType.get(resourcePhase);
- exclude(alreadyIncludedResources);
- alreadyIncludedResources.clear();
- final LinkedHashMap<String, Jsonable> alreadyIncludedData = includedDataByType.get(resourcePhase);
- this.excludedData.addAll(alreadyIncludedData.keySet());
- alreadyIncludedData.clear();
- }
- @Nonnull
- public RawRequest deepClone() {
- return new RawRequest(this);
- }
- /**
- * Adds a {@link Requestable} to the exclusion list of resources.
- *
- * @param requestableToExclude The requestable resource to be excluded.
- * @return {@code true}: The requestable was added to the exclusion set.
- * {@code false}: The requestable was not added to the exclusion set.
- */
- public boolean exclude(@Nonnull final Requestable requestableToExclude) {
- requireNonNull(requestableToExclude, "The requestable resource is mandatory for the exclusion.");
- return excludedResources.add(requestableToExclude);
- }
- /**
- * @see RawRequest#exclude(Requestable)
- */
- public void exclude(@Nullable final Collection<Requestable> resources) {
- ofNullable(resources)
- .orElseGet(LinkedHashSet::new)
- .forEach(this::exclude);
- }
- /**
- * @see RawRequest#include(ResourcePhase, RawRequest)
- */
- public boolean include(@Nonnull final Requestable resource) {
- return include(defaultPhase(), resource);
- }
- /**
- * @see RawRequest#include(Requestable)
- */
- public void include(@Nullable final Collection<Requestable> resources) {
- ofNullable(resources)
- .orElseGet(LinkedHashSet::new)
- .forEach(this::include);
- }
- /**
- * @see RawRequest#include(ResourcePhase, Requestable)
- */
- public void include(@Nonnull final ResourcePhase resourcePhase, @Nullable final Collection<Requestable> resources) {
- ofNullable(resources)
- .orElseGet(LinkedHashSet::new)
- .forEach(resource -> include(resourcePhase, resource));
- }
- /**
- * @see RawRequest#include(ResourcePhase, RawRequest)
- */
- public boolean include(@Nonnull final RawRequest request) {
- return include(defaultPhase(), request);
- }
- /**
- * @see RawRequest#include(ResourcePhase, RawRequest)
- */
- public boolean include(@Nonnull final ResourcePhase resourcePhase, @Nonnull final Requestable resource) {
- requireNonNull(resource, "The resourceToInclude resource is mandatory for the inclusion.");
- requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
- return includedResourcesByType.get(resourcePhase).add(resource);
- }
- /**
- * Adds a {@link Requestable} to the inclusion list of resources for a specific type.
- *
- * @param resourcePhase The resource resourcePhase type for the resource.
- * @param requestToInclude The requestable resource to be included.
- * @return {@code true}: The requestable was added to the inclusion set.
- * {@code false}: The requestable was not added to the inclusion set.
- */
- public boolean include(@Nonnull final ResourcePhase resourcePhase, @Nonnull final RawRequest requestToInclude) {
- requireNonNull(requestToInclude, "The request is mandatory for the inclusion.");
- requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
- boolean wereIncludedResourcesAdded = includedResourcesByType.get(resourcePhase).addAll(requestToInclude.getIncluded());
- boolean wereExcludedResourcesAdded = excludedResources.addAll(requestToInclude.getExcluded());
- return wereIncludedResourcesAdded && wereExcludedResourcesAdded;
- }
- /**
- * Adds a {@link Requestable} to the inclusion list of resources for a specific type as the top of the priority.
- *
- * @param resourcePhase The resource resourcePhase type for the resource.
- * @param resource The requestable resource to be included.
- */
- public void includeFirst(@Nonnull final ResourcePhase resourcePhase, @Nonnull final Requestable resource) {
- requireNonNull(resource, "The resourceToInclude resource is mandatory for the inclusion.");
- requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
- includedResourcesByType.get(resourcePhase).addFirst(resource);
- }
- /**
- * @see RawRequest#includeFirst(ResourcePhase, Requestable)
- */
- public void includeFirst(@Nonnull final Requestable resource) {
- includeFirst(defaultPhase(), resource);
- }
- /**
- * Get a flat set with all the resources for each type.
- *
- * The order is not meant to be significant. However, some products depend on it.
- * @return A flatted set with all the resources to be included.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
- */
- @Nonnull
- public LinkedHashSet<Requestable> getIncluded() {
- return includedResourcesByType
- .values()
- .stream()
- .flatMap(Collection::stream)
- .collect(toCollection(LinkedHashSet::new));
- }
- /**
- * Returns the included {@link Requestable} as their string representation for retro-compatibility.
- *
- * The order is not meant to be significant. However, some products depend on it.
- * @return A flattened set with all the resources to be included.
- * @deprecated In the future the only method to be used will be {@link RawRequest#getIncluded()}.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
- */
- @Nonnull
- @Deprecated
- public LinkedHashSet<String> getIncludedAsLooseType() {
- return includedResourcesByType
- .values()
- .stream()
- .flatMap(Collection::stream)
- .map(Requestable::toLooseType)
- .collect(toCollection(LinkedHashSet::new));
- }
- /**
- * Retrieve all the resources to be included by phase type.
- *
- * The order is not meant to be significant. However, some products depend on it.
- *
- * @param phaseType The phase type to be used for the filtering.
- * @return The filtered set with the resources or empty if there is not any for the type.
- * @throws NullPointerException If the resource phase type is not defined.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
- * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
- */
- @Nonnull
- public LinkedHashSet<Requestable> getIncluded(@Nonnull final ResourcePhase phaseType) {
- requireNonNull(phaseType, "The resource phase type is mandatory to retrieve the resources included.");
- return new LinkedHashSet<>(includedResourcesByType.get(phaseType));
- }
- @Nonnull
- public LinkedHashMap<String, Jsonable> getIncludedData(@Nonnull final ResourcePhase phaseType) {
- requireNonNull(phaseType, "The resource phase type is mandatory to retrieve the resources data included.");
- return includedDataByType.get(phaseType);
- }
- @Nonnull
- public LinkedHashMap<String, Jsonable> getIncludedData() {
- return includedDataByType
- .values()
- .stream()
- .flatMap(data -> data.entrySet().stream())
- .collect(toMap(Entry::getKey, Entry::getValue, (data1, data2) -> data1, LinkedHashMap::new));
- }
- /**
- * Returns all {@link Requestable} items whose content should not be served to the client in this request.
- * @return A copy of the current excluded resources keys.
- */
- @Nonnull
- public LinkedHashSet<Requestable> getExcluded() {
- return new LinkedHashSet<>(excludedResources);
- }
- /**
- * Returns the excluded {@link Requestable} as their string representation for retro-compatibility.
- *
- * @return A copy of the current excluded resources keys.
- * @deprecated In the future the only method to be used will be {@link RawRequest#getExcluded()}.
- */
- @Nonnull
- @Deprecated
- public LinkedHashSet<String> getExcludedAsLooseType() {
- return excludedResources
- .stream()
- .map(Requestable::toLooseType)
- .collect(toCollection(LinkedHashSet::new));
- }
- @Nonnull
- public Set<String> getExcludedData() {
- return excludedData;
- }
- /**
- * Removes a certain {@link Requestable} from the current {@link RawRequest}.
- * To remove it will clone the current request and create a new one without specific resource.
- *
- * @param resource The resource to be removed.
- * @deprecated It is deprecated due to the fact that the performance will be affected.
- */
- @Deprecated
- public void removeExcluded(@Nonnull final Requestable resource) {
- excludedResources.remove(resource);
- }
- @Override
- public String toString() {
- return reflectionToString(this);
- }
- }