Skip to content

Package: EntityLoader

EntityLoader

nameinstructionbranchcomplexitylinemethod
EntityLoader()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
discoverEntities(String)
M: 7 C: 59
89%
M: 0 C: 6
100%
M: 0 C: 4
100%
M: 2 C: 13
87%
M: 0 C: 1
100%
discoverEntityClasses(Configuration)
M: 0 C: 28
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
getUrlAsUri(URL)
M: 16 C: 3
16%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 2 C: 1
33%
M: 0 C: 1
100%
processClass(String, Set)
M: 7 C: 13
65%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 2 C: 5
71%
M: 0 C: 1
100%
processDirectory(File, String, Set)
M: 1 C: 78
99%
M: 1 C: 9
90%
M: 1 C: 5
83%
M: 1 C: 13
93%
M: 0 C: 1
100%
processJarFile(URL, String, Set)
M: 14 C: 68
83%
M: 1 C: 7
88%
M: 1 C: 4
80%
M: 2 C: 17
89%
M: 0 C: 1
100%
static {...}
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.loaders;
14:
15: import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
16: import cz.cvut.kbss.jopa.model.annotations.OWLClass;
17: import cz.cvut.kbss.jopa.utils.Configuration;
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.URI;
24: import java.net.URISyntaxException;
25: import java.net.URL;
26: import java.util.Enumeration;
27: import java.util.HashSet;
28: import java.util.Objects;
29: import java.util.Set;
30: import java.util.jar.JarEntry;
31: import java.util.jar.JarFile;
32:
33: public class EntityLoader {
34:
35: private static final Logger LOG = LoggerFactory.getLogger(EntityLoader.class);
36:
37: private static final String JAR_FILE_SUFFIX = ".jar";
38: private static final String CLASS_FILE_SUFFIX = ".class";
39:
40: /**
41: * Discovers and returns all entity classes within the scan package and its subpackages.
42: * <p>
43: * I.e., it looks for classes annotated with {@link OWLClass}.
44: *
45: * @param configuration Persistence configuration, should contain value for the {@link
46: * JOPAPersistenceProperties#SCAN_PACKAGE} property
47: * @return Set of entity classes
48: * @throws IllegalArgumentException If {@link JOPAPersistenceProperties#SCAN_PACKAGE} values is missing
49: */
50: public Set<Class<?>> discoverEntityClasses(Configuration configuration) {
51: Objects.requireNonNull(configuration);
52:• if (!configuration.contains(JOPAPersistenceProperties.SCAN_PACKAGE)) {
53: throw new IllegalArgumentException(
54: "Missing the " + JOPAPersistenceProperties.SCAN_PACKAGE + " property.");
55: }
56: String toScan = configuration.get(JOPAPersistenceProperties.SCAN_PACKAGE);
57:• if (toScan.isEmpty()) {
58: throw new IllegalArgumentException(JOPAPersistenceProperties.SCAN_PACKAGE + " property cannot be empty.");
59: }
60: return discoverEntities(toScan);
61: }
62:
63:
64: /**
65: * Using code from https://github.com/ddopson/java-class-enumerator
66: */
67: private Set<Class<?>> discoverEntities(String scanPath) {
68: final Set<Class<?>> all = new HashSet<>();
69: final ClassLoader loader = Thread.currentThread().getContextClassLoader();
70: try {
71: Enumeration<URL> urls = loader.getResources(scanPath.replace('.', '/'));
72:• while (urls.hasMoreElements()) {
73: final URL url = urls.nextElement();
74:• if (url.toString().startsWith("jar:")) {
75: processJarFile(url, scanPath, all);
76: } else {
77: processDirectory(new File(getUrlAsUri(url).getPath()), scanPath, all);
78: }
79: }
80: } catch (IOException e) {
81: throw new JopaInitializationException("Unable to scan packages for entity classes.", e);
82: }
83:• if (all.isEmpty()) {
84: LOG.warn("No entity classes found in package " + scanPath);
85: }
86: return all;
87: }
88:
89: private static URI getUrlAsUri(URL url) {
90: try {
91: // Transformation to URI handles encoding, e.g. of whitespaces in the path
92: return url.toURI();
93: } catch (URISyntaxException ex) {
94: throw new JopaInitializationException(
95: "Unable to scan resource " + url + ". It is not a valid URI.", ex);
96: }
97: }
98:
99: private void processJarFile(URL jarResource, String packageName, Set<Class<?>> entityClasses) {
100: final String relPath = packageName.replace('.', '/');
101: final String jarPath = jarResource.getPath().replaceFirst("[.]jar[!].*", JAR_FILE_SUFFIX)
102: .replaceFirst("file:", "");
103:
104: LOG.trace("Scanning jar file {} for entity classes.", jarPath);
105: try (final JarFile jarFile = new JarFile(jarPath)) {
106: final Enumeration<JarEntry> entries = jarFile.entries();
107:• while (entries.hasMoreElements()) {
108: final JarEntry entry = entries.nextElement();
109: final String entryName = entry.getName();
110: String className = null;
111:• if (entryName.endsWith(CLASS_FILE_SUFFIX) && entryName.startsWith(relPath)) {
112: className = entryName.replace('/', '.').replace('\\', '.').replace(CLASS_FILE_SUFFIX, "");
113: }
114:• if (className != null) {
115: processClass(className, entityClasses);
116: }
117: }
118: } catch (IOException e) {
119: throw new JopaInitializationException("Unexpected IOException reading JAR File " + jarPath, e);
120: }
121: }
122:
123: private void processClass(String className, Set<Class<?>> entityClasses) {
124: try {
125: final Class<?> cls = Class.forName(className);
126:• if (cls.getAnnotation(OWLClass.class) != null) {
127: entityClasses.add(cls);
128: }
129: } catch (ClassNotFoundException e) {
130: throw new JopaInitializationException("Unexpected ClassNotFoundException when scanning for entities.", e);
131: }
132: }
133:
134: private void processDirectory(File dir, String packageName, Set<Class<?>> entityClasses) {
135: LOG.trace("Scanning directory {} for entity classes.", dir);
136: // Get the list of the files contained in the package
137: final String[] files = dir.list();
138:• if (files == null) {
139: return;
140: }
141:• for (String fileName : files) {
142: String className = null;
143: // we are only interested in .class files
144:• if (fileName.endsWith(CLASS_FILE_SUFFIX)) {
145: // removes the .class extension
146: className = packageName + '.' + fileName.substring(0, fileName.length() - 6);
147: }
148:• if (className != null) {
149: processClass(className, entityClasses);
150: }
151: final File subDir = new File(dir, fileName);
152:• if (subDir.isDirectory()) {
153: processDirectory(subDir, packageName + '.' + fileName, entityClasses);
154: }
155: }
156: }
157: }