001: /*
002: * Copyright 2004,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.bsf.util;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Modifier;
022:
023: import org.apache.bsf.BSFEngine;
024: import org.apache.bsf.BSFException;
025: import org.apache.bsf.BSFManager;
026:
027: /**
028: * This class contains utilities that language integrators can use
029: * when implementing the BSFEngine interface.
030: *
031: * @author Sanjiva Weerawarana
032: * @author Sam Ruby
033: * @author Rony G. Flatscher (added addEventListenerReturningEventInfos)
034: */
035: public class EngineUtils {
036: // the BSF class loader that knows how to load from the a specific
037: // temp directory
038: static BSFClassLoader bsfCL;
039:
040: // ---rgf, 2003-02-13, determine whether changing accessibility of Methods is possible
041: static boolean bMethodHasSetAccessible = false;
042: static {
043: Class mc = Method.class; // get the "Method" class object
044: Class arg[] = { boolean.class }; // define an array with the primitive "boolean" pseudo class object
045: try {
046: mc.getMethod("setAccessible", arg); // is this method available?
047: bMethodHasSetAccessible = true; // no exception, hence method exists
048: } catch (Exception e) {
049: bMethodHasSetAccessible = false;// exception occurred, hence method does not exist
050: }
051: }
052:
053: /**
054: * Add a script as a listener to some event coming out of an object. The
055: * first two args identify the src of the event and the event set
056: * and the rest identify the script which should be run when the event
057: * fires.
058: *
059: * @param bean event source
060: * @param eventSetName name of event set from event src to bind to
061: * @param filter filter for events
062: * @param engine BSFEngine which can run this script
063: * @param manager BSFManager of the above engine
064: * @param source (context info) the source of this expression
065: * (e.g., filename)
066: * @param lineNo (context info) the line number in source for expr
067: * @param columnNo (context info) the column number in source for expr
068: * @param script the script to execute when the event occurs
069: *
070: * @exception BSFException if anything goes wrong while running the script
071: */
072: public static void addEventListener(Object bean,
073: String eventSetName, String filter, BSFEngine engine,
074: BSFManager manager, String source, int lineNo,
075: int columnNo, Object script) throws BSFException {
076: BSFEventProcessor ep = new BSFEventProcessor(engine, manager,
077: filter, source, lineNo, columnNo, script);
078:
079: try {
080: ReflectionUtils.addEventListener(bean, eventSetName, ep);
081: } catch (Exception e) {
082: e.printStackTrace();
083: throw new BSFException(BSFException.REASON_OTHER_ERROR,
084: "ouch while adding event listener: " + e, e);
085: }
086: }
087:
088: /**
089: * Add a script as a listener to some event coming out of an object. The
090: * first two args identify the src of the event and the event set
091: * and the rest identify the script which should be run when the event
092: * fires. The processing will use the engine's apply() method.
093: *
094: * @param bean event source
095: * @param eventSetName name of event set from event src to bind to
096: * @param filter filter for events
097: * @param engine BSFEngine which can run this script
098: * @param manager BSFManager of the above engine
099: * @param source (context info) the source of this expression (e.g., filename)
100: * @param lineNo (context info) the line number in source for expr
101: * @param columnNo (context info) the column number in source for expr
102: * @param script the script to execute when the event occurs
103: * @param dataFromScriptingEngine
104: * this contains any object supplied by the scripting engine and gets sent
105: * back with the supplied script, if the event occurs.
106: * This could be used e.g. for indicating to the scripting engine which
107: * scripting engine object/routine/function/procedure
108: * should be ultimately informed of the event occurrence.
109: *
110: * @exception BSFException if anything goes wrong while running the script
111: */
112: public static void addEventListenerReturningEventInfos(Object bean,
113: String eventSetName, String filter, BSFEngine engine,
114: BSFManager manager, String source, int lineNo,
115: int columnNo, Object script, Object dataFromScriptingEngine)
116: throws BSFException {
117: BSFEventProcessorReturningEventInfos ep = new BSFEventProcessorReturningEventInfos(
118: engine, manager, filter, source, lineNo, columnNo,
119: script, dataFromScriptingEngine);
120:
121: try {
122: ReflectionUtils.addEventListener(bean, eventSetName, ep);
123: } catch (Exception e) {
124: e.printStackTrace();
125: throw new BSFException(BSFException.REASON_OTHER_ERROR,
126: "ouch while adding event listener: " + e, e);
127: }
128: }
129:
130: /**
131: * Finds and invokes a method with the given signature on the given
132: * bean. The signature of the method that's invoked is first taken
133: * as the types of the args, but if that fails, this tries to convert
134: * any primitive wrapper type args to their primitive counterparts
135: * to see whether a method exists that way. If it does, done.
136: *
137: * @param bean the object on which to invoke the method
138: * @param methodName name of the method
139: * @param args arguments to be given to the method
140: *
141: * @return the result of invoking the method, if any
142: *
143: * @exception BSFException if something goes wrong
144: */
145: public static Object callBeanMethod(Object bean, String methodName,
146: Object[] args) throws BSFException {
147: Class[] argTypes = null;
148: // determine arg types. note that a null argtype
149: // matches any object type
150:
151: if (args != null) {
152: argTypes = new Class[args.length];
153: for (int i = 0; i < args.length; i++) {
154: argTypes[i] = (args[i] == null) ? null : args[i]
155: .getClass();
156: }
157: }
158:
159: // we want to allow a static call to occur on an object, similar
160: // to what Java allows. So isStaticOnly is set to false.
161: boolean isStaticOnly = false;
162: Class beanClass = (bean instanceof Class) ? (Class) bean : bean
163: .getClass();
164:
165: // now try to call method with the right signature
166: try {
167: Method m;
168: try {
169: m = MethodUtils.getMethod(beanClass, methodName,
170: argTypes, isStaticOnly);
171: } catch (NoSuchMethodException e) {
172: // ok, so that didn't work - now try converting any primitive
173: // wrapper types to their primitive counterparts
174: try {
175: // if args is null the NullPointerException will get caught
176: // below and the right thing'll happen .. ugly but works
177: for (int i = 0; i < args.length; i++) {
178: if (args[i] instanceof Number) {
179: if (args[i] instanceof Byte)
180: argTypes[i] = byte.class;
181: else if (args[i] instanceof Integer)
182: argTypes[i] = int.class;
183: else if (args[i] instanceof Long)
184: argTypes[i] = long.class;
185: else if (args[i] instanceof Float)
186: argTypes[i] = float.class;
187: else if (args[i] instanceof Double)
188: argTypes[i] = double.class;
189: else if (args[i] instanceof Short)
190: argTypes[i] = short.class;
191: } else if (args[i] instanceof Boolean)
192: argTypes[i] = boolean.class;
193: else if (args[i] instanceof Character)
194: argTypes[i] = char.class;
195: }
196:
197: m = MethodUtils.getMethod(beanClass, methodName,
198: argTypes, isStaticOnly);
199: } catch (Exception e2) {
200: // throw the original
201: throw e;
202: }
203: }
204:
205: // call it, and return the result
206: try {
207: return m.invoke(bean, args);
208: } catch (Exception e) // 2003-02-23, --rgf, maybe an IllegalAccessException?
209: {
210: if (e instanceof IllegalAccessException
211: && bMethodHasSetAccessible
212: && Modifier.isPublic(m.getModifiers())) // if a public method allow access to it
213: {
214: m.setAccessible(true); // allow unconditional access to method
215: return m.invoke(bean, args);
216: }
217: // re-throw the exception
218: throw e;
219: }
220:
221: } catch (Exception e) {
222: // something went wrong while invoking method
223: Throwable t = (e instanceof InvocationTargetException) ? ((InvocationTargetException) e)
224: .getTargetException()
225: : null;
226: throw new BSFException(BSFException.REASON_OTHER_ERROR,
227: "method invocation failed: "
228: + e
229: + ((t == null) ? ""
230: : (" target exception: " + t)), t);
231: }
232: }
233:
234: /**
235: * Creates a new bean. The signature of the constructor that's invoked
236: * is first taken as the types of the args, but if that fails, this tries
237: * to convert any primitive wrapper type args to their primitive
238: * counterparts to see whether a method exists that way. If it does, done.
239: *
240: * @param className fully qualified name of class to instantiate
241: * @param args array of constructor args (or null if none)
242: *
243: * @return the created bean
244: *
245: * @exception BSFException if something goes wrong (@see
246: * org.apache.cs.util.MethodUtils for the real
247: * exceptions that can occur).
248: */
249: public static Object createBean(String className, Object args[])
250: throws BSFException {
251: Bean obj;
252: Class[] argTypes = null;
253:
254: if (args != null) {
255: argTypes = new Class[args.length];
256: for (int i = 0; i < args.length; i++) {
257: argTypes[i] = (args[i] != null) ? args[i].getClass()
258: : null;
259: }
260: }
261:
262: try {
263: try {
264: obj = ReflectionUtils.createBean(null, className,
265: argTypes, args);
266: return obj.value;
267: } catch (NoSuchMethodException me) {
268: // ok, so that didn't work - now try converting any primitive
269: // wrapper types to their primitive counterparts
270: try {
271: // if args is null the NullPointerException will get caught
272: // below and the right thing'll happen .. ugly but works
273: for (int i = 0; i < args.length; i++) {
274: if (args[i] instanceof Number)
275: argTypes[i] = byte.class;
276: else if (args[i] instanceof Boolean)
277: argTypes[i] = boolean.class;
278: else if (args[i] instanceof Character)
279: argTypes[i] = char.class;
280: }
281: obj = ReflectionUtils.createBean(null, className,
282: argTypes, args);
283: return obj.value;
284: } catch (Exception e) {
285: // throw the previous exception
286: throw me;
287: }
288: }
289: } catch (Exception e) {
290: throw new BSFException(BSFException.REASON_OTHER_ERROR, e
291: .getMessage(), e);
292: }
293: }
294:
295: /**
296: * Given a class return the type signature string fragment for it.
297: * That is, return "I" for int, "J" for long, ... etc..
298: *
299: * @param cl class object for whom the signature fragment is needed.
300: *
301: * @return the string representing the type signature
302: */
303: public static String getTypeSignatureString(Class cl) {
304: if (cl.isPrimitive()) {
305: if (cl == boolean.class)
306: return "Z";
307: else if (cl == byte.class)
308: return "B";
309: else if (cl == char.class)
310: return "C";
311: else if (cl == short.class)
312: return "S";
313: else if (cl == int.class)
314: return "I";
315: else if (cl == long.class)
316: return "J";
317: else if (cl == float.class)
318: return "F";
319: else if (cl == double.class)
320: return "D";
321: else
322: return "V";
323: } else {
324: StringBuffer sb = new StringBuffer("L");
325: sb.append(cl.getName());
326: sb.append(";");
327: return sb.toString().replace('.', '/');
328: }
329: }
330:
331: /**
332: * Load a class using the class loader of given manager. If that fails
333: * try using a class loader that loads from the tempdir of the manager.
334: *
335: * @param mgr BSFManager who's classLoader and tempDir props are
336: * consulted
337: * @param name name of the class to load
338: *
339: * @return the loaded class
340: *
341: * @exception BSFException if something goes wrong.
342: */
343: public static Class loadClass(BSFManager mgr, String name)
344: throws BSFException {
345: ClassLoader classLoader = mgr.getClassLoader();
346:
347: try {
348: return (classLoader == null) ?
349: // Class.forName (name)
350: Thread.currentThread().getContextClassLoader().loadClass(
351: name)
352: : classLoader.loadClass(name);
353: } catch (ClassNotFoundException e) {
354: // try to load it from the temp dir using my own class loader
355: try {
356: if (bsfCL == null)
357: bsfCL = new BSFClassLoader();
358: bsfCL.setTempDir(mgr.getTempDir());
359: return bsfCL.loadClass(name);
360: } catch (ClassNotFoundException e2) {
361: throw new BSFException(BSFException.REASON_OTHER_ERROR,
362: "unable to load class '" + name + "':" + e, e);
363: }
364: }
365: }
366: }
|