-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathembeddedmode.html
613 lines (514 loc) · 22.1 KB
/
embeddedmode.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
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>Embedding BeanShell in Your Application</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="standalonemode.html#Modes_of_Operation"><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="remotemode.html#Remote_Server_Mode"><img
src="images/forwardbutton.png" /><br />Next
</a></td>
</tr>
</table>
<h1>Embedding BeanShell in Your Application</h1>
<img src="images/embeddedmode.png" />
<br CLEAR="ALL" />
BeanShell was designed not only as a standalone scripting language - to run
scripts from files or on the command line - but to be easily embeddable in
and extensible by your applications. When we talk about
embedding BeanShell in your application we mean simply that you can use the
BeanShell Interpreter in your own classes, to evaluate scripts and work with
objects dynamically.
<p CLEAR="ALL" />
There are a number of reasons you might use BeanShell in this way. Here
are a few:
<p CLEAR="ALL" />
<h3>Highly Customizable Applications</h3>
You can use BeanShell to make your applications highly customizable by users
without requiring them to compile Java classes or even to know all of the
Java syntax.
During development you can use BeanShell to "factor out" volatile or
environmentally dependent algorithms from your application and leave them
in the scripting domain while they are under the most intense change.
Later it is easy to move them back into compiled Java if you wish because
BeanShell syntax is Java syntax.
<p CLEAR="ALL" />
<h3>Macros and Evaluation</h3>
BeanShell can be used as a generic language for "macros" or other complex
tasks that must be described by a user of your application.
For example, the popular JEdit Java editor uses
BeanShell to allow users to implement macros for key bindings. This gives
user power to customize the behavior of the editor, using as much (or
as little) of the full power of Java as desired.
<p CLEAR="ALL" />
Java with loose variables is a very simple and appealing language; especially
because there is already so much Java out there. Even a non-programmer
will be familiar with the name "Java" and more inclined to want to work
with it than an arbitrary new language.
<p CLEAR="ALL" />
BeanShell can also be used to perform dynamic evaluation of complex expressions
such as mathematics or computations entered by the user. Why write an
arithmetic expression parser when you can let your user enter equations using
intermediate variables, operators, and other constructs. If strict control
is desired, you can generate the script yourself using your own rules, and
still leave the evaluation to BeanShell.
<h3>Simplify Complex Configuration Files</h3>
Many applications use simple Java properties files or XML for the majority
of their runtime configuration.
It is very common in the development of a large applications for configuration
files like this to become increasingly complex. It can begin in a number
of seemingly harmless ways - with the desire to make "cross references"
inside the config files (XML supports this nicely). Then comes the desire
to do something like variable substitution - which introduces some new syntax
such as "${variable}" and usually a second pass in the parsing stage.
Usually, at some point, integration with Java forces the introduction of
class names into the mix. The configuration files soon want to start
assigning parameters for object construction. Ultimately what you'll
discover is that you are creating your own scripting language - and one that
is probably not as easy to read as plain old Java.
<p CLEAR="ALL" />
BeanShell solves the problem of complex configuration files by allowing
users to work not only with simple properties style values
(loose variable assignment)
but also to have the full power of Java to construct objects, arrays,
perform loops and conditionals, etc. And as we'll see, BeanShell scripts
can work seamlessly with objects from the application, <b>without</b>
the need to turn them into strings to cross the script boundary.
<p CLEAR="ALL" />
<h2><a name="The_BeanShell_Core_Distribution">The BeanShell Core Distribution</a></h2>
Beanshell is fairly small, even in its most general distribution. The
full JAR with all utilities weighs in at about 250K. But BeanShell is
also distributed in a componentized fashion, allowing you to choose to add
only the utilities and other pieces that you need. The core distribution
includes only the BeanShell interpreter and is currently about 130K. <em>We
expect this size to drop in the future with improvements in the parser
generator.</em> Any significant new features will always be provided in
the form of add-on modules, so that the core language can remain small.
<p CLEAR="ALL" />
More and more people are using BeanShell for embedded applications in small
devices. We have reports of BeanShell running everywhere from palm-tops
to autonomous buoys in the Pacific ocean!
<h2><a name="Calling_BeanShell_From_Java">Calling BeanShell From Java</a></h2>
Invoking BeanShell from Java is easy. The first step is to create in
instance of the bsh.Interpreter class. Then you can use it to evaluate
strings of code, source external scripts. You can pass your data in to
the Interpreter as ordinary BeanShell variables, using the Interpreter
set() and get() methods.
<p CLEAR="ALL" />
In "QuickStart" we showed a few examples:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
import bsh.Interpreter;
Interpreter i = new Interpreter(); // Construct an interpreter
i.set("foo", 5); // Set variables
i.set("date", new Date() );
Date date = (Date)i.get("date"); // retrieve a variable
// Eval a statement and get the result
i.eval("bar = foo*10");
System.out.println( i.get("bar") );
// Source an external script file
i.source("somefile.bsh");
</pre>
</td>
</tr>
</table>
</center>
<p />
The default constructor for the Interpreter assumes that it is going to be
used for simple evaluation. Other constructors allow you to
set up the Interpreter to work with interactive sources including streams
and the GUI console.
<p CLEAR="ALL" />
<h2><a name="eval()">eval()</a></h2>
The Interprete eval() method accepts a script as a string and interprets it,
optionally returning a result.
The string can contain any normal BeanShell script text with any number of
Java statements.
The Interpreter maintains state over any number of eval() calls, so you can
interpret statements individually or all together.
<p />
<center>
<table cellpadding="5" border="1" width="90%">
<tr>
<td bgcolor="#eeeebb"><strong>Note:</strong><br CLEAR="ALL" />
It is not necessary to add a trailing ";" semi-colon at the end of the
evaluated string. BeanShell always adds one at the end of the string.
</td>
</tr>
</table>
</center>
<p />
The result of the evaluation of the last statement or expression in the
evaluated string is returned as the value of the eval(). Primitive types
(e.g int, char, boolean) are returned wrapped in their primitive wrappers
(e.g. Integer, Character, Boolean).
If an evaluation of a statement or expression yields a "void" value; such
as would be the case for something like a for-loop or a void type method
invocation, eval() returns null.
<p CLEAR="ALL" />
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
Object result = i.eval( "long time = 42; new Date( time )" ); // Date
Object result = i.eval("2*2"); // Integer
</pre>
</td>
</tr>
</table>
</center>
<p />
You can also evaluate text from a java.io.Reader stream using eval():
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
reader = new FileReader("myscript.bsh");
i.eval( reader );
</pre>
</td>
</tr>
</table>
</center>
<p />
<h2><a name="EvalError">EvalError</a></h2>
The bsh.EvalError exception is the general exception type for an error in
evaluating a BeanShell script. Subclasses of EvalError - ParseException
and TargetError - indicate the specific conditions where a textual
parsing error was encountered or where the script itself caused an exception
to be generated.
<p CLEAR="ALL" />
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
try {
i.eval( script );
} catch ( EvalError e ) {
// Error evaluating script
}
</pre>
</td>
</tr>
</table>
</center>
<p />
You can get the error message, line number and source file of the error
from the EvalError with the following methods:
<p CLEAR="ALL" />
<table border="1" cellpadding="5">
<tr>
<td>
String getErrorText() {
</td>
</tr>
<tr>
<td>
int getErrorLineNumber() {
</td>
</tr>
<tr>
<td>
String getErrorSourceFile() {
</td>
</tr>
</table>
<p CLEAR="ALL" />
<h3>ParseException</h3>
ParseException extends EvalError and indicates that the exception was
caused by a syntactic error in reading the script. The error message will
indicate the cause.
<h3>TargetError</h3>
TargetError extends EvalError and indicates that the exception was
not related to the evaluation of the script, but caused the by script itself.
For example, the script may have explicitly thrown an exception
or it may have caused an application level exception such as a NullPointer
exception or an ArithmeticException.
<p CLEAR="ALL" />
The TargetError contains the "cause" exception. You can retrieve it
with the getTarget() method.
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
try {
i.eval( script );
} catch ( TargetError e ) {
// The script threw an exception
Throwable t = e.getTarget();
print( "Script threw exception: " + t );
} catch ( ParseException e ) {
// Parsing error
} catch ( EvalError e ) {
// General Error evaluating script
}
</pre>
</td>
</tr>
</table>
</center>
<p />
<h2><a name="source()">source()</a></h2>
The Interpreter source() method can be used to read a script from an external
file:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
i.source("myfile.bsh");
</pre>
</td>
</tr>
</table>
</center>
<p />
The Interpreter source() method may throw FileNotFoundException and
IOException in addition to EvalError. Aside from that source() is simply
and eval() from a file.
<h3>set(), get(), and unset()</h3>
As we've seen in the examples thus far, set() and get() can be used to
pass objects into the BeanShell interpreter as variables and retrieve the
value of variables, respectively.
<p CLEAR="ALL" />
It should be noted that get() and set() are capable of evaluation of
arbitrarily complex or compound variable and field expression.
For example:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
import bsh.Interpreter;
i=new Interpreter();
i.eval("myobject=object()" );
i.set("myobject.bar", 5);
i.eval("ar=new int[5]");
i.set("ar[0]", 5);
i.get("ar[0]");
</pre>
</td>
</tr>
</table>
</center>
<p />
The get() and set() methods have all of the evaluation capabilities of
eval() except that they will resolve only one variable target or value
and they will expect the expression to be of the appropriate resulting type.
<p CLEAR="ALL" />
<em>The deprecated setVariable() and getVariable() methods are no longer
used because the did not allow for complex evaluation of variable names</em>
<p CLEAR="ALL" />
You can use the unset() method to return a variable to the undefined state.
<h3>Getting Interfaces from Interpreter</h3>
We've talked about the usefulness of writing scripts that implement
Java interfaces. By wrapping a script in an interface you can make it
transparent to the rest of your Java code.
As we described in the "Interfaces" section earlier, within the BeanShell
interpreter scripted objects automatically implement any interface necessary
when they are passed as arguments to methods requiring them.
However if you are going to pass a reference outside of BeanShell you may
have to perform an explicit cast inside the script, to get it to manufacture
the correct type.
<p CLEAR="ALL" />
The following example scripts a global actionPerformed() method and returns a
reference to itself as an ActionListener type:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
// script the method globally
i.eval( "actionPerformed( e ) { print( e ); }");
// Get a reference to the script object (implementing the interface)
ActionListener scriptedHandler =
(ActionListener)i.eval("return (ActionListener)this");
// Use the scripted event handler normally...
new JButton.addActionListener( scriptedHandler );
</pre>
</td>
</tr>
</table>
</center>
<p />
Here we have performed the explicit cast in the script as we returned the
reference. (And of course we could have used the standard Java anonymous
inner class style syntax as well.)
<p CLEAR="ALL" />
An alternative would have been to have used the Interpreter getInterface()
method, which asks explicitly for the global scope to be cast to a specific
type and returned.
The following example fetches a reference to the interpreter global namespace
and cast it to the specified type of interface type.
<pre>
Interpreter interpreter = new Interpreter();
// define a method called run()
interpreter.eval("run() { ... }");
// Fetch a reference to the interpreter as a Runnable
Runnable runnable =
(Runnable)interpreter.getInterface( Runnable.class );
</pre>
<p CLEAR="ALL" />
The interface generated is an adapter (as are all interpreted interfaces).
It does not interfere with other uses of the global scope or other
references to it.
We should note also that the interpreter does *not* require that any or all
of the methods of the interface be defined at the time the interface is
generated. However if you attempt to invoke one that is not defined
you will get a runtime exception.
<p CLEAR="ALL" />
<h2><a name="Multiple_Interpreters_vs._Multi-threading">Multiple Interpreters vs. Multi-threading</a></h2>
A common design question is whether to use a single BeanShell
interpreter or multiple interpreter instances in your application.
<p CLEAR="ALL" />
The Interpreter class is, in general, thread safe and allows you to
work with threads, within the normal bounds of the Java language. BeanShell
does not change the normal application level threading issues of multiple
threads from accessing the same variables: you still have to synchronize
access using some mechanism if necessary.
However it is legal to perform multiple simultaneous evaluations.
You can also write multi-threaded scripts within the language, as we discussed
briefly in "Scripting Interfaces".
<p CLEAR="ALL" />
Since working with multiple threads introduces issues of synchronization
and application structure, you may wish to simply create multiple Interpreter
instances. BeanShell Interpreter instances were designed to be very light
weight. Construction time is usually negligible and in simple tests, we have
found that it is possible to maintain hundreds (or even thousands) of
instances.
<p CLEAR="ALL" />
There are other options in-between options as well. It is possible to
retrieve BeanShell scripted objects from the interpreter and "re-bind" them
again to the interpreter. We'll talk about that in the next section.
You can also get and set the root level bsh.NameSpace object for the
entire Interpreter. The NameSpace is roughly equivalent to a BeanShell method
context. Each method context has an associated NameSpace object.
<p />
<center>
<table cellpadding="5" border="1" width="100%">
<tr>
<td><strong>Tip:</strong><br CLEAR="ALL" />
You can clear all variables, methods, and imports from a scope using
the clear() command.
</td>
</tr>
</table>
</center>
<p />
<em>Note: at the time of this writing the synchronized language keyword
is not implemented. This will be corrected in an upcoming release.</em>
<p CLEAR="ALL" />
See also "The BeanShell Parser" for more about performance issues.
<h2><a name="Serializing_Interpreters_and_Scripted_Objects">Serializing Interpreters and Scripted Objects</a></h2>
The BeanShell Interpreter is serializable, assuming of course that all
objects referenced by variables in the global scope are also serializable.
So you can save the entire static state of the interpreter by serializing
it and storing it. Note that serializing the Intepreter does not "freeze"
execution of BeanShell scripts in any sense other than saving the current
state of the variables. In general if you serialize an Interpreter while
it is executing code the results will be undetermined. De-serializing an
interpreter does not automatically restart method executions; it simply
restores state.
<p CLEAR="ALL" />
<p />
<center>
<table cellpadding="5" border="1" width="90%">
<tr>
<td bgcolor="#eeeebb"><strong>Note:</strong><br CLEAR="ALL" />
There is serious Java bug that affects BeanShell serializability in Java
versions prior to 1.3. When using these versions of Java the primitive type
class identifiers cannot be de-serialized. See the FAQ for a workaround.
</td>
</tr>
</table>
</center>
<p />
It is also possible to serialize individual BeanShell scripted objects
('this' type references and interfaces to scripts). The same rules apply.
One thing to note is that by default serializing a scripted object context
will also serialize all of that object's parent contexts up to the global
scope - effectively serializing the whole interpreter.
<p CLEAR="ALL" />
To detach a scripted object from its parent namespace you can use the
namespace prune() method:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
// From BeanShell
object.namespace.prune();
// From Java
object.getNameSpace().prune();
</pre>
</td>
</tr>
</table>
</center>
<p />
To bind a BeanShell scripted object back into a particular method scope
you can use the bind() command:
<p />
<center>
<table border="1" cellpadding="5" width="100%">
<tr>
<td bgcolor="#dfdfdc">
<pre>
// From BeanShell
bind( object, this.namespace );
// From Java
bsh.This.bind( object, namespace, interpreter );
</pre>
</td>
</tr>
</table>
</center>
<p />
The bind() operation requires not only the namespace (method scope) into which
to bind the object, but an interpreter reference as well. The interpreter
reference is the "declaring interpreter" of the object and is used for cases
where there is no active interpreter - e.g. where an external method call
from compiled Java enters the object.
<p CLEAR="ALL" />
The BeanShell save() command which serializes objects recognize when you are
trying to save a BeanShell scripted object (a bsh.This reference) type and
automatically prune()s it from the parent namespace, so that saving the object
doesn't drag along the whole interpreter along for the ride. Similarly,
load() binds the object to the current scope.
<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="standalonemode.html#Modes_of_Operation"><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="remotemode.html#Remote_Server_Mode"><img
src="images/forwardbutton.png" /><br />Next
</a></td>
</tr>
</table>
</body>
</html>