|
|
||
Pat Niemeyer's BlogProgramming ArchivesThe new javax.script API....Posted by pat on February 11, 2005 at 12:11 AM | Permalink | Comments (12)A few days ago the JCP cranked out a public review of JSR-223, which has so far been received with a few blog notes and relatively little discussion. This is not surprising, given that if you you read the JCP description for this group it appears to have something to do with getting PHP pages into WAR files. (Useful, not very exciting). But I'm going to let you in on a little secret. There are really two APIs buried in this specification and one of them is actually quite interesting. Java is about to get a standardized API for working with scripting languages and it's not just about web applications any more. While the original focus of JSR-223 was indeed to develop a standard API for deploying scripted pages in web applications, over the past year the expert group has actually spent most of its time on the lower level, prerequisite API for exposing scripting language engines to the Java platform. The new javax.script package provides a standardized script engine API, similar to the IBM/Apache BSF (Bean Scripting Framework), but takes the integration much further, offering: a more powerful discovery mechanism, fine grained namespace and script environment management, powerful application integration, and other advanced features. Since much of the specification is aimed at scripting engine developers, some of these user APIs may not be immediately obvious. I'll just run down some of the important points here and leave the details for an upcoming article. The Java Scripting API includes:
The reference implementation is available from the JSR-223 home page: http://jcp.org/aboutJava/communityprocess/pr/jsr223/ The download includes javax.script engine implementations for JavaScript, PHP, and Groovy. Interestingly, despite numerous references to the BeanShell Java scripting language in the specification, the development of a BeanShell engine by the spec lead, and the participation of the primary BeanShell developer (me) in the spec development, the Sun download does not include a BeanShell engine implementation or examples. (The politics of Groovy continue to amaze me.) But not to fear, the next release of BeanShell will include an engine implementation as part of its own distribution. Check it out! Stupid Scanner tricks...Posted by pat on October 24, 2004 at 01:18 AM | Permalink | Comments (4)One of the things I've always wanted in Java is a "one liner" trick to read all of the text from a stream. For example, I often want to be able to grab the contents of a URL or file as a simple String, without a lot of typing. The URL class tantalizingly holds out its getContent() method, but sadly, content handlers were never really taken seriously. I don't even particularly care about performance, I'd just like something for the simple case, in standard Java, that's not too hard to remember. Well, the Java 1.5 java.util.Scanner class finally has the answer... Suppose I have a stream:
InputStream source = new URL("http://pat.net/misc/foo.txt").openStream();
The canonical way to gather it to a String has always been to use a BufferedReader, e.g.
BufferedReader br = new BufferedReader( new InputStreamReader( source ) );
StringBuffer text = new StringBuffer();
for ( String line; (line = br.readLine()) ! = null )
text.append( line );
This is about 4 lines of tediousness code (assuming the resulting StringBuffer is good enough), uses two classes, a loop, and too many parentheses. I must have typed code like this a million times, as I bet a lot of people have. I've often been tempted to try to shorten it a bit using the DataInputStream readFully() method:
byte [] buf = new byte[ source.available() ];
new DataInputStream( source ).readFully( buf );
String text = new String( buf );
That would be a bit less typing and involve only an array and a class. The problem is that it relies on the input stream's available() method to reflect the total size of the data to be returned... which in general it doesn't. The available() method works for files and you could always substitute your own size if you can get it from other meta-data, but it's still a messy solution and doesn't exactly roll off of the finger tips. Finally now with Java 1.5's Scanner I have a true one-liner:
String text = new Scanner( source ).useDelimiter("\\A").next();
One line, one class. The only tricky is to remember the regex \A, which matches the beginning of input. This effectively tells Scanner to tokenize the entire stream, from beginning to (illogical) next beginning. As a bonus, Scanner can work not only with an InputStream as the source, but also a File, Channel, or anything that implements the new java.lang.Readable interface. For example, to read a file:
String text = new Scanner( new File("poem.txt") ).useDelimiter("\\A").next();
Finally, before someone chastizes me I should point out that you can accommodate a specific character set with all of the above examples. In the first you'd set the charset in the InputStreamReader, in the second you'd specify it with the String constructor, and in the Scanner example you can pass a charset to the constructor. Enjoy! | ||
|
|