Chapter 1: Hello Java......................................................................................................................................

The Power of the Concept...............................................................................................................................

Network-Awareness....................................................................................................................................................

Portability....................................................................................................................................................................

Security.........................................................................................................................................................................

Memory Segmentation................................................................................................................................................

Native Methods...........................................................................................................................................................

Access to System Resources via Java.........................................................................................................................

Object Orientation....................................................................................................................................................

Cooperating Objects..................................................................................................................................................

Conclusion...............................................................................................................................................................

Exercises...................................................................................................................................................................


 

Chapter 1: Hello Java

Java is cool.  In its short existence Java has generated more excitement, and more wild speculation, than all the other programming languages put together.  Some of that is simple hype, a result of the fact that the software industry is now a very big business, but much of it is quite real.  Java is a revolutionary force. 

Over the course of this book, we’ll develop a large application, the Agent system, consisting of a small standalone Java program and a bunch of Java applets, as well as a number of smaller Java applets that demonstrate particular features of the language.  Through the Agent system, I’ll try not only to illustrate Java programming techniques, but to give a glimpse of the new classes of applets and applications that you can write using Java.

The Agent system allows Web users to dispatch a program (agent) to run on each machine in a network of internet-connected machines and return its results via the Web.  This agent is a Java class.  We’ll develop a couple of basic agents to do things like finding files, but the power of the system, like the power of Java, is that by using the base Agent class users can write their own agents to do things I’ve never dreamed of.  The base Agent class, and the standalone agent server deal entirely with the problem of distributing the agent throughout the network.

The Agent system provides a good intro to Java programming because it utilizes three varieties of Java classes: applets, standalone applications, and classes that can function in either applet or standalone mode.  Over the course of developing these classes, we’ll get to see the possibilities, and especially, the limitations of each.

applet: a small program that runs within another, larger, host program, whose purpose is usually to enhance the functionality of the host program and which could not exist without the host program.

This book is about writing Java applets, but what is an applet?  The term itself only came into existence in the last year or two.  As the above definition states, an applet exists within, and is designed to enhance a host program.  In Java’s case, a Java applet enhances the functionality of a Web browser.  A browser can display the text “Hello World”.  Add an applet and you can make that text bounce around the screen like a ping-pong ball.  That’s all we really mean when we talk about an applet - a program that runs within a Web browser.

And so, without any further ado, an applet:

package chap1;

 

import java.awt.Graphics;

import java.awt.*;

import java.applet.Applet;

 

public class ch1_fig1 extends Applet {

 

public void paint(Graphics g) {               

                g.drawString( "Hello world", 20, 50 );

                }

}

Figure 1-1: The Hello Java applet.

Figure 1-2: The Hello Java applet running in Sun’s appletviewer.

Pretty exciting, eh?  Well there’s lots more in store.  But before we launch into a detailed look at Java programming, let’s take a quick look at what makes Java useful in the first place.

The Power of the Concept

When people talk about Java, as in “Java is cool”, it’s not always clear what exactly they’re talking about, because Java is not just a language.  Java is both a language and a set of class libraries.  Much, if not most, of the power of Java comes from these class libraries which, in Java, are known as packages.  In that sense, Java is comparable to something like Microsoft C++ with Foundation Classes.  Figure 1-3 shows the package hierarchy for Java.

Figure 1-3: the Java package hierarchy

Java’s revolutionary potential is not due to a single feature of the language but to a combination of four features, network-awareness, portability, security and object-orientation.  Some of these are a function of the language while others are a result of the class libraries.  These feature make possible a whole range of applications that we could only dream of before Java.

Network-Awareness

Java’s tight integration with the Internet in general, and the World-Wide-Web in particular is the main force driving its explosive growth.  Network-awareness, as we use it, simply means that every provision has been made to allow Java applications to incorporate network capability.  For the internet this amounts to providing a low-level socket interface.

The internet is populated by a bunch of servers that perform a particular function, for instance telnet, ftp, or WWW.  Each of these servers runs on a machine, listening to a port, waiting for a client to connect.  Both client and server connect to the network at the lowest level via a socket interface.  In Java, for instance, a server program running on mymachine.com can open a socket and accept calls on it with two lines of code:

ServerSocket MyServerSock = new ServerSocket( 1037 );

Socket s = MyServerSock.accept();

and a client can connect to it with one line of code, as in:

Socket MyClientSocket = new Socket( “mymachine.com”, 1037 );

When those two calls return, the two machines are connected and can read and/or write to that connection. 

Dealing with network IO at the socket level, while neccessary for some applications, can be very tedious, requiring as it does implementation of whatever protocol you intend to use.  In many cases you don’t need to do this because Java provides higher level classes for such cases.  The following example manages to download an HTML file, while avoiding the explicit use of sockets entirely.

public class DLHTMLFile extends Applet {

public void start() {

                try {

                URL u = new URL("http://www.channel1.com/users/ajrodley/index.html");

                System.out.println( "u="+u );

                                try {

                                URLConnection uc = u.openConnection();

                                InputStream is = uc.getInputStream();

                                byte b[] = new byte[is.available()];

                                is.read( b );                             

                                System.out.println( "received byte array "+new String(b,0));

                                } catch( IOException e ) {System.out.println( "ioex "+e);}

                } catch( MalformedURLException e1 ) {System.out.println( "mfuex "+e1);}

}

}

In this example, Java’s URLConnection class handled

·         connecting to the http daemon which serves HTML files at www.channel1.com

·         finding the file we want at that server

·         creating a byte stream that we can read from that file

The InputStream handled the rest.  What you see when you run this applet is the text of my home page printing out on the standard out (or Java console in Netscape).

The messages that the client and server exchange once they connect via sockets is what Java refers to as content.  Examples of content would be the graphics formats GIF and JPG or the document format HTML.  The Java class that deals with a particular type of content is known as a content handler.

Say we want to get a GIF file that we can display on the screen.  What you want to end up with in memory is a Java Image object.  The process of getting that image has three levels:

·         the socket connection

·         a stream of GIF formatted bytes (content) flowing over that connection

·         an Image object created from that GIF format byte stream (by a content handler)

We’ve seen how Java gives you easy control over the first two levels, but that last step is the payoff. For business reasons, Sun has decided NOT to give coders direct access to their image, ftp and HTML content handlers, even though the image handlers at least ARE useable via the Applet class.  This means that content handlers will be one area where Netscape and other browsers can provide ‘extra’  value, for which, presumably, users will be willing to pay.

Even with that caveat, Java has taken us a long way toward complete network connectivity.  And even though their content handlers are inaccessible, Java does provide a platform on which to build your own content handlers.

Java’s net-centricity is embodied in the packages (class-libraries), java.net and java.applet.  java.net supplies the basic classes for dealing with URLs and TCP/IP sockets.  java.applet provides Java’s entree to the WWW, via the Applet class - a class that allows an application to appear in the middle of an HTML document when that document is viewed with a Web browser.

The URLs and sockets of java.net are fine building blocks, but it is the concept of embedding programs (applets) within HTML documents that makes Java a revolutionary force.  A browsed Web document is a fairly static item.  The text just sits there while the user browses through it.  Java applets, on the other hand, are active items.  A Web page with applets can mutate according to any of a wide variety of stimuli.  These stimuli can come directly from the user, from other applets or processes on the same workstation or from some process out on the net.  They can come from anywhere, and in a manner that is invisible, and/or indecipherable to the user.

Thus, with applets, you can create a Web page that behaves more like the user interface of a traditional application than like an old HTML page.  And there is where the revolution starts.  If you can get the functionality of a traditional application via a web page, then why buy traditional applications?

The internet has physically connected a huge number of machines.  Yet, in many cases, their only connection to each are the happy faces they display via HTML.  With Java, these machines can work together rather than just smiling stupidly at one another.  Net connected machines now have a language by which they can both request AND PROVIDE services.

[SIDEBAR: HTML is itself a language and Web browsers are essentially HTML interpreters.  HTML’s sole purpose, however, is to describe static text documents.  A browser is an intelligent ‘book’, with each HTML document an intelligent page.  That is HTML’s limitation: its central metaphor is ‘a book of pages’, and there are only so many things you can make a book do.  Java uses an entirely different metaphor - the abstract CPU known as the Java Virtual Machine.

The Java Virtual Machine is a specification that describes the instruction set for an abstract CPU.  Every CPU - 486, Pentium, PowerPC - has such a specification.  An actual CPU implements that specification in hardware, so you end up with a chip that understands instructions from that instruction set.

There is no CPU that understands the Java VM instruction set.  Instead, Java relies on an interpreter to translate the Java VM instructions into instructions that the local CPU understands.  Thus, for every different CPU/Operating System there is a different java interpreter.]

Portability

Java is a portable language.  It achieves this portability by taking a different approach than current C and C++ development packages.  In C, under DOS, you might write a source file “hello.c” that looks like this:

int main() {printf( “hello world\n” );}

The C compiler turns that file into another file, “hello.exe” that is essentially a stream of instructions from the 80386 instruction set.  In fact, if you disassemble hello.exe, you see a stream of 386 instructions like:

PUSH SP

MOV BP, SP

...

RET

If you compile the same program on a 68000, hello.exe would contain a different stream of instructions - ones from the 68000 instruction set.  When you want to run hello.exe, the operating system simply feeds the instruction stream directly to the CPU[1].

This approach is very efficient but it has one drawback: because different computers use different instruction sets, a compiled program will usually run only on the machine for which it was compiled.

The Internet, however, connects computers of many different types.  Therefore, a Java program must be able to run on all (or at least most) of those computer types.  The traditional approach of compiling directly to machine code doesn’t get the job done.

Java takes a different approach.  Instead of translating the Java source code directly into the instruction set of a particular hardware CPU, the Java compiler translates it into a “bytecode” file.  This bytecode file contains a stream of instructions from the instruction set of an imaginary CPU known as The Java Virtual Machine.  These bytecodes can NOT be fed directly to any real CPU, because no real CPU understands them.  Instead, a Java interpreter translates those bytecodes into instructions that the real CPU understands.

The Java equivalent of hello.c, hello.java, would go through the following steps:

Source file hello.java

public class Hello {

                public static void main() {

                                System.out.println( “Hello Java world” );

                                }

                }

Bytecode file, hello.class, which, disassembled might look like this:

PUSH STACK

...

RETURN

Then, when you run hello.class, the Java interpreter would turn the bytecodes into a stream that looks like the instructions from our compiled hello.exe file:

PUSH SP

MOV BP, SP

...

RET

Running the bytecodes through the Java interpreter IS an extra step that traditional compiled programs don’t go through.  It is this extra step, though, that gives Java its portability because bytecodes mean the same thing no matter what computer you’re running on.  Our hello.class program will thus run on any machine for which a Java interpreter has been written.

A running Java program thus consists of three pieces: the source file, the bytecode file and the Java interpreter.  The bytecode file is very similar to the traditional executable in that it contains a stream of machine instructions.  The difference is that those machine instructions don’t work on any particular physical CPU.  They only make sense to the Java interpreter.

This approach to portability is not new.  UCSD pascal enjoyed a brief moment in the sun back in the late 1970s with its implementation of a bytecode interpreter.  Unfortunately, it was ahead of its time.  The market demand for portable applications was overwhelmed by the demand for the kind of high-performance applications that C could provide.

Java’s portability is not especially cheap either.  In order to achieve it, someone has had to “port” the Java interpreter to each of the platforms that Java runs on.  Whatever portability advantage Java has over other languages comes from reducing the porting problem to its absolute minimum.  Instead of porting each individual application to each new architecture, you only need to port the Java interpreter, at which point any Java program will run on the machine. 

This is technically correct, but significantly understates the problem of porting Java to new operating systems.  A huge portion, if not a majority, of the functionality of Java is contained in native methods - dynamic link libraries that are compiled to native machine code.  Many of these native methods use native C++ class libraries that have been built up over the years, such as Microsoft Foundation Classes.  So when you hear Java enthusiasts, myself included, opining as to how Java will replace C++, just remember that for the current generation of hardware, Java rests on a foundation of C++ code.  This will only change when machines which support the Java VM instruction set begin to appear.

Security

Up to now, we’ve talked mainly about the rewards of Javability, the gold-paved streets of Java world.  As with every potential reward, there is also an element of risk, and in Java world much of that risk involves security.

How secure is Java?  The short answer is “pretty secure”.  For many of us, that answer’s not good enough.  What does security mean to Java and why should we worry about it?  It’s probably better to start with what isn’t security under Java.  Java’s network-awareness, for example, is simply a product of existing network protocols: http, ftp, and gopher ...  Java’s network communications are no more or less secure than these underlying protocols.  Java does not encrypt, although that doesn’t mean you can’t do it yourself.  It’s just not part of the language.  What this means is that if someone breaks one of these protocols, then any Java application using those protocols is also broken.

Running Java applets via the web does open a HUGE security hole in a whole class of machines that never had to worry about security before.  In the bad old days of dial-up bulletin boards (pre-1993), a pretty reliable rule of safe computing was “never run anything downloaded from a bulletin board”.  Especially in the Mac world, computer viruses were as common as the cold virus is among humans, and the easiest way to catch one was to run something you found out on the net.  Now, Java postulates a world where running downloaded executables is THE basic computing activity. 

The question is one of risk-reward.  For a single user, running a Web browser for education and entertainment, the risks of unsecure network computing are fairly small.  Even in the worst case, a downloaded virus wipes out the user’s machine, it’s only one machine, the work-product of one person.  A painful loss, but in absolute terms, fairly small.

The risks multiply exponentially when you start talking about corporate computing.  Large-scale corporate computing lurks somewhere in the background of almost every human activity.  When the machines that make up this underlying base of computing power start running executables that have been passed, unverified, from one machine to another, the public danger posed by a malignant executable is limited only by the imagination of its inventor.

Thus, for many security conscious applications, the migration to Java world requires a leap of faith equivalent to the one the Pilgrims took when the left England for North America.  What kind of protection can Java offer the security conscious?

Memory Segmentation

Internally, Java maintains two separate memory sections, one that the Java interpreter uses for itself, and one that it uses to satisfy the needs of Java applets.  An applet can not, via Java itself, access memory from the Java segment.  This is a product both of internal security measures AND Java’s pointer-less design.  It would be nearly impossible to get this kind of  protection in a language that supported pointers.

Java relies on the native operating system to protect it from malicious external applications.  All of the operating systems that Java runs on enforce some system whereby the memory of one application, in this case, the Java applet, is automatically protected from the code of any other application.  Java’s immunity to this kind of malicious external attack is largely dependent on the native OS.

Native Methods

Some large proportion of Java’s security risk is embodied in the ability of applets to get ‘outside’ of Java via native method and native executable calls.  Java allows applets to call out to dynamic link libraries and executables that are assumed to already exist on the workstation.  These native methods/executables are, by definition, completely insecure - they can be written in any language, by anyone, for any purpose, malicious or otherwise.  This may sound like a fatal weakness, but it really isn’t.  While Java does not protect you from malicious native methods, it doesn’t offer  native method virus writers any help either. 

While Java classes are downloaded over the net as a basic operating procedure, native methods/executables are not.  A native method must already exist on a computer in order for a Java applet to execute it.  It does not get downloaded.  This means that it is as difficult to distribute a native method virus as it always has been to distribute regular viruses.  Native methods, malignant or not, do not appear automagically on a user’s workstation in the way that Java classes do.  Individual users must be convinced to load them on their workstations, one at a time.  Thus, distributed attack via a native method is unworkable.  The risk of a successful, large-scale attack via the Java distributed computing mechanism is entirely contained within Java itself. 

Access to System Resources via Java

System resources - the network, the file-system and so on - are the ultimate target of spies and virus writers.  Programmatic access to them is dangerous, but to completely eliminate that access would be a crippling burden on any language.  Java deals with this security/functionality tradeoff by funnelling all potentially dangerous system resource access requests through the Java SecurityManager. 

The SecurityManager allows the browser to manage its applets access to system resources.  The SecurityManager is like a moat between the system resources and the hoard of barbarian applets.  The browser can choose to fill that moat with obstacles and barbed-wire, or it can pave the moat over allowing applets “the run of the castle”.  The SecurityManager class is discussed in more detail in Chapter X.

The first crop of browsers have mostly chosen to cop-out on the security question by putting the burden on the user.  HotJava, for instance, allows the user to configure network and file IO access through an options dialog box.

There is no “solution” to this part of the security problem.  You must provide access to system resources to allow applets to get anything done, but providing that access opens the system to attack.  The SecurityManager provides browsers with a chance to validate that access before granting, or denying it.

Java is neither secure nor insecure.  Security, like the common cold, is actually a number of problems, some of which are solvable, some of which are not.  Java’s overall approach to security is to deal with the solvable security issues automatically, and provide the user/browser with a way, via the SecurityManager, to gain protection from the rest.  This strategy is the only reasonable one.  Time will tell if Java’s execution of the strategy has been successful.

Object Orientation

The term “object-orientation” has been applied so indiscriminately that only managers still think it means anything.  This is unfortunate, because Java combined with the internet has the potential to start fulfilling some of the dreams that have long driven O-O designers.

There are a two basic concepts behind all definitions of O-O: inheritance and encapsulation.  Inheritance is based on the notion that many objects will be modified versions of existing objects.  Inheritance says that if one object inherits another, then it gets all the functionality of that object.  So, in Java, if we say:

class convertible extends car { ...

we’re saying that the new class convertible will inherit all the functionality of the existing car class.  convertible becomes a sub-class of car, and conversely, car is convertible’s super-class. 

Encapsulation is an equally simple idea.  All it means is that every element of a program (method, variable ...) must exist within a class, and only those elements (methods and variables) that an object needs to see should be visible to that object.  Thus, a class must explicitly specify whether a method or variable is “visible” to other classes.  For instance, in this example:

class car {

protected int VehicleID;

public Scratch scratches[];

}

class AutoFactory {

public void makeACar() {

                car TheAudi = new car();

                }

}

An object of class AutoFactory can change TheAudi’s array of Scratches, but it can’t change the vehicleID.  This is encapsulation.

Where C programs are divided into modules, a Java program is divided into classes.  Classes are simply a way of dividing the functionality of the program into discreet chunks.  The theory of classes is that they “model” the items in the real world that they’re supposed to describe.  Thus, a program that showed a car moving across the screen would probably have a class named Car that had variables like speed, color, and bumper-stickers.  Every method (function) and variable in a Java program exists within a class.  Notice in our hello.java program above, where hello.c simply defined a main function, we had to create a class that contained the main method.

A running Java program is made up entirely of objects.  An object is simply an instance of a class.  Take our car example.  We can describe a car with the class below:

class car {

int speed;

Color color;

String bumper_stickers[];

                public car() {

                                color = new Color(Color.black);

                                speed = 100;

                                bumper_sticker = new String(“Code happens”);

                }

                public speed_up() {

                                speed++;

                                }

}

but all this means is that we know how to create a car.  In order to actually create one, we’d need to call new, like this:

public void MakeACar() {

                car TheAudi = new Car();

                while( !speedTrap ) {

                                TheAudi.speed_up();

                                }

                }

This is known as instantiating the class.  When you instantiate the class you get back an object.  In our example, car is a class, while TheAudi is an object.

Interfaces, described in more detail below, are an O-O feature that Java does not inherit from C++.  In simple terms, an interface is a description of a class.  For instance, our car example above, as an interface would read:

interface car {

                public speed_up();

}

But an interface is ONLY a description.  You can’t instantiate an interface.  In order to get a “car” object, you have to instantiate a class that implements the car interface, as in:

class convertible implements car {

                int speed;

                Color color;

                Scratch scratches[];

                public convertible() {

                                }

                public speed_up() {

                                speed += 10;

                                }

                }

Now we have a class that can be instantiated via new.

Cooperating Objects

The old computing world is made up of millions of islands of computing power, where huge, traditional applications toil away in splendid isolation from one another.  Through various torturous mechanisms, these applications can sometimes exchange data, but for the most part they treat each other like obnoxious relatives - with tolerance but not much respect.

The new computing world, according to Java, is populated by teams of objects working cooperatively over the Internet.  These Java teams are similar to human work-teams.  As anyone who’s ever worked for a living knows, in order to work together, the members of a team need to know what to expect from one another.  The knowledge you use to deal with a co-worker can be split into three parts: that he’s a human being[2], he has a particular job description, and he fulfills that job description in a particular way.  These three levels of understanding have exact analogs in the Java system.  Any object is an instance of the class Object (the human being), it has a particular interface (the job description), and it presents a particular implementation of that interface (the way that person does that job).

The Object class is THE base class for every class in a Java program.  This class provides a basic level at which objects which know absolutely nothing about one another can deal with each other.  Where in C, or C++, an unknown object would be passed as a void *, and accessed either as a lump of bytes, or by a dangerous type cast, in Java an unknown object is passed as an instance of class Object and accessed via Objects methods.  Dangerous type-casts can be avoided by querying the object’s type, and illegal type-casts are caught by the interpreter.

Java gives programmers two ways to use Objects: classes and interfaces.  By subclassing one of the supplied Java classes, a new class inherits all the functionality of that Java class.  Any other object that knows how to use that Java class, can also use the new subclass. 

Sub-classing is akin to adding a musician to a band.  Think of a rock band with guitar, bass and drum, Rodley’s Racketeers.  The band tours, playing a short set of instrumental songs.  Now add a singer to the group and call it Rodley’s New Racketeers.  The new group can still play the old instrumental set, but it can also play a new set of vocal music too.  That’s subclassing.

Subclassing has one drawback.  It ties you to an implementation of the functionality.  If, for instance, you subclassed a class that implemented a sorted list, you would be stuck with whatever sort routine that superclass used.  To use our band example, if you subclass Rodley’s Racketeers, you’re stuck with our drummer Keith.  If you don’t like Keith, you have to “override” him by replacing him with your own drummer.  If you don’t like the guitar player, or the bassist either, you can replace them too, but then you’ve ended up replacing the whole band.

That’s where interfaces come in.  Interfaces are a newer feature, not inherited from C++, that really complement classes.  An interface is just that - a description of one way that a class knows how to interact with the world.  Unlike a class, there’s no implementation behind an interface.  Practically speaking, an interface is merely a set of method definitions, in C a set of function prototypes.  In that sense, an interface is like a class where all the methods are overridden.

Computer language theorists talk about classes being made up of interface (the set of method definitions) and implementation (the actual code that implements the methods).  In our band example, the “definition” of a rock band -- guitar, bass, drum and vocalist -- is the interface.  The actual people, Keith et al, are the implementation of that interface.  Splitting the definition of a rock band from the people who implement it allows us to hire bands other than Rodley’s Racketeers.   Because they’re rock bands -- objects that implement the rock band interface -- we know that we can ask them to play Stairway to Heaven and it will come out right.

It’s important to think of any particular class as the methods it implements AS WELL AS all the methods its super-classes implement.  Throughout the following chapters I’ve provided class inheritance diagrams that show which super-classes make up each Java class.  Occasionally, for important classes, I also provide cumulative class desciptions, where all the class and superclass methods are collapsed into one class description.

Conclusion

Java’s power comes from four features.  Its network awareness allows Java objects to connect to each other.  Its portability allows Java objects to run on any machine they can connect to.  Its security features give users the confidence to allow Java objects to run on their workstations.  And finally, its approach to object orientation makes it possible to design objects that understand each other just enough to be able to work together, while avoiding the unneccessary dependencies that complicate the design and maintenance of similar objects in other languages.

Exercises

1.    What is the Java interpreter?  How does it differ from the Java Virtual Machine?

2.    How does the Java VM differ from a PowerPC CPU?

3.    What is the difference between objects and classes?  How about between interfaces and classes?

4.    Can you have an instance of an interface?

5.    How is Java’s memory segment protected from other applications on the same machine?

6.    What is a super-class?

7.    What programs do you need to run a Java applet?

8.    What is a Java content-handler?

9.    What low-level mechanism do Java applications use to talk to Internet server daemons such as http, or ftp?



[1]This is a gross simplification.

[2]This is a generalization. Your co-workers may vary from this standard.