012a9356b5179016d1988df2be6f1e02b210a797
[org.ibex.jinetd.git] / src / org / ibex / jinetd / Loader.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.jinetd;
6 import org.ibex.io.*;
7 import org.ibex.util.*;
8 import java.io.*;
9 import java.util.*;
10 import java.text.*;
11 import java.net.*;
12 import java.util.zip.*;
13
14 /** represents a file or directory which is scanned for updates */
15 public class Loader extends Watcher {
16
17     ClassLoader parentClassLoader = null;
18     static final ClassLoader mycl = Loader.class.getClassLoader();
19
20     protected ThreadGroup tg = new ThreadGroup(getAbsolutePath());
21
22     public Loader(String path) { super(path); }
23     //public Loader(String path, ClassLoader pcl) { super(path); this.parentClassLoader = pcl; }
24     
25     private TreeClassLoader classloader = null;
26     public synchronized void scan() throws IOException { super.scan(); }
27     public synchronized ClassLoader getClassLoader() {
28         ClassLoader classloader = this.classloader;
29         if (classloader == null) {
30             String s = getClassPath();
31             StringTokenizer st = new StringTokenizer(s, File.pathSeparatorChar+"");
32             URL[] urls = new URL[st.countTokens()];
33             try {
34                 for(int i=0; i<urls.length; i++) {
35                     String us = st.nextToken();
36                     //if (us.endsWith(".jar")) us = "jar:file:"+us+"!/";
37                     if (us.endsWith(".jar")) us = "file:"+us;
38                     else us = "file:"+us+"/";
39                     urls[i] = new URL(us);
40                 }
41             } catch (MalformedURLException e) {
42                 Log.error(this, e);
43                 return null;
44             }
45             classloader = this.classloader = new TreeClassLoader(urls/*, parentClassLoader*/);
46             try { compileSource(); } catch (Exception e) { Log.error(this, e); }
47         }
48         return classloader;
49     }
50
51     private void fill(Vec vec, File dir) {
52         if (!dir.exists()) return;
53         if (!dir.isDirectory()) {
54             if (!dir.getPath().endsWith(".java")) return;
55             vec.addElement(dir.getAbsolutePath());
56         } else {
57             String[] list = dir.list();
58             for(int i=0; i<list.length; i++)
59                 fill(vec, new File(dir.getAbsolutePath() + File.separatorChar + list[i]));
60         }
61     }
62
63
64     public String getClassPath() {
65         String classpath = System.getProperty("java.class.path");
66         String [] l = new File(Root.root + "/LIB/").list();
67         for(int i=0; l != null && i<l.length; i++) {
68             if (!l[i].endsWith(".jar")) continue;
69             classpath += File.pathSeparatorChar;
70             classpath += Root.root + "/LIB/" + l[i];
71         }
72         l = new File(this.path + File.separatorChar + "LIB").list();
73         for(int i=0; l!=null && i<l.length; i++) {
74             if (!l[i].endsWith(".jar")) continue;
75             classpath += File.pathSeparatorChar;
76             classpath += this.path + "/LIB/" + l[i];
77         }
78         return classpath + File.pathSeparatorChar + this.path + "/BIN";
79     }
80
81     private void compileSource() throws Exception {
82         File srcdir = new File(this.path + File.separatorChar + "SRC");
83         if (!srcdir.exists()) return;
84         /*
85         if (new File("/usr/bin/jikes").exists()) {
86             File bindir = new File(this.path + File.separatorChar + "BIN");  bindir.mkdirs();
87             String bootclasspath = System.getProperty("sun.boot.class.path", "");
88             Vec args = new Vec();
89             args.addElement("/usr/bin/jikes");
90             args.addElement("+E");
91             args.addElement("-nowarn");
92             args.addElement("-bootclasspath");
93             args.addElement(bootclasspath);
94             args.addElement("-classpath");
95             args.addElement(getClassPath());
96             args.addElement("-sourcepath");
97             args.addElement(srcdir.getAbsolutePath());
98             args.addElement("-d");
99             args.addElement(bindir.getAbsolutePath());
100             fill(args, srcdir);
101             String[] all = new String[args.size()];
102             args.copyInto(all);
103             Log.info(this, "invoking jikes");
104             for(int i=0; i<all.length; i++) Log.info(this, "   " + all[i]);
105             final Process jikes = Runtime.getRuntime().exec(all);
106             final BufferedReader out = new BufferedReader(new InputStreamReader(jikes.getInputStream()));
107             final BufferedReader err = new BufferedReader(new InputStreamReader(jikes.getErrorStream()));
108             new Thread() { public void run() {
109                 try { for(String s = out.readLine(); s != null; s = out.readLine()) Log.info("jikes[stdout]", s); }
110                 catch (Exception e) { Log.warn("jikes", e); } } }.start();
111             new Thread() { public void run() {
112                 try { for(String s = err.readLine(); s != null; s = err.readLine()) Log.info("jikes[stderr]", s); }
113                 catch (Exception e) { Log.warn("jikes", e); } } }.start();
114             jikes.waitFor();
115         } else {
116             Log.error(this, "ACK! jikes not found, javac not (yet) supported");
117         }
118         */
119
120             File bindir = new File(this.path + File.separatorChar + "BIN");  bindir.mkdirs();
121             Vec args = new Vec();
122             args.addElement("/usr/bin/javac");
123             args.addElement("-nowarn");
124             args.addElement("-classpath");
125             args.addElement(getClassPath());
126             args.addElement("-sourcepath");
127             args.addElement(srcdir.getAbsolutePath());
128             args.addElement("-d");
129             args.addElement(bindir.getAbsolutePath());
130             fill(args, srcdir);
131             String[] all = new String[args.size()];
132             args.copyInto(all);
133             Log.info(this, "invoking javac for " + srcdir.getAbsolutePath());
134             final Process javac = Runtime.getRuntime().exec(all, new String[] { "PATH=/bin:/usr/bin" });
135             final BufferedReader out = new BufferedReader(new InputStreamReader(javac.getInputStream()));
136             final BufferedReader err = new BufferedReader(new InputStreamReader(javac.getErrorStream()));
137             new Thread() { public void run() {
138                 try { for(String s = out.readLine(); s != null; s = out.readLine()) Log.info("javac [stdout]", s); }
139                 catch (Exception e) { Log.warn("javac", e); } } }.start();
140             new Thread() { public void run() {
141                 try { for(String s = err.readLine(); s != null; s = err.readLine()) Log.info("javac [stderr]", s); }
142                 catch (Exception e) { Log.warn("javac", e); } } }.start();
143             javac.waitFor();
144
145     }
146
147     // only watch SRC and LIB for changes
148     public Watched slash(String path) {
149         return (path.equals("LIB") ||
150                 path.equals("BIN") ||
151                 path.equals("SRC") ||
152                 path.endsWith(".jar") ) ? super.slash(path) : null; }
153
154
155     private void nuke() {
156         if (tg.activeCount() == 0) return;
157         Log.info(this, "killing all threads for: " + path);
158         Log.info(this, "   thread count before interrupt: " + tg.activeCount());
159         tg.interrupt();
160         try { Thread.sleep(3000); } catch (Exception e) { Log.error(this, e); }
161         Log.info(this, "   thread count before kill: " + tg.activeCount());
162         Thread[] all = new Thread[tg.activeCount()];
163         tg.enumerate(all, true);
164         for(int i=0; i<all.length; i++) Stream.kill(all[i]);
165         try { Thread.sleep(3000); } catch (Exception e) { Log.error(this, e); }
166         Log.info(this, "   thread count after kill: " + tg.activeCount());
167         if (tg.activeCount() > 0) {
168             Log.warn(this, "    annoying threads:");
169             Thread[] annoying = new Thread[tg.activeCount()];
170             tg.enumerate(annoying, true);
171             for(int i=0; i<annoying.length; i++) {
172                 Log.warn(this, "      " + annoying[i]);
173                 StackTraceElement[] stack = annoying[i].getStackTrace();
174                 for(int j=0; j<stack.length; j++) Log.warn(this, "        " + stack[j]);
175                 Log.warn(this, " ");
176             }
177         }
178     }
179
180     // dump the classloader if anything changes
181     public void changed(Watched w) {
182         if (w.path.indexOf("BIN") != -1) return;
183         if (classloader != null) {
184             for(int i=0; i<3; i++) nuke();
185             tg = new ThreadGroup(getAbsolutePath());
186             Log.info(this, "scheduling classes for reload due to change in: " + w.path);
187             classloader = null;
188         }
189     }
190     
191     public class TreeClassLoader extends java.net.URLClassLoader {
192
193         public String getClassPath() { return Loader.this.getClassPath(); }
194
195         public TreeClassLoader(java.net.URL[] urls) { super(urls); }
196         //private Hashtable cache = new Hashtable();
197         /*
198         public InputStream getResourceAsStream(String name) { return getInputStream(name); }
199
200         private synchronized Class defineClass(String name) {
201             try {
202                 InputStream is = null;
203                 byte[] b = null;
204                 try {
205                     is = getInputStream(name.replace('.', File.separatorChar) + ".class");
206                     if (is == null) return null;
207                     b = InputStreamToByteArray.convert(is);
208                 } finally { if (is != null) is.close(); }
209                 return defineClass(b, 0, b.length);
210             } catch (Exception e) {
211                 Log.error(this, e);
212                 return null;
213             }
214         }
215
216         private InputStream getInputStream(String name) {
217             // first see if it's just sitting there
218             File classFile = slash("BIN").slash(name);
219             if (classFile.exists()) try {
220                 return new FileInputStream(classFile);
221             } catch (Exception e) { Log.warn(this, e); }
222
223             // then scan the jarfiles for it
224             File lib = slash("LIB");
225             if (lib.exists() && lib.isDirectory()) try {
226                 boolean first = true;
227                 while(true) {
228                     String[] paths = first ? lib.list() : list(); 
229                     for(int i=0; i<paths.length; i++) {
230                         if (paths[i].endsWith(".jar")) {
231                             File f = new File(getAbsolutePath()+File.separatorChar+"LIB"+File.separatorChar+paths[i]);
232                             Log.debug(this, "  scanning " + f.getAbsolutePath() + " for " + name);
233                             ZipFile zf = new ZipFile(f);
234                             ZipEntry ze = zf.getEntry(name);
235                             if (ze != null) return zf.getInputStream(ze);
236                             zf.close();
237                         }
238                     }
239                     if (!first) break;
240                     first = false;
241                 }
242             } catch (Exception e) { Log.warn(this, e); }
243
244             // finally, resort to compiling it if we have to
245             //File src = new File(getAbsolutePath() + File.separatorChar + "SRC");
246             // FIXME
247             //if (!sourcebuilt) buildSource();
248             return null;
249         }
250
251         public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
252             Class c = (Class)cache.get(name);
253             if (c == null) try { c = findSystemClass(name); } catch (ClassNotFoundException cfe) { }
254             if (c == null) try { c = Class.forName(name); } catch (ClassNotFoundException cfe) { }
255             if (c == null) c = defineClass(name);
256             if (c == null) throw new ClassNotFoundException();
257             cache.put(name, c);
258             if (resolve) resolveClass(c);
259             return c;
260         }
261         */
262     }
263 }