1/*2 * Licensed to the Apache Software Foundation (ASF) under one3 * or more contributor license agreements. See the NOTICE file4 * distributed with this work for additional information5 * regarding copyright ownership. The ASF licenses this file6 * to you under the Apache License, Version 2.0 (the7 * "License"); you may not use this file except in compliance8 * with the License. You may obtain a copy of the License at9 *10 * http://www.apache.org/licenses/LICENSE-2.011 *12 * Unless required by applicable law or agreed to in writing, software13 * distributed under the License is distributed on an "AS IS" BASIS,14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15 * See the License for the specific language governing permissions and16 * limitations under the License.17 */1819package org.apache.giraph.utils;
2021import java.io.FileInputStream;
22import java.io.IOException;
23import java.lang.annotation.Annotation;
24import java.io.File;
25import java.util.ArrayList;
26import java.util.Iterator;
27import java.util.List;
28import java.util.NoSuchElementException;
29import java.util.jar.JarEntry;
30import java.util.jar.JarInputStream;
3132/**33 * Helper class to deal with annotations in runtime.34 */35publicclassAnnotationUtils {
36/** Do not instantiate. */37privateAnnotationUtils() {
38 }
3940/**41 * Finds all classes within a package which are annotated with certain42 * annotation.43 *44 * @param annotation Annotation which we are looking for45 * @param <T> Annotation class46 * @param packageName Package in which to search47 * @return The list of annotated classes48 */49publicstatic <T extends Annotation> List<Class<?>> getAnnotatedClasses(
50 Class<T> annotation, String packageName) {
51 ArrayList<Class<?>> ret = new ArrayList<Class<?>>();
52for (Iterator<Class<?>> it = getClassesIterator(packageName);
53 it.hasNext();) {
54 Class<?> clazz = it.next();
55if (clazz.getAnnotation(annotation) != null) {
56 ret.add(clazz);
57 }
58 }
59return ret;
60 }
6162/**63 * @param packageName Package through which to iterate64 * @return Iterator through the classes of this jar file (if executed from65 * jar) or through the classes of org package (if .class file is66 * executed)67 */68publicstatic Iterator<Class<?>> getClassesIterator(String packageName) {
69if (isExecutedFromJar()) {
70try {
71returnnewJarClassesIterator(packageName);
72 } catch (IOException e) {
73thrownew RuntimeException(e);
74 }
75 } else {
76returnnewGeneralClassesIterator(packageName);
77 }
78 }
7980/**81 * @return Whether or not the code is executed from jar file82 */83privatestaticboolean isExecutedFromJar() {
84return AnnotationUtils.class.getResource("AnnotationUtils.class")
85 .getProtocol().equals("jar");
86 }
8788/**89 * To be used when {@link #isExecutedFromJar()} is true.90 *91 * @return If executed from jar file returns the path to the jar92 * (otherwise it will return the folder in which this .class file is93 * located)94 */95privatestatic String getCurrentJar() {
96return AnnotationUtils.class.getProtectionDomain().getCodeSource()
97 .getLocation().getFile();
98 }
99100/**101 * To be used when {@link #isExecutedFromJar()} is true.102 * <p/>103 * Iterator through classes of this jar file.104 */105privatestaticclassJarClassesIteratorimplements Iterator<Class<?>> {
106/** Used to go through classes in current jar */107privatefinal JarInputStream jarIn;
108/** Are we positioned on the next class entry */109privateboolean entryLoaded;
110/** Next class entry */111private JarEntry currentEntry;
112/** Folder in which to look */113privatefinal String path;
114115/**116 * @param packageName Package through which to iterate117 * @throws IOException118 */119publicJarClassesIterator(String packageName) throws IOException {
120 jarIn = new JarInputStream(new FileInputStream(new File(
121 getCurrentJar())));
122 entryLoaded = false;
123 currentEntry = null;
124 path = packageName.replace(".", File.separator);
125 }
126127 @Override
128publicboolean hasNext() {
129 loadNextEntry();
130return currentEntry != null;
131 }
132133 @Override
134public Class<?> next() {
135 loadNextEntry();
136if (currentEntry == null) {
137thrownew NoSuchElementException();
138 }
139 entryLoaded = false;
140141 String className = currentEntry.getName().replace(".class",
142"").replace(File.separator, ".");
143return loadClass(className);
144 }
145146/**147 * Sets position to next class entry148 */149privatevoid loadNextEntry() {
150while (!entryLoaded) {
151try {
152 currentEntry = jarIn.getNextJarEntry();
153if (currentEntry == null || (currentEntry.getName().endsWith(
154".class") && (currentEntry.getName().startsWith(path)))) {
155 entryLoaded = true;
156 } else {
157 currentEntry = null;
158 }
159 } catch (IOException e) {
160thrownew RuntimeException(e);
161 }
162 }
163if (currentEntry == null) {
164try {
165 jarIn.close();
166 } catch (IOException e) {
167thrownew RuntimeException(e);
168 }
169 }
170 }
171172 @Override
173publicvoid remove() {
174thrownew UnsupportedOperationException(
175"Can't remove from classes iterator");
176 }
177 }
178179/**180 * To be used when {@link #isExecutedFromJar()} is false.181 * <p/>182 * Iterator through classes of some package.183 */184privatestaticclassGeneralClassesIteratorimplements Iterator<Class<?>> {
185/** From which position in path the package name starts */186privatefinalint stringPosition;
187/** Recursive directory iterator */188privatefinal Iterator<File> iterator;
189190/**191 * @param packageName Package through which to iterate192 */193publicGeneralClassesIterator(String packageName) {
194 String mainPath = AnnotationUtils.class.getProtectionDomain()
195 .getCodeSource().getLocation().getFile();
196 String subPath = packageName.replace(".", File.separator);
197 File directory = new File(mainPath + subPath);
198 stringPosition = directory.getPath().length() - packageName.length();
199 List<File> files = new ArrayList<File>();
200 addAllClassFiles(directory, files);
201 iterator = files.iterator();
202 }
203204/**205 * Recursively add all .class files from the directory to the list.206 *207 * @param directory Directory from which we are adding files208 * @param files List we add files to209 */210 @edu.umd.cs.findbugs.annotations.SuppressWarnings(
211"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
212privatevoid addAllClassFiles(File directory, List<File> files) {
213for (File file : directory.listFiles()) {
214if (file.isDirectory()) {
215 addAllClassFiles(file, files);
216 } elseif (file.getName().endsWith(".class")) {
217 files.add(file);
218 }
219 }
220 }
221222 @Override
223publicboolean hasNext() {
224return iterator.hasNext();
225 }
226227 @Override
228public Class<?> next() {
229 String className = iterator.next().getPath().substring(stringPosition)
230 .replace(".class", "").replace(File.separator, ".");
231return loadClass(className);
232 }
233234 @Override
235publicvoid remove() {
236thrownew UnsupportedOperationException(
237"Can't remove from classes iterator");
238 }
239 }
240241/**242 * Loads the class with the specified name243 *244 * @param className Name of the class we are loading245 * @return Class with the specified name246 */247privatestatic Class<?> loadClass(String className) {
248try {
249return Class.forName(className);
250 } catch (ClassNotFoundException e) {
251thrownew RuntimeException("Error loading class " + className, e);
252 } catch (NoClassDefFoundError e) {
253thrownew RuntimeException("Error loading class " + className, e);
254 }
255 }
256 }