1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.cms.util;
35
36 import info.magnolia.cms.core.SystemProperty;
37 import info.magnolia.init.MagnoliaInitPaths;
38 import info.magnolia.objectfactory.Components;
39
40 import java.io.File;
41 import java.io.FilenameFilter;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.UnsupportedEncodingException;
45 import java.net.URL;
46 import java.net.URLClassLoader;
47 import java.net.URLDecoder;
48 import java.util.Collection;
49 import java.util.Enumeration;
50 import java.util.HashSet;
51 import java.util.Set;
52 import java.util.jar.JarEntry;
53 import java.util.jar.JarFile;
54 import java.util.regex.Pattern;
55
56 import org.apache.commons.beanutils.BeanUtils;
57 import org.apache.commons.io.FileUtils;
58 import org.apache.commons.io.filefilter.TrueFileFilter;
59 import org.apache.commons.lang3.StringUtils;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63
64
65
66 public class ClasspathResourcesUtil {
67 private static final Logger log = LoggerFactory.getLogger(ClasspathResourcesUtil.class);
68
69
70
71
72 public static interface Filter {
73 public boolean accept(String name);
74 }
75
76
77
78
79 public static class PatternFilter implements Filter {
80 private final Pattern pattern;
81
82 public PatternFilter(String pattern) {
83 this.pattern = Pattern.compile(pattern);
84 }
85
86 public PatternFilter(Pattern pattern) {
87 this.pattern = pattern;
88 }
89
90 @Override
91 public boolean accept(String name) {
92 return pattern.matcher(name).matches();
93 }
94 }
95
96 private static boolean isCache() {
97 final String devMode = SystemProperty.getProperty("magnolia.develop");
98 return !"true".equalsIgnoreCase(devMode);
99 }
100
101
102
103
104
105
106 public static String[] findResources(String regex) {
107 return findResources(new PatternFilter(regex));
108 }
109
110
111
112
113
114
115 public static String[] findResources(Pattern regex) {
116 return findResources(new PatternFilter(regex));
117 }
118
119
120
121
122
123
124 public static String[] findResources(Filter filter) {
125 final Set<String> resources = new HashSet<String>();
126 final ClassLoader cl = getCurrentClassLoader();
127
128
129
130 if (cl instanceof URLClassLoader) {
131 final URLClassLoader urlClassLoader = (URLClassLoader) cl;
132 final URL[] urls = urlClassLoader.getURLs();
133 log.debug("Loading resources from: {}", urls);
134 if (urls.length == 1 && urls[0].getPath().endsWith("WEB-INF/classes/")) {
135
136 log.warn("Looks like we're in a JBoss 5 expanded war directory, will attempt to load resources from the file system instead; see MAGNOLIA-2577.");
137 } else {
138 collectFromURLs(resources, urls, filter);
139 return resources.toArray(new String[resources.size()]);
140 }
141 }
142
143 try {
144
145
146
147
148
149 String classpath = BeanUtils.getProperty(cl, "classPath");
150
151 if (StringUtils.isNotEmpty(classpath)) {
152 collectFromClasspathString(resources, classpath, filter);
153 return resources.toArray(new String[resources.size()]);
154 }
155 } catch (Throwable e) {
156
157 }
158
159
160
161 collectFromFileSystem(filter, resources);
162 return resources.toArray(new String[resources.size()]);
163 }
164
165 protected static void collectFromURLs(Collection<String> resources, URL[] urls, Filter filter) {
166
167 for (int j = 0; j < urls.length; j++) {
168 final File tofile = sanitizeToFile(urls[j]);
169 if (tofile != null) {
170 collectFiles(resources, tofile, filter);
171 }
172 }
173 }
174
175 protected static void collectFromClasspathString(Collection<String> resources, String classpath, Filter filter) {
176 String[] paths = classpath.split(File.pathSeparator);
177 for (int j = 0; j < paths.length; j++) {
178 final File tofile = new File(paths[j]);
179
180 if (tofile.exists()) {
181 collectFiles(resources, tofile, filter);
182 }
183 }
184 }
185
186 protected static void collectFromFileSystem(Filter filter, Collection<String> resources) {
187
188 String rootDire = Components.getComponent(MagnoliaInitPaths.class).getRootPath();
189 String libString = new File(new File(rootDire), "WEB-INF/lib").getAbsolutePath();
190
191 File dir = new File(libString);
192 if (dir.exists()) {
193 File[] files = dir.listFiles(new FilenameFilter() {
194 @Override
195 public boolean accept(File file, String name) {
196 return name.endsWith(".jar");
197 }
198 });
199
200 for (int i = 0; i < files.length; i++) {
201 collectFiles(resources, files[i], filter);
202 }
203 }
204
205
206 String classString = new File(new File(rootDire), "WEB-INF/classes").getAbsolutePath();
207 File classFileDir = new File(classString);
208
209 if (classFileDir.exists()) {
210 collectFiles(resources, classFileDir, filter);
211 }
212 }
213
214 public static File sanitizeToFile(URL url) {
215 if (!"file".equals(url.getProtocol()) && !StringUtils.startsWith(url.toString(), "jar:file:")) {
216 log.warn("Cannot load resources '{}' from '{}' protocol. Only 'file' and 'jar-file' protocols are supported.", url, url.getProtocol());
217 return null;
218 }
219 try {
220 String fileUrl = url.getFile();
221
222 fileUrl = URLDecoder.decode(fileUrl, "UTF-8");
223
224
225 fileUrl = StringUtils.removeStart(fileUrl, "file:");
226 fileUrl = StringUtils.removeEnd(fileUrl, "!/");
227 return new File(fileUrl);
228 } catch (UnsupportedEncodingException e) {
229 throw new RuntimeException(e);
230 }
231 }
232
233
234
235
236
237
238
239
240 private static void collectFiles(Collection<String> resources, File jarOrDir, Filter filter) {
241
242 if (!jarOrDir.exists()) {
243 log.warn("missing file: {}", jarOrDir.getAbsolutePath());
244 return;
245 }
246
247 if (jarOrDir.isDirectory()) {
248 log.debug("looking in dir {}", jarOrDir.getAbsolutePath());
249
250 Collection<File> files = FileUtils.listFiles(jarOrDir, TrueFileFilter.TRUE, TrueFileFilter.TRUE);
251 for (File file : files) {
252 String name = StringUtils.substringAfter(file.getPath(), jarOrDir.getPath());
253
254
255 name = StringUtils.replace(name, "\\", "/");
256 if (!name.startsWith("/")) {
257 name = "/" + name;
258 }
259
260 if (filter.accept(name)) {
261 resources.add(name);
262 }
263 }
264 } else if (jarOrDir.getName().endsWith(".jar")) {
265 log.debug("looking in jar {}", jarOrDir.getAbsolutePath());
266 JarFile jar;
267 try {
268 jar = new JarFile(jarOrDir);
269 } catch (IOException e) {
270 log.error("IOException opening file {}, skipping", jarOrDir.getAbsolutePath());
271 return;
272 }
273 for (Enumeration<JarEntry> em = jar.entries(); em.hasMoreElements(); ) {
274 JarEntry entry = em.nextElement();
275 if (!entry.isDirectory()) {
276 if (filter.accept("/" + entry.getName())) {
277 resources.add("/" + entry.getName());
278 }
279 }
280 }
281 try {
282 jar.close();
283 } catch (IOException e) {
284 log.error("Failed to close jar file : {}", e.getMessage());
285 log.debug("Failed to close jar file", e);
286 }
287 } else {
288 log.debug("Unknown (not jar) file in classpath: {}, skipping.", jarOrDir.getName());
289 }
290
291 }
292
293 public static InputStream getStream(String name) throws IOException {
294 return getStream(name, isCache());
295 }
296
297
298
299
300
301
302 public static InputStream getStream(String name, boolean cache) throws IOException {
303 if (cache) {
304 return getCurrentClassLoader().getResourceAsStream(StringUtils.removeStart(name, "/"));
305 }
306
307
308 URL url = getResource(name);
309 if (url != null) {
310 return url.openStream();
311 }
312
313 log.debug("Can't find {}", name);
314 return null;
315 }
316
317
318
319
320
321
322
323
324
325
326 public static ClassLoader getCurrentClassLoader() {
327 return Thread.currentThread().getContextClassLoader();
328 }
329
330
331
332
333
334
335
336 public static URL getResource(String name) {
337 return getCurrentClassLoader().getResource(StringUtils.removeStart(name, "/"));
338 }
339
340 }