001: /*
002: * Created on 09/01/2005
003: *
004: * Swing Components - visit http://sf.net/projects/gfd
005: *
006: * Copyright (C) 2004 Igor Regis da Silva Simões
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022: package br.com.igor.plugin.core;
023:
024: import java.io.DataInputStream;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileNotFoundException;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.net.MalformedURLException;
032: import java.net.URL;
033: import java.util.Enumeration;
034: import java.util.HashMap;
035: import java.util.Map;
036: import java.util.Properties;
037: import java.util.Vector;
038: import java.util.zip.ZipEntry;
039: import java.util.zip.ZipFile;
040:
041: import br.com.igor.util.FileManager;
042:
043: /**
044: * @author Igor Regis da Silva Simoes
045: */
046: public class DynamicClassLoader extends ClassLoader {
047: private static DynamicClassLoader instance = null;
048:
049: private Map<String, Vector<URL>> index = new HashMap<String, Vector<URL>>();
050:
051: /**
052: *
053: */
054: private DynamicClassLoader() {
055: // Overrided to change the visibility to private
056: performClassPathIndexing();
057: }
058:
059: /**
060: * Retorna um singleton desta classe
061: * @return DynamicClassLoader
062: */
063: public static final DynamicClassLoader getClassLoader() {
064: if (instance == null)
065: instance = new DynamicClassLoader();
066: return instance;
067: }
068:
069: /**
070: * Find the file that contains the class definition return the class defined by this file.
071: * The search path will be the plugin directory that must exist under the execution dir
072: * @see java.lang.ClassLoader#findClass(java.lang.String)
073: */
074: @Override
075: protected Class<?> findClass(String name)
076: throws ClassNotFoundException {
077: Vector<URL> classeList = null;
078: if ((classeList = index.get(name)) != null) {
079: try {
080: DataInputStream fonte = new DataInputStream(classeList
081: .get(0).openStream());
082: byte[] data = new byte[fonte.available()];
083: fonte.readFully(data);//Read the class definition
084: fonte.close();
085: return defineClass(name, data, 0, data.length);//define the byte array and return de defined class
086: } catch (IOException e) {
087: // Não vamos fazer nada apenas continuar até o proximo arquivo
088: }
089: }
090: File[] classPath = getDynamicClassPath();
091: File[] arqPlugin;
092:
093: //For each content file...
094: for (int i = 0; i < classPath.length; i++) {
095: if (classPath[i].isDirectory()) {
096: arqPlugin = classPath[i].listFiles();//We get the content
097:
098: for (int j = 0; j < arqPlugin.length; j++) {
099: if (arqPlugin[j].isFile()
100: && arqPlugin[j].getName().endsWith(".jar"))//If it's a jar file...
101: {
102: try {
103: ZipEntry ze = null;
104: ZipFile zf = new ZipFile(arqPlugin[j]);
105:
106: //Check if the class file exists in this file
107: if ((ze = zf.getEntry(name.replaceAll(
108: "\\.", "/")
109: + ".class")) != null) {
110: DataInputStream fonte = new DataInputStream(
111: zf.getInputStream(ze));
112: byte[] data = new byte[fonte
113: .available()];
114:
115: fonte.readFully(data);//Read the class definition
116: fonte.close();
117: return defineClass(name, data, 0,
118: data.length);//define the byte array and return de defined class
119: }
120: zf.close();
121: } catch (Exception e) {
122: //Não vamos fazer nada apenas continuar até o proximo arquivo
123: }
124: }
125: }
126: }
127: }
128: //Let's see what the parent class loader can find
129: return super .findClass(name);
130: }
131:
132: /**
133: * Build the dynamic classpath to be used by this ClassLoader
134: * @return Array of File's with all files that are inside of classpath
135: */
136: private File[] getDynamicClassPath() {
137: //We get the plugins directory that will ever be under
138: //of the user dir (execution dir)
139: File pluginsDir = new File(System.getProperty("user.dir")
140: + File.separator + "plugins");
141:
142: //We get the content of this directory
143: File[] plugins = pluginsDir.listFiles();
144: File[] classPath = new File[plugins.length + 2];
145:
146: for (int i = 0; i < plugins.length; i++) {
147: classPath[i] = plugins[i];
148: }
149:
150: //Add the look and feel folder
151: classPath[classPath.length - 2] = new File(System
152: .getProperty("user.dir")
153: + File.separator + "lookAndFeel");
154: //Add current dir (must be the gfp folder)
155: classPath[classPath.length - 1] = new File(System
156: .getProperty("user.dir"));
157:
158: return classPath;
159: }
160:
161: public void performClassPathIndexing() {
162: File[] classPath = getDynamicClassPath();
163: File[] arqPlugin;
164:
165: for (int i = 0; i < classPath.length; i++) {
166: if (classPath[i].isDirectory()) {
167: arqPlugin = classPath[i].listFiles();//We get the content
168:
169: for (int j = 0; j < arqPlugin.length; j++) {
170: if (arqPlugin[j].isFile()
171: && arqPlugin[j].getName().endsWith(".jar"))//If it's a jar file...
172: {
173: try {
174: ZipFile zf = new ZipFile(arqPlugin[j]);
175: Enumeration entries = zf.entries();
176:
177: while (entries.hasMoreElements()) {
178: ZipEntry entry = (ZipEntry) entries
179: .nextElement();
180: if (entry.isDirectory())
181: continue;
182:
183: String indice = null;
184: if (entry.getName().endsWith(".class")) {
185: indice = entry
186: .getName()
187: .replace('/', '.')
188: .replace('\\', '.')
189: .substring(
190: 0,
191: entry
192: .getName()
193: .indexOf(
194: ".class"));
195: } else if (entry.getName().endsWith(
196: ".properties")) {
197: indice = entry.getName();
198: // replace('/', '.').
199: // replace('\\', '.').substring(0, entry.getName().indexOf(".properties"));
200: } else {
201: indice = entry.getName();
202: }
203: // if (indice.toLowerCase().indexOf("visa") != -1) //For debug only
204: // {
205: // System.out.println(arqPlugin[j]);
206: // System.out.println("Index " + indice);
207: // }
208: Vector<URL> urls = null;
209: if (index.get(indice) != null)
210: urls = index.get(indice);
211: else
212: urls = new Vector<URL>();
213:
214: urls
215: .add(new URL(
216: "jar:file:///"
217: + arqPlugin[j]
218: .getAbsolutePath()
219: .replaceAll(
220: "\\\\",
221: "/")
222: .replaceAll(
223: "//",
224: "")
225: + "!/"
226: + entry
227: .getName()));
228: index.put(indice, urls);
229: }
230: zf.close();
231: } catch (Exception e) {
232: e.printStackTrace();
233: //Não vamos fazer nada apenas continuar até o proximo arquivo
234: }
235: }
236: }
237: }
238: }
239: }
240:
241: /**
242: * Find all resources with the given name
243: * @see java.lang.ClassLoader#findResources(java.lang.String)
244: */
245: @SuppressWarnings("unchecked")
246: @Override
247: protected Enumeration findResources(String name) throws IOException {
248: Vector<URL> classeList = null;
249: String indiceCache = name.substring(1);
250: if ((classeList = index.get(indiceCache)) != null) {
251: if (classeList.size() == 0)
252: return super .findResources(name);//Let's see what the parent class loader can find
253: else
254: return classeList.elements();
255: }
256:
257: Vector<URL> urls = new Vector<URL>();
258: File[] classPath = getDynamicClassPath();
259: File[] arqPlugin;
260:
261: //For each content file...
262: for (int i = 0; i < classPath.length; i++) {
263: if (classPath[i].isDirectory()) {
264: arqPlugin = classPath[i].listFiles();//We get the content
265:
266: for (int j = 0; j < arqPlugin.length; j++) {
267: if (arqPlugin[j].isFile()
268: && arqPlugin[j].getName().endsWith(".jar"))//If it's a jar file...
269: {
270: try {
271: ZipFile zf = new ZipFile(arqPlugin[j]);
272:
273: //Check if the class file exists in this file
274: if (zf.getEntry(name.substring(1)) != null) {
275: urls
276: .add(new URL(
277: "jar:file:///"
278: + arqPlugin[j]
279: .getAbsolutePath()
280: .replaceAll(
281: "\\\\",
282: "/")
283: .replaceAll(
284: "//",
285: "")
286: + "!" + name));
287: // jar:file:///c:/GFD/plugins/maisUm/teste.jar!/xml/actions/Actions.xml
288: }
289: zf.close();
290: } catch (Exception e) {
291: e.printStackTrace();
292: //Não vamos fazer nada apenas continuar até o proximo arquivo
293: }
294: }
295: }
296: }
297: }
298: urls.trimToSize();
299: if (urls.size() == 0)
300: return super .findResources(name);//Let's see what the parent class loader can find
301: else
302: return urls.elements();
303: }
304:
305: /**
306: * Find all resources whose the name end with the given parameter
307: * @param name Suffix
308: * @return Enumeration com todos os recursos que terminam com o nome
309: */
310: public static Enumeration<URL> getSystemResourcesEndingWith(
311: String name) {
312: Vector<URL> urls = new Vector<URL>();
313: File[] classPath = getClassLoader().getDynamicClassPath();
314: File[] arqPlugin;
315:
316: //For each content file...
317: for (int i = 0; i < classPath.length; i++) {
318: if (classPath[i].isDirectory()) {
319: arqPlugin = classPath[i].listFiles();//We get the content
320:
321: for (int j = 0; j < arqPlugin.length; j++) {
322: if (arqPlugin[j].isFile()
323: && arqPlugin[j].getName().endsWith(".jar"))//If it's a jar file...
324: {
325: try {
326: ZipEntry ze = null;
327: ZipFile zf = new ZipFile(arqPlugin[j]);
328: //Check if the class file exists in this file
329: Enumeration entries = zf.entries();
330: while (entries.hasMoreElements()) {
331: ze = (ZipEntry) entries.nextElement();
332: if (ze.getName().endsWith(name)) {//FIXME Tem erro aqui
333: urls.add(new URL("jar:file://"
334: + arqPlugin[j]
335: .getAbsolutePath()
336: .replace('C', '/')
337: .replace(':', '/')
338: .replaceAll("\\\\",
339: "/")
340: .replaceAll("//",
341: "") + "!/"
342: + ze.getName()));
343: // jar:file:///GFD/plugins/maisUm/teste.jar!/xml/actions/Actions.xml
344: }
345: }
346: zf.close();
347: } catch (Exception e) {
348: e.printStackTrace();
349: //Não vamos fazer nada apenas continuar até o proximo arquivo
350: }
351: }
352: }
353:
354: //Se for o diretorio de lookAndFeel nós vamos procurar dentro dele
355: if (classPath[i].getName().indexOf("lookAndFeel") != -1) {
356: Object[] recursos = FileManager.lsMenosR(
357: classPath[i].getAbsolutePath(), name);
358: for (int j = 0; j < recursos.length; j++) {
359: String recurso = recursos[j]
360: .toString()
361: .substring(
362: classPath[i].getAbsolutePath()
363: .length() + 1,
364: recursos[j].toString().length());
365: try {
366: urls.add(new URL(recurso));
367: } catch (MalformedURLException e) {
368: // TODO Auto-generated catch block
369: e.printStackTrace();
370: }
371: }
372: }
373: }
374: }
375: urls.trimToSize();
376:
377: return urls.elements();
378: }
379:
380: /**
381: * Get a stream for a resource on jar files inseide the dynamic classpath
382: * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
383: */
384: @Override
385: public InputStream getResourceAsStream(String name) {
386: if (!name.endsWith(".properties")) {
387: return getOneResourceAsStream(name);
388: }
389: File tempFile = null;
390: Vector<URL> classeList = null;
391: if ((classeList = index.get(name)) != null) {
392: String nomeArquivo = name
393: .substring(name.lastIndexOf('/') != -1 ? name
394: .lastIndexOf('/') + 1 : name
395: .lastIndexOf('\\') + 1);
396: tempFile = new File(System.getProperty("user.dir")
397: + File.separator + "temp" + File.separator
398: + nomeArquivo);
399: Properties tempProperties = new Properties();
400:
401: //Lets use the default classpath resulto too
402: URL supperResult = super .getResource(name);
403: if (supperResult != null)
404: classeList.add(supperResult);
405:
406: for (int i = 0; i < classeList.size(); i++) {
407: try {
408: Properties currentFile = new Properties();
409: InputStream fonte = classeList.get(i).openStream();
410: currentFile.load(fonte);
411: Enumeration properties = currentFile.keys();
412: while (properties.hasMoreElements()) {
413: String key = properties.nextElement()
414: .toString();
415: tempProperties.put(key, currentFile
416: .getProperty(key));
417: }
418: fonte.close();
419: } catch (IOException ioe) {
420: //Fazemos nada
421: }
422: }
423:
424: try {
425: if (tempProperties.size() > 0) {
426: if (!tempFile.exists())
427: tempFile.createNewFile();
428: tempFile.deleteOnExit();
429: tempProperties.store(
430: new FileOutputStream(tempFile), null);
431: }
432: } catch (IOException e) {
433: // TODO Auto-generated catch block
434: e.printStackTrace();
435: }
436:
437: } else {
438: File[] classPath = getDynamicClassPath();
439: File[] arqPlugin;
440: //Nome do arquivo que está sendo procurado
441: String nomeArquivo = name
442: .substring(name.lastIndexOf('/') != -1 ? name
443: .lastIndexOf('/') + 1 : name
444: .lastIndexOf('\\') + 1);
445: tempFile = new File(System.getProperty("user.dir")
446: + File.separator + "temp" + File.separator
447: + nomeArquivo);
448: Properties tempProperties = new Properties();
449:
450: if (!tempFile.exists()) {
451: //For each content file...
452: for (int i = 0; i < classPath.length; i++) {
453: if (classPath[i].isDirectory()) {
454: arqPlugin = classPath[i].listFiles();//We get the content
455:
456: for (int j = 0; j < arqPlugin.length; j++) {
457: if (arqPlugin[j].isFile()
458: && arqPlugin[j].getName().endsWith(
459: ".jar"))//If it's a jar file...
460: {
461: try {
462: ZipEntry ze = null;
463: ZipFile zf = new ZipFile(
464: arqPlugin[j]);
465:
466: //Check if the proerties file exists in this file
467: if ((ze = zf.getEntry(name)) != null) {
468: DataInputStream fonte = new DataInputStream(
469: zf.getInputStream(ze));
470:
471: Properties currentFile = new Properties();
472: currentFile.load(fonte);
473: Enumeration properties = currentFile
474: .keys();
475: while (properties
476: .hasMoreElements()) {
477: String key = properties
478: .nextElement()
479: .toString();
480: tempProperties
481: .put(
482: key,
483: currentFile
484: .getProperty(key));
485: }
486: fonte.close();
487: }
488: zf.close();
489: } catch (Exception e) {
490: //Não vamos fazer nada apenas continuar até o proximo arquivo
491: }
492: }
493: }
494: }
495: }
496: try {
497: if (tempProperties.size() > 0) {
498: if (!tempFile.exists())
499: tempFile.createNewFile();
500: tempFile.deleteOnExit();
501: tempProperties.store(new FileOutputStream(
502: tempFile), null);
503: }
504: } catch (IOException e) {
505: // TODO Auto-generated catch block
506: e.printStackTrace();
507: }
508: }
509: }
510:
511: if (tempFile.exists()) {
512: try {
513: return new FileInputStream(tempFile);
514: } catch (FileNotFoundException e) {
515: // TODO Auto-generated catch block
516: e.printStackTrace();
517: }
518: }
519: //Let's see what the parent class loader can find
520: return super .getResourceAsStream(name);
521: }
522:
523: /**
524: * Retorna um inputstream para um recurso
525: * @param name
526: * @return InputStream
527: */
528: private InputStream getOneResourceAsStream(String name) {
529: Vector<URL> classeList = null;
530: if ((classeList = index.get(name)) != null) {
531: if (classeList.size() == 0)
532: return super .getResourceAsStream(name);//Let's see what the parent class loader can find
533: else
534: try {
535: return classeList.get(0).openStream();
536: } catch (IOException e) {
537: //Fazemos nada
538: }
539: }
540:
541: File[] classPath = getDynamicClassPath();
542: File[] arqPlugin;
543:
544: //For each content file...
545: for (int i = 0; i < classPath.length; i++) {
546: if (classPath[i].isDirectory()) {
547: arqPlugin = classPath[i].listFiles();//We get the content
548:
549: for (int j = 0; j < arqPlugin.length; j++) {
550: if (arqPlugin[j].isFile()
551: && arqPlugin[j].getName().endsWith(".jar"))//If it's a jar file...
552: {
553: try {
554: ZipEntry ze = null;
555: ZipFile zf = new ZipFile(arqPlugin[j]);
556:
557: //Check if the proerties file exists in this file
558: if ((ze = zf.getEntry(name)) != null) {
559: DataInputStream fonte = new DataInputStream(
560: zf.getInputStream(ze));
561: return fonte;
562: }
563: zf.close();
564: } catch (Exception e) {
565: //Não vamos fazer nada apenas continuar até o proximo arquivo
566: }
567: }
568: }
569: }
570: }
571: //Let's see what the parent class loader can find
572: return super .getResourceAsStream(name);
573: }
574:
575: /*
576: //* Test show how to load a class with the dynamic classpath (class must be on the right search path)
577: public static void main(String[] args) throws Throwable
578: {
579: System.out.println(new DynamicClassLoader().loadClass("br.com.igor...").newInstance().getClass().getName());
580: }
581: */
582: }
|