1 package com.atlassian.plugin.webresource;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static com.google.common.collect.Iterables.contains;
5 import static java.util.Collections.emptyMap;
6 import static java.util.Collections.unmodifiableCollection;
7 import static java.util.Collections.unmodifiableMap;
8
9 import com.atlassian.plugin.ModuleDescriptor;
10 import com.atlassian.util.concurrent.ResettableLazyReference;
11
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 import com.google.common.collect.Iterables;
16
17 import java.util.Collections;
18 import java.util.LinkedHashMap;
19 import java.util.LinkedHashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.Stack;
24
25 class DefaultResourceDependencyResolver implements ResourceDependencyResolver
26 {
27 private static final Logger log = LoggerFactory.getLogger(DefaultResourceDependencyResolver.class);
28
29 private final WebResourceIntegration webResourceIntegration;
30 private final ResourceBatchingConfiguration batchingConfiguration;
31 private final Cache cached = new Cache();
32
33 public DefaultResourceDependencyResolver(final WebResourceIntegration webResourceIntegration, final ResourceBatchingConfiguration batchingConfiguration)
34 {
35 this.webResourceIntegration = webResourceIntegration;
36 this.batchingConfiguration = batchingConfiguration;
37 }
38
39 public Iterable<WebResourceModuleDescriptor> getSuperBatchDependencies()
40 {
41 return cached.resourceMap().values();
42 }
43
44 private Iterable<String> getSuperBatchDependencyKeys()
45 {
46 return cached.resourceMap().keySet();
47 }
48
49 public Iterable<WebResourceModuleDescriptor> getDependencies(final String moduleKey, final boolean excludeSuperBatchedResources)
50 {
51 final LinkedHashMap<String, WebResourceModuleDescriptor> orderedResources = new LinkedHashMap<String, WebResourceModuleDescriptor>();
52 final Iterable<String> superBatchResources = excludeSuperBatchedResources ? getSuperBatchDependencyKeys() : Collections.<String> emptyList();
53 resolveDependencies(moduleKey, orderedResources, superBatchResources, new Stack<String>(), null);
54 return orderedResources.values();
55 }
56
57 public Iterable<WebResourceModuleDescriptor> getDependenciesInContext(final String context)
58 {
59 return getDependenciesInContext(context, new LinkedHashSet<String>());
60 }
61
62 public Iterable<WebResourceModuleDescriptor> getDependenciesInContext(final String context, final Set<String> skippedResources)
63 {
64 final Set<WebResourceModuleDescriptor> contextResources = new LinkedHashSet<WebResourceModuleDescriptor>();
65 final Class<WebResourceModuleDescriptor> clazz = WebResourceModuleDescriptor.class;
66 for (final WebResourceModuleDescriptor descriptor : webResourceIntegration.getPluginAccessor().getEnabledModuleDescriptorsByClass(clazz))
67 {
68 if (descriptor.getContexts().contains(context))
69 {
70 final LinkedHashMap<String, WebResourceModuleDescriptor> dependencies = new LinkedHashMap<String, WebResourceModuleDescriptor>();
71 resolveDependencies(descriptor.getCompleteKey(), dependencies, getSuperBatchDependencyKeys(), new Stack<String>(), skippedResources);
72 for (final WebResourceModuleDescriptor dependency : dependencies.values())
73 {
74 contextResources.add(dependency);
75 }
76 }
77 }
78 return unmodifiableCollection(contextResources);
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 private void resolveDependencies(final String moduleKey, final Map<String, WebResourceModuleDescriptor> orderedResourceKeys, final Iterable<String> superBatchResources, final Stack<String> stack, final Set<String> skippedResources)
96 {
97 if (contains(superBatchResources, moduleKey))
98 {
99 log.debug("Not requiring resource: {} because it is part of a super-batch", moduleKey);
100 return;
101 }
102 if (stack.contains(moduleKey))
103 {
104 log.warn("Cyclic plugin resource dependency has been detected with: {} \nStack trace: {}", moduleKey, stack);
105 return;
106 }
107
108 final ModuleDescriptor<?> moduleDescriptor = webResourceIntegration.getPluginAccessor().getEnabledPluginModule(moduleKey);
109 if (!(moduleDescriptor instanceof WebResourceModuleDescriptor))
110 {
111 if (webResourceIntegration.getPluginAccessor().getPluginModule(moduleKey) != null)
112 {
113 log.warn("Cannot include disabled web resource module: " + moduleKey);
114 }
115 else
116 {
117 log.warn("Cannot find web resource module for: " + moduleKey);
118 }
119 return;
120 }
121
122 final WebResourceModuleDescriptor webResourceModuleDescriptor = (WebResourceModuleDescriptor) moduleDescriptor;
123
124 if ((skippedResources != null) && (webResourceModuleDescriptor.getCondition() != null))
125 {
126 skippedResources.add(moduleKey);
127 return;
128 }
129 else if (!webResourceModuleDescriptor.shouldDisplay())
130 {
131 log.debug("Cannot include web resource module {} as its condition fails", moduleDescriptor.getCompleteKey());
132 return;
133 }
134
135 final List<String> dependencies = webResourceModuleDescriptor.getDependencies();
136 if (log.isDebugEnabled())
137 {
138 log.debug("About to add resource [{}] and its dependencies: {}", moduleKey, dependencies);
139 }
140 stack.push(moduleKey);
141 try
142 {
143 for (final String dependency : dependencies)
144 {
145 if (orderedResourceKeys.get(dependency) == null)
146 {
147 resolveDependencies(dependency, orderedResourceKeys, superBatchResources, stack, skippedResources);
148 }
149 }
150 }
151 finally
152 {
153 stack.pop();
154 }
155 orderedResourceKeys.put(moduleKey, webResourceModuleDescriptor);
156 }
157
158 final class Cache
159 {
160 ResettableLazyReference<SuperBatch> lazy = new ResettableLazyReference<SuperBatch>()
161 {
162 @Override
163 protected SuperBatch create() throws Exception
164 {
165
166 final String version = webResourceIntegration.getSuperBatchVersion();
167 return new SuperBatch(version, loadDescriptors(batchingConfiguration.getSuperBatchModuleCompleteKeys()));
168 }
169
170 Map<String, WebResourceModuleDescriptor> loadDescriptors(final Iterable<String> moduleKeys)
171 {
172 if (Iterables.isEmpty(moduleKeys))
173 {
174 return emptyMap();
175 }
176 final Map<String, WebResourceModuleDescriptor> resources = new LinkedHashMap<String, WebResourceModuleDescriptor>();
177 for (final String moduleKey : moduleKeys)
178 {
179 resolveDependencies(moduleKey, resources, Collections.<String> emptyList(), new Stack<String>(), null);
180 }
181 return unmodifiableMap(resources);
182 }
183 };
184
185 Map<String, WebResourceModuleDescriptor> resourceMap()
186 {
187 if (!batchingConfiguration.isSuperBatchingEnabled())
188 {
189 log.debug("Super batching not enabled, but getSuperBatchDependencies() called. Returning empty.");
190 return emptyMap();
191 }
192 while (true)
193 {
194 final SuperBatch batch = lazy.get();
195 if (batch.version.equals(webResourceIntegration.getSuperBatchVersion()))
196 {
197 return batch.resources;
198 }
199
200
201 lazy.reset();
202 }
203 }
204 }
205
206 static final class SuperBatch
207 {
208 final String version;
209 final Map<String, WebResourceModuleDescriptor> resources;
210
211 SuperBatch(final String version, final Map<String, WebResourceModuleDescriptor> resources)
212 {
213 this.version = checkNotNull(version);
214 this.resources = checkNotNull(resources);
215 }
216 }
217 }