You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClassPathExplorer.java 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package com.vaadin.terminal.gwt.rebind;
  2. import java.io.File;
  3. import java.io.FileFilter;
  4. import java.io.IOException;
  5. import java.net.JarURLConnection;
  6. import java.net.URL;
  7. import java.net.URLConnection;
  8. import java.util.Collection;
  9. import java.util.Enumeration;
  10. import java.util.HashMap;
  11. import java.util.HashSet;
  12. import java.util.Map;
  13. import java.util.Set;
  14. import java.util.jar.JarEntry;
  15. import java.util.jar.JarFile;
  16. import com.vaadin.terminal.Paintable;
  17. import com.vaadin.ui.ClientWidget;
  18. /**
  19. * Utility class to find server side widgets with {@link ClientWidget}
  20. * annotation. Used by WidgetMapGenerator to implement some monkey coding for
  21. * you.
  22. * <p>
  23. * If you end up reading this comment, I guess you have faced a sluggish
  24. * performance of widget compilation or unreliable detection of components in
  25. * your classpaths. The thing you might be able to do is to use annotation
  26. * processing tool like apt to generate the needed information. Then either use
  27. * that information in {@link WidgetMapGenerator} or create the appropriate
  28. * monkey code for gwt directly in annotation processor and get rid of
  29. * {@link WidgetMapGenerator}. Using annotation processor might be a good idea
  30. * when dropping Java 1.5 support (integrated to javac in 6).
  31. *
  32. */
  33. public class ClassPathExplorer {
  34. private final static FileFilter DIRECTORIES_ONLY = new FileFilter() {
  35. public boolean accept(File f) {
  36. if (f.exists() && f.isDirectory()) {
  37. return true;
  38. } else {
  39. return false;
  40. }
  41. }
  42. };
  43. private static Map<URL, String> classpathLocations = getClasspathLocations();
  44. private ClassPathExplorer() {
  45. }
  46. public static Collection<Class<? extends Paintable>> getPaintablesHavingWidgetAnnotation() {
  47. Collection<Class<? extends Paintable>> paintables = new HashSet<Class<? extends Paintable>>();
  48. Set<URL> keySet = classpathLocations.keySet();
  49. for (URL url : keySet) {
  50. searchForPaintables(url, classpathLocations.get(url), paintables);
  51. }
  52. return paintables;
  53. }
  54. /**
  55. * Determine every URL location defined by the current classpath, and it's
  56. * associated package name.
  57. */
  58. public final static Map<URL, String> getClasspathLocations() {
  59. Map<URL, String> locations = new HashMap<URL, String>();
  60. String pathSep = System.getProperty("path.separator");
  61. String classpath = System.getProperty("java.class.path");
  62. String[] split = classpath.split(pathSep);
  63. for (int i = 0; i < split.length; i++) {
  64. String classpathEntry = split[i];
  65. if (acceptClassPathEntry(classpathEntry)) {
  66. File file = new File(classpathEntry);
  67. include(null, file, locations);
  68. }
  69. }
  70. return locations;
  71. }
  72. private static boolean acceptClassPathEntry(String classpathEntry) {
  73. if (!classpathEntry.endsWith(".jar")) {
  74. // accept all non jars (practically directories)
  75. return true;
  76. } else {
  77. // accepts jars that comply with vaadin-component packaging
  78. // convention (.vaadin. or vaadin- as distribution packages),
  79. if (classpathEntry.contains("vaadin-")
  80. || classpathEntry.contains(".vaadin.")) {
  81. return true;
  82. } else {
  83. return false;
  84. }
  85. }
  86. }
  87. /**
  88. * Recursively add subdirectories and jar files to classpathlocations
  89. *
  90. * @param name
  91. * @param file
  92. * @param locations
  93. */
  94. private final static void include(String name, File file,
  95. Map<URL, String> locations) {
  96. if (!file.exists()) {
  97. return;
  98. }
  99. if (!file.isDirectory()) {
  100. // could be a JAR file
  101. includeJar(file, locations);
  102. return;
  103. }
  104. if (file.isHidden() || file.getPath().contains(File.separator + ".")) {
  105. return;
  106. }
  107. if (name == null) {
  108. name = "";
  109. } else {
  110. name += ".";
  111. }
  112. // add all directories recursively
  113. File[] dirs = file.listFiles(DIRECTORIES_ONLY);
  114. for (int i = 0; i < dirs.length; i++) {
  115. try {
  116. // add the present directory
  117. locations.put(new URL("file://" + dirs[i].getCanonicalPath()),
  118. name + dirs[i].getName());
  119. } catch (Exception ioe) {
  120. return;
  121. }
  122. include(name + dirs[i].getName(), dirs[i], locations);
  123. }
  124. }
  125. private static void includeJar(File file, Map<URL, String> locations) {
  126. try {
  127. URL url = new URL("file:" + file.getCanonicalPath());
  128. url = new URL("jar:" + url.toExternalForm() + "!/");
  129. JarURLConnection conn = (JarURLConnection) url.openConnection();
  130. JarFile jarFile = conn.getJarFile();
  131. if (jarFile != null) {
  132. locations.put(url, "");
  133. }
  134. } catch (Exception e) {
  135. // e.printStackTrace();
  136. return;
  137. }
  138. }
  139. private static String packageNameFor(JarEntry entry) {
  140. if (entry == null) {
  141. return "";
  142. }
  143. String s = entry.getName();
  144. if (s == null) {
  145. return "";
  146. }
  147. if (s.length() == 0) {
  148. return s;
  149. }
  150. if (s.startsWith("/")) {
  151. s = s.substring(1, s.length());
  152. }
  153. if (s.endsWith("/")) {
  154. s = s.substring(0, s.length() - 1);
  155. }
  156. return s.replace('/', '.');
  157. }
  158. private final static void searchForPaintables(URL location,
  159. String packageName,
  160. Collection<Class<? extends Paintable>> paintables) {
  161. // Get a File object for the package
  162. File directory = new File(location.getFile());
  163. if (directory.exists() && !directory.isHidden()) {
  164. // Get the list of the files contained in the directory
  165. String[] files = directory.list();
  166. for (int i = 0; i < files.length; i++) {
  167. // we are only interested in .class files
  168. if (files[i].endsWith(".class")) {
  169. // remove the .class extension
  170. String classname = files[i].substring(0,
  171. files[i].length() - 6);
  172. classname = packageName + "." + classname;
  173. tryToAdd(classname, paintables);
  174. }
  175. }
  176. } else {
  177. try {
  178. // check files in jar file, entries will list all directories
  179. // and files in jar
  180. URLConnection openConnection = location.openConnection();
  181. if (openConnection instanceof JarURLConnection) {
  182. JarURLConnection conn = (JarURLConnection) openConnection;
  183. JarFile jarFile = conn.getJarFile();
  184. Enumeration<JarEntry> e = jarFile.entries();
  185. while (e.hasMoreElements()) {
  186. JarEntry entry = e.nextElement();
  187. String entryname = entry.getName();
  188. if (!entry.isDirectory()
  189. && entryname.endsWith(".class")
  190. && !entryname.contains("$")) {
  191. String classname = entryname.substring(0, entryname
  192. .length() - 6);
  193. if (classname.startsWith("/")) {
  194. classname = classname.substring(1);
  195. }
  196. classname = classname.replace('/', '.');
  197. tryToAdd(classname, paintables);
  198. }
  199. }
  200. }
  201. } catch (IOException e) {
  202. System.err.println(e);
  203. }
  204. }
  205. }
  206. private static void tryToAdd(final String fullclassName,
  207. Collection<Class<? extends Paintable>> paintables) {
  208. try {
  209. Class<?> c = Class.forName(fullclassName);
  210. if (c.getAnnotation(ClientWidget.class) != null) {
  211. paintables.add((Class<? extends Paintable>) c);
  212. System.out.println("Found paintable " + fullclassName);
  213. }
  214. } catch (ExceptionInInitializerError e) {
  215. // e.printStackTrace();
  216. } catch (ClassNotFoundException e) {
  217. // e.printStackTrace();
  218. } catch (NoClassDefFoundError e) {
  219. // NOP
  220. } catch (UnsatisfiedLinkError e) {
  221. // NOP
  222. } catch (Exception e) {
  223. e.printStackTrace();
  224. }
  225. }
  226. /**
  227. * Test method for helper tool
  228. */
  229. public static void main(String[] args) {
  230. Collection<Class<? extends Paintable>> paintables = ClassPathExplorer
  231. .getPaintablesHavingWidgetAnnotation();
  232. System.out.println("Found annotated paintables:");
  233. for (Class<? extends Paintable> cls : paintables) {
  234. System.out.println(cls.getCanonicalName());
  235. }
  236. }
  237. }