PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-plugins-webresource/src/main/java/com/atlassian/plugin/webresource/models/RawRequest.java

https://bitbucket.org/atlassian/atlassian-plugins-webresource
Java | 345 lines | 171 code | 35 blank | 139 comment | 2 complexity | 109a175c1fe28db16da60d9164452a00 MD5 | raw file
  1. package com.atlassian.plugin.webresource.models;
  2. import com.atlassian.json.marshal.Jsonable;
  3. import com.atlassian.webresource.api.assembler.resource.ResourcePhase;
  4. import javax.annotation.Nonnull;
  5. import javax.annotation.Nullable;
  6. import java.util.ArrayDeque;
  7. import java.util.Collection;
  8. import java.util.Deque;
  9. import java.util.EnumMap;
  10. import java.util.LinkedHashMap;
  11. import java.util.LinkedHashSet;
  12. import java.util.Map;
  13. import java.util.Map.Entry;
  14. import java.util.Set;
  15. import static com.atlassian.webresource.api.assembler.resource.ResourcePhase.defaultPhase;
  16. import static com.atlassian.webresource.api.assembler.resource.ResourcePhase.values;
  17. import static java.util.Arrays.stream;
  18. import static java.util.Objects.requireNonNull;
  19. import static java.util.Optional.ofNullable;
  20. import static java.util.stream.Collectors.toCollection;
  21. import static java.util.stream.Collectors.toMap;
  22. import static org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString;
  23. /**
  24. * <p>
  25. * Represents the specific {@link Requestable} keys a developer has explicitly
  26. * included or excluded for a given request, along with the phases they should be loaded in.
  27. * </p>
  28. * <p>
  29. * The class is "raw" because it is unaware of the inter-relationships between
  30. * {@link Requestable} items, such as whether or where they are included in a graph.
  31. * No processing is done, nor should be done, on the inputs.
  32. * Other classes may perform analysis and optimisations on the raw request.
  33. * </p>
  34. * <p>
  35. * To keep the data in this class "raw", it must only ever reference "entry points"
  36. * in to a data structure (e.g., a complete {@link com.atlassian.plugin.webresource.graph.DependencyGraph}).
  37. * Any operations that need to work with a complete, resolved list of {@link Requestable} items
  38. * (e.g., resolved excluded dependencies) should use a more appropriate class to
  39. * represent the result of that operation.
  40. * </p>
  41. */
  42. public final class RawRequest {
  43. private static final String RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE = "The resource resourcePhase type is mandatory for the resource inclusion.";
  44. /**
  45. * Unique keys to include in the request by phase.
  46. */
  47. private final Map<ResourcePhase, Deque<Requestable>> includedResourcesByType;
  48. private final Map<ResourcePhase, LinkedHashMap<String, Jsonable>> includedDataByType;
  49. /**
  50. * Unique keys to ignore if found when resolving the request.
  51. */
  52. private final LinkedHashSet<Requestable> excludedResources;
  53. private final Set<String> excludedData;
  54. public RawRequest() {
  55. excludedResources = new LinkedHashSet<>();
  56. excludedData = new LinkedHashSet<>();
  57. includedResourcesByType = new EnumMap<>(ResourcePhase.class);
  58. includedDataByType = new EnumMap<>(ResourcePhase.class);
  59. stream(values()).forEach(type -> includedResourcesByType.put(type, new ArrayDeque<>()));
  60. stream(values()).forEach(type -> includedDataByType.put(type, new LinkedHashMap<>()));
  61. }
  62. private RawRequest(@Nonnull final RawRequest other) {
  63. requireNonNull(other, "The raw request is mandatory for the cloning.");
  64. excludedResources = new LinkedHashSet<>(other.excludedResources);
  65. excludedData = new LinkedHashSet<>(other.excludedData);
  66. includedResourcesByType = other.includedResourcesByType.entrySet()
  67. .stream()
  68. .collect(toMap(Entry::getKey, entry -> new ArrayDeque<>(entry.getValue())));
  69. includedDataByType = other.includedDataByType.entrySet()
  70. .stream()
  71. .collect(toMap(Entry::getKey, entry -> new LinkedHashMap<>(entry.getValue())));
  72. }
  73. /**
  74. * Check if anything has been requested in a given phase.
  75. * @param phase the phase to check for presence of requestables.
  76. * @return true if any requestables exist in the given resource phase, false if it is empty.
  77. */
  78. public boolean hasAny(final ResourcePhase phase) {
  79. return !(includedResourcesByType.get(phase).isEmpty() && includedDataByType.get(phase).isEmpty());
  80. }
  81. /**
  82. * Clear all the resources from the excluded list.
  83. */
  84. public void clearExcluded() {
  85. excludedResources.clear();
  86. }
  87. /**
  88. * <p>
  89. * Removes all resources and data requested in {@link ResourcePhase} and
  90. * adds them to the excluded list.
  91. *</p>
  92. * <p>
  93. * <b>API note:</b> this method mutates the object state in a non-recoverable way.
  94. * if you need to know what the request was at a particular point in time,
  95. * the object would need to be cloned at that point in time.
  96. *</p>
  97. * <p>
  98. * <b>Implementation note:</b> since phases are sequential, this method need not exist;
  99. * it could be replaced with a `getExcluded(ResourcePhase)` method, which
  100. * would return all developer-excluded resources along with all included
  101. * resources from all previous phases. This would allow us to make this class
  102. * immutable.
  103. * </p>
  104. */
  105. public void setPhaseCompleted(@Nonnull final ResourcePhase resourcePhase) {
  106. final Deque<Requestable> alreadyIncludedResources = includedResourcesByType.get(resourcePhase);
  107. exclude(alreadyIncludedResources);
  108. alreadyIncludedResources.clear();
  109. final LinkedHashMap<String, Jsonable> alreadyIncludedData = includedDataByType.get(resourcePhase);
  110. this.excludedData.addAll(alreadyIncludedData.keySet());
  111. alreadyIncludedData.clear();
  112. }
  113. @Nonnull
  114. public RawRequest deepClone() {
  115. return new RawRequest(this);
  116. }
  117. /**
  118. * Adds a {@link Requestable} to the exclusion list of resources.
  119. *
  120. * @param requestableToExclude The requestable resource to be excluded.
  121. * @return {@code true}: The requestable was added to the exclusion set.
  122. * {@code false}: The requestable was not added to the exclusion set.
  123. */
  124. public boolean exclude(@Nonnull final Requestable requestableToExclude) {
  125. requireNonNull(requestableToExclude, "The requestable resource is mandatory for the exclusion.");
  126. return excludedResources.add(requestableToExclude);
  127. }
  128. /**
  129. * @see RawRequest#exclude(Requestable)
  130. */
  131. public void exclude(@Nullable final Collection<Requestable> resources) {
  132. ofNullable(resources)
  133. .orElseGet(LinkedHashSet::new)
  134. .forEach(this::exclude);
  135. }
  136. /**
  137. * @see RawRequest#include(ResourcePhase, RawRequest)
  138. */
  139. public boolean include(@Nonnull final Requestable resource) {
  140. return include(defaultPhase(), resource);
  141. }
  142. /**
  143. * @see RawRequest#include(Requestable)
  144. */
  145. public void include(@Nullable final Collection<Requestable> resources) {
  146. ofNullable(resources)
  147. .orElseGet(LinkedHashSet::new)
  148. .forEach(this::include);
  149. }
  150. /**
  151. * @see RawRequest#include(ResourcePhase, Requestable)
  152. */
  153. public void include(@Nonnull final ResourcePhase resourcePhase, @Nullable final Collection<Requestable> resources) {
  154. ofNullable(resources)
  155. .orElseGet(LinkedHashSet::new)
  156. .forEach(resource -> include(resourcePhase, resource));
  157. }
  158. /**
  159. * @see RawRequest#include(ResourcePhase, RawRequest)
  160. */
  161. public boolean include(@Nonnull final RawRequest request) {
  162. return include(defaultPhase(), request);
  163. }
  164. /**
  165. * @see RawRequest#include(ResourcePhase, RawRequest)
  166. */
  167. public boolean include(@Nonnull final ResourcePhase resourcePhase, @Nonnull final Requestable resource) {
  168. requireNonNull(resource, "The resourceToInclude resource is mandatory for the inclusion.");
  169. requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
  170. return includedResourcesByType.get(resourcePhase).add(resource);
  171. }
  172. /**
  173. * Adds a {@link Requestable} to the inclusion list of resources for a specific type.
  174. *
  175. * @param resourcePhase The resource resourcePhase type for the resource.
  176. * @param requestToInclude The requestable resource to be included.
  177. * @return {@code true}: The requestable was added to the inclusion set.
  178. * {@code false}: The requestable was not added to the inclusion set.
  179. */
  180. public boolean include(@Nonnull final ResourcePhase resourcePhase, @Nonnull final RawRequest requestToInclude) {
  181. requireNonNull(requestToInclude, "The request is mandatory for the inclusion.");
  182. requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
  183. boolean wereIncludedResourcesAdded = includedResourcesByType.get(resourcePhase).addAll(requestToInclude.getIncluded());
  184. boolean wereExcludedResourcesAdded = excludedResources.addAll(requestToInclude.getExcluded());
  185. return wereIncludedResourcesAdded && wereExcludedResourcesAdded;
  186. }
  187. /**
  188. * Adds a {@link Requestable} to the inclusion list of resources for a specific type as the top of the priority.
  189. *
  190. * @param resourcePhase The resource resourcePhase type for the resource.
  191. * @param resource The requestable resource to be included.
  192. */
  193. public void includeFirst(@Nonnull final ResourcePhase resourcePhase, @Nonnull final Requestable resource) {
  194. requireNonNull(resource, "The resourceToInclude resource is mandatory for the inclusion.");
  195. requireNonNull(resourcePhase, RESOURCE_PHASE_TYPE_MANDATORY_MESSAGE);
  196. includedResourcesByType.get(resourcePhase).addFirst(resource);
  197. }
  198. /**
  199. * @see RawRequest#includeFirst(ResourcePhase, Requestable)
  200. */
  201. public void includeFirst(@Nonnull final Requestable resource) {
  202. includeFirst(defaultPhase(), resource);
  203. }
  204. /**
  205. * Get a flat set with all the resources for each type.
  206. *
  207. * The order is not meant to be significant. However, some products depend on it.
  208. * @return A flatted set with all the resources to be included.
  209. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
  210. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
  211. */
  212. @Nonnull
  213. public LinkedHashSet<Requestable> getIncluded() {
  214. return includedResourcesByType
  215. .values()
  216. .stream()
  217. .flatMap(Collection::stream)
  218. .collect(toCollection(LinkedHashSet::new));
  219. }
  220. /**
  221. * Returns the included {@link Requestable} as their string representation for retro-compatibility.
  222. *
  223. * The order is not meant to be significant. However, some products depend on it.
  224. * @return A flattened set with all the resources to be included.
  225. * @deprecated In the future the only method to be used will be {@link RawRequest#getIncluded()}.
  226. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
  227. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
  228. */
  229. @Nonnull
  230. @Deprecated
  231. public LinkedHashSet<String> getIncludedAsLooseType() {
  232. return includedResourcesByType
  233. .values()
  234. .stream()
  235. .flatMap(Collection::stream)
  236. .map(Requestable::toLooseType)
  237. .collect(toCollection(LinkedHashSet::new));
  238. }
  239. /**
  240. * Retrieve all the resources to be included by phase type.
  241. *
  242. * The order is not meant to be significant. However, some products depend on it.
  243. *
  244. * @param phaseType The phase type to be used for the filtering.
  245. * @return The filtered set with the resources or empty if there is not any for the type.
  246. * @throws NullPointerException If the resource phase type is not defined.
  247. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-640">PLUGWEB-640</a> for patch to satisfy Confluence.
  248. * @see <a href="https://ecosystem.atlassian.net/browse/PLUGWEB-495">PLUGWEB-495</a> for long-term solution.
  249. */
  250. @Nonnull
  251. public LinkedHashSet<Requestable> getIncluded(@Nonnull final ResourcePhase phaseType) {
  252. requireNonNull(phaseType, "The resource phase type is mandatory to retrieve the resources included.");
  253. return new LinkedHashSet<>(includedResourcesByType.get(phaseType));
  254. }
  255. @Nonnull
  256. public LinkedHashMap<String, Jsonable> getIncludedData(@Nonnull final ResourcePhase phaseType) {
  257. requireNonNull(phaseType, "The resource phase type is mandatory to retrieve the resources data included.");
  258. return includedDataByType.get(phaseType);
  259. }
  260. @Nonnull
  261. public LinkedHashMap<String, Jsonable> getIncludedData() {
  262. return includedDataByType
  263. .values()
  264. .stream()
  265. .flatMap(data -> data.entrySet().stream())
  266. .collect(toMap(Entry::getKey, Entry::getValue, (data1, data2) -> data1, LinkedHashMap::new));
  267. }
  268. /**
  269. * Returns all {@link Requestable} items whose content should not be served to the client in this request.
  270. * @return A copy of the current excluded resources keys.
  271. */
  272. @Nonnull
  273. public LinkedHashSet<Requestable> getExcluded() {
  274. return new LinkedHashSet<>(excludedResources);
  275. }
  276. /**
  277. * Returns the excluded {@link Requestable} as their string representation for retro-compatibility.
  278. *
  279. * @return A copy of the current excluded resources keys.
  280. * @deprecated In the future the only method to be used will be {@link RawRequest#getExcluded()}.
  281. */
  282. @Nonnull
  283. @Deprecated
  284. public LinkedHashSet<String> getExcludedAsLooseType() {
  285. return excludedResources
  286. .stream()
  287. .map(Requestable::toLooseType)
  288. .collect(toCollection(LinkedHashSet::new));
  289. }
  290. @Nonnull
  291. public Set<String> getExcludedData() {
  292. return excludedData;
  293. }
  294. /**
  295. * Removes a certain {@link Requestable} from the current {@link RawRequest}.
  296. * To remove it will clone the current request and create a new one without specific resource.
  297. *
  298. * @param resource The resource to be removed.
  299. * @deprecated It is deprecated due to the fact that the performance will be affected.
  300. */
  301. @Deprecated
  302. public void removeExcluded(@Nonnull final Requestable resource) {
  303. excludedResources.remove(resource);
  304. }
  305. @Override
  306. public String toString() {
  307. return reflectionToString(this);
  308. }
  309. }