"Java Scripting Languages: Which Is Right for You?"

David Kearns

Jython does support multidimensional arrays

David,

You mention that Jython does not support multidimensional arrays. However, Jython and Python support multidimensional arrays exactly the same way Java does, through jagged arrays. That is, you can have arrays of arrays. Note that jagged arrays do not constitute a true multidimensional array feature—and Microsoft loves to jab Java for this lack of support.

On the other hand, because Jython supports operator overloading, it can support true multidimensional arrays through standard array syntax, which Java can't. Standard Python has an extension library called Numeric that supports matrix math through such features.

I don't know if Jython supports Numeric (or NumPy, as it is often called), but Jython could if someone put the time into it.

Tom Palmer

Tom, I see your point about multidimensional arrays. When I found that Jython didn't support the standard multidimensional array syntax, I tried unsuccessfully to get the array-of-arrays implementation you suggest to work. (Perhaps I need to try again.) Then I tried to find external Jython libraries to provide this capability and didn't find any. At that point, I felt I should just say that Jython lacked support for the syntax. I had not considered your idea about overloading operators to add multidimensional array support. (Editors' note: David has updated "Java Scripting Languages: Which Is Right for You?" since its original publication to reflect Jython's indirect support for multidimensional arrays.) David Kearns

David,

Sorry to harp too much, but I feel I should add a comment: You shouldn't say Jython is hard simply because you don't know the language.

I assume you already knew Tcl, as its syntax differs significantly from Java's, more than Python. I also assume you're familiar with Rhino and BeanShell because of their JavaScript and Java use. You wrote:

On the other hand, I hadn't previously used Rhino/JavaScript or BeanShell either, and they were no trouble at all to figure out and use.

That does not provide the correct perspective.

Learning a new language can be a drawback, but that should have been the drawback you mentioned, not that Jython was "difficult."

For more details on Python see "Tips for Scripting Java with Jython," Noel Rappin (OnJava.com, March 2002).

By the way, I'm not a Python purist. It has its drawbacks, just as Java does.

Tom

Tom, You clearly know your stuff, and I appreciate your input. I tried to balance the information on Jython. I concluded that it was well supported, very fast, and powerful. And I don't think that Jython is unusably hard, but for an experienced Java programmer, it requires more of a learning curve than other scripting languages. Another engineer I work with shares my opinion. David Kearns

"Generate JavaBean Classes Dynamically with XSLT"

Victor Okunev

How do you compile Java code dynamically?

Victor,

You mentioned the magic words "compile it dynamically." I'm dying to know how to compile Java code dynamically. I searched all over the Web and found the following:

  1. Use Runtime.exec() to call javac
  2. Use sun.tools.javac.Main
  3. Use java.lang.Compiler

As far as I know, Solutions 1 and 2 are not portable and require the JDK. Solution 3 is in fact unusable, as java.lang.Compiler is only a hook into the JIT (just-in-time) compiler. Are these comments correct? Do you have any suggestions for dynamic compilation?

David

David, You are absolutely right about those three options. Sun Microsystems uses Solution 1 in their Bean Development Kit. Your best bet is to parameterize and externalize the actual call to the compiler. Use something like this:

public boolean compile( String filename, String classpath, String sourcepath ) {
//Set formatter
MessageFormat formatter = new MessageFormat( resource.getString( 
"CompileCommand" ) );
//Now use the formatter to build a compiler command
String command = formatter.format( new Object[] { classpath, sourcepath, filename } );
try {
//Now invoke the compiler (according to Sun this is the best way, see ClassCompiler.java in BDK1.1 )
Process p = Runtime.getRuntime().exec( command );
return( p.waitFor() == 0 ) ? true : false; // status code 0 indicates success
 } catch( Throwable th ) {
return false;
}
}

Victor Okunev

"Evolve Your Apps with the Phased Process Pattern"

Jon Benton

Introducing ProgramFlow

Mr. Benton,

I thought you might be interested to learn that I've used a similar mechanism in a different context—Web-based programming.

As you know, on the Web, adhering to a Model-View-Controller (MVC) architecture when writing an application that processes multiple forms can prove difficult. Often control logic exists in more than one place—in the HTML page's form action, in a servlet, in a JSP (JavaServer Page), and so on. This makes applications tough to debug and even tougher to modify.

Therefore, when writing a Web application with multiple forms, I create an object called ProgramFlow, which acts as a traffic cop for all requests and responses. At init time, a servlet instantiates this object, which is held in memory. ProgramFlow consists of two maps: The first map is for requests and stores strings representing all the application's form names as the key, and instances of FormHandlerCommand (based on the Command pattern) that handle the forms as the value. The second map is for responses and stores strings identifying a view as the key, and that view's URL as the value. The FormHandlerCommand takes an HttpServletRequest as a parameter and returns a string representing the view as stored on the second response map in ProgramFlow.

In general, messages move as follows: A form posts to a single control servlet, including its name as one of the inputs. The servlet gets the form name from the request and uses it to get the corresponding FormHandlerCommand from ProgramFlow. The FormHandlerCommand handles the request in its own way and returns a view name. The servlet uses the view name to get the corresponding URL from ProgramFlow and redirects.

Some code snippets follow. Here are parts of the servlet:

      // class variable
      private ProgramFlow _programFlow ;
      public void doPost(HttpServletRequest request,HttpServletRequestresponse) 
      throws ServletException, IOException {
      
            // error checking omitted
            String formName = request.getParameter("formName") ;
            FormHandlerCommand handler =
_programFlow.getHandler(formName) ;
            String viewName = handler.execute(request) ;
            String url = _programFlow.getView(viewName) ;
            response.sendRedirect(url) ;
      }
      public void init() {
            _programFlow = new ProgramFlow() ;
      }

Here is ProgramFlow:

      class ProgramFlow {
            private Map _handlers ;
            private Map _views ;
      
            ProgramFlow() {
                  _handlers = new HashMap() ;
                  // put all form names and new instances of FormHandlerCommand onto map
            
                  _views = new HashMap() ;
                  // put all view ids and urls onto map
            }
            FormHandlerCommand getCommand(String formName) {
                  // error checking omitted
                  return (FormHandlerCommand)_handlers.get(formName) ;
            }
            String getView(String viewName) {
                  // error checking omitted
                  return (String)_views.get(viewName) ;
            }
      }

Here is the FormHandlerCommand interface:

      // error checking omitted -- this object could throw an exception
      interface FormHandlerCommand {
            String execute(HttpServletRequest request) ;
      }

A FormHandlerCommand could possibly return one of several view names based on its state, such as a good view or an error condition view. Multiple FormHandlerCommands could possibly return the same view name, such as the application's final view. A FormHandlerCommand could possibly return an object, rather than a string, that contains the view name along with additional information, such as cookies, that the HttpServletResponse can use.

I like this architecture for many reasons: Views can be added, deleted, or reordered. You can modify FormHandlerCommands in isolation without disrupting the application. The servlet is dumb, and you never have to change it. Though writing unit tests for servlets proves difficult, you can easily write them for ProgramFlow and the FormHandlerCommands. In addition to the points you made in your article—decoupling sequential operations, unifying the API for phased processes, providing a flexible dispatch mechanism—the Phased Process pattern is self-documenting and, most importantly, testable.

Alexander Mecs

Alexander, Your ProgramFlow pattern is a real gem. As you point out, writing an MVC pattern in a Web-based environment proves tricky at best, absolute spaghetti at worst. Your pattern nicely condenses conditional flow into one neat package—a great thing considering how many different places control can exist and how many languages they can be written in. I'm developing a system that uses Java to generate SQL, which passes through PHP (Hypertest Prepocessor) to a server, then back. Sometimes it passes straight from Java to PHP, which contains logic to generate the SQL (not, I must point out, my design). I can easily imagine a slightly tweaked version of ProgramFlow, which would contain all that logic in one place. That would be sweet, indeed. I complement you on your design for ProgramFlow and suggest you share it with the Java community. Jon Benton

Tips 'N Tricks

"Java Tip 126: Prepare Cross-Server Database Access Methods with JDBC"

Michael Juntao Yuan

How does PreparedStatement perform?

Michael,

Have you tested PreparedStatement's speed? I thought creating a new instance resulted in overhead, which was offset by reusing it multiple times. But if you don't cache the statements with the connection, is PreparedStatement much slower than regular statements? What about across different database servers?

Ross Dyson

Ross, I have not done a benchmark on PreparedStatement. Like you said, the performance gain of PreparedStatement depends on your application and therefore is impossible to benchmark in a universal way. For most applications, you use similar SQL statements repeatedly, and PreparedStatement can give you a large performance gain. I agree that caching proves more important for PreparedStatement than for regular Statement objects due to the template reuse. In my tip, I concentrate on PreparedStatement itself and regard object pooling as a higher-level design issue. Michael Juntao Yuan

How do you treat those cases where you need a sequence number?

Michael,

Your tip only covers two of three requirements. I am not sure how this approach improves performance. The SQL still has to be parsed for each query.

Also, how do you treat cases where you need a sequence number? In Oracle, seq_name.nextVal obtained the number, but Sysbase and Microsoft SQL Server lack this facility.

Using stored procedures in the general insert/update/delete would prove simpler:

  1. Java code simply calls the same stored procedure name so the code can be used across all database servers
  2. The performance improves because the stored procedure is only parsed once and cached in a database

The drawbacks: You have to write a stored procedure for each database server, plus maintenance could prove problematic.

Chester Chen

Chester, Yes, you need to reuse

PreparedStatement

and possibly cache it across multiple pages (in a Web setting) to fully leverage its better performance. However, I consider that a design issue, whereas my tip covers only

PreparedStatement

's benefits and usage. You say:

The drawbacks: You have to write a stored procedure for each database server, plus maintenance could prove problematic.

Like you said, stored procedures would be database specific, and some popular databases fail to support stored procedures (like MySQL). Michael Juntao Yuan

"Design Networked Applications in RMI Using the Adapter Design Pattern"

Dan Becker

RemoteMapServer is thread safe

Mr. Becker,

I have used your technique in the Sun Certified Java Developer Exam. It has saved me a lot of time, and the solution now looks elegant. Thank you for publishing such a practical article.

I have a question: Can you use the RemoteMapServer that publishes a RemoteMapAdapter as is, and can multiple clients simultaneously access it?

Bala Paranjo

Bala, Yes, RemoteMapServer is thread safe and can be used by multiple clients at the same time. However, that is not always the case. In my example, I use a java.util.Hashtable, which is thread safe. Other implementations might use other non-thread-safe collections. To be safe, the class, which implements the remote API, should consist of atomic operations (as in hash tables) or should have proper synchronization blocks around nonatomic operations. Dan Becker

"Exceptional Practices"

Part 1: Use exceptions effectively in your programs

Part 2: Use exception chaining to preserve debugging information

Part 3: Use message catalogs for easy localization

Brian Goetz

Does ChainedException preserve the original exception?

Brian,

I wonder if the class ChainedException you propose loses the message information from the original exception. I think you must overwrite the toString() method so that it outputs a message plus the original message. What do you think?

Ture Friese

Page 1 of 2Page 2 of 2

Ture, The entire original exception is preserved, so nothing is thrown away. The printStackTrace() method calls the original message's printStackTrace(), which also prints the message string. You can also redefine toString() to concatenate the caught.toString(). Brian Goetz

"Master Merlin's New I/O Classes"

Michael T. Nygard

How do you do successive read/write operations on a Buffer?

Michael,

I am confused about java.nio.Buffer: I can't figure out how to do successive read/write operations on a Buffer if the inequality > position <= limit always strictly holds. The limit never increases.

Albert

Albert, The first thing to consider is that a

Buffer

is meant to be alternately filled and read completely. That doesn't necessarily mean filled to the limit; that just means you expect to put everything in at once, then hand it off to be read. Or, conversely, read everything from it, then have it filled again. Between reading and writing, you must

flip

the

Buffer

. For example, I might fill the

Buffer

by calling

put()

repeatedly. This puts data into the

Buffer

and advances the position. Before I can read the

Buffer

, I must call

flip()

to make my current position the limit and reset the position. In effect, this says "Everything that can be read is now in this buffer." Calling

get()

at this point will return the

Buffer

's first element and advance the position. I can continue to call

get()

until I reach the

Buffer

's limit, which was the position before I called

flip()

. Once the

Buffer

has been read (possibly by a

Channel

), I call

clear()

to get it ready for more

put()

s.

clear()

sets the position to zero and sets the limit to the

Buffer

's capacity. I can now call

put()

until I reach the limit (i.e., the

Buffer

's capacity). In short, while writing into a

Buffer

, the limit is the

Buffer

's capacity. After I flip the Buffer, I put in the limit as the last element, and the position returns to zero. Then, while reading the Buffer, the position can advance to the limit. Thus, we have the invariant:

0 <= position <= limit <= capacity

Michael T. Nygard

"Decorate Your Java Code"

David Geary

How do you combine a sorting Decorator with a filtering Decorator?

David,

Since I'm working on a JTable in a project, sorting as well as filtering is of concern. How do you combine both a sorting Decorator and a filtering Decorator. Obviously you have to sort after applying a filter.

Alex

Alex, See the homework from the "Last Time" section in the subsequent

Java Design Patterns

column: "

Take Control with the Proxy Design Pattern

" (February 2002). Actually, the order of sorting and filtering depends on the order in which you construct the decorators; for example:

TableFilterDecorator filterDecorator =
new TableHighPriceFilter(new TableSortDecorator(table.getModel()));

will sort first, then filter. But you can also do:

TableSortDecorator sortDecorator =
new TableSortDecorator (new TableHighPriceFilter (table.getModel()));

That code will filter, then sort. David Geary

Page 2 of 2