-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathinterfaces.html
470 lines (408 loc) · 17.8 KB
/
interfaces.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>Scripting Interfaces</title>
</head>
<body bgcolor="ffffff">
<table cellspacing="10">
<tr>
<td align="center"><a href="http://www.beanshell.org/"><img src="images/homebutton.png" /><br />Home</a>
</td>
<td><a href="scope.html#Scope_Modifiers"><img src="images/backbutton.png" /><br />Back
</a></td>
<td align="center"><a href="contents.html"><img src="images/upbutton.png" /><br />Contents</a></td>
<td align="center"><a href="specialvarsvalues.html#Special_Variables_and_Values"><img
src="images/forwardbutton.png" /><br />Next
</a></td>
</tr>
</table>
<h1>Scripting Interfaces</h1>
One of the most powerful features of BeanShell is the ability to script
Java interfaces. This feature allows you to write scripts that serve
as event handlers, listeners, and components of other Java APIs. It also
makes calling scripted components from within your applications easier
because they can be made to look just like any other Java object.
<p CLEAR="ALL" />
<h2><a name="Anonymous_Inner-Class_Style">Anonymous Inner-Class Style</a></h2>
One way to get a scripted component to implement a Java interface is by
using the standard Java anonymous inner class syntax to construct a scripted
object implementing the interface type. For example:
<p CLEAR="ALL" />
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
buttonHandler = new ActionListener() {
actionPerformed( event ) {
print(event);
}
};
button = new JButton();
button.addActionListener( buttonHandler );
frame(button);
</pre>
</td>
</tr>
</table>
</center>
<p />
<p CLEAR="ALL" />
In the above example we have created an object that implements the
<code>ActionListener</code> interface and assigned it to a variable called
buttonHandler.
The buttonHandler object contains the scripted method actionPerformed(),
which will be called to handle invocations of that method on the interface.
<p CLEAR="ALL" />
Note that in the example we registered our scripted ActionListener with a
JButton using its addActionListener() method. The JButton is, of course,
a standard Swing component written in Java. It has no knowledge that when it
invokes the buttonHandler's actionPerformed() method it will actually be
causing the BeanShell interpreter to run a script to evaluate the outcome.
<p CLEAR="ALL" />
To generalize beyond this example a bit - Scripted interfaces work by looking
for scripted methods to implement the methods of the interface.
A Java method invocation on a script that implements an interface causes
BeanShell to look for a corresponding scripted method with
a matching signature (name and argument types). BeanShell then invokes the
method, passing along the arguments and passing back any return value.
When BeanShell runs in the same Java VM as the rest of the code, you can
freely pass "live" Java objects as arguments and return values, working
with them dynamically in your scripts; the integration can be seamless.
<p CLEAR="ALL" />
See also <a href="examples/dragtext.html">the dragText example</a>.
<p CLEAR="ALL" />
<h2><a name="'this'_references_as_Interface_Types">'this' references as Interface Types</a></h2>
The anonymous inner class style syntax which we just discussed allows you
to explicitly create an object of a specified interface type, just as you
would in Java. But BeanShell is more flexible than that. In fact,
within your BeanShell scripts, any 'this' type script reference can
automatically implement any interface type, as needed. This means that you can
simply use a 'this' reference to your script or a scripted object anywhere
that you would use the interface type. BeanShell will automatically "cast"
it to the correct type and perform the method delegation for you.
<p CLEAR="ALL" />
For example, we could script an event handler for our button even
more simply using just a global method, like this:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
actionPerformed( event ) {
print( event );
}
button = new JButton("Foo!");
button.addActionListener( this );
frame( button );
</pre>
</td>
</tr>
</table>
</center>
<p />
Here, instead of making a scripted object to hold our actionPerformed()
method we have simply placed the method in the current context
(the global scope) and told BeanShell to look there for the method.
<p CLEAR="ALL" />
Just as before, when <code>ActionEvents</code> are fired by the button, your
actionPerformed() method will be invoked. The BeanShell 'this' reference
to our script implements the interface and directs method invocations to the
appropriately named method, if it exists.
<p CLEAR="ALL" />
<p />
<center>
<table cellpadding="5" border="1" width="90%">
<tr>
<td bgcolor="#eeeebb"><strong>Note:</strong><br CLEAR="ALL" />
If you want to have some fun, try entering the previous example interactively
in a shell or on the command line. You'll see that you can then redefine
actionPerformed() as often as you like by simply entering the method again.
Each button press will find the current version in your shell. In a sense,
you are working inside a dynamic Java object that you are creating and
modifying as you type. Neat, huh? Be the Bean!
</td>
</tr>
</table>
</center>
<p />
Of course, you don't have to define all of your interface methods globally.
You can create references in any scope, as we discussed in "Scripting Objects".
For example, the following code creates a scripted message button object which
displays a message when its pushed. The scripted object holds its own
actionPerformed() method, along with a variable to hold the Frame used for
the GUI:
<p CLEAR="ALL" />
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
messageButton( message ) {
JButton button = new JButton("Press Me");
button.addActionListener( this );
JFrame frame = frame( button );
actionPerformed( e ) {
print( message );
frame.setVisible(false);
}
}
messageButton("Hey you!");
messageButton("Another message...");
</pre>
</td>
</tr>
</table>
</center>
<p />
The above example creates two buttons, with separate messages. Each button
prints its message when pushed and then dismisses itself.
The buttons are created by separate calls to the messageButton() method,
so each will have its own method context, separate local variables, and a
separate instance of the ActionListener interface handler. Each registers
itself (its own method context) as the ActionListener for its button, using
its own 'this' reference.
<p CLEAR="ALL" />
In this example all of the "action" is contained in messageButton() method
context. It serves as a scripted object that implements the interface and
also holds some state, the frame variable, which is used to dismiss the GUI.
More generally however, as we saw in the "Scripting Objects" section,
we could have returned the 'this' reference to the caller, allowing it to
work with our messageButton object in other ways.
<p CLEAR="ALL" />
<h2><a name="Interface_Types_and_Casting">Interface Types and Casting</a></h2>
It is legal, but not usually necessary to perform an explicit cast of a
BeanShell scripted object to an interface type. For example:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
actionPerformed( event ) {
print( event );
}
button.addActionListener(
(ActionListener)this ); // added cast
</pre>
</td>
</tr>
</table>
</center>
<p />
In the above, the cast to ActionListener would have been done automatically
by BeanShell when it tried to match the 'this' type argument to the signature
of the addActionListener() method.
<p CLEAR="ALL" />
Doing the cast explicitly has the same effect, but takes a different route
internally. With the cast, BeanShell creates the necessary adapter
that implements the ActionListener interface first, at the time of the cast,
and then later finds that the method is a perfect match.
<p CLEAR="ALL" />
What's the difference? Well, there are times where performing
an explicit cast to control when the type is created may be important.
Specifically, when you are passing references out of your script, to Java
classes that don't immediately use them as their intended type.
In our earlier discussion we said that automatic casting happens "within
your BeanShell scripts". And in our examples so far BeanShell has always
had the opportunity to arrange for the scripted object to become the correct
type, before passing it on. But it is possible for you to pass a 'this'
reference to a method that, for example, takes the type 'Object', in which
case BeanShell would have no way of knowning what it was destined for later.
You might do this, for example, if you were placing your scripted objects
into a collection (Map or List) of some kind. In that case, you can
control the process by performing an explicit cast to the desired type before
the reference leaves your script.
<p CLEAR="ALL" />
Another case where you may have to perform a cast is where you are using
BeanShell in an embedded application and returning a scripted object as
the result of an eval() or a get() variable from the Interpreter class.
There again
is a case where BeanShell has no way of knowing the intended type within
the script. By performing an explicit cast you can create the type
before the reference leaves your script.
<p CLEAR="ALL" />
We'll discuss embedded applications of BeanShell in the
"Embedding BeanShell" section a bit later, along with the Interpreter
getInterface() method, which is another way of accomplishing this type of
cast from outside a script.
<p CLEAR="ALL" />
<h2><a name=""Dummy"_Adapters_and_Incomplete_Interfaces">"Dummy" Adapters and Incomplete Interfaces</a>
</h2>
It is common in Java to see "dummy" adapters created for interfaces that
have more than one method. The job of a dummy adapter is to implement all
of the methods of the interface with stubs (empty bodies), allowing the
developer to extend the adapter and override just the methods of interest.
<p CLEAR="ALL" />
We hinted in our earlier discussion that BeanShell could handle scripted
interfaces that implement only the subset of methods that are actually used
and that is indeed the case. You are free in BeanShell to script only the
interface methods that you expect to be called. The penalty for leaving out
a method that is actually invoked is a special run-time exception:
java.lang.reflect.UndeclaredThrowableException, which the caller will
receive.
<p CLEAR="ALL" />
The UndeclaredThrowableException is an artifact of Java Proxy API that makes
dynamic interfaces possible. It says that an interface threw a checked
exception type that was not prescribed by the method signature. This is
a situation that cannot normally happen in compiled Java. So the Java
reflection API handles it by wrapping the checked exception in this special
unchecked (RuntimeException) type in order to throw it.
You can get the underlying
error using the exception's getCause() method, which will, in this case,
reveal the BeanShell EvalError exception, reporting that the scripted method
of the correct signature was not found.
<p CLEAR="ALL" />
<h3>The invoke() Meta-Method</h3>
BeanShell provides a very simple short-hand mechanism for scripting interfaces
with large numbers of methods. You can implement the special method
<em>invoke( name, args )</em> in any scripted
context. The invoke() method will be called to handle the invocation of
any method of the interface that is not defined. For example:
<p CLEAR="ALL" />
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
mouseHandler = new MouseListener() {
mousePressed( event ) {
print("mouse button pressed");
}
invoke( method, args ) {
print("Undefined method of MouseListener interface invoked:"
+ name +", with args: "+args
);
}
};
</pre>
</td>
</tr>
</table>
</center>
<p />
<p CLEAR="ALL" />
In the above example we have neglected to implement four of the five
methods of the MouseListener interface. They will be handled by the invoke()
method, which will simply print the name of the method and its arguments.
However since mousePressed() is defined it will be called for the interface.
<p CLEAR="ALL" />
Here is a slightly more realistic example of where this comes in handy.
Let's use the invoke() method to print the names of methods called via
the ContentHandler interface of the Java SAX API, while parsing an XML
document.
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
import javax.xml.parsers.*;
import org.xml.sax.InputSource;
factory = SAXParserFactory.newInstance();
saxParser = factory.newSAXParser();
parser = saxParser.getXMLReader();
parser.setContentHandler( this );
invoke( name, args ) {
print( name );
}
parser.parse( new InputSource(bsh.args[0]) );
</pre>
</td>
</tr>
</table>
</center>
<p />
By running this script with the XML file as an argument, we can see which
of the dozen or so methods of the SAX API are being exercised by the structure
of the document, without having to write a stub for each of them.
<p CLEAR="ALL" />
<p />
<center>
<table cellpadding="5" border="1" width="100%">
<tr>
<td><strong>Tip:</strong><br CLEAR="ALL" />
You can use the invoke( name, args ) meta-method directly in your own scope or
in the global scope as well, in which case you can handle arbitrary "unknown"
method invocations yourself, perhaps to implement your own "virtual" commands.
Try typing this on the command line:
<pre>
invoke(name,args) { print("Command: "+name+" invoked!"); }
noSuchMethod(); // prints "Command: noSuchMethod() invoked!"
</pre>
</td>
</tr>
</table>
</center>
<p />
<h2><a name="Threads_-_Scripting_Runnable">Threads - Scripting Runnable</a></h2>
BeanShell 'this' type references can implement the standard java.lang.Runnable
interface. So you can declare a "run()" method in your bsh objects and make
it the target of a Thread:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
foo() {
run() {
// do work...
}
return this;
}
foo = foo();
// Start two threads on foo.run()
new Thread( foo ).start();
new Thread( foo ).start();
</pre>
</td>
</tr>
</table>
</center>
<p />
<p CLEAR="ALL" />
BeanShell is thread-safe internally, so as long as your scripts do not
explicitly do anything ordinarily non-thread safe (e.g. access shared
variables or objects) you can write multi-threaded scripts.
<p CLEAR="ALL" />
<p />
<center>
<table cellpadding="5" border="1" width="90%">
<tr>
<td bgcolor="#eeeebb"><strong>Note:</strong><br CLEAR="ALL" />
You can use the bg() "background" command to run an external script in a
separate thread. See bg().
</td>
</tr>
</table>
</center>
<p />
<p CLEAR="ALL" />
<h2><a name="Limitations">Limitations</a></h2>
<p CLEAR="ALL" />
When running under JDK 1.3 or greater BeanShell can script any kind of
Java interface. However when running under JDK 1.2 (or JDK1.1 + Swing) only
the core AWT and Swing interfaces are available. To support those legacy
cases a special extension of the 'this' reference implementation (the
bsh.This class) is loaded which implements these interfaces along with
Runnable, statically.
<p CLEAR="ALL" />
<table cellspacing="10">
<tr>
<td align="center"><a href="http://www.beanshell.org/"><img src="images/homebutton.png" /><br />Home</a>
</td>
<td><a href="scope.html#Scope_Modifiers"><img src="images/backbutton.png" /><br />Back
</a></td>
<td align="center"><a href="contents.html"><img src="images/upbutton.png" /><br />Contents</a></td>
<td align="center"><a href="specialvarsvalues.html#Special_Variables_and_Values"><img
src="images/forwardbutton.png" /><br />Next
</a></td>
</tr>
</table>
</body>
</html>