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