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