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.lang.ArrayUtils;
60 import org.apache.commons.lang.StringUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71 public class ClasspathResourcesUtil {
72 private static final Logger log = LoggerFactory.getLogger(ClasspathResourcesUtil.class);
73
74
75
76
77
78
79 public static interface Filter {
80 public boolean accept(String name);
81 }
82
83
84
85
86 public static class PatternFilter implements Filter{
87 private final Pattern pattern;
88
89 public PatternFilter(String pattern) {
90 this.pattern = Pattern.compile(pattern);
91 }
92
93 @Override
94 public boolean accept(String name) {
95 return pattern.matcher(name).matches();
96 }
97 }
98
99 private static boolean isCache() {
100 final String devMode = SystemProperty.getProperty("magnolia.develop");
101 return !"true".equalsIgnoreCase(devMode);
102 }
103
104
105
106
107
108 public static String[] findResources(String regex) {
109 return findResources(new PatternFilter(regex));
110 }
111
112
113
114
115
116
117 public static String[] findResources(Filter filter) {
118 final Set<String> resources = new HashSet<String>();
119 final ClassLoader cl = getCurrentClassLoader();
120
121
122
123 if (cl instanceof URLClassLoader) {
124 final URLClassLoader urlClassLoader = (URLClassLoader) cl;
125 final URL[] urls = urlClassLoader.getURLs();
126 if(log.isDebugEnabled()){
127 log.debug("Loading resources from: " + ArrayUtils.toString(urls));
128 }
129 if (urls.length == 1 && urls[0].getPath().endsWith("WEB-INF/classes/")) {
130
131 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.");
132 } else {
133 collectFromURLs(resources, urls, filter);
134 return resources.toArray(new String[resources.size()]);
135 }
136 }
137
138 try {
139
140
141
142
143
144 String classpath = BeanUtils.getProperty(cl, "classPath");
145
146 if (StringUtils.isNotEmpty(classpath)) {
147 collectFromClasspathString(resources, classpath, filter);
148 return resources.toArray(new String[resources.size()]);
149 }
150 }
151 catch (Throwable e) {
152
153 }
154
155
156
157 collectFromFileSystem(filter, resources);
158 return resources.toArray(new String[resources.size()]);
159 }
160
161 protected static void collectFromURLs(Collection<String> resources, URL[] urls, Filter filter) {
162
163 for (int j = 0; j < urls.length; j++) {
164 final File tofile = sanitizeToFile(urls[j]);
165 if (tofile != null) {
166 collectFiles(resources, tofile, filter);
167 }
168 }
169 }
170
171 protected static void collectFromClasspathString(Collection<String> resources, String classpath, Filter filter) {
172 String[] paths = classpath.split(File.pathSeparator);
173 for (int j = 0; j < paths.length; j++) {
174 final File tofile = new File(paths[j]);
175
176 if (tofile.exists()) {
177 collectFiles(resources, tofile, filter);
178 }
179 }
180 }
181
182 protected static void collectFromFileSystem(Filter filter, Collection<String> resources) {
183
184 String rootDire = Components.getComponent(MagnoliaInitPaths.class).getRootPath();
185 String libString = new File(new File(rootDire), "WEB-INF/lib").getAbsolutePath();
186
187 File dir = new File(libString);
188 if (dir.exists()) {
189 File[] files = dir.listFiles(new FilenameFilter() {
190 @Override
191 public boolean accept(File file, String name) {
192 return name.endsWith(".jar");
193 }
194 });
195
196 for (int i = 0; i < files.length; i++) {
197 collectFiles(resources, files[i], filter);
198 }
199 }
200
201
202 String classString = new File(new File(rootDire), "WEB-INF/classes").getAbsolutePath();
203 File classFileDir = new File(classString);
204
205 if (classFileDir.exists()) {
206 collectFiles(resources, classFileDir, filter);
207 }
208 }
209
210 public static File sanitizeToFile(URL url) {
211 if (!"file".equals(url.getProtocol()) && !StringUtils.startsWith(url.toString(), "jar:file:")) {
212 log.warn("Cannot load resources '{}' from '{}' protocol. Only 'file' and 'jar-file' protocols are supported.", url, url.getProtocol());
213 return null;
214 }
215 try {
216 String fileUrl = url.getFile();
217
218 fileUrl = URLDecoder.decode(fileUrl, "UTF-8");
219
220
221 fileUrl = StringUtils.removeStart(fileUrl, "file:");
222 fileUrl = StringUtils.removeEnd(fileUrl, "!/");
223 return new File(fileUrl);
224 }
225 catch (UnsupportedEncodingException e) {
226 throw new RuntimeException(e);
227 }
228 }
229
230
231
232
233
234
235
236 private static void collectFiles(Collection<String> resources, File jarOrDir, Filter filter) {
237
238 if (!jarOrDir.exists()) {
239 log.warn("missing file: {}", jarOrDir.getAbsolutePath());
240 return;
241 }
242
243 if (jarOrDir.isDirectory()) {
244 log.debug("looking in dir {}", jarOrDir.getAbsolutePath());
245
246 Collection<File> files = FileUtils.listFiles(jarOrDir, TrueFileFilter.TRUE, TrueFileFilter.TRUE);
247 for (File file : files) {
248 String name = StringUtils.substringAfter(file.getPath(), jarOrDir.getPath());
249
250
251 name = StringUtils.replace(name, "\\", "/");
252 if (!name.startsWith("/")) {
253 name = "/" + name;
254 }
255
256 if (filter.accept(name)) {
257 resources.add(name);
258 }
259 }
260 }
261 else if (jarOrDir.getName().endsWith(".jar")) {
262 log.debug("looking in jar {}", jarOrDir.getAbsolutePath());
263 JarFile jar;
264 try {
265 jar = new JarFile(jarOrDir);
266 }
267 catch (IOException e) {
268 log.error("IOException opening file {}, skipping", jarOrDir.getAbsolutePath());
269 return;
270 }
271 for (Enumeration<JarEntry> em = jar.entries(); em.hasMoreElements();) {
272 JarEntry entry = em.nextElement();
273 if (!entry.isDirectory()) {
274 if (filter.accept("/" + entry.getName())) {
275 resources.add("/" + entry.getName());
276 }
277 }
278 }
279 try {
280 jar.close();
281 }
282 catch (IOException e) {
283 log.error("Failed to close jar file : " + e.getMessage());
284 log.debug("Failed to close jar file", e);
285 }
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
303 public static InputStream getStream(String name, boolean cache) throws IOException {
304 if (cache) {
305 return getCurrentClassLoader().getResourceAsStream(StringUtils.removeStart(name, "/"));
306 }
307
308
309 URL url = getResource(name);
310 if (url != null) {
311 return url.openStream();
312 }
313
314 log.debug("Can't find {}", name);
315 return null;
316 }
317
318
319
320
321
322 private static ClassLoader getCurrentClassLoader() {
323 return Thread.currentThread().getContextClassLoader();
324 }
325
326
327
328
329
330
331
332 public static URL getResource(String name) {
333 return getCurrentClassLoader().getResource(StringUtils.removeStart(name, "/"));
334 }
335
336 }