KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > bsf > engines > netrexx > NetRexxEngine


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2002 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if
20  * any, must include the following acknowlegement:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowlegement may appear in the software itself,
24  * if and wherever such third-party acknowlegements normally appear.
25  *
26  * 4. The names "Apache BSF", "Apache", and "Apache Software Foundation"
27  * must not be used to endorse or promote products derived from
28  * this software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache"
32  * nor may "Apache" appear in their names without prior written
33  * permission of the Apache Group.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many individuals
50  * on behalf of the Apache Software Foundation and was originally created by
51  * Sanjiva Weerawarana and others at International Business Machines
52  * Corporation. For more information on the Apache Software Foundation,
53  * please see <http://www.apache.org/>.
54  */

55
56 package org.apache.bsf.engines.netrexx;
57
58 import java.util.*;
59 import java.io.*;
60 import java.lang.*;
61 import java.lang.reflect.*;
62
63 import org.apache.bsf.*;
64 import org.apache.bsf.util.*;
65 import org.apache.bsf.debug.util.DebugLog;
66
67 /**
68  * This is the interface to NetRexx from the
69  * Bean Scripting Framework.
70  * <p>
71  * The NetRexx code must be written script-style, without a "class" or
72  * "properties" section preceeding the executable code. The NetRexxEngine will
73  * generate a prefix for this code:
74  * <pre>
75  * <code>
76  * class $$CLASSNAME$$;
77  * method BSFNetRexxEngineEntry(bsf=org.apache.bsf.BSFManager) public static;
78  * </code>
79  * </pre>
80  * $$CLASSNAME$$ will be replaced by a generated classname of the form
81  * BSFNetRexx*, and the bsf parameter can be used to retrieve application
82  * objects registered with the Bean Scripting Framework.
83  * <p>
84  * If you use the placeholder string $$CLASSNAME$$ elsewhere
85  * in your script -- including within text strings -- BSFNetRexxEngine will
86  * replace it with the generated name of the class before the NetRexx code
87  * is compiled.
88  * <p>
89  * If you need to use full NetRexx functionality, we recommend that your
90  * NetRexx script define and invoke a "minor class", with or without the
91  * "dependent" keyword as suits your needs. You'll have to use $$CLASSNAME$$
92  * in naming the minor class, since the name of the main class is synthesized;
93  * for example, to create the minor class "bar" you'd write
94  * "class $$CLASSNAME$$.Bar".
95  * <p>
96  * <h2>Hazards:</h2>
97  * <p>
98  * Since NetRexx has to be _compiled_ to a Java classfile, invoking it involves
99  * a fair amount of computation to load and execute the compiler. We are
100  * currently making an attempt to manage that by caching the class
101  * after it has been loaded, but the indexing is fairly primitive; we
102  * hash against the script string to find the class for it.
103  * <p>
104  * Minor-class .class files are now being deleted after the major class loads.
105  * This coould potentially cause problems.
106  *
107  * @author Joe Kesselman
108  * @author Sanjiva Weerawarana
109  */

110 public class NetRexxEngine extends BSFEngineImpl
111 {
112     BSFFunctions mgrfuncs;
113     private boolean bsfHandleCreated = false;
114     static Hashtable codeToClass=new Hashtable();
115     static String JavaDoc serializeCompilation="";
116     static String JavaDoc placeholder="$$CLASSNAME$$";
117     String JavaDoc minorPrefix;
118   
119     /**
120      * Create a scratchfile, open it for writing, return its name.
121      * Relies on the filesystem to provide us with uniqueness testing.
122      * NOTE THAT uniqueFileOffset continues to count; we don't want to
123      * risk reusing a classname we have previously loaded in this session
124      * even if the classfile has been deleted.
125      *
126      * I've made the offset static, due to concerns about reuse/reentrancy
127      * of the NetRexx engine.
128      */

129   private static int uniqueFileOffset=0;
130   private class GeneratedFile
131   {
132     File file=null;
133     FileOutputStream fos=null;
134     String JavaDoc className=null;
135     GeneratedFile(File file,FileOutputStream fos,String JavaDoc className)
136       {
137           this.file=file;
138           this.fos=fos;
139           this.className=className;
140       }
141   }
142     
143     // rexxclass used to be an instance variable, on the theory that
144
// each NetRexxEngine was an instance of a specific script.
145
// BSF is currently reusing Engines, so caching the class
146
// no longer makes sense.
147
// Class rexxclass;
148

149     /**
150      * Constructor.
151      */

152     public NetRexxEngine ()
153     {
154         /*
155           The following line is intended to cause the constructor to
156           throw a NoClassDefFoundError if the NetRexxC.zip dependency
157           is not resolved.
158           
159           If this line was not here, the problem would not surface until
160           the actual processing of a script. We want to know all is well
161           at the time the engine is instantiated, not when we attempt to
162           process a script.
163           */

164         
165         new netrexx.lang.BadArgumentException();
166     }
167     /**
168      * Return an object from an extension.
169      * @param Object object from which to call our static method
170      * @param method The name of the method to call.
171      * @param args an array of arguments to be
172      * passed to the extension, which may be either
173      * Vectors of Nodes, or Strings.
174      */

175     public Object JavaDoc call (Object JavaDoc object,String JavaDoc method, Object JavaDoc[] args)
176     throws BSFException
177     {
178         throw new BSFException(BSFException.REASON_UNSUPPORTED_FEATURE,
179                                "NetRexx doesn't currently support call()",
180                                null);
181     }
182     /**
183      * Invoke a static method.
184      * @param rexxclass Class to invoke the method against
185      * @param method The name of the method to call.
186      * @param args an array of arguments to be
187      * passed to the extension, which may be either
188      * Vectors of Nodes, or Strings.
189      */

190     Object JavaDoc callStatic(Class JavaDoc rexxclass, String JavaDoc method, Object JavaDoc[] args)
191     throws BSFException
192     {
193         //***** ISSUE: Currently supports only static methods
194
Object JavaDoc retval = null;
195         try
196         {
197             if (rexxclass != null)
198             {
199                 //***** This should call the lookup used in BML, for typesafety
200
Class JavaDoc[] argtypes=new Class JavaDoc[args.length];
201                 for(int i=0;i<args.length;++i)
202                     argtypes[i]=args[i].getClass();
203                 
204                 Method m=MethodUtils.getMethod(rexxclass, method, argtypes);
205                 retval=m.invoke(null,args);
206             }
207             else
208             {
209                 DebugLog.stderrPrintln ("NetRexxEngine: ERROR: rexxclass==null!", DebugLog.BSF_LOG_L3);
210             }
211         }
212         catch(Exception JavaDoc e)
213         {
214             e.printStackTrace ();
215             if (e instanceof InvocationTargetException)
216             {
217                 Throwable JavaDoc t = ((InvocationTargetException)e).getTargetException ();
218                 t.printStackTrace ();
219             }
220             throw new BSFException (BSFException.REASON_IO_ERROR,
221                                     e.getMessage (),
222                                     e);
223         }
224         return retval;
225     }
226     public void declareBean (BSFDeclaredBean bean) throws BSFException {}
227     /**
228      * Override impl of execute. In NetRexx, methods which do not wish
229      * to return a value should be invoked via exec, which will cause them
230      * to be generated without the "returns" clause.
231      * Those which wish to return a value should call eval instead.
232      * which will add "returns java.lang.Object" to the header.
233      *
234      * Note: It would be nice to have the "real" return type avaialable, so
235      * we could do something more type-safe than Object, and so we could
236      * return primitive types without having to enclose them in their
237      * object wrappers. BSF does not currently support that concept.
238      */

239     public Object JavaDoc eval (String JavaDoc source, int lineNo, int columnNo,
240                     Object JavaDoc script)
241     throws BSFException
242     {
243         return execEvalShared(source, lineNo, columnNo, script,true);
244     }
245     /**
246      * Override impl of execute. In NetRexx, methods which do not wish
247      * to return a value should be invoked via exec, which will cause them
248      * to be generated without the "returns" clause.
249      * Those which wish to return a value should call eval instead.
250      * which will add "returns java.lang.Object" to the header.
251      */

252     public void exec (String JavaDoc source, int lineNo, int columnNo,
253                   Object JavaDoc script)
254     throws BSFException
255     {
256          execEvalShared(source, lineNo, columnNo, script,false);
257     }
258     /**
259      * This is shared code for the exec() and eval() operations. It will
260      * evaluate a string containing a NetRexx method body -- which may be
261      * as simple as a single return statement.
262      * It should store the "bsf" handle where the
263      * script can get to it, for callback purposes.
264      * <p>
265      * Note that NetRexx compilation imposes serious overhead -- 11 seconds for
266      * the first compile, about 3 thereafter -- but in exchange you get
267      * Java-like speeds once the classes have been created (minus the cache
268      * lookup cost).
269      * <p>
270      * Nobody knows whether javac is threadsafe.
271      * I'm going to serialize access to the compilers to protect it.
272      */

273     public Object JavaDoc execEvalShared (String JavaDoc source, int lineNo, int columnNo,
274                               Object JavaDoc oscript,boolean returnsObject)
275     throws BSFException
276     {
277         Object JavaDoc retval=null;
278         String JavaDoc classname=null;
279         GeneratedFile gf=null;
280         
281         // Moved into the exec process; see comment above.
282
Class JavaDoc rexxclass=null;
283         
284         String JavaDoc basescript=oscript.toString();
285         String JavaDoc script=basescript; // May be altered by $$CLASSNAME$$ expansion
286

287         try {
288                     // Do we already have a class exactly matching this code?
289
rexxclass=(Class JavaDoc)codeToClass.get(basescript);
290                     
291                     if(rexxclass!=null)
292             {
293                             DebugLog.debugPrintln ("NetRexxEngine: Found pre-compiled class" +
294                                                    " for script '" + basescript + "'", DebugLog.BSF_LOG_L3);
295                             classname=rexxclass.getName();
296             }
297                     else
298             {
299                             gf=openUniqueFile(tempDir,"BSFNetRexx",".nrx");
300                             if(gf==null)
301                                 throw new BSFException("couldn't create NetRexx scratchfile");
302                             
303                             // Obtain classname
304
classname=gf.className;
305                             
306                             // Decide whether to declare a return type
307
String JavaDoc returnsDecl="";
308                             if(returnsObject)
309                                 returnsDecl="returns java.lang.Object";
310                             
311                             // Write the kluge header to the file.
312
// ***** By doing so we give up the ability to use Property blocks.
313
gf.fos.write(("class "+classname+";\n")
314                                          .getBytes());
315                             gf.fos.write(
316                                          ("method BSFNetRexxEngineEntry(bsf=org.apache.bsf.util.BSFFunctions) "+
317                                           " public static "+returnsDecl+";\n")
318                                  .getBytes());
319                 
320                             // Edit the script to replace placeholder with the generated
321
// classname. Note that this occurs _after_ the cache was
322
// checked!
323
int startpoint,endpoint;
324                             if((startpoint=script.indexOf(placeholder))>=0)
325                 {
326                                     StringBuffer JavaDoc changed=new StringBuffer JavaDoc();
327                                     for(;
328                                         startpoint>=0;
329                                         startpoint=script.indexOf(placeholder,startpoint))
330                     {
331                                             changed.setLength(0); // Reset for 2nd pass or later
332
if(startpoint>0)
333                                                 changed.append(script.substring(0,startpoint));
334                                             changed.append(classname);
335                                             endpoint=startpoint+placeholder.length();
336                                             if(endpoint<script.length())
337                                                 changed.append(script.substring(endpoint));
338                                             script=changed.toString();
339                     }
340                 }
341                             
342                             BSFDeclaredBean tempBean;
343                             String JavaDoc className;
344                             
345                             for (int i = 0; i < declaredBeans.size (); i++)
346                 {
347                                     tempBean = (BSFDeclaredBean) declaredBeans.elementAt (i);
348                                     className = StringUtils.getClassName (tempBean.type);
349                                     
350                                     gf.fos.write ((tempBean.name + " =" + className + " bsf.lookupBean(\"" +
351                                                    tempBean.name + "\");").getBytes());
352                 }
353                             
354                             if(returnsObject)
355                                 gf.fos.write("return ".getBytes());
356                             
357                             // Copy the input to the file.
358
// Assumes all available -- probably mistake, but same as
359
// other engines.
360
gf.fos.write(script.getBytes());
361                             gf.fos.close();
362                             
363                             DebugLog.debugPrintln ("NetRexxEngine: wrote temp file " +
364                                                    gf.file.getPath () + ", now compiling", DebugLog.BSF_LOG_L3);
365                             
366                             // Compile through Java to .class file
367
String JavaDoc command=gf.file.getPath(); //classname;
368
if (DebugLog.getLogLevel() >= 3) {
369                         command += " -verbose4";
370                     } else {
371                         command += " -noverbose";
372                         command += " -noconsole";
373                     }
374                     
375                     netrexx.lang.Rexx cmdline= new netrexx.lang.Rexx(command);
376                     int retValue;
377                     
378                     // May not be threadsafe. Serialize access on static object:
379
synchronized(serializeCompilation)
380                         {
381                             // compile to a .java file
382
retValue =
383                                 COM.ibm.netrexx.process.NetRexxC.main(cmdline,
384                                                                       new PrintWriter(DebugLog.getDebugStream()));
385                         }
386
387                 // Check if there were errors while compiling the Rexx code.
388
if (retValue == 2)
389                 {
390                   throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
391                                          "There were NetRexx errors.");
392                 }
393
394                 // Load class.
395
DebugLog.debugPrintln ("NetRexxEngine: loading class "+classname, DebugLog.BSF_LOG_L3);
396                 rexxclass=EngineUtils.loadClass (mgr, classname);
397
398                 // Stash class for reuse
399
codeToClass.put(basescript,rexxclass);
400                         }
401
402             Object JavaDoc[] args={mgrfuncs};
403             retval=callStatic(rexxclass, "BSFNetRexxEngineEntry",args);
404                 }
405                 catch (BSFException e)
406                     {
407                         // Just forward the exception on.
408
throw e;
409                     }
410                 catch(Exception JavaDoc e)
411                     {
412             e.printStackTrace ();
413             if (e instanceof InvocationTargetException)
414             {
415                 Throwable JavaDoc t = ((InvocationTargetException)e).getTargetException ();
416                 t.printStackTrace ();
417             }
418             throw new BSFException (BSFException.REASON_IO_ERROR,
419                                     e.getMessage (), e);
420         }
421         finally
422         {
423             // Cleanup: delete the .nrx and .class files
424
// (if any) generated by NetRexx Trace requests.
425

426             if(gf!=null && gf.file!=null && gf.file.exists())
427                 gf.file.delete(); // .nrx file
428

429             if(classname!=null)
430             {
431                 // Generated src
432
File file=new File(tempDir+File.separatorChar+classname+".java");
433                 if(file.exists())
434                     file.delete();
435                 
436                 // Generated class
437
file=new File(classname+".class");
438                 if(file.exists())
439                     file.delete();
440                 
441                 // Can this be done without disrupting trace?
442
file=new File(tempDir+File.separatorChar+classname+".crossref");
443                 if(file.exists())
444                     file.delete();
445                 
446                 // Search for and clean up minor classes, classname$xxx.class
447
file=new File(tempDir);
448                 minorPrefix=classname+"$"; // Indirect arg to filter
449
String JavaDoc[] minor_classfiles=
450                     file.list(
451                         // ANONYMOUS CLASS for filter:
452
new FilenameFilter()
453                         {
454                             // Starts with classname$ and ends with .class
455
public boolean accept(File dir,String JavaDoc name)
456                             {
457                                 return
458                                     (0==name.indexOf(minorPrefix))
459                                     &&
460                                     (name.lastIndexOf(".class")==name.length()-6)
461                                     ;
462                             }
463                         }
464                         );
465                 if(minor_classfiles!=null)
466                     for(int i=minor_classfiles.length;i>0;)
467                     {
468                         file=new File(minor_classfiles[--i]);
469                         file.delete();
470                     }
471             }
472         }
473         
474         return retval;
475     }
476     public void initialize(BSFManager mgr, String JavaDoc lang,Vector declaredBeans)
477     throws BSFException
478     {
479         super.initialize(mgr, lang, declaredBeans);
480         mgrfuncs = new BSFFunctions (mgr, this);
481     }
482 private GeneratedFile openUniqueFile(String JavaDoc directory,String JavaDoc prefix,String JavaDoc suffix)
483     {
484         File file=null,obj=null;
485         FileOutputStream fos=null;
486         int max=1000; // Don't try forever
487
GeneratedFile gf=null;
488         int i;
489         String JavaDoc className = null;
490         for(i=max,++uniqueFileOffset;
491             fos==null && i>0;
492             --i,++uniqueFileOffset)
493         {
494             // Probably a timing hazard here... ***************
495
try
496                 {
497                     className = prefix+uniqueFileOffset;
498                     file=new File(directory+File.separatorChar+className+suffix);
499                     obj=new File(directory+File.separatorChar+className+".class");
500                     if(file!=null && !file.exists() & obj!=null & !obj.exists())
501                         fos=new FileOutputStream(file);
502                 }
503             catch(Exception JavaDoc e)
504                 {
505                     // File could not be opened for write, or Security Exception
506
// was thrown. If someone else created the file before we could
507
// open it, that's probably a threading conflict and we don't
508
// bother reporting it.
509
if(!file.exists())
510                     {
511                         DebugLog.stderrPrintln("openUniqueFile: unexpected "+e, DebugLog.BSF_LOG_L0);
512                     }
513                 }
514         }
515         if(fos==null)
516             DebugLog.stderrPrintln("openUniqueFile: Failed "+max+"attempts.", DebugLog.BSF_LOG_L0);
517         else
518             gf=new GeneratedFile(file,fos,className);
519         return gf;
520     }
521
522     public void undeclareBean (BSFDeclaredBean bean) throws BSFException {}
523 }
524
Popular Tags