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