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