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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. package com.vaadin.terminal.gwt.widgetsetutils;
  2. import java.io.File;
  3. import java.io.FileFilter;
  4. import java.io.IOException;
  5. import java.net.JarURLConnection;
  6. import java.net.MalformedURLException;
  7. import java.net.URL;
  8. import java.net.URLConnection;
  9. import java.util.Collection;
  10. import java.util.Enumeration;
  11. import java.util.HashMap;
  12. import java.util.HashSet;
  13. import java.util.Map;
  14. import java.util.Set;
  15. import java.util.jar.Attributes;
  16. import java.util.jar.JarEntry;
  17. import java.util.jar.JarFile;
  18. import java.util.jar.Manifest;
  19. import com.vaadin.terminal.Paintable;
  20. import com.vaadin.ui.ClientWidget;
  21. /**
  22. * Utility class to collect widgetset related information from classpath.
  23. * Utility will seek all directories from classpaths, and jar files having
  24. * "Vaadin-Widgetsets" key in their manifest file.
  25. * <p>
  26. * Used by WidgetMapGenerator and ide tools to implement some monkey coding for
  27. * you.
  28. * <p>
  29. * Developer notice: If you end up reading this comment, I guess you have faced
  30. * a sluggish performance of widget compilation or unreliable detection of
  31. * components in your classpaths. The thing you might be able to do is to use
  32. * annotation processing tool like apt to generate the needed information. Then
  33. * either use that information in {@link WidgetMapGenerator} or create the
  34. * appropriate monkey code for gwt directly in annotation processor and get rid
  35. * of {@link WidgetMapGenerator}. Using annotation processor might be a good
  36. * idea when dropping Java 1.5 support (integrated to javac in 6).
  37. *
  38. */
  39. public class ClassPathExplorer {
  40. private final static FileFilter DIRECTORIES_ONLY = new FileFilter() {
  41. public boolean accept(File f) {
  42. if (f.exists() && f.isDirectory()) {
  43. return true;
  44. } else {
  45. return false;
  46. }
  47. }
  48. };
  49. private static Map<URL, String> classpathLocations = getClasspathLocations();
  50. private ClassPathExplorer() {
  51. }
  52. /**
  53. * Finds server side widgets with {@link ClientWidget} annotation.
  54. */
  55. public static Collection<Class<? extends Paintable>> getPaintablesHavingWidgetAnnotation() {
  56. Collection<Class<? extends Paintable>> paintables = new HashSet<Class<? extends Paintable>>();
  57. Set<URL> keySet = classpathLocations.keySet();
  58. for (URL url : keySet) {
  59. searchForPaintables(url, classpathLocations.get(url), paintables);
  60. }
  61. return paintables;
  62. }
  63. /**
  64. * Finds available widgetset names.
  65. *
  66. * @return
  67. */
  68. public static Collection<String> getAvailableWidgetSets() {
  69. Collection<String> widgetsets = new HashSet<String>();
  70. Set<URL> keySet = classpathLocations.keySet();
  71. for (URL url : keySet) {
  72. searchForWidgetSets(url, widgetsets);
  73. }
  74. return widgetsets;
  75. }
  76. private static void searchForWidgetSets(URL location,
  77. Collection<String> widgetsets) {
  78. File directory = new File(location.getFile());
  79. if (directory.exists() && !directory.isHidden()) {
  80. // Get the list of the files contained in the directory
  81. String[] files = directory.list();
  82. for (int i = 0; i < files.length; i++) {
  83. // we are only interested in .gwt.xml files
  84. if (files[i].endsWith(".gwt.xml")) {
  85. // remove the extension
  86. String classname = files[i].substring(0,
  87. files[i].length() - 8);
  88. classname = classpathLocations.get(location) + "."
  89. + classname;
  90. widgetsets.add(classname);
  91. }
  92. }
  93. } else {
  94. try {
  95. // check files in jar file, entries will list all directories
  96. // and files in jar
  97. URLConnection openConnection = location.openConnection();
  98. if (openConnection instanceof JarURLConnection) {
  99. JarURLConnection conn = (JarURLConnection) openConnection;
  100. JarFile jarFile = conn.getJarFile();
  101. Manifest manifest = jarFile.getManifest();
  102. String value = manifest.getMainAttributes().getValue(
  103. "Vaadin-Widgetsets");
  104. if (value != null) {
  105. String[] widgetsetNames = value.split(",");
  106. for (int i = 0; i < widgetsetNames.length; i++) {
  107. String widgetsetname = widgetsetNames[i].trim()
  108. .intern();
  109. widgetsets.add(widgetsetname);
  110. }
  111. }
  112. }
  113. } catch (IOException e) {
  114. System.err.println(e);
  115. }
  116. }
  117. }
  118. /**
  119. * Determine every URL location defined by the current classpath, and it's
  120. * associated package name.
  121. */
  122. private final static Map<URL, String> getClasspathLocations() {
  123. Map<URL, String> locations = new HashMap<URL, String>();
  124. String pathSep = System.getProperty("path.separator");
  125. String classpath = System.getProperty("java.class.path");
  126. if (classpath.startsWith("\"")) {
  127. classpath = classpath.substring(1);
  128. }
  129. if (classpath.endsWith("\"")) {
  130. classpath = classpath.substring(0, classpath.length() - 1);
  131. }
  132. System.err.println("Classpath: " + classpath);
  133. String[] split = classpath.split(pathSep);
  134. for (int i = 0; i < split.length; i++) {
  135. String classpathEntry = split[i];
  136. if (acceptClassPathEntry(classpathEntry)) {
  137. File file = new File(classpathEntry);
  138. include(null, file, locations);
  139. }
  140. }
  141. return locations;
  142. }
  143. private static boolean acceptClassPathEntry(String classpathEntry) {
  144. if (!classpathEntry.endsWith(".jar")) {
  145. // accept all non jars (practically directories)
  146. return true;
  147. } else {
  148. // accepts jars that comply with vaadin-component packaging
  149. // convention (.vaadin. or vaadin- as distribution packages),
  150. if (classpathEntry.contains("vaadin-")
  151. || classpathEntry.contains(".vaadin.")) {
  152. return true;
  153. } else {
  154. URL url;
  155. try {
  156. url = new URL("file:"
  157. + new File(classpathEntry).getCanonicalPath());
  158. url = new URL("jar:" + url.toExternalForm() + "!/");
  159. JarURLConnection conn = (JarURLConnection) url
  160. .openConnection();
  161. System.out.println(url);
  162. JarFile jarFile = conn.getJarFile();
  163. Manifest manifest = jarFile.getManifest();
  164. Attributes mainAttributes = manifest.getMainAttributes();
  165. if (mainAttributes.getValue("Vaadin-Widgetsets") != null) {
  166. System.err.println("Accepted jar file" + url);
  167. return true;
  168. }
  169. } catch (MalformedURLException e) {
  170. // TODO Auto-generated catch block
  171. e.printStackTrace();
  172. } catch (IOException e) {
  173. // TODO Auto-generated catch block
  174. e.printStackTrace();
  175. }
  176. return false;
  177. }
  178. }
  179. }
  180. /**
  181. * Recursively add subdirectories and jar files to classpathlocations
  182. *
  183. * @param name
  184. * @param file
  185. * @param locations
  186. */
  187. private final static void include(String name, File file,
  188. Map<URL, String> locations) {
  189. if (!file.exists()) {
  190. return;
  191. }
  192. if (!file.isDirectory()) {
  193. // could be a JAR file
  194. includeJar(file, locations);
  195. return;
  196. }
  197. if (file.isHidden() || file.getPath().contains(File.separator + ".")) {
  198. return;
  199. }
  200. if (name == null) {
  201. name = "";
  202. } else {
  203. name += ".";
  204. }
  205. // add all directories recursively
  206. File[] dirs = file.listFiles(DIRECTORIES_ONLY);
  207. for (int i = 0; i < dirs.length; i++) {
  208. try {
  209. // add the present directory
  210. locations.put(new URL("file://" + dirs[i].getCanonicalPath()),
  211. name + dirs[i].getName());
  212. } catch (Exception ioe) {
  213. return;
  214. }
  215. include(name + dirs[i].getName(), dirs[i], locations);
  216. }
  217. }
  218. private static void includeJar(File file, Map<URL, String> locations) {
  219. try {
  220. URL url = new URL("file:" + file.getCanonicalPath());
  221. url = new URL("jar:" + url.toExternalForm() + "!/");
  222. JarURLConnection conn = (JarURLConnection) url.openConnection();
  223. JarFile jarFile = conn.getJarFile();
  224. if (jarFile != null) {
  225. locations.put(url, "");
  226. }
  227. } catch (Exception e) {
  228. // e.printStackTrace();
  229. return;
  230. }
  231. }
  232. private final static void searchForPaintables(URL location,
  233. String packageName,
  234. Collection<Class<? extends Paintable>> paintables) {
  235. // Get a File object for the package
  236. File directory = new File(location.getFile());
  237. if (directory.exists() && !directory.isHidden()) {
  238. // Get the list of the files contained in the directory
  239. String[] files = directory.list();
  240. for (int i = 0; i < files.length; i++) {
  241. // we are only interested in .class files
  242. if (files[i].endsWith(".class")) {
  243. // remove the .class extension
  244. String classname = files[i].substring(0,
  245. files[i].length() - 6);
  246. classname = packageName + "." + classname;
  247. tryToAdd(classname, paintables);
  248. }
  249. }
  250. } else {
  251. try {
  252. // check files in jar file, entries will list all directories
  253. // and files in jar
  254. URLConnection openConnection = location.openConnection();
  255. if (openConnection instanceof JarURLConnection) {
  256. JarURLConnection conn = (JarURLConnection) openConnection;
  257. JarFile jarFile = conn.getJarFile();
  258. Enumeration<JarEntry> e = jarFile.entries();
  259. while (e.hasMoreElements()) {
  260. JarEntry entry = e.nextElement();
  261. String entryname = entry.getName();
  262. if (!entry.isDirectory()
  263. && entryname.endsWith(".class")
  264. && !entryname.contains("$")) {
  265. String classname = entryname.substring(0, entryname
  266. .length() - 6);
  267. if (classname.startsWith("/")) {
  268. classname = classname.substring(1);
  269. }
  270. classname = classname.replace('/', '.');
  271. tryToAdd(classname, paintables);
  272. }
  273. }
  274. }
  275. } catch (IOException e) {
  276. System.err.println(e);
  277. }
  278. }
  279. }
  280. private static void tryToAdd(final String fullclassName,
  281. Collection<Class<? extends Paintable>> paintables) {
  282. try {
  283. Class<?> c = Class.forName(fullclassName);
  284. if (c.getAnnotation(ClientWidget.class) != null) {
  285. paintables.add((Class<? extends Paintable>) c);
  286. // System.out.println("Found paintable " + fullclassName);
  287. }
  288. } catch (ExceptionInInitializerError e) {
  289. // e.printStackTrace();
  290. } catch (ClassNotFoundException e) {
  291. // e.printStackTrace();
  292. } catch (NoClassDefFoundError e) {
  293. // NOP
  294. } catch (UnsatisfiedLinkError e) {
  295. // NOP
  296. } catch (Exception e) {
  297. e.printStackTrace();
  298. }
  299. }
  300. /**
  301. * Test method for helper tool
  302. */
  303. public static void main(String[] args) {
  304. Collection<Class<? extends Paintable>> paintables = ClassPathExplorer
  305. .getPaintablesHavingWidgetAnnotation();
  306. System.out.println("Found annotated paintables:");
  307. for (Class<? extends Paintable> cls : paintables) {
  308. System.out.println(cls.getCanonicalName());
  309. }
  310. System.out.println();
  311. System.out.println("Searching available widgetsets...");
  312. Collection<String> availableWidgetSets = ClassPathExplorer
  313. .getAvailableWidgetSets();
  314. for (String string : availableWidgetSets) {
  315. System.out.println(string);
  316. }
  317. }
  318. }