Skip to content

Method: getUrlAsUri(URL)

1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.loaders;
16:
17: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
18: import org.slf4j.Logger;
19: import org.slf4j.LoggerFactory;
20:
21: import java.io.File;
22: import java.io.IOException;
23: import java.net.MalformedURLException;
24: import java.net.URI;
25: import java.net.URISyntaxException;
26: import java.net.URL;
27: import java.util.ArrayList;
28: import java.util.Enumeration;
29: import java.util.List;
30: import java.util.function.Consumer;
31: import java.util.jar.JarEntry;
32: import java.util.jar.JarFile;
33:
34: /**
35: * Processes classes available to the current classloader.
36: */
37: class ClasspathScanner {
38:
39: private static final Logger LOG = LoggerFactory.getLogger(ClasspathScanner.class);
40:
41: private static final String JAR_FILE_SUFFIX = ".jar";
42: private static final String CLASS_FILE_SUFFIX = ".class";
43:
44: private final List<Consumer<Class<?>>> listeners = new ArrayList<>();
45:
46: void addListener(Consumer<Class<?>> listener) {
47: listeners.add(listener);
48: }
49:
50: /**
51: * Inspired by https://github.com/ddopson/java-class-enumerator
52: */
53: void processClasses(String scanPath) {
54: final ClassLoader loader = Thread.currentThread().getContextClassLoader();
55: try {
56: Enumeration<URL> urls = loader.getResources(scanPath.replace('.', '/'));
57: while (urls.hasMoreElements()) {
58: final URL url = urls.nextElement();
59: if (isJar(url.toString())) {
60: processJarFile(url, scanPath);
61: } else {
62: processDirectory(new File(getUrlAsUri(url).getPath()), scanPath);
63: }
64: }
65: // Scan jar files on classpath
66: Enumeration<URL> resources = loader.getResources(".");
67: while (resources.hasMoreElements()) {
68: URL resourceURL = resources.nextElement();
69: if (isJar(resourceURL.toString()))
70: processJarFile(resourceURL, scanPath);
71: }
72: } catch (IOException e) {
73: throw new OWLPersistenceException("Unable to scan packages for entity classes.", e);
74: }
75: }
76:
77: private static boolean isJar(String filePath) {
78: return filePath.startsWith("jar:") || filePath.endsWith(JAR_FILE_SUFFIX);
79: }
80:
81: private static URI getUrlAsUri(URL url) {
82: try {
83: // Transformation to URI handles encoding, e.g. of whitespaces in the path
84: return url.toURI();
85: } catch (URISyntaxException ex) {
86: throw new OWLPersistenceException(
87: "Unable to scan resource " + url + ". It is not a valid URI.", ex);
88: }
89: }
90:
91: private void processJarFile(URL jarResource, String packageName) {
92: final String relPath = packageName.replace('.', '/');
93: final String jarPath = jarResource.getPath().replaceFirst("[.]jar[!].*", JAR_FILE_SUFFIX)
94: .replaceFirst("file:", "");
95:
96: LOG.trace("Scanning jar file {} for entity classes.", jarPath);
97: try (final JarFile jarFile = new JarFile(jarPath)) {
98: final Enumeration<JarEntry> entries = jarFile.entries();
99: while (entries.hasMoreElements()) {
100: final JarEntry entry = entries.nextElement();
101: final String entryName = entry.getName();
102: String className = null;
103: if (entryName.endsWith(CLASS_FILE_SUFFIX) && entryName.startsWith(relPath)) {
104: className = entryName.replace('/', '.').replace('\\', '.');
105: className = className.substring(0, className.length() - CLASS_FILE_SUFFIX.length());
106: }
107: if (className != null) {
108: processClass(className);
109: }
110: }
111: } catch (IOException e) {
112: throw new OWLPersistenceException("Unexpected IOException reading JAR File " + jarPath, e);
113: }
114: }
115:
116: private void processClass(String className) {
117: try {
118: final Class<?> cls = Class.forName(className);
119: listeners.forEach(listener -> listener.accept(cls));
120: } catch (ClassNotFoundException e) {
121: throw new OWLPersistenceException("Unexpected ClassNotFoundException when scanning for entities.", e);
122: }
123: }
124:
125: private void processDirectory(File dir, String packageName)
126: throws MalformedURLException {
127: LOG.trace("Scanning directory {} for entity classes.", dir);
128: // Get the list of the files contained in the package
129: final String[] files = dir.list();
130: if (files == null) {
131: return;
132: }
133: for (String fileName : files) {
134: String className = null;
135: // we are only interested in .class files
136: if (fileName.endsWith(CLASS_FILE_SUFFIX)) {
137: // removes the .class extension
138: className = packageName + '.' + fileName.substring(0, fileName.length() - 6);
139: }
140: if (className != null) {
141: processClass(className);
142: }
143: final File subDir = new File(dir, fileName);
144: if (subDir.isDirectory()) {
145: processDirectory(subDir, packageName + (!packageName.isEmpty() ? '.' : "") + fileName);
146: } else if (isJar(subDir.getAbsolutePath())) {
147: processJarFile(subDir.toURI().toURL(), packageName);
148: }
149: }
150: }
151: }