JavaRanch Newsletter Articles in this issue :
Puzzle Corner - Bitter SpousesErnest Friedman-Hill
Printable Version
The RowSet InterfaceThomas Paul
Printable Version
The Coffee House - A Pattern of BehaviorSolveig Haugland
Printable Version
An Introduction to Java Development with NetBeans IDEDirk Schreckmann
Printable Version
Installing a native library to the Applet client's computerErika Kupkova
Printable Version
SSH Tunneling for Java RMI, Part-IPankaj Kumar
Printable Version
Saloon Question and Answer of the Month - How to code review for performance?Dirk Schreckmann
Printable Version
Book Review of the Month - JSTL: Practical Guide for JSP Programmers by Sue SpielmanThomas Paul and Matthew Phillips
Printable Version
Movin' them Doggies on the Cattle Drive Michael Matola
Printable Version
Book Promotions For October Thomas Paul Printable Version

Puzzle Corner

Ernest J. Friedman-Hill

(The first in an occasional series of articles)

The following puzzle was posted to the "Programming Diversions" forum by Stevie Kaligis on February 17, 2002.

Three men, A, B, C and their respective wives, Aw, Bw and Cw, were hunting in deepest Peru, when they came across a large river. Luckily there was one boat, however, it could only carry two people at the same time. Due to bitter jealousy, no woman could be left with another man unless her husband was present. How did they manage to cross the river?

A lively discussion ensued, in which some people actually tried to solve the problem.

Think for a minute about how you might solve this puzzle with a computer. Computers are good at brute force search, and so that's probably how you'd want to do it. Coding something like this in Java could be tricky and hard to understand; a rule-based language like Jess (See "http://herzberg.ca.sandia.gov/jess") would be better suited. Java is a rule engine and scripting environment written in Java; it complements, and integrates nicely with, existing Java software.

In this article, I'll show how to solve a logic puzzle like this one in Jess. We will indeed use search, but it will be a heuristically guided search, meaning that irrelevant parts of the search space will be cheaply pruned off and never explored -- making this far more efficient than a true brute-force search using a nested set of for loops.

Rule-based programming

Rule-based programming is declarative, meaning that you don't write down a precise list of instructions for the computer to follow, but rather concentrate on describing the problem, and allowing the computer to come up with the solution. The description in a rule-based language takes the form of rules; basically, things for the computer to do in a specific situation. The language runtime, called a rule engine, takes all the rules, all the data you supply, and applies the rules to the data according to the semantics of the language. It's a perfect arrangement for non-algorithmic problems -- problems you don't consciously know how to solve.

Declarative programming is powerful, but it's still uncommon enough that many people aren't familiar with it. Furthermore, the concepts that you need to express in declarative programming are different from those you deal with in everyday procedural programming, and this means that the language has to be rather different from Java. This has led to a widespread belief that they're difficult to learn or use, which really isn't true.

What it boils down to is that there are two kinds of rule-based languges in common use: powerful ones, and simple ones. The powerful ones tend to have more difficult syntax, and that scares some people off. Jess is one of the powerful ones, and so its syntax requires a bit of learning. I won't have enough space here to teach you everything about the Jess language, although I'll try to hit the high points. See the Jess web site or the book Jess in Action for more details. I will take the opportunity, though, to try to debunk the myth that the rule syntax is gratuitously hard to understand.

It turns out that this "Jealous husband problem" is not especially easy to solve in Jess. Here, by "not easy" I mean that the solution isn't very short -- it's about 300 well-commented lines of code. This will give us an opportunity to explore what's possible in Jess.

Data structures

Like any program, ours will need data structures. Since we're Java programmers, we'll use inheritance to express the idea that men and women are categories of person:

(deftemplate person
  "One of the people who is travelling."
  (slot name))

(deftemplate man extends person
  "A male person"
  (slot wife))

(deftemplate woman extends person
  "A female person"
  (slot husband))

Jess templates are very much like Java classes, and slots are like JavaBeans properties -- member variables with getX() and setX() methods. Since we're going to do a search of possible solutions, we'll also need a template to represent one individual node in the search tree. A "state" will represent a moment in time where the boat is on one shore or another, and all the people are out of the boat.

(deftemplate state
  "One step along the path to the solution."
  (slot search-depth)
  (slot parent)
  (slot locations)
  (slot last-move))

The "locations" slot will hold a Java HashMap with people's names as the keys, and their locations as the values, so that we know where everybody is at each point in the search tree. The "search-depth" and "parent" slots will keep track of the tree structured relationships between the states, and the "last-move" slot will record which of our intrepid travellers were in the boat in the time leading up to this state.

Then we just define all the people:

(deffacts MAIN::people
  "All the people involved in the problem."
  (man (name A) (wife Aw))
  (man (name B) (wife Bw))
  (man (name C) (wife Cw))
  (woman (name Aw) (husband A))
  (woman (name Bw) (husband B))
  (woman (name Cw) (husband C)))

This construct creates a bunch of objects, or as they're called in Jess, facts. A fact is an instantiation of a template, just as an object is an instantiation of a class. There's one fact for each person mentioned in the problem statement.

Legal moves

Here's how we're going to solve this problem: we'll start from an initial state with all the people on one side of the river. Then we'll create new "state" facts to represent a superset of the legal moves from that state. We'll refine the superset until only legal moves are left. Then we'll repeat the process starting from the new set of states. When a state is created where all the people are on the other side of the river, that's a solution to the problem. We'll print it out along with all the states that led to it, then delete this whole branch of the search tree.

Jess is a rule-based language, so we'll generate the valid moves with rules based on the problem statement. All these rules will make heavy use of utility functions specific to this problem, which we'll look at a little later on.

The complete solution is available here. You'll need to download the trial version of Jess from the web site to run the problem.

A rule consists of two main parts, basically an "if" part and a "then" part. The "if" part comes first and consists of a list of patterns. Each pattern is used to select matching facts from the collection of available facts. A pattern

  (man (name ?n))

means this rule should apply to all men, and furthermore, we want to use the variable ?n to mean this man's name (variables in Jess start with a '?'.) If you were reading the rule out loud, you'd say "There's a man, let his name be ?n".

Patterns that start with test are Boolean function calls that can further qualify where the rule applies.

The second main part of each rule is the "then" part; there's an arrow => in between the two parts. The "then" part can contain arbitrary Jess code -- the Jess language includes a complete procedural language as well, and it can work with Java objects and call Java methods, too.

The first rule, move-alone, says that at any time, a person who, in some given state along the search tree, is on the same side of the river as the boat can take the boat and move across the river, creating a new state. Note that a "person" pattern can match either a man or a woman fact, because of their inheritance relationship, just like passing arguments to a method in Java.

(defrule MAIN::move-alone
  "Any person on the same shore as the boat
   can move alone across the river."

  ;; If there is a person named ?n
  (person (name ?n))

  ;; And there is a state
  ?state <- (state)

  ;; And in that state, the person is on the same shore
  ;; as the boat
  (test (same-shore-as-boat ?n ?state))
  =>
  ;; then generate a new state where they've moved
  ;; across the river
  (move-alone ?n ?state))

The next rule says that two people of the same gender, who again, are in the same location as the boat, can use it to cross. The "~" symbol means negation, and the "&" symbol means logical "and," so "?n2&~?n1" means "this person's name, let's call it ?n, is different from ?n1." If these person facts were Java objects, the same code might look something like

Person p1 = getOnePerson();
Person p2 = getOnePerson();
String n1;
String n2;
if (p1 != null && p2 != null &&
  !(n1 = p1.getName()).equals((n2 = p2.getName())))
   ...
}
The "?" and "~" and "&" and "<-" may look strange to you, but note that Java uses just as many odd punctuation marks (and a whole lot of other characters besides!) to express the same concept! In any case, here's the rule:
(defrule MAIN::move-together-same-gender
  "Any two people of the same gender,
   both on the same shore as the
   boat, can move across the river."

  ;; Given one person
  ?p1 <- (person (name ?n1))

  ;; and another person with a different name...
  ?p2 <- (person (name ?n2&~?n1))

  ;; but the same gender
  (test (same-gender ?p1 ?p2))

  ;; (considering their names in alphabetical order)
  (test (alphabetical-order ?n1 ?n2))

  ;; and in some state
  ?state <- (state)

  ;; one is on the same shore as the boat
  (test (same-shore-as-boat ?n1 ?state))

  ;; and the other is on the same shore as the first
  (test (same-shore ?n1 ?n2 ?state))
  =>

  ;; then make a new state where they have crossed together.
  (move-together ?n1 ?n2 ?state))

The next rule says that a woman and her husband can cross together. I'm not going to comment this one as heavily -- I think you get the idea by now:

(defrule MAIN::move-together-married-couple
  "Any married couple, both on the same shore as the boat,
   can move across the river."
  (man (name ?n1))
  (woman (name ?n2) (husband ?n1))
  ?state <- (state)
  (test (same-shore-as-boat ?n1 ?state))
  (test (same-shore ?n1 ?n2 ?state))
  =>
  (move-together ?n1 ?n2 ?state))

There are two more path-generation rules. They're in a different Jess module, and it's an auto-focus rule. Together, this makes each one something like a background thread: it jumps in and does its business whenever it's needed, but otherwise it stays out of the way.

This rule enforces the "bitter jealousy" constraint by retracting states where a husband and wife are separated, but the wife and another man are on the same shore:

(defrule CONSTRAINTS::unmarried
  "A woman and a man not her husband cannot both be
  on the same shore, unless the husband is also present."
  (declare (auto-focus TRUE))

  (woman (name ?wife) (husband ?husband))
  (man (name ?husband))
  (man (name ?other-guy&~?husband))
  ?state <- (state)

  ;; The husband and wife are not on the same shore,
  ;; but the wife and the other guy are.
  (test (and (not (same-shore ?husband ?wife ?state))
             (same-shore ?other-guy ?wife ?state)))
  =>

  ;; Delete this state
  (retract ?state))

The final move-generating rule looks for circular paths -- states that lead from an identical state earlier in the tree -- and deletes them. This rule uses the "search-depth" and "locations" slots of the state facts directly. The ":" syntax introduces a Boolean function call that constrains that value of a variable -- i.e., "?sd2&:(< ?sd1 ?sd2)" means to assign the value of this slot to the variable ?sd2, but only if it's greater than ?sd1.

(defrule CONSTRAINTS::circular-path
  "If a path contains a duplicate state, it is cyclical,
  and should be discarded."

  (declare (auto-focus TRUE))
  (state (search-depth ?sd1) (locations ?map1))
  ?state <- (state (search-depth ?sd2&:(< ?sd1 ?sd2))
                   (locations ?map2&:(?map1 equals ?map2)))
  =>
  (retract ?state))

Displaying solutions

That's it! The preceding rules will generate all the possible solutions to the problem. Note that these rules are much more general than they could be, so they can actually solve many related problems: for example, they would still work with a different number of couples, and they'd even work if the list included unmarried people. Although there may not always actually be a solution for every set of initial conditions, if a solution exists, these rules will find it.

All that's left is to recognize solutions and print them out. Because a solution is actually a list of states, we need a data structure to represent one. This template holds a pointer to the latest state in the solution, and a list of moves. Each move in the list is just the names of the people who moved at that step. The moves list will be updated as the solution rules walk through the list of states, which are chained together by their "parent" slots.

(deftemplate SOLUTION::moves
  "A holder for a list of moves (i.e., a solution.)"
  (slot last-state)
  (multislot moves-list))

The first solution rule recognizes that a solution exists using the utility function all-across, which returns true when all the people in that state are on the other side; it then creates a "moves" fact to represent this solution.

(defrule SOLUTION::recognize-solution
  "If, in a particular state, all
   the people are on shore-2, then that
   state is the last move in a solution."
  (declare (auto-focus TRUE))
  ?state <- (state (parent ?parent)
                   (locations ?map&:(all-across ?map))
                   (last-move ?move))
  =>
  ;; Remove the state
  (retract ?state)

  ;; Create a "moves" fact to describe this solution
  (assert (moves (last-state ?parent) (moves-list ?move))))

The next rule iteratively updates the "moves" fact by adding new moves (the modify function updates the values in the individual slots of a fact; it's similar to calling a setter method in Java)

(defrule SOLUTION::further-solution
  "Walk down the list of states representing a solution,
  and accumulate the moves."
  ;; If there's a state
  ?state <- (state (parent ?parent) (last-move ?move))

  ;; and a moves list that indicates this state is the next step
  ?mv <- (moves (last-state ?state) (moves-list $?rest))
  =>

  ;; Then modify the moves list to include this state.
  (modify ?mv (last-state ?parent) (moves-list ?move ?rest)))

The initial state's "last-move" slot contains no-move, and its parent slot is nil (the Jess equivalent of Java's null). This acts as a sentinel and terminates the search for moves.

To actually display the solution, we just need to walk the contents of moves-list in a particular moves fact and print each item, appropriately formatted. Note that the bind function is how you assign a value to a variable in Jess.

(defrule SOLUTION::print-solution
  "A 'moves' fact representing a solution starts with 'no-move'.
   When we see one, print the whole thing out as a solution."

  ?mv <- (moves (last-state no-parent) (moves-list no-move $?moves))
  =>

  (retract ?mv)
  (bind ?i 1)
  (bind ?shore "shore-2")

  (printout t crlf "Solution found: " crlf crlf)
  (foreach ?move ?moves
    (printout t ?move
              (if (> (?thing indexOf " ") -1) then
                " move to "
                else
                " moves to ")
              ?shore crlf)
    (bind ?shore (opposite-of ?shore))
    (bind ?i (+ 1 ?i))))

The windup

These are all the utility functions we used in writing our rules. Some of them provide an interesting overview of how Jess code can interact with Java objects. Many of them work directly with the HashMap stored in the locations slot of each state fact. For example, the first function iterates though the map and returns true if all the people are located on the opposite shore. You can see the while loop is using a Java Iterator in the normal fashion.

(deffunction all-across (?map)
  "If none of the people are on shore-1, we've found a solution."
  (bind ?it ((?map values) iterator))
  (while (?it hasNext)
    (if (eq "shore-1" (?it next)) then
      (return FALSE)))
  (return TRUE))

Here are a few more simple functions. This one uses the compareTo method in the String class:

(deffunction alphabetical-order(?n1 ?n2)
  "Returns true if the two arguments are in alphabetical order."
  (> (?n1 compareTo ?n2) 0))

This one checks that the two given facts have the same "class name," indicating that the two people are of the same gender:

(deffunction same-gender(?p1 ?p2)
  (eq (?p1 getName) (?p2 getName)))

These Boolean functions check the contents of the HashMap in a given state to determine whether two people are on the same shore, or whether one person is on the same shore as the boat.

(deffunction same-shore-as-boat(?name ?state)
  (bind ?map (fact-slot-value ?state locations))
  (bind ?num (fact-slot-value ?state search-depth))
  (eq (?map get ?name) (boat-location ?num)))

(deffunction same-shore(?name1 ?name2 ?state)
  (bind ?map (fact-slot-value ?state locations))
  (eq (?map get ?name1) (?map get ?name2)))

These two functions actually create states. In both functions, the named people are moved to the other side of the river in the new state. Both use the Java clone method to make a copy of the previous state's HashMap and then change the appropriate entries.

(deffunction move-alone (?name ?state)
  ;; Get a copy of the locations map for the old state
  (bind ?map ((fact-slot-value ?state locations) clone))

  ;; Get the old search depth
  (bind ?num (fact-slot-value ?state search-depth))

  ;; Get the name of the new shore
  (bind ?newshore (opposite-of (?map get ?name)))

  ;; Update the map for the person's new location
  (?map put ?name ?newshore)

  ;; Create the new state fact
  (assert (state (search-depth (+ 1 ?num))
                 (parent ?state)
                 (locations ?map)
                 (last-move ?name)))

(deffunction move-together (?name1 ?name2 ?state)
  (bind ?map ((fact-slot-value ?state locations) clone))
  (bind ?num (fact-slot-value ?state search-depth))
  (bind ?newshore (opposite-of (?map get ?name1)))
  (?map put ?name1 ?newshore)
  (?map put ?name2 ?newshore)
  (assert (state (search-depth (+ 1 ?num))
                 (parent ?state)
                 (locations ?map)
                 (last-move (str-cat ?name1 " and " ?name2))))

These last two functions provide nice labels for the two shores. The first one contains the nugget of information that shore-1 and shore-2 are opposite one another.

(deffunction opposite-of (?shore)
  (if (eq ?shore "shore-1") then
    "shore-2"
    else
    "shore-1"))

(deffunction boat-location (?num)
  "The boat alternates between the two shores."
  (if (evenp ?num) then
    "shore-2"
    else
    "shore-1"))

The pitch

We're almost ready to run our program. The only remaining thing that's necessary is to create the initial state fact. We could hard-code the information, but I'm going to adhere to the D.R.Y. (Don't Repeat Yourself) principle and create the initial state by querying Jess for the list of all person facts, then iterating over that list.

(import java.util.HashMap)

(defquery MAIN::all-people
  "List all the person facts, so that we can set up the initial state."
  (person))

(deffunction set-initial-state ()
  "Set up the first state fact, in which all the people are on one
   side of the river. All the other state facts will be created by
   cloning this one."

  (bind ?map (new HashMap))

  ;; Get the list of all the people
  (bind ?it (run-query MAIN::all-people))

  ;; Populate the map
  (while (?it hasNext)
    (bind ?name (fact-slot-value ((?it next) fact 1) name))
    (?map put ?name shore-1))

  ;; Create the state
  (assert (state (search-depth 1)
                 (parent no-parent)
                 (locations ?map)
                 (last-move no-move))))

It's a home run

Now, we just execute the program:

;; Reset the engine, assert the deffacts
(reset)

;; Call the function that creates the initial state
(set-initial-state)

;; Finally, fire the rules!
(run)

If you've got the Jess library available in the file jess.jar, and this code in the file wives.txt, then you can run the program using the command

java -classpath jess.jar jess.Main wives.txt

The program finds 8 solutions to the problem as given, taking about one second to run. Each solution looks like

Solution found:

Cw and Bw move to shore-2
Cw moves to shore-1
Cw and Aw move to shore-2
Cw moves to shore-1
B and A move to shore-2
A and Aw move to shore-1
C and A move to shore-2
Bw moves to shore-1
Bw and Aw move to shore-2
Bw moves to shore-1
Cw and Bw move to shore-2

Conclusion

Rule-based programming is generally used in more serious pursuits than solving logic puzzles: in processing insurance applications, giving financial advice, making purchase recommendations, inventory control, implementing business rules in the enterprise, and more. Neverthess, the unique characteristics of rule engines make them an ideal way to attack this kind of problem as well. Solving puzzles with Jess is a good way to learn about declarative and rule-based programming. You can read more about Jess at "http://herzberg.ca.sandia.gov/jess"(free trial version available to download) or check out the book Jess in Action.




Return to Top

The RowSet Interface

by Thomas Paul

One of the problems with JDBC is the lack of support for using the rows returned from a database in a way that makes them accessible without maintaining a connection to the database. The RowSet interface was added to J2SE 1.4 to respond to this problem. The API for that version did not include any implementations of this interface although several sample implementations have been included as part of the early release program. J2SE 1.5 will include five standard implementations of the RowSet interface. In this article I would like to answer some questions about what RowSets are and how they might be used and take an advanced look at the upcoming implementations.

Q: What is a RowSet?

A: A RowSet is a disconnected, serializable version of a JDBC ResultSet.

Q: Could you explain that a little more clearly?

A: One of the problems with working with a ResultSet is that you can't pass it around from class to class across a network or across the Internet. A ResultSet maintains a connection to a database and because of that it can't be serialized. There are many situations where it would be a nice feature to be able to pass a ResultSet from one Java class to another across a network.

Suppose you had a client that invokes a server process to get some rows from a database. This might be an applet accessing a servlet or a JavaBean invoking a J2EE session bean. The server process would create a ResultSet and then it would have to read the data from the ResultSet and load it into some type of Collection object or array. Of course, you might want to include the metadata from the request (column names, display lengths, etc.) so you would need to figure out some way to pass that as well. However, no matter how you solved that problem it wouldn't be a standard solution.

The RowSet could be used in place of creating that Collection object and worrying about how to pass metadata. The RowSet extends the ResultSet interface so it has all the methods of ResultSet. The RowSet can be serialized because it doesn't have a connection to any database. When you run the execute method of the RowSet, all the data from the query is loaded into the RowSet and the connection is closed. The RowSet is a disconnected object in that it does not need to be connected to the database in order to provide data. Since it is disconnected it can be passed across a network.

Q: Will a RowSet have all the functionality of a ResultSet such as scrolling and updating?

A: Absolutely! In fact, you can take your RowSet, scroll through it, update it, store it in a file, load it back from the file at a later date, and use it to update the database.

Q: So why do I need ResultSet?

A: There is an obvious limitation at work here. The ResultSet does not contain all the data from your query. Since it has a connection to the database, when you run the next() method if the ResultSet needs more data it can go to the database and get it. The RowSet can't do that since it isn't connected to the database so it must load and hold all the data from your query as soon as you run the execute() method. If your query returns a lot or rows you could encounter very slow processing and out of memory errors. However, if the number of rows to be returned is a reasonable number then a RowSet can be used.

Q: Wait a minute. You said that the API would be supplying five standard implementations of the RowSet. Why are we getting standard implementations of RowSet when none of the other interfaces in the API have standard implementations?

A: The RowSet is different than other JDBC interfaces in that you can write a RowSet to be vendor neutral. A third party could write a RowSet implementation that could be used with any JDBC-compliant database. The standard implementation supplied by Sun uses a ResultSet to read the rows from a database and then stores those rows as Row objects in a Vector inside the RowSet. In fact, a RowSet implementation could be written to get its data from any source. The only requirement is that the RowSet acts as if it was a ResultSet. Of course, there is no reason that a vendor couldn't write a RowSet implementation that is vendor specific.

Q: So what are these five standard implementations?

A: The standard implementations have been designed to provide a fairly good range of functionality. The implementations provided are:

CachedRowSetImpl - This is the implementation of the RowSet that is closest to the definition of RowSet functionality that we discussed earlier. There are two ways to load this RowSet. The execute( ) method will load the RowSet using a Connection object. The populate( ) method will load the RowSet from a previously loaded ResultSet.

WebRowSetImpl - This is very similar to the CachedRowSetImpl (it is a child class) but it also includes methods for converting the rows into an XML document and loading the RowSet with an XML document. The XML document can come from any Stream or Reader/Writer object. This could be especially useful for Web Services.

JdbcRowSetImpl - This is a different style of implementation that is probably less useful in normal circumstances. The purpose of this RowSet is to make a ResultSet look like a JavaBean. It is not serializable and it must maintain a connection to the database.

The remaining two implementations are used with the first three implementations:

FilteredRowSetImpl - This is used to filter data from an existing RowSet. The filter will skip records that don't match the criteria specified in the filter when a next() is used on the RowSet.

JoinRowSetImpl - This is used to simulate a SQL join command between two or more RowSet objects.

Q: Where can I find these standard implementations?

A: As of October 2003, these are available as "Public Review Draft" versions and can be downloaded from here: http://java.sun.com/products/jdbc/download.html




Return to Top

The Coffee House

coffee house

A Pattern of Behavior

It was a gray, gray in early October. The time of glorious sunny crisp afternoons had passed and the time for cruddy, depressing, dead-bugs-in-your-boots dusks had begun. Sid and Zeke and everyone in the coffee house were darned depressed.

"Darned depressed!" muttered Zeke.
"Antsy," groaned Sid.
"Lonely," moaned Lefty, clicking his teeth and twitching.

Brenda and Lacey were bored. They had installed a Java kiosk so that the cowboys could generally order their own coffees, and except for the occasional mochachinacummulonimbus concoction, there wasn't much for them to do but entertain the troops. They looked around,  looked at each other, and Brenda said, "So, you think it's story time?"

Lacey looked around again, thought a bit, and nodded. "We might as well let'em know about the whole thing."

Brenda got up and said, "Hey y'all cowboys! You look like you could use a little cheerin' up, a little story time."

Sid looked up, hopefully. Zeke asked, "You aren't going to try to give us EJB training without us knowin' it, are yah? Cuz that wasn't a nice thing, not at all."

"Nope, nothin' like that. This is a story, and it's gossip, too. The best kind: software gossip."

"Alrighty!" There were a few whoops and few tired nods, and the dozen or so cowboys gathered around Lacey and Brenda.

"They're going to tell! They're going to tell!" A bedraggled cowboy at the back named Nigel whispered from the back.

"All righty then. Here you go. So, you know that there Gang, them four you think are so special with their software design patterns and their Mediator and Proxy and all them there reusable solutions to recurring problems?"

Mumbleumble, the cowboys agreed. Sid sang out, "That Gang's a genius! They musta worked for years on this software design patterns stuff to come up with that system o'patterns!"

"Well, it ain't quite like that. You remember how Lacey and me told you about them dating design patterns we come up with?"

"Yep, shore do," nodded Sid enthusiastically. "Didn't you come up with them all outa your pretty little heads?"

Lacey sighed. "Well, that one we did, yes. But you know what? That whole thing about Software Design Patterns is just a big crock. It was all just a big fat red heifer, to fool us all into not knowing about what they was really working on, which was actually Dating Design Patterns the whole time. The whole Gang of Four plus a bunch of other folks came up with Dating Design Patterns when they was lonely young cowboys out in Deadwood!"

Murmmermurmer! replied the cowboys. "What the dickens you talkin' about!?" yelled Zeke. "That there Gang never had a thing to do with Dating Patterns!"

"They sure did," continued Brenda. "They were a bunch of lonely cowboys, workin' hard every day and meeting no ladies at all on the weekends, such as they had. At least not young ladies who they could bring home to momma. And so one night over the camp fire, Dead-Eye Erich says to the rest of the guys, 'Hey, you know, we're smart guys, right? We can find cows where no one else can. We can sense which way a steer's going to bolt before he knows himself. So how come we ain't finding any lady company/'"

"And Coolhand Christopher replied,'" Lacey returned, "'You know, Dead-Eye, you're right. Me, I am a downright genius at recognizing consistent shrub architectures within which cows hide, breed, and chew cud. I find there are consistent shrub architectures on which one can count. So one would have to depend on the fact that there are places where nice young ladies hang out. And like you said, if we can figure out cattle, especially them twitchy dairy cows, we can figure out how ladies act and how to act to make'em act all calm and affectionate.'"

"'Well then! Let's figure it out!' is what Erich said back, and they all saddled up and him and Grady and the whole gang, six or eight of'em, rode back to Dodge City where there were actually ladies. And they rode all the way back to Kansas City, and all over the west, and they figured out these patterns for how to date. They had patterns called Mediator, and Proxy, and Half Bad Boy Plus Protocol, and pretty soon they become known far and wide across the west as the most deadly ladykilling cowboys and dating architects the world had ever seen."

"You dated one of them for a few months, didn't you, Brenda?" smiled Lacey.

"Ohhhh yes! It was marvelous, but then he had to move on, and I also had a sneaking feeling there was a little multi-threading going on. So we ended it, but, you know, amicable like."

"But--but, if they were so all fired hot, why didn't they tell the rest of us cowboys how to do this!!! They can't need all the women for themselves, can they?" Lefty was outraged and a little red in the face. The other cowboys were getting tense too; it looked like a room full of Sun Java programmers at a Microsoft Hate Rally.

"Now, keep your pants on," cautioned Brenda. "When the gang was all busy with their ladies, they weren't wrassling cattle or coding Java. The .NET programmers and the outlaws started taking over. And nobody was colonizing new states or Winning the West. The whole central part of the US was turning into a wimpy Eastern-type place."

"I remember that!" gasped Zeke. "All the papers started talking about a new dark ages out west, and one rancher actually had to apply to the city council for permission to have a manure pile. It was a horrible time."

"Exactly," said Brenda. "And so when the Gang realized that when cowboys have social lives, the world goes straight to hell. So they made a pact not to continue their glorious date-filled lifestyle anymore. They buried their ingenious but dangerous dating design patterns deep below the earth (at www.datingdesignpatterns.com) and then they came up with a red heifer sort of project to throw to people who'd heard about their patterns. They pretended that all the time they'd been busy with software design patterns and in order to convince people they published a book with fake software patterns."

"And boy, were they surprised when it took off! They were amused but heck, if it worked, well, that was fine with them. And having Java ranchers all occupied with software design patterns didn't have the same risks that having'em all out salsa dancing did. So they just let the whole thing go."

"Y'all just seemed so unhappy and bored tonight that we thought we'd tell you about it. Next time you go into Dodge, you take one of two o'them there patterns and you try them out. Heck, we'll implement Bridge Interpreter or Trojan Proxy with you and help you out a little.

The cowboys were elated. Frowns turned to smiles, combs were brought out and applied, and Lefty pulled out his notebook and wrote "buy new suit!" in big underlined letters.

There was just one mystery.

Sid looked up and frowned, puzzled. "So...how do you know all this, Brenda?"

You could hear an exception drop.

"Well..." she smiled, "let's just say that John talks in his sleep."


Solveig Haugland is an independent trainer and author near Boulder, Colorado. Her businesses include GetOpenOffice.org for those switching from Microsoft Office (www.getopenoffice.org); Techwriter Stuff: The Single Source, tshirts and posters for techwriters and those who love them; and of course Dating Design Patterns, the original reusable solutions to recurring problems.

"Dating Design Patterns" will be published this fall through www.datingdesignpatterns.com  and other sources.





Return to Top

An Introduction to Java Development with NetBeans IDE
A NetBeans IDE Project Basics Tutorial

by Dirk Schreckmann

In a nutshell, what is the NetBeans IDE?

NetBeans IDE is a free, open source, popular (with approximately 1 million downloads), integrated development environment used by many developers. Out of the box, it provides built-in support for developing in Java, C, C++, XML, and HTML. And this author especially likes the support for editing JSPs, including syntax highlighting, HTML tag completion, JSP tag completion, and Java code completion.

NetBeans IDE is available for free downloaded at http://www.netbeans.org

Following are step-by-step instructions to help NetBeans IDE greenhorns to get started developing Java applications with NetBeans IDE. The basic steps described are as follows.

  1. Create a new project
  2. Mount a directory - specify a location to save project files
  3. Add a new class to the project
  4. Compile and run a Java program

1. Create a New Project

Start NetBeans.

Start a new project: Main Menu -> Project -> Project Manager -> New Project

[Click to enlarge image]

Click New.

[Click to enlarge image]

Give the project a name. Click OK.

[Click to enlarge image]

2. Mount a directory - specify a location to save project files

To specify a location on a disk drive where project files will be stored, mount a directory as part of the project.

To the project, add a directory to hold project files: Main Menu -> File -> Mount Filesystem...

[Click to enlarge image]

Under Filesystems, Select Local Directory. Click Next.

[Click to enlarge image]

Select a directory to hold the project. (Note: Don't open the desired directory, just select it. If you accidentally open the desired directory, just click the up directory arrow next to the "Look in" drop down list, and then select the desired project directory with a single click.) Click Finish.

[Click to enlarge image]

3. Add a new class to the project

Right mouse click on a mounted project directory. Select New -> Java Class

[Click to enlarge image]

Name the class and click Finish.

[Click to enlarge image]

Edit the Java class as desired. Following is the code used in this tutorial.

public class Foo
{
    public static void main(String[] args)
    {
        System.out.println("Hello World!");
    }
}

[Click to enlarge image]

4. Compile and run a Java program

In the Filesystems Explorer, assure that the main Java class is selected.

Go to Main Menu -> Build -> Execute to compile and run the main Java class in one step.

[Click to enlarge image]

Note: To just compile a class, go to Main Menu -> Build -> Compile.

After executing, you should see results similar to the following. (Note that the NetBeans IDE captures console output, not unlike JBuilder. So, the output of this program appears in the lower IDE panel.)

[Click to enlarge image]

Resources

Again, NetBeans IDE is available for free download at www.netbeans.org

For further information and assistence on using NetBeans IDE, visit The NetBeans Community and don't forget to mosey on over to The IDEs and Other Tools Forum in the JavaRanch Saloon.





Return to Top

Installing a native library to the Applet client's computer

by Erika Kupkova

To use an external package including native libraries by an applet isn't that easy. It's not enough just to pack it to the applet's jar file - the run time machine would not find it.

You need to copy the native library to the client's JVM.

The InstallApplet does the installation of a java package to the client's JVM (both the native and java libraries). After the installation, the package becomes part of the JVM, so any applet can use it.

To have the right to write to the client's disk, the InstallApplet jar file has to be signed or the client's java security policy has to be modified.

In this particular case, the applet is signed and is installing the javatwain.dll and javatwain.jar libraries from the www.gnome.sk site. You can see this applet working online at http://www.gnome.sk/Twain/remote/www/twain/server/php/remote.html. As well as the applets using the "Morena 6.0 - Image Acquisition Framework for Java(tm) Platform" for remote scanning. Both javatwain.dll and javatwain.jar libraries are part of the Morena Package.

The code of the installation applet is below.

01 /**
02  *  InstallApplet demonstrates the first step of the two-step application
03  *  installation. It downloads the javatwain.dll to the lib/ext/x86 and the
04  *  javatwain.jar to the lib/ext subdirectory of the clients JRE.
05  *  In order to get access to the java.home system property, this applet has 
06  *  to be signed.
07  */
08  
09 import java.applet.*;
10 import java.awt.*;
11 import java.awt.image.*;
12 import java.net.*;
13 import java.io.*;
14  
15 public class InstallApplet extends Applet 
16 
17   String javaHome=System.getProperty("java.home");
18   String osArch=System.getProperty("os.arch");
19   static String status1=null;
20   static String status2=null;
21   static String status3=null;
22   
23   public void init()
24   
25     try
26     
27       super.init();
28       setLayout(null);
29       // install the javatwain.dll and javatwain.jar libraries
30       // to the clients computer
31       // the libraries have to placed in the same folder as the
32       // applet's jar file is
33       URL codeBase=this.getCodeBase();
34       installFile(this,new URL(codeBase, "javatwain.jar"), javaHome+"\\lib\\ext\\javatwain.jar");
35       installFile(this,new URL(codeBase, "javatwain.dll"), javaHome+"\\lib\\ext\\x86\\javatwain.dll");
36     }
37     catch (Exception exception)
38     
39       exception.printStackTrace();
40     }
41   }
42 
43   public void paint(Graphics g)
44   
45     super.paint(g);
46     if (status1!=null)
47     
48       g.drawString(status1,120,140);
49       g.drawString(status2,120,155);
50     }
51     if (status3!=null)
52     
53       g.drawString(status3,120,170);
54     }    
55   }
56   
57   protected static void installFile(Applet applet, URL sourceUrl, String destFileName)
58   
59     File destFile=new File(destFileName);
60     if (!destFile.exists())
61       try
62       
63         System.err.println("installing file " + destFileName)
64         destFile.getParentFile().mkdirs();
65         URLConnection connection=sourceUrl.openConnection();
66         InputStream is=connection.getInputStream();
67         FileOutputStream fos=new FileOutputStream(destFile);
68         byte[] buff = new byte[8192];
69         BufferedInputStream in = new BufferedInputStream(is, buff.length);
70         BufferedOutputStream out = new BufferedOutputStream(fos, buff.length);
71         int i;
72         int count = 0;
73         while ((i = in.read(buff,0,buff.length)) != -1)
74         out.write(buff,0,i);
75           count += i;
76         }  
77         applet.showStatus(count+" bytes copied ...");
78         in.close();
79         out.close();
80         status1="The javatwain library has been downloaded. To finish the" ;
81         status2="installation, you have to close the browser now. Reopen ";
82         status3="the browser to run the scanner application."
83       }
84       catch (Exception exception)
85       
86         exception.printStackTrace();
87         applet.showStatus(exception.toString());
88       }
89     else 
90     
91       System.err.println("file " + destFileName + " already exists")
92       status1="The javatwain library has been on your computer already";
93       status2="No installation is needed";
94     
95   
96 }





Return to Top

SSH Tunneling for Java RMI, Part-I

Pankaj Kumar, Author of J2EE Security for Servlets, EJBs and Web Services, Oct. 2003
Copyright © 2003 Pankaj Kumar. All Rights Reserved.

Abstract

This article is the first in a series of two, describing the steps to setup SSH tunneling for Java RMI. In this part, we focus on setting up SSH tunnel for interactions between (a) a RMI client and the RMI registry for looking up the remote reference; and (b) a RMI client and a RMI server for method invocations. The second part in the series will identify and address additional issues related to (a) SSH tunneling due to ability of an RMI programs to download code; and (b) presence of corporate firewalls between a RMI client and a RMI server.

Introduction

Much like most of the early networking technologies, Java RMI was designed for a trusted distributed computing environment (with the exception of security from downloaded code, I should add). In Java RMI, there is no authentication of communicating end-points, meaning the server program does not refuse service to unknown client programs and vice-versa. There is no privacy or confidentiality of the data being exchanged, meaning anyone connected to the network and with access to the proper tools can see and analyze invocation parameters and return values. Additionally, there is no guarantee of message integrity, meaning an intermediate node or program can modify the messages being exchanged and the end-points won't suspect a thing.

These security issues do not pose serious problems within the safety of a protected network, but could be serious security threats within a large corporate intranet or over the open Internet.

A common approach to make RMI secure is to use RMI over SSL. SSL, as we know, is a layer over TCP/IP and supports end-point authentication, message integrity and confidentiality. Though RMI does not use SSL by default, it is fairly straight-forward to add this support using custom socket factories as explained in this example from RMI documentation and illustrated in my J2EE Security for Servlets, EJBs and Web Services book.

However, use of RMI over SSL is not a panacea for all RMI security problems:

  • RMI over SSL using custom socket factories does not protect the communication between the client program and the RMI registry.
  • An organization may have standardized upon a different security solution, such as one based on SSH, and may not want to deal with the additional complexity of managing X.509 certificates for SSL.

In this article, we will describe how RMI applications can be secured within a network that relies on SSH for security needs. SSH is a security protocol that can be used for end point authentication, message integrity and confidentiality. It is already widely deployed and is used by developers for a variety of purposes such as command execution on a remote machine, remote login, file copy to remote machines and tunneling for a large number of third-party applications. A typical SSH implementation consists of a daemon program called sshd and clients ssh and scp.

The Setup

To understand and carry out the steps in setting up SSH tunneling, we will need two machines: one to run the client program and another to run the RMI Registry and server program. Both machines should have J2SE SDK. The client machine should have SSH client and the server machine should have SSH daemon. For testing purposes, it would be good if we are able to close all ports other than the port 22, where SSH daemon listens for incoming connections, on the server machine.

I used a Windows 2000 machine with J2SE SDK v1.4.2 and CygWin with OpenSSH (an open source implementation of SSH) as the client machine. For the server, I used a machine running RedHat Linux 8 and loaded with J2SE SDK v.1.4.2. Also, we will assume that an SSH daemon is running on the server machine (this is the case with normal Linux installation). This setup is shown in Figure 1.

RH Linux 8 is based on 2.4 kernel and allows control of open ports via iptables, a network packet filtering software for Linux. We will use it to shut off all ports but port 22 of the server machine in some of our experimentation.

SSH Tunneling

SSH tunneling between the client and the server machines allows a client program to communicate with the server program without directly establishing a TCP/IP connection with the server program, as shown in Figure 2.

The process of establishing the tunnel essentially involves starting the SSH client program on the client machine with information about the local ports to be forwarded, corresponding remote ports and the remote host running the SSH daemon. The SSH client, after going through the end point authentication, informs the SSH daemon about remote connection addresses and starts listening for incoming connections on all the local ports.

Whenever this SSH client gets a connection request on port that has been forwarded for tunneling, it accepts the connection and informs the SSH daemon, which, in turn, establishes connection with the corresponding remote address. After that, all data exchange between the client and the server program flows through the SSH client and the SSH daemon and is encrypted to ensure privacy and message integrity.

Enough theory! Let us now establish a SSH tunnel between the client and the server machine for a specific port. For this purpose, we will use the default HTTP port, port 80. Let us assume that the server machine has Apache HTTP daemon running on this port. You can verify this by pointing your browser to the server machine. Now we will block all ports other than the SSH port 22 by running the following commands, while logged-in to the server machine as root:

    [root@vishnu /]# /etc/init.d/iptables stop
    [root@vishnu /]# iptables -A INPUT -p tcp --syn --destination-port 22 -j ACCEPT
    [root@vishnu /]# iptables -A INPUT -p tcp -i lo --syn -j ACCEPT
    [root@vishnu /]# iptables -A INPUT -p tcp --syn -j DROP
    

The first command clears all the current entries for processing network traffic. These entries are created during system boot time by /etc/init.d/iptables script file based on rules in the file /etc/sysconfig/iptables. To re-instate these entries, simply issue the command:

    [root@vishnu /]# /etc/init.d/iptables start
    

Rest of the commands essentially define the rules for processing incoming SYN packets (SYN packets initiate a TCP/IP connection), and hence rules for accepting incoming TCP/IP connections. The first command sets up the rule to accept SYN packets destined to port 22. The second command accepts all SYN packets on the loopback interface. This is required for the SSH daemon to establish connection with other programs on the same machine. The last rule drops all other SYN packets.

After all ports, except port 22, have been blocked, point your browser to the server machine. Are you able to fetch the HTML page? If yes, you are not successful in blocking the ports. If the browser times out, the ports are blocked.

Let us establish a SSH tunnel so that port 7550 of the client machine is forwarded to port 80 of the server machine. Why port 7550? Well, it could be any unused port on the client machine. For our illustrations, we simply chose this specific port.

     c:\>ssh -N -L7550:vishnu:80 -l pankaj vishnu
     pankaj@vishnu's password: ********
    

Notice that the client has been authenticated via a password. It is also possible to setup SSH for public and private key based authentication.

To test the correct functioning of the tunnel, point your browser to the URL http://localhost:7550. This should display the HTML page served by the HTTP daemon of the server machine. As you would guess, under the hood, the browser talks to the local SSH client, the SSH client encrypts the message and forwards it to the SSH daemon on the server machine, which forwards it to the port 80, after decryption.

Now that we understand SSH tunneling, let us write a simple RMI program and see how this tunneling can be used for RMI.

A Simple RMI Program

This program consists of a Server program and a Client program. Source code for these programs are assumed to be available within a directory tree rooted at ex1, as shown below:

    ex1/server/EchoServerIntf.java
              /EchoServerImpl.java
              /Main.java
       /client/Main.java
    

The Server Program

The Server programs consists of Java interface EchoServerIntf, implementation class EchoServerImpl and the main class Main. Their sources, along with brief descriptions, are presented below:

The interface EchoServerIntf has only one operation: String echo(String inp).

ex1\server\EchoServerIntf.java
      package server;

      public interface EchoServerIntf extends java.rmi.Remote {
        public String echo(String args) throws java.rmi.RemoteException;
      }

The implementation class EchoServerImpl extends the RMI class java.rmi.server.UnicastRemoteObject and implements the interface EchoServerIntf.

ex1\server\EchoServerImpl.java
      package server;
      public class EchoServerImpl extends java.rmi.server.UnicastRemoteObject
			implements EchoServerIntf {
        private static int counter = -1;
        public EchoServerImpl() throws java.rmi.RemoteException {
          super();
        }
      public String echo(String arg) throws java.rmi.RemoteException {
        counter++;
        System.out.println("[" + counter + "] EchoServerImpl::echo(" + arg + ")");
          return "[" + counter + "] EchoServer>> " + arg;
        }
      }

The Main class creates an instance of EchoServerImpl and binds it to an RMI URL. Note that this URL assumes that the RMI Registry is running on the same machine as the main program.

ex1\server\Main.java
      // File: ex1\server\Main.java
      package server;
      public class Main {
        public static void main(String[] args) throws Exception {
          EchoServerImpl impl = new EchoServerImpl();
          System.out.println("impl = " + impl);

          String url = "rmi://localhost/EchoServer";
          java.rmi.Naming.rebind(url, impl);

          System.out.println("EchoServer bound to the rmiregistry.");
          return;
	    }
      }

To compile the .java files and generate the stub and skeleton corresponding to the EchoServerImpl class, issue the following commands (assuming the current directory to be ex1):

    c:>\rmi-ssh\ex1>javac -classpath . server\*.java
    c:>\rmi-ssh\ex1>rmic -classpath . server.EchoServerImpl
    

Leave the compiled .class files in the same directory as the corresponding source files.

The Client Program

The client program consists of a single class with main() method. Within this method, it takes the hostname of the machine running the RMI registry as commandline argument, forms an RMI URL, retrieves the remote reference of the server RMI class by looking up the registry and invokes the echo() method.

ex1\client\Main.java
      package client;
      import server.EchoServerIntf;

      public class Main {
        public static void main(String[] args) throws Exception {
          String host = "localhost"; // host running the rmiregistry
          String arg = "Hello, Java";
          if (args.length > 0)
            host = args[0];
          String url = "rmi://" + host + "/EchoServer";
          System.out.println("RMI Service url: " + url);

          EchoServerIntf obj = (EchoServerIntf)java.rmi.Naming.lookup(url);
          System.out.println("RMI Service stub: " + obj);
          System.out.println();
          System.out.println();

          System.out.println("Calling: stub.echo(" + arg + ")...");
          String ret = obj.echo(arg);
          System.out.println("... returned value: " + ret);
        }
      }

Compile this .java file:

    c:\rmi-ssh\ex1>javac -classpath . client\Main.java
    

Running the Programs

To see the RMI in action, We will run the RMI registry and the Server program on the Linux Box. To do this, transfer the complete directory structure to the Linux box (assuming that the sources were compiled on the Windows 2000 machine). Also, make sure that the normal ports are not blocked.

Now, run the rmiregistry from directory ex1, in its own shell window:

    [pankaj@vishnu ex1]$ export CLASSPATH=.
    [pankaj@vishnu ex1]$ rmiregistry
    

It is worthwhile to mention that by default, rmiregistry accepts connections on port 1099. Also, it needs access to the stub classes, either locally or by downloading from another machine, for all the Remote References that get registered with it.

We have set the CLASSPATH to the current directory so that rmiregistry can find the stub classes in CLASSPATH. This way we are able to avoid the need to download the stub classes. Note that some versions of JDK do not require explicit setting of CLASSPATH but some do.

The next step is to launch the main server program from a different shell window:

    [pankaj@vishnu ex1]$ java -cp . server.Main
    impl = server.EchoServerImpl[RemoteStub [ref: \
    [endpoint:[192.168.1.105:33045](local),objID:[0]]]]
    EchoServer bound to the rmiregistry.
    

Pay attention to the displayed value of the impl variable, of type EchoServerImpl. It indicates the IP address of the machine and a randomly assigned port. The client program uses this address and port combination to communicate with the server and hence has special significance for SSH tunneling.

Now that the server components are up and running, run the client program from the Windows 2000 machine, specifying the name of the server machine as a commandline argument.

    C:\rmi-ssh\ex1>java -cp . client.Main vishnu
    RMI Service url: rmi://vishnu/EchoServer
    RMI Service stub: server.EchoServerImpl_Stub[RemoteStub [ref: \
    [endpoint:[192.168.1.105:33045](remote),objID:[1ad086a:f7fad5260a:-8000, 0]]]]

    Calling: stub.echo(Hello, Java)...
    ... returned value: [0] EchoServer>> Hello, Java
    

You can see that the client displays the same endpoint address as the server.

Now use the iptables command, as explained in the SSH Tunneling section, to block all ports but port 22. After this, try running the client program. Does it succeed?

RMI Through SSH Tunnel

To create SSH tunnel for RMI calls, we need to forward two ports:

  1. Port 1099 where rmiregistry waits for incoming connection. Doing this is fairly straight-forward.
  2. The port at which the RMI server program waits for incoming connection. This requires some work, for, we have seen that the server program is assigned a random port. Another problem is that the remote reference obtained by the client program has the IP address of the server program. But for tunneling to work, the client needs to specify the localhost. As we will see soon, this is much harder to accomplish.

We can assign a fixed port to the RMI server program by adding an extra constructor to the EchoServerImpl and slightly modifying the main class. The modified sources can be found under directory ex2. The additional or modified code is shown below:

ex2\server\EchoServerImpl.java
      package server;
      ...
      public EchoServerImpl(int port) throws java.rmi.RemoteException {
        super(port);
      }
      public EchoServerImpl(int port, java.rmi.server.RMIClientSocketFactory csf,
          java.rmi.server.RMIServerSocketFactory ssf) throws java.rmi.RemoteException {
        super(port, csf, ssf);
      }
      ...
      }

The first constructor allows a programmer assigned port to be used by the RMI server class for accepting connections. The second class allows not only specification of the port but also the mechanism to create the client and server sockets. As it is possible to create a ServerSocket tied with a specific address using the constructor ServerSocket(int port, int backlog, InetAddress bindAddr), one may think that it would be possible to specify special IP address "127.0.0.1", representing the loopback address, as the IP address for the RMI server class. However, this didn't work for me.

For SSH tunneling to work, we need the loopback address in the remote reference for the client program to connect to the SSH client on local machine. I was able to do this by modifying the DNS entry for the server machine so that resolving the hostname vishnu always returned the address "127.0.0.1" on machine vishnu. This is a hack which works but is not very elegant.

Now let us look at the modified main class that makes use of fixed port 9000 to construct the remote object.

ex2\server\EchoServerImpl.java
    package server;

    public class Main {
      public static void main(String[] args) throws Exception {
        int inport = 9000;
        EchoServerImpl impl = new EchoServerImpl(inport);
        System.out.println("impl = " + impl);
        String url = "rmi://localhost/EchoServer";
        java.rmi.Naming.rebind(url, impl);
        System.out.println("EchoServer bound at port " + inport + " in rmiregistry.");
        return;
      }
    }

The compilation steps are same as in the last section and hence are not duplicated here.

To setup the tunnel for ports 1099 (for RMI registry) and 9000 (for RMI server), issue the following command on the client machine:

     c:\>ssh -N -v -L1099:vishnu:1099 -L9000:vishnu:9000 -l pankaj vishnu
     pankaj@vishnu's password: ********
    

Option -v directs the SSH client to print debug messages and helps in understanding exchange of messages.

Start the RMI registry and the server programs the same way as in the last section. While starting the client program, specify localhost as the commandline argument.

    C:\rmi-ssh\ex2>java -cp . client.Main localhost
    RMI Service url: rmi://localhost/EchoServer
    RMI Service stub: server.EchoServerImpl_Stub[RemoteStub [ref: \
    [endpoint:[127.0.0.1:9000](remote),objID:[1ad086a:f7fad5260a:-8000, 0]]]]

    Calling: stub.echo(Hello, Java)...
    ... returned value: [0] EchoServer>> Hello, Java
    

Congratulations! You got SSH tunneling for RMI working.

Next Part

The next part of this series will talk about setup requirements for code downloadable by RMI registry and the RMI client and the issues that arise due to SSH tunneling. We will also talk about how to tunnel the SSH tunnel itself through HTTP so that an RMI client sitting inside a corporate intranet can talk to an outside server.

Acknowledgment

The basic motivation for this article came from this discussion thread at JavaRanch Security forum.

References

  1. Source files in a single ZIP file
  2. J2EE Security for Servlets, EJBs and Web Services
  3. Remote Computing via Ssh Port Tunneling
  4. Iptables Basics



Return to Top
The Big Moose Saloon Question and Answer of the Month

Mosey on in and pull up a stool. The JavaRanch Big Moose Saloon is the place to get your Java questions answered. Our bartenders keep the peace, and folks are pretty friendly anyways, so don't be shy!

Question: How to code review for performance?

Answer:Well folks, the conversation following this topic opener is a bit much to be pasting in this lil' rag. Head on over to The Performance Forum, and get yourself an earful of the good advice in Vikram Chandna's "How to code review for performance?" thread.




Return to Top
Book Review of the Month

JSTL: Practical Guide for JSP Programmers
Sue Spielman
JSTL provides a set of common tag libraries for JSP programmers. This book explains the JSTL and it does it extremely well. In about 200 pages, the book covers what the JSTL is, what to use it for, how to use it, and gives plenty of examples.

The start of the book covers the basics of tag libraries and explains why we need JSTL. Next, the basics of JSTL and the expression language are covered. The one small flaw in the book is that the expression language could have been covered in a bit more detail. The rest of the book covers each of the tags (actions) broken up into the separate libraries. The core, XML, internationalization and formatting, and SQL actions are each given their own chapters. The author doesn't just cover the tags but also provides enough background information to insure that you can understand how the tags are used. For example, in the XML chapter, the author starts by explaining the different technologies around XML and then shows how the XML actions can be used to simplify the task of using XML in your JSPs. The SQL chapter explains why you would never want to use the SQL actions before she discusses the actions themselves. The book ends with a "quick reference" section.

Sue Spielman has a very easy writing style that makes reading her books a pleasure. Her book is short and complete, a very difficult combination to pull off.

(Thomas Paul - Sheriff, October 2003)
This might be one of the most effective IT book I have ever read. It's short but comprehensive. All four libraries are covered and covered quite well.

The first few chapters provide an introduction to JSTL, including the reasons and a few brief examples. The chapter on the EL seemed to be the weakest chapter, but it was detailed enough to get a solid start with using it.

Each library has a pretty good sized chapter with coverage of all of the tags and their most common attributes. The code samples covered what you are most likely to do with the tags, although I would have like to see some uncommon uses as well.

The only negative was the sample code that I downloaded from the site. Although the book states that it was tested with Tomcat 4.1.20 and Tomcat 5.0. In both cases, I had problems with Tomcat validating the web.xml file. A few simple changes and the code was up and running, though.

Overall, this book provides a quick source of information for learning JSTL.

(Matthew Phillips - Bartender, September 2003)
More info at Amazon.com || More info at Amazon.co.uk


Other books reviewed in September :

In Search of Stupidity: Over 20 Years of High-Tech Marketing Disasters by Merrill Chapman
XML for Data Architects Designing for Reuse and Integration by James Bean
Mastering Resin by Richard Hightower, Joseph D. Gradecki
Amazon Hacks by Paul Bausch
The Weblog Handbook by Rebecca Blood
Content Critical by Gerry McGovern, Rob Norton
We've Got Blog by Rebecca Blood
Turtles, Termites and Traffic Jams by Mitchel Resnick



Return to Top
Movin' them doggies on the Cattle Drive

It's where you come to learn Java, and just like the cattle drivers of the old west, you're expected to pull your weight along the way.

The Cattle Drive forum is where the drivers get together to complain, uh rather, discuss their assignments and encourage each other. Thanks to the enthusiastic initiative of Johannes de Jong, you can keep track of your progress on the drive with the Assignment Log. If you're tough enough to get through the nitpicking, you'll start collecting moose heads at the Cattle Drive Hall of Fame.

Fresh rider on the Drive...
Give a hearty welcome, all y'all, to that new rider on the Drive, Jana Smith. The trail ahead is long, but it's a mighty rewardin' ride.

Another moose on the wall for...
Yep, that's right, you make it through, you get yerself a nice little moose to hang on the wall. Well, OK, so it's a virtual moose on a virtual wall, but you can be right proud of 'em! Thanks to the efforts of Marilyn deQueiroz, you can now proudly admire your codin' accomplishments at the Cattle Drive Hall of Fame. Check it out, pardner, it's worth a wink.

No moose is safe any more with these folks a-huntin'. Sherry Cuenco, Amy Phillips, and Nick Burgett each bagged themself another moose this month for completing the OOP assignments. And David Malott got his first moose (one-eyed and equipped with flies, they tell me) for completing the first set of assignments.

Back on the Drive
They say Cattle Drivin's in yer blood, and the Drive saw the return of some of its own this month. Sam Tilley's back working on the Servlets assignments (somethun tells me them new letters after his name -- SCWCD -- might help see him throught some bumpy patches on the servlets trail). And Jeremy Thornton and Wirianto Djunaidi are back in the saddle after spending a spell out on the range.

Nitpicking is hard work too...
We know they're workin' reeeeeally hard, after all, we've seen what those assignments look like once the nitpickers have combed through 'em. Hats off to Marilyn deQueiroz, Pauline McNamara, and Jason Adam for their dedication and patience with the pesky vermin that always manage to make their way into those assignments.

Joinin' the Drive
You think ya got what it takes? You ready for some work and some good learnin'? Then pull up a stool and read up on Joinin' the Drive. Good luck!

Cattle Drive update by Ol' Timer Michael Matola




Return to Top

Scheduled Book Promotions for October and early November:

Starting
Date
Book Author(s) Publisher JavaRanch Forum
October 14 Beginning Java Objects: From Concepts to Code Jacquie Barker APress Java in General (beginner)
October 21 Java Rules Douglas Dunn Addison Wesley Programmer Certification (SCJP)
October 28 Head First EJB Kathy Sierra and Bert Bates O'Reilly EJB Certification (SCBCD)
November 4 JUnit in Action Vincent Massol Manning Testing



Return to Top
Managing Editor: Dirk Schreckmann

Comments or suggestions for JavaRanch's NewsLetter can be sent to the Newsletter Staff.

For advertising opportunities contact the Newsletter Advertising Staff.