< Zurück | Inhalt | Weiter >

5.8.5 RMI Summary

RMI greatly simplifies the business of writing multitier client-server applica- tions. It is suitable for many classes of distributed computing problems, but it does lack several features that required in large, mission-critical applications. For one thing, it lacks any sort of transaction support. If a method invocation fails, the client may not know for certain whether the server finished some work, like writing to a database, before the failure. Also, the rmiregistry program is


a very simplistic naming/lookup system. Clients must know where to find the registry with the resources they need.

RMI is very useful for problems of a certain scale, but it is not, in and of itself, sufficient for high-volume, highly available, mission-critical enterprise systems.22 But that is what J2EE and EJB are for. We’ll deal with those in Part V later in the book.


5.9 THE JAVA DEBUGGER


How can you stand using the SDK? It doesn’t even have a debugger!

Wrong. It has a debugger. It just has an extremely basic command-line debugger. Example 5.16 shows the output of its help.

Again, we are not going to document everything here. That’s what the online Sun Microsystems Java SDK documentation is for. Instead, we will use the debugger to step through the execution of our simple application and show you some of the debugger’s basic operations.

There are two ways to invoke jdb. One is to attach it to an already running JVM that has been started with remote debugging enabled. See the Java SDK documentation for details on that method. Here we’ll show you the simpler case of invoking the program locally by running the application directly under the debugger.

The basic invocation is:


$ jdb


You may optionally name the class whose main() is to be executed under the debugger, but we usually use the run from inside the debugger itself to do this. Remember that if you want to be able to view local variables in the debug- ger, you must have compiled your class or classes with the -g option of javac.

In the rest of this section, we will examine an actual debug session. We will run our single-class application, FetchURL, and use it to retrieve the index.php file from the Web server on the laptop on which this chapter is being written. To refresh your memory, remember that the source code for FetchURL is at Example 3.30. Example 5.17 is what that file looks like.


image

22. If that sentence did not cause you to get “buzzword bingo,” then you aren’t trying.


image

Example 5.16 The Java debugger help command output

$ jdb GetUser Initializing jdb ...

> help

** command list **

run [class [args]] -- start execution of application's main class


threads [threadgroup] -- list threads thread <thread id> -- set default thread

suspend [thread id(s)] -- suspend threads (default: all) resume [thread id(s)] -- resume threads (default: all) where [thread id] | all -- dump a thread's stack

wherei [thread id] | all -- dump a thread's stack, with pc info up [n frames] -- move up a thread's stack

down [n frames] -- move down a thread's stack

kill <thread> <expr> -- kill a thread with the given exception object interrupt <thread> -- interrupt a thread


print <expr> -- print value of expression

dump <expr> -- print all object information

eval <expr> -- evaluate expression (same as print)

set <lvalue> = <expr> -- assign new value to field/variable/array element locals -- print all local variables in current stack frame


classes -- list currently known classes class <class id> -- show details of named class methods <class id> -- list a class's methods fields <class id> -- list a class's fields


threadgroups -- list threadgroups threadgroup <name> -- set current threadgroup


stop in <class id>.<method>[(argument_type,...)]

-- set a breakpoint in a method stop at <class id>:<line> -- set a breakpoint at a line clear <class id>.<method>[(argument_type,...)]

-- clear a breakpoint in a method clear <class id>:<line> -- clear a breakpoint at a line clear -- list breakpoints

catch [uncaught|caught|all] <exception-class id>

-- break when specified exception occurs ignore [uncaught|caught|all] <exception-class id>

-- cancel 'catch' for the specified exception watch [access|all] <class id>.<field name>

-- watch access/modifications to a field unwatch [access|all] <class id>.<field name>

-- discontinue watching access/modifications to a field trace methods [thread] -- trace method entry and exit


untrace methods [thread] -- stop tracing method entry and exit step -- execute current line

step up -- execute until the current method returns to its caller

stepi -- execute current instruction

next -- step one line (step OVER calls)

cont -- continue execution from breakpoint


list [line number|method] -- print source code use (or sourcepath) [source file path]

-- display or change the source path exclude [class id ... | "none"]

-- do not report step or method events for specified classes

classpath -- print classpath info from target VM


monitor <command> -- execute command each time the program stops monitor -- list monitors

unmonitor <monitor#> -- delete a monitor

read <filename> -- read and execute a command file


lock <expr> -- print lock info for an object threadlocks [thread id] -- print lock info for a thread


pop -- pop the stack through and including the current frame

reenter -- same as pop, but current frame is reentered redefine <class id> <class filename>

-- redefine the code for a class


disablegc <expr> -- prevent garbage collection of an object enablegc <expr> -- permit garbage collection of an object


!! -- repeat last command

<n> <command> -- repeat command n times

help (or ?) -- list commands

version -- print version information

exit (or quit) -- exit debugger


<class id> or <exception-class id>: full class name with package qualifiers or a pattern with a leading or trailing wildcard ('*') NOTE: any wildcard pattern will be replaced by at most one full class name matching the pattern.

<thread id>: thread number as reported in the 'threads' command

<expr>: a Java(tm) Programming Language expression. Most common syntax is supported.


Startup commands can be placed in either "jdb.ini" or ".jdbrc" in user.home or user.dir

>


image


image

Example 5.17 index.php used in jdb session

<HTML>

<HEAD>

<TITLE>RedHat Linux Laptop</TITLE>

</HEAD>

<BODY>

<H1>RedHat Linux Laptop</H1>

<P>You have contacted Michael Schwarz's RedHat Linux Laptop. You would probably rather

<A HREF="http://www.multitool.net/">see his permanent Web page</A> since this server goes up and down all the time, what with it being on a laptop.</P>

</BODY>

</HTML>


image


Example 5.18 is an actual transcript of a real jdb session. It is annotated with explanatory comments. Our goal here is to get you going. The best way to learn jdb, or indeed any of these tools, is to use them.

Obviously, this little session has merely scratched the surface of the Java debugger. You can debug multithreaded applications with commands that can suspend and resume individual threads, list the running threads, switch your “executable view” between threads, and so forth. You can trace method calls. You can monitor variables. You can execute expressions (including assignment expressions, allowing you to force variables to certain values). You can browse classes. You can dump all local variables with a single command. The debugger is quite capable, if a bit limited in user interface.23 Learn it. Play with it. Step through your favorite Java program with it.


image

23. As you know, we do not automatically like IDEs and GUI development tools (see Sec- tion 5.2). A debugger is an exception to that rule. When debugging, a well designed UI with a code pane, a stack pane, a data viewer, a class browser, a thread selection pane, and so on is enormously helpful. You need to be able to see all these elements nearly simultaneously; you need to see the whole system as it runs. The command-line debugger makes everything you need available, but with a traditional “glass-teletype” UI that is quite awkward. By all means, learn the CLI debugger, but then find a good Java debugger with a windowed UI of some kind. It is hard to say which compiler UI is the best, but I think we can safely say the command-line debugger UI is the worst! You should know it as a last resort, but use it as a last resort!


image

Example 5.18 An actual jdb session, with commentary

$ jdb

Initializing jdb ...

> stop in FetchURL.main (1)

Deferring breakpoint FetchURL.main.

It will be set after the class is loaded.

> run FetchURL http://localhost run FetchURL http://localhost Set uncaught java.lang.Throwable

Set deferred uncaught java.lang.Throwable

>

VM Started: Set deferred breakpoint FetchURL.main


Breakpoint hit: "thread=main", FetchURL.main(), line=48 bci=0

48 for (i = 0; i < args.length; i++)


main[1] list 44 {

45 int i;

46 FetchURL f; 47

48 =>(2) for (i = 0; i < args.length; i++) 49 {

50 System.out.println(args[i] + ":");

51 System.out.println(new FetchURL(args[i])); 52 }

53 }

main[1] step

>

Step completed: "thread=main", FetchURL.main(), line=50 bci=5

50 System.out.println(args[i] + ":");


main[1] step

> http://localhost:


Step completed: "thread=main", FetchURL.main(), line=51 bci=32

51 System.out.println(new FetchURL(args[i]));


main[1] step

>

Step completed: "thread=main", FetchURL.<init>(), line=8 bci=0 8 {


main[1] list

4 public class FetchURL {

5 private URL requestedURL;


6

7 public FetchURL(String urlName) 8 => {

9 try {

10 requestedURL = new URL(urlName);

11 } catch (Exception e) {

12 e.printStackTrace(); 13 }

main[1] step

>

Step completed: "thread=main", FetchURL.<init>(), line=10 bci=4

10 requestedURL = new URL(urlName);


main[1] step (3)

>

Step completed: "thread=main", FetchURL.<init>(), line=11 bci=16

11 } catch (Exception e) {


main[1] step

>

Step completed: "thread=main", FetchURL.<init>(), line=14 bci=27 14 }


main[1] step

>

Step completed: "thread=main", FetchURL.main(), line=51 bci=45

51 System.out.println(new FetchURL(args[i]));


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=19 bci=2

19 String rc = "";


main[1] list 15

16

17 public String toString()

18 {

19 => String rc = "";

20 String line;

21 BufferedReader rdr; 22

23 try {

24 rdr = new BufferedReader( main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=24 bci=3

24 rdr = new BufferedReader(


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=30 bci=28

30 while ((line = rdr.readLine()) != null)


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=32 bci=31

32 rc = rc + line + "\n";


main[1] list

28 );

29

30 while ((line = rdr.readLine()) != null)

31 {

32 => rc = rc + line + "\n"; 33 }

34 } catch (Exception e) {

35 e.printStackTrace();

36 rc = null;

37 }

main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=30 bci=55

30 while ((line = rdr.readLine()) != null)


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=32 bci=31

32 rc = rc + line + "\n";


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=30 bci=55

30 while ((line = rdr.readLine()) != null)


main[1] step

>

Step completed: "thread=main", FetchURL.toString(), line=32 bci=31

32 rc = rc + line + "\n";


main[1] dump this (4)

this = {

requestedURL: instance of java.net.URL(id=378)

}

main[1] dump rc (5)

rc = "<HTML>

<HEAD>


"

main[1] list 36

32 rc = rc + line + "\n"; 33 }

34 } catch (Exception e) {

35 e.printStackTrace();

36 => rc = null; 37 }

38

39 return rc; 40 }

41

main[1] stop at FetchURL:39 (6)

Set breakpoint FetchURL:39 main[1] cont

>

Breakpoint hit: "thread=main", FetchURL.toString(), line=39 bci=79

39 return rc;


main[1] dump rc rc = "<HTML>

<HEAD>

<TITLE>RedHat Linux Laptop</TITLE>

</HEAD>

<BODY>

<H1>RedHat Linux Laptop</H1>

<P>You have contacted Michael Schwarz's RedHat Linux Laptop. You would probably rather

<A HREF="http://www.multitool.net/">see his permanent Web page</A> since this server goes up and down all the time, what with it being on a laptop.</P>

</BODY>

</HTML>

"

main[1] step

> <HTML>

<HEAD>

<TITLE>RedHat Linux Laptop</TITLE>

</HEAD>

<BODY>

<H1>RedHat Linux Laptop</H1>

<P>You have contacted Michael Schwarz's RedHat Linux Laptop. You would probably rather

<A HREF="http://www.multitool.net/">see his permanent Web page</A> since this server goes up and down all the time, what with it being on a laptop.</P>

</BODY>

</HTML>


Step completed: "thread=main", FetchURL.main(), line=48 bci=48

48 for (i = 0; i < args.length; i++)


main[1] step

>

Step completed: "thread=main", FetchURL.main(), line=53 bci=57 53 }


main[1] step

>

The application exited

$


image


1. Here we tell the debugger where to break execution to let us run debugger commands. We do so at the start of the FetchURL class’ main() method. If we did not set a breakpoint, the run would have run the program to termination, making it no different from running it with the java command (except perhaps a bit slower).

2. The list command shows the source line that is about to be executed, along with some more lines to either side. It is a handy way to get a little context. The standard “next line” prompt isn’t enough for most of us to get context (unless, of course, we are looking at a line-numbered printout of the source or an editor window at the same time, which we often do).

3. The step steps execution one “line” (what a line is can be a bit fuzzy when there’s a lot of vertical whitespace in the source, or when multiple method calls occur on one line). Note the information in the status message. The name of the thread is given (our sample is single-threaded, so it is always “main”), as is the line number in the source file and the bci. Note that there is a very similar command, next, that advances to the next line in the same stack frame. In other words, it won’t step into method calls, it steps over them.

4, 5. Here we see two uses of the dump command. First, we apply it to this (which is an implicit argument to any nonstatic method call) to dump the currently executing ob- ject. The second instance dumps the rc local variable, which is an accumulating string containing the requested Web page. At the moment, it contains only the first few lines.

6. Here we set a breakpoint on a specific source line number. We then use the cont

command to resume “full speed” code execution.


image


5.10 RETURN TO THE SOURCE: THE JAVA DECOMPILER


Java includes a decompiler of sorts called javap. It is sometimes referred to as the “class file disassembler.” We titled this section “Return to the Source,” but it is a bit misleading; javap simply provides a way to examine the members and methods of a compiled Java class24 even when you do not have its source code. The javap command takes the same access-modifier command-line argu- ments as javadoc (-public, -protected, -package, -private) to determine which attributes and methods are to be reported. An additional switch, -c, causes the bytecodes of methods to be reported. For details, see Sun’s documen-

tation for javap.25

Example 5.19 shows what you get if you run javap -c on our FetchURL example.


5.11 BUNDLING A JAVA PROGRAM: PUT IT IN A JAR


Distributing a Java application can be a pain. All but the simplest of applica- tions will have many public classes—and since there can only be one public Java class per source file, each Java source file becomes a class file, and the ele- ments of a package name become directory nodes in the path to the class, you end up with a fairly complex collection of directories and files. Wouldn’t it be nice to be able to roll the whole mess up into a single binary file for distribution?

Well, you can. The tool to do the job is called jar, which stands for Java ARchive.26 The files produced by this utility are called JAR files. The JAR for- mat is the common DOS/Windows ZIP file format, with a few special files to support some special features we will explain as they come up.


image

24. In Chapter 7 we will introduce gcj, the GNU Compiler for Java, which compiles Java to native machine code. javap is useless with such a file. It deals only with JVM bytecodes as documented in Sun’s JVM Specification.

25. http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/javap.php

26. An abbreviation made up of syllables from words instead of just initials is called a portmanteau. The US Navy is particularly keen on them, using terms like COMSURPAC (Commander, Surface Fleet, Pacific), COMSUBLANT (Commander, Submarine Fleet, Atlantic), and so forth. There. Now you can’t claim you didn’t learn anything from this book.


image

Example 5.19 javap output for FetchURL.class

Compiled from FetchURL.java

public class FetchURL extends java.lang.Object { private java.net.URL requestedURL;

public FetchURL(java.lang.String); public java.lang.String toString();

public static void main(java.lang.String[]);

}


Method FetchURL(java.lang.String)

0 aload_0

1 invokespecial #1 <Method java.lang.Object()>

4 aload_0

5 new #2 <Class java.net.URL>

8 dup

9 aload_1

10 invokespecial #3 <Method java.net.URL(java.lang.String)>

13 putfield #4 <Field java.net.URL requestedURL>

16 goto 27

19 astore_2

20 aload_2

21 invokevirtual #6 <Method null>

24 goto 27

27 return Exception table:

from to target type

4 16 19 <Class java.lang.Exception>


Method java.lang.String toString()

0 ldc #7 <String "">

2 astore_1

3 new #8 <Class java.io.BufferedReader>

6 dup

7 new #9 <Class java.io.InputStreamReader>

10 dup

11 aload_0

12 getfield #4 <Field java.net.URL requestedURL>

15 invokevirtual #10 <Method java.net.URLConnection openConnection()>

18 invokevirtual #11 <Method java.io.InputStream getInputStream()>

21 invokespecial #12 <Method java.io.InputStreamReader(java.io.InputStream)>

24 invokespecial #13 <Method java.io.BufferedReader(java.io.Reader)>

27 astore_3

28 goto 55

31 new #14 <Class java.lang.StringBuffer>

34 dup

35 invokespecial #15 <Method java.lang.StringBuffer()>

38 aload_1


39 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)>

42 aload_2

43 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)>

46 ldc #17 <String "

">

48 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)>

51 invokevirtual #18 <Method java.lang.String toString()>

54 astore_1

55 aload_3

56 invokevirtual #19 <Method java.lang.String readLine()>

59 dup

60 astore_2

61 ifnonnull 31

64 goto 79

67 astore 4

69 aload 4

71 invokevirtual #20 <Method null>

74 aconst_null

75 astore_1

76 goto 79

79 aload_1

80 areturn

Exception table:

from to target type

3 64 67 <Class java.lang.Exception>


Method void main(java.lang.String[])

0 iconst_0

1 istore_1

2 goto 51

5 getstatic #21 <Field java.io.PrintStream out>

8 new #14 <Class java.lang.StringBuffer>

11 dup

12 invokespecial #15 <Method java.lang.StringBuffer()>

15 aload_0

16 iload_1

17 aaload

18 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)>

21 ldc #22 <String ":">

23 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)>

26 invokevirtual #18 <Method java.lang.String toString()>

29 invokevirtual #23 <Method void println(java.lang.String)>

32 getstatic #21 <Field java.io.PrintStream out>

35 new #24 <Class FetchURL>

38 dup

39 aload_0

40 iload_1

41 aaload

42 invokespecial #25 <Method FetchURL(java.lang.String)>


45 invokevirtual #26 <Method void println(java.lang.Object)>

48 iinc 1 1

51 iload_1

52 aload_0

53 arraylength

54 if_icmplt 5

57 return


image


A JAR file packages a subdirectory and its descendants into a single file. A Java CLASSPATH specification may contain a JAR filename everywhere it might contain a directory name. Let’s say you use the GPL’ed Java personal finance program called jgnash and you’ve compiled it from source, so you have a direc- tory off your home directory called jgnash/bin. Suppose you run the program by directly invoking java to run the class jgnashMain and you have

$HOME/jgnash/bin on your CLASSPATH. You could clean up the mess on your hard drive by using the jar command to squash all the files in jgnash/bin together into a single JAR file, as shown in Example 5.20.



image

Example 5.20 Making a JAR file

$ cd ; mkdir jars

$ jar cvf jars/jgnash.jar jgnash/bin


image


You could then replace the $HOME/jgnash/bin entry in your CLASSPATH with $HOME/jars/jgnash.jar. After that you would still run jgnash with exactly the same java command you always did, but now you got rid of the cluttered pile of files.

This is only the most basic purpose of jar, however. Its uses extend well beyond merely concatenating and compressing collections of .class files.