java hosting


JavaRanch Newsletter Articles in this issue :
Getting started with Web Services Using Apache AxisGreg Barish Printable Version
Logging in J2SE 1.4Thomas Paul Printable Version
Cindy's Segment - a Whimsical View of the WorldCindy Glass Printable Version
Cattle Drive UpdateJason Adam
Pauline McNamara
Printable Version
Book Review of the MonthCindy Glass
Madhav Lakkapragada
Printable Version
May GiveawaysCarl Trusiak Printable Version
May NewsCarl Trusiak Printable Version

Getting started with Web Services Using Apache Axis

NOTE:This article pertains to Beta 2 of Apache Axis, released in April 2002.

In this article, I will discuss the basics of web services and explore how Apache Axis, an open-source SOAP toolkit, and can be used to create, deploy, and access such services. After a quick introduction, we'll discuss downloading and installing Axis. Then, we'll build a simple web service and communicate with it. Finally, we'll discuss some of the details behind a more advanced example.

Introduction

Web services technology has quickly become an important enabler of true distributed computing over the Internet. By using web services, consumers and providers of functionality can quickly connect, without the usual integration hassles, in a way that is language-independent and platform-independent. For example, if you develop remote functionality in C++ on a Microsoft Windows NT system, your friend using Java on Sun Solaris will be access it over the Internet.

To motivate the need for web services, let's consider an example. Suppose you have developed a useful Java class that you would like to distribute to others via the Internet. How would you (at Internet site x) make Java functionality accessible to your remote friend (at Internet site y)?

One option is to use something like Java IDL (i.e., CORBA). After all, doesn't CORBA and the IIOP protocol allow distributed objects (potentially written in different languages) to communicate with each other over the Internet? Well, yes and no. CORBA-IIOP solutions, while possible, can be problematic in terms of security and ease of deployment. In terms of security, ensuring that IIOP will work through firewalls is often painful, since IIOP traffic does not travel on port 80, as does normal HTTP traffic. A second major issue is the lack of simple deployment. Unless they want to really do some lower level programming, both participants in an IIOP exchange must have Object Request Brokers (ORBs), which manage objects locally and route requests to available instances. There are other issues, but we don't have the space to get into a full-blown debate. However, in all fairness, I should at least note that there do exist counter-arguments .

Well, what about skipping all of this complexity and just using servlets? For example, you could embed your functionality in a servlet and route its output back to callers. However, doing so involves a lot of ugliness:

  • You have to build servlet wrappers for each piece of functionality you want to deploy.
  • You need to manually print the output of the Java methods of interest.
  • All variable types have been lost (unless you describe them explicitly -- and all your clients know about and obey your cooked-up protocol)

What we really want is some way to combine the flexibility and interoperability of CORBA with the ease of deployments that Java servlets provide. So, does such as solution exist? As it turns out, it does: web services.

Web Services

Web services leverage the de-facto platform indepedence of the Web and the extensibility and flexibility of XML to simplify distributed computing on the Internet. The concept is straightforward: those that want to distribute functionality publish web services, those that want to use that functionality access those services.

The conceptual web services technology stack is shown in Figure 1.

Figure 1

Let's start by explaining a few of the acronyms:

  • WSDL: Web Services Description Language, which is how web services are declared.
  • SOAP: Simple Object Access Protocol, which is the mechanism for how services are invoked.
  • UDDI: Universal Description, Discovery, and Integration specification, which is how services can be located.

There are a variety of transports that one can use to communicate with a web service - RPC, email, FTP, and HTTP, for example. In this short tutorial, we will focus on HTTP, since that is the protocol most readers will likely find applicable.

So then, in a nutshell, the technology stack in Figure 1 describes how web services can be described, deployed, and accessed. In this article, we will assume that we know the web service we want and where it is deployed; thus, it only makes sense that access (SOAP) is our chief concern. And this is exactly where Apache Axis comes in.

Apache Axis

Apache Axis is a SOAP toolkit that makes it easy to create, deploy, and consume web services. By using Axis, you will be able to quickly convert existing Java functionality into web services, deploy those services, and be able to communicate with them remotely via the Internet, usually through firewalls (if necessary).

More precisely, Axis is an implementation of SOAP. Most people like to think of it as a toolkit. From the programmer's point of view, it's like an API. It makes it easy for you to communicate - with SOAP - to a remote object, without having to worry about the details associated with the protocol. In practice, it is a lot like using RPC, CORBA, RMI, or any of the other distributed computing technologies you may have experience with. The process is largely the same: you describe the target of your communication, invoke the remote method and marshall your parameters, and then demarshall the return values. Since AXIS makes this process simpler than ever, development is a breeze.

The SOAP Protocol

SOAP is a simple an extensible protocol for communication between application-level objects. It is a standard that originated between a number of vendors, including Microsoft and IBM. However, its support has grown to include Sun and is now generally viewed as the best open standard for structured communication between objects on the Web.

As described above, SOAP messages are encoded with XML and the protocol can be deployed over a variety of transports. In contrast to other synchronous protocols, SOAP communication is one-way: from sender to receiver. Upon this, higher-level request-response designs can be implemented.

A SOAP message consists of an envelope that contains an optional SOAP header and a mandatory SOAP body. The header is a mechanism for extensibility; we won't worry about it much more here. The body contains the guts of the communication between parties. For example, the body could contain information on the remote method to be called, on the parameters to call it with, etc. As a detailed example of a SOAP message, consider the simple example (note that there is no header here -- remember, it is optional) shown in Figure 2.

Figure 2

This example illustrates a request to a SOAP node such that the method "GetCurrentTime" is invoked with the parameters "city" (Las Vegas) and "state" (of Nevada). The service containing that method is found at example.com. With this simple example, you now have a feel for what SOAP messages look like. If you are interested in a more detailed description of the SOAP protocol, check out the W3C note. For our discussion here, I summarized SOAP messaging for the purposes of underscoring that:

  • SOAP messages are encoded with XML and thus make it a message-based protocol.
  • Methods and parameters can be declared in a SOAP message, but they ultimately are associated with service and typing information (such as whether the parameters are integers, floating points, etc.), so that translation of those messages is proper and accurate.
  • SOAP is a relatively simple protocol.

If you inferred that much from the above, you got the major points. Time to move on.

Getting started

In the rest of this article, I will describe what you need to do to get started with Axis. Afterwards , you should be able to:
  • Download and install Axis
  • Create and deploy web services
  • Access those services remotely over the network

Although it is possible to describe how to generally use Axis, I am going to take a more "in the trenches" approach, since I want you to be able to run the examples I describe. Although the existing Apache Axis documentation does a fine job of getting you started, it does take a little while to identify some of the key configuration issues. My hope is that this document streamlines that process even more.

Assumptions and Dependencies

First, keep in mind that this article pertains to the Beta 2 of Apache Axis, released in April of 2002. Pre-production open source software often changes and it may be possible that post-beta-2 changes make some of this document invalid, so please consider that if trying to use this document with any other release of Axis.

Second, since this document goes into the details of configuring Axis, it makes some assumptions:

  • You are using Microsoft Windows 2000.
  • You have the Java 1.3 JDK
  • You have TCP/IP network access.
  • You have no services running on port 8080 of your machine (although you could easily adapt the examples below if you do).

Third, before you get started with Axis, you also need to have a J2EE compliant subsystem that implements the Java 2.2 Servlet specification. A good candidate for that is the Apache Tomcat Java servlet container. This tutorial assumes that you are using the latest (at least at this time!) stable version: Tomcat 4.03. As you know, you can run Tomcat independent of your web server. It runs on a specified port (8080 by default) and can be started up with a simple boot script that is included with the release. You can install Tomcat anywhere on your machine. It will likely choose c:\Program Files\Apache Tomcat 4.0 by default. Whether it is this or another location of your choosing, no matter - the point of installation is referred to hereafter as $TOMCAT_HOME.

Now, just to make sure no odd problems occur during our installation of Axis, make sure that Tomcat is not running. We will start it up later.

Finally, to parse the XML, you need to have an XML parsing library available, such as Xerces or the Java API for XML Processing (JAXP). Opting for an all-Apache approach, I used Xerces 2.0.1, which comes with the following libraries (available in the top level directory of the Xerces release):

  • xmlParserAPIs.jar
  • xercesImpl.jar
  • xercesSamples.jar
You probably only need the first two of these JAR files, but it is important that you have Xerces 2 and that you know where these files are. You will need them later.

Downloading, Installing, and Configuration

In this section, I describe how to download and configure Apache Axis as a web application for your servlet container (Tomcat). Once installed, clients will be able to access SOAP nodes on your server via an Apache Axis servlet.

The first step is to download the Axis release. This is usually a ZIP file. At the time of this writing it was located here. You can install this release anywhere on your machine; for example, I installed it under c:\Program Files\Apache Group\xml-axis-beta2.

The second step is to integrate Axis as a web application of Apache Tomcat. This step is extremely simple: just copy (recursively) the webapps/axis directory under the webapps directory of your Tomcat installation (i.e., $TOMCAT_HOME\webapps). Next, copy the XML parser libraries (such as the 3 Xerces JAR files mentioned above) to the $TOMCAT_HOME\webapps\axis\lib directory.

Next, a very important point that is sort of buried in the current Axis instructions and FAQ: relocation of the RPC library Axis uses. Specifically, you need to move (or copy) the file lib/jaxrpc.jar to common/lib directory, under your Tomcat home (i.e., $TOMCAT_HOME\common\lib).

Finally, one point about setting up your environment. Later in this article, you will need to compile some Java classes. To do so means that you need to setup your classpath to use the Axis libraries. I find it most simple to define a setup file that augments the classpath to include the following libraries (all found in $AXIS_HOME/lib):

  • axis.jar
  • commons-logging.jar
  • log4j-core.jar
  • jaxrpc.jar
  • tt-bytecode.jar
  • wsdl4j.jar
  • the XML libraries associated with your parser, such as xercesImpl.jar (or whatever your XML parser requires) and xmlParserAPIs.jar

Before moving on, it might be useful to do a sanity check and make sure that Tomcat works fine outside of Axis. That way, if you have a problem in the next section, you can potentially rule out Tomcat. So, for example, you should be able to point your web browser at http://localhost:8080/examples/servlets/index.html and be able to execute the Hello World servlet example.

Web services made easy

Okay, you've downloaded and installed everything. You've ensured that Tomcat works. Time to start building and testing out web services with Axis. We'll start out with a very easy example - this should quickly convince you how easy it is to deploy web services with Axis.

Turning Java classes into instant web services

The easiest way to get started is to simply copy any independent Java class into your Axis web application directory and access it via SOAP remotely. This is easier than you think. There are only three steps involved: (a) developing the Java class, (b) deploying it, and (c) building and running a client to access it.

The first step is to develop a Java class. Let's keep things really simple and try out a Hello World -style class. We'll develop one method, called greet() that takes someone's name as a parameter and returns a nice greeting message with that person's name.

To do this, locate the $AXIS_HOME/samples directory. Create a new subdirectory called "hello". In this subdirectory, create the file Hello.java, and include the following code:

    public class Hello { public String greet(String a_name) { return "Nice to meet you, "+a_name; } }

NOTE:We're keeping Hello.java in the $AXIS_HOME/samples/hello directory to keep things consistent with the rest of the Axis built-in examples.

Now, do NOT compile Hello.java. Just copy it into $TOMCAT_HOME/webapps/axis and name it Hello.jws. Clients (such as the one we develop next) will request this service and the Axis servlet will compile and execute our Hello.java class on the fly! As you can see, developing a web service could not be more painless. We didn't have to use a single library or implement a single interface.

Accessing a web service

Time to build a client to access our service. This is where we finally see some library code. But, as you will soon notice, it's very simple and straightforward.

Again, to keep things consistent with the examples provided by the Axis release, let us develop our client code in the file HelloClient.java, which we will store at $AXIS_HOME/samples/hello. In an abstract sense, HelloClient.java needs to do the following:

  • Connect to the our Hello service (a SOAP endpoint)
  • Invoke the service method remotely, marshalling parameters in and out.
  • Print out the reply from the service.

Below is code for HelloClient.java that enables this:

    1 package samples.hello; 2 3 import org.apache.axis.client.Call; 4 import org.apache.axis.client.Service; 5 import org.apache.axis.encoding.XMLType; 6 import org.apache.axis.utils.Options; 7 8 import javax.xml.rpc.ParameterMode; 9 10 public class HelloClient 11 { 12 public static void main(String [] args) 13 throws Exception 14 { 15 Options options = new Options(args); 16 17 String endpoint = "http://localhost:" + options.getPort() + 18 "/axis/Hello.jws"; 19 20 args = options.getRemainingArgs(); 21 22 if (args == null || args.length != 1) { 23 System.err.println("Usage: Hello "); 24 return; 25 } 26 27 Service service = new Service(); 28 Call call = (Call) service.createCall(); 29 30 call.setTargetEndpointAddress(new java.net.URL(endpoint)); 31 call.setOperationName("greet"); 32 call.addParameter("name", XMLType.XSD_STRING, ParameterMode.IN); 33 call.setReturnType(XMLType.XSD_STRING); 34 35 String msg = (String)call.invoke(new Object[] {args[0]}); 36 37 System.out.println("Reply: " + msg); 38 } 39 }

Here, there are obviously a few more things to note. Let's take them one at a time.

  • Line 1: We place our code in the samples/hello package, to keep things consistent with the other Axis examples.
  • Lines 15-20: We collect runtime options, such as the port number of the destination service and the arguments to that service.
  • Lines 27-33: We set key parameters of our call to the remote method. This is the heart of the client, where we set our endpoint, describe the method we want to invoke and the parameters we are providing, and then declare the return type of that method. This prepares the remote call.
  • Line 35: We invoke the remote method and cast the return object, since we know what know the type it will return. Note that our runtime argument is passed in as a parameter as part of an Object array. Thus, integers, strings, and other types can be mixed (heterogenously) in this array for other types of methods.
  • Line 37: We print out the reply.

Now, we can simply compile this client, specifically:

    javac samples/hello/HelloClient.java
and then run the client:
    java samples.hello.HelloClient -p8080 Greg
which returns the message:
    Nice to meet you, Greg!

As you can see, development is pretty simple with Axis. It hides behind the scenes and makes SOAP-based connectivity quick and painless. Part of the magic behind why you were able to simply copy the Hello.java file and have it accessible to your client has to do with the $TOMCAT_HOME/webapps/axis/WEB-INF/web.xml deployment descriptor file. In that file, you will find the following section:

Figure 3

These instructions tell Tomcat to run the Axis servlet on all requests to files that end in .jws. You'll also notice that the compiled class is stored in $TOMCAT_HOME/webapps/axis/WEB-INFjwsClasses as Hello.class.

Wait, where's the SOAP?

The Axis API does such a good job of hiding the details in example above, you're probably wondering: where is the SOAP? Actually, on the wire, XML-encoded SOAP messages are being exchanged. Axis comes with a tool called tcpmon that allows you to see this (it monitors the target TCP port). If you were to look at it, the request would look something like what you see in Figure 4 and the reply like you see in Figure 5.
Figure 4
Figure 5

A more advanced example

As the Axis documentation suggests, there are a variety of cases where you cannot simply copy your Java source files into the web application deployment directory. You may not have the source code or you may want to use other handlers during processing, etc. Both reasons motivate us to look at a more general method for deploying and accessing services with Axis

In this example, let us focus on the development and deployment of a service that we will call "LoudService". This service takes an input string and returns the uppercase form of the string, as if the string were being spoken loudly.

The steps involved in development and deployment are:

  1. Development of the LoudService class
  2. Development of a LoudService client
  3. Development of the deployment descriptor
  4. Deployment of the service using the descriptor
  5. Access by the client
Previously, steps 3 and 4 were hidden from us; now we will perform them explicitly.

Developing the LoudService

The code for LoudService is just as simple as the code for the Hello service we developed earlier:

    package samples.loud; public class LoudService { public String serviceMethod(String msg) { return msg.toUpperCase(); } }
Let us store this file in a new directory at $AXIS_HOME/samples/loud. We can compile this file and store the resulting LoudService.class in another new directory, this time under the Tomcat hierarchy: $TOMCAT_HOME/webapps/axis/WEB-INF/classes/samples/loud.

Developing the client

Developing the client is not that much different from what we did earlier. As the code below shows, the main difference is that we use the QName API to specify the name of the service (LoudService) and the method (serviceMethod) we intend to execute. Notice that the endpoint URL is obtained automatically (in this case, it is: http://localhost:8080/axis/servlet/AxisServlet -- the class and method are resolved dynamically at runtime).

    package samples.loud; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; import org.apache.axis.utils.Options; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.namespace.QName; public class Client { public static void main(String [] args) { try { Options options = new Options(args); String endpointURL = options.getURL(); String msg = null; args = options.getRemainingArgs(); if ((args == null) || (args.length != 1)) { System.err.println("You must send something."); System.exit(-1); } else { msg = args[0]; } Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(new java.net.URL(endpointURL)); call.setOperationName(new QName("LoudService", "serviceMethod")); call.addParameter("mesg", XMLType.XSD_STRING, ParameterMode.IN); call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING); String reply = (String)call.invoke(new Object[] {msg}); System.out.println("Reply: "+reply); } catch (Exception e) { System.err.println(e.toString()); } }

We can then store this file in $AXIS_HOME/samples/loud and compile it with the Java compiler.

Developing the deployment descriptor

The Axis web services deployment descriptor (WSDD) is an XML-encoded way of specifying details about how SOAP-accessible functionality can be deployed. There are a variety of options for deployment, but we'll focus on the simple task of deployment as a web service. The type of descriptor we need is shown in Figure 6. We don't have the space to go into all of the options for how to deploy services; but, using the example below, you should be able to get an idea.

Figure 6

We name this descriptor deploy.wsdd and store it in $AXIS_HOME/samples/loud.

Deployment and client access

Now it is time to deploy our new service. To do that, we use the Axis administrative service and call it with the deployment descriptor above:

    java org.apache.axis.client.AdminClient deploy.wsdd
The admin service reports that the service was loaded and now you can use the client to access this service.
    java samples.loud.Client "what did you say?"
which returns
    WHAT DID YOU SAY?
Although this example was a little more complicated because of the deployment considerations, we see that it is not terribly difficult and in return we gain greater flexibility.

Conclusion

Of course, there are many more features and interesting details related to Apache Axis than we can cover here. However, the two examples we discussed above should hopefully clarify where Axis fits in the overall web services architecture, give you a better understanding about what Axis can do, get you started using its tools and libraries, and leave you curious about more advanced features. As you can Axis is simple, easy to use, and powerful. We can deploy web services from existing functionality in a very short time and thus rapidly move towards true Internet distributed computing.


Return to Top

Logging in J2SE 1.4

By Thomas Paul

The ability to log error, warning, or debugging information can be a critical part of any application. Logging is so critical that logging systems have been independently developed and implemented hundreds if not thousands of times. The Apache project developed a framework (log4j) which is widely used. With the release of J2SE 1.4, we finally have a logging API built into the language. The new API is found in the package, java.util.logging.

The functionality of the logging API has been divided into three different types of objects. Logger objects are the controllers. Handler objects determine where the logging information will be published. Formatter objects control what the published log information will look like.

Listing 1 contains a listing of a program using logging. Listing 2 contains the log output of the program.

Loggers

Logger objects are the main entry points into the logging API. Messages you wish to log will be passed into a logger object. The logger will determine if the message should be logged and send it to the appropriate Handler object.

Loggers are not directly instantiated but are created with the getLogger(String loggerName) static method of the Logger class. Logger objects are named using the package naming standard. Here is an example of creating a Logger object:

     Logger logger = Logger.getLogger("com.aag");

The Logger class has many methods that can be used to send log messages to the Handler objects. The basic version is:

     void log(Level level, String msg)

The Level class defines seven different levels as static constants. Ranging from highest to lowest, these are: SEVERE, WARNING, INFO, CONFIG, FINE , FINER, and FINEST. To send a warning level message to the log we created earlier we would use:

     logger.log(Level.WARNING, "A warning message");

You can also use:

     logger.warning("A warning message");

There is a version of the log method for each of the seven levels. There is also an overloaded version of the log method that you can use to log exceptions:

     void log(Level level, String msg, Throwable thrown)

To log an exception you could code this:

catch (Exception e) {
     logger.log(Level.SEVERE, "Exception caught!", e);
}

The Logger class has a setLevel method that can be used to filter logging messages:

     void setLevel(Level level)

The logger will ignore messages that are below the level specified in the setLevel method. For example, if you wanted to ignore all messages except SEVERE and WARNING level messages, you would use:

     logger.setLevel(Level.WARNING);

In addition to the seven static constant levels we mentioned earlier, the Level class also contains two other constants. Level.ALL is used to accept all messages. This ends up being the same as specifying Level.FINEST. Level.OFF is used to reject all messages sent to the Logger.

By default, the Logger object will send all messages to System.err in a format that is human readable. If some other functionality is required, the API provides Handler and Formatter classes.

Handlers

A Handler object controls how messages are published but not how they will appear. The API provides five Handler classes:

  • MemoryHandler ? sends the log messages to a memory buffer.
  • StreamHandler ? sends the log messages to an output stream. The other three Handlers are sub-classed from StreamHandler.
  • ConsoleHandler ? sends messages to System.err.
  • FileHandler ? sends messages to a file. FileHandler allows you to create a group of rotating files to hold log messages.
  • SocketHandler ? sends messages to a socket.

The Logger class has the addHandler(Handler handler) and removeHandler(Handler handler) methods to determine which Handlers will receive log messages. To change the Logger to send all log messages to a file named "out.log" instead of using the default ConsoleHandler, you would use:

     Handler handler = new FileHandler("out.log");
     logger.addHandler(handler);

Creating your own Handler class is fairly simple. The Handler abstract class contains only three methods:

  • public void close( )
  • public void flush( )
  • public void publish(LogRecord log)

I will leave it as an exercise for the reader to design a Handler that sends log messages to a JMS topic.

The level can be set for Handlers just as we can set it for Loggers. In this way a Logger can have multiple Handlers attached that to it with each Handler processing messages based on its own level criteria. For example, SEVERE messages could be sent to the console while INFO messages and above are logged in a file. To set the level for a Handler to report only SEVERE messages, you would use:

     handler.setLevel(Level.SEVERE);

Different Handlers have different default levels. ConsoleHandler uses Level.INFO as the default while FileHandler uses Level.ALL as its default.

Formatters

In addition to controlling where messages are sent, the logging API also provides two classes to control the appearance of the log messages. SimpleFormatter produces a log message in "human readable" form. This is the default Formatter used by ConsoleHandler. XMLFormatter writes the log messages in an XML format. This is the default used by FileHandler. Samples of both formats are shown blow. To change the FileHandler to use the SimpleFormatter you would code:

     Formatter formatter = new SimpleFormatter();
     handler.setFormatter(formatter);

Writing your own Formatter class is a simple exercise. The Formatter abstract class has three methods to use:

  • String format(LogRecord log)
  • String getHead(Handler handler)
  • String getTail(Handler handler)

The getHead( ) and getTail( ) methods are invoked at the beginning and ending of logging. These can be used to specify information that should be written either before or after the actual logging data. The format( ) method is invoked once for each message to be logged. The method is given a LogRecord object that contains all the information about the log message. The LogRecord has several useful methods:

  • Level getLevel( ) - returns the Level of this LogRecord
  • String getMessage( ) - returns the actual message
  • long getMillis( ) - returns a long representing the date/time this LogRecord was created
  • String getSourceClassName( )returns the name of the class that generated this message
  • String getSourceMethodName( ) - returns the name of the method that generated this message
  • Throwable getThrown( ) - returns the exception associated with this log message if there is one

See Listing 3 for a sample Formatter class.

Filters

The logging API provides a Filter interface that can be used to filter messages either at the Logger or at the Handler. The Filter interface contains one method:

     boolean isLoggable(LogRecord log)

If the Filter returns false, the LogRecord is ignored and not sent or processed by the Handler. Filters can be used to reject log records based on any criteria. For example, log records could be rejected based on the class that generated them or the day of the week or by the message contents. Adding a Filter to a Handler or Logger is done using the setFilter( ) method. An example of using this:

     Filter filter = new DayOfWeekFilter("Monday");
     handler.setFilter(filter);

Conclusions

There are several areas where the logging API has some shortcomings. Security is an issue that may have potential problems. Another area of potential annoyance is with multi-threaded applications. The API does not synchronize any methods so there may be a problem of log messages not appearing in the order they were written to the log.

Overall, however, the logging API is a well written, if basic, logging system. I recommend that anyone doing development examine this API to see if it meets their needs. For those who are unsatisfied with the API, the Apache Project's log4j is available and continues in development.


Return to Top
Cindy's Segment - a Whimsical View of the World

Segment 3:
The Games Objects Play
Or -
Hiding in Obscure Shadows

Well, I was going to talk about Stereotyping this time - but that is such a SERIOUS topic. So I thought that we would just take a pause and go over some of the games that are played in JVMLand.

Let's go over the rules for the following games:

  • 1. Dark Shadows
  • 2. What's my Line
  • 3. Hide and go Seek
  • I. Dark Shadows
    This is a game of solitaire that is played by one object inside itself. The trick is to get one of your members to prevent another one from being seen. In order to do this you have to learn how to shadow the Scope of another member.

    For instance, if you have an instance variable named x, you could cleverly create a local variable in a method with the SAME name. Now without that local variable it would be easy to get the instance variable using its simple name even from within the method. The scope of x extends into the method easily. But once you put in the local variable - poof - you have succeeded in shadowing x! Now the only way to get the instance variable is to specify "this.x" to tell it apart from your local x. (Very Wicked).

    Now it's your turn. Go ahead and try this with any field, local variable, method parameter, constructor parameter or exception handler parameter.

    Try it with Labels! Try it with methods! You can use a method of an inner class to shadow the method of the containing class and REALLY cause confusion! Notice that you can do all this with NO inheritance involved.

    When you get to the advanced stage you can try shadowing types. If you put an import package statement at the top of your class that would bring in a class named X, then at the last minute use the single-import syntax (you know - where you provide the fully qualified name of a class) then you can shadow the type X class that WOULD have been used.

    It's easy and it's fun!

    II. What's my Line
    This is a game similar to Dark Shadows but uses mixed double declarations. The trick is to make your names so obscure that you confuse the compiler so much that it can not tell what you are talking about. If you do it correctly, the compiler will resort to the rule book to make the decision on what to do.

    For instance, take any two of the following: a variable, a class name or interface name, a package declaration. Now make sure that they have the exact same name. Now use that name to invoke an action.

    For instance, create a class named ABC. Now have another class that is also visible that has an instance variable and name it ABC: SomeClass ABC = new SomeClass();

    Now go ahead and try to use ABC. Try using "ABC.x" and see what happens. The compiler sits there thinking - do they want a static variable named x from class ABC, or do they want the instance variable x from the object referenced by variable ABC???

    HA! You have succeeded in obscuring the declaration. No way the compiler can figure it out. So it's time to go to the rules.

    If you go ask the JLS Wizard (the keeper of the rules) he will say: "In these situations, the rules specify that a variable will be chosen in preference to a type, and that a type will be chosen in preference to a package. "

    So, in this case, the compiler will treat ABC as the variable instead of the class type. It will decide that you want the instance variable x from the object referenced by variable ABC.

    Now it's your turn. Try it with interfaces and package names, or whatever combination you prefer.

    But Beware!! If you get TOO obscure - so that the compiler can not decide which one to pick, you will get compliants about being "ambiguous". So if you inherit two different variables with the same name but different types - neither one will take priority and the compiler will be very upset.

    III. Hide and go Seek
    This is a game that is played by Parents and their Subs and is by far the most complicated. The rules go like this. The Parent has a bunch of members including methods, static methods, instance variables and static variables. All these things might potentially be inherited by the Sub. The Sub (either a sub-class or a sub-interface depending on who is playing) tries to hide the parent's member. If he succeeds in hiding it, the Sub gets a point. If he can't hide it, the Parent gets the point.

    The test for whether or not it works is this:

  • It must compile cleanly.
  • The member must be available to be inherited. So a version of the Sub without the member of the same name should still be able to reference that name because it inherited it from the Parent.
  • Having the member in the Sub must cause the Sub's version to be used.
  • You must be able to get the Parent's version from a variable of the type of the Parent. This eliminates overriding from being the cause. If there is overriding, you just can't get that Parent's version. That late binding stuff is strict.
  • Of course anyone who has played this game very much realizes that the real trick to this game is making sure that you know what can be inherited and which of those can be overridden. Anything that can be inherited but can't be overridden - can be hidden.

    For hiding variables with the same name it is almost TOO easy - the types do not even need to match.

    >
    If the Parent's member is a: and the sub uses the same name for a: Sub Score Parent Score Comments
    static variable static variable +1   Get hidden using Parent.varName
    static variable instance variable +1   Get hidden using super.varName or Parent.varName
    instance variable static variable +1   Get at hidden using super.varName
    instance variable instance variable +1   Get hidden using super.varName
    private variable any variable   +1 Can't hide what you can't inherit. But of course you can still HAVE a variable with the same name - it is just not hiding anything.
    final variable any variable +1   Get hidden using super.varName

    For hiding methods things get much more complicated:

    For it to compile the Subs methods must

  • have the same return type (even though variables don't have to be the same type - methods DO - I know, life is not fair)
  • be as much or more accessible than the Parent
  • not have a throws clause that conflicts with the Parent throws - this keeps things typesafe
  • If the Parent's member is a: and the sub uses the same name for a: Sub Score Parent Score Comments
    static method static method +1   Get hidden using Parent.methodName
    instance method instance method   +1 Polymorphism is not hiding
    abstract method any method   +1 Polymorphism is not hiding
    static method instance method 0 0 Compile error. Attempting to "override" a static method with an instance method.
    instance method static method 0 0 Compile error. Attempting to override an instance method with a static method.
    final method static or instance method 0 0 Compile error. Final means FINAL!
    private method Method with same return type and throws.   +1 Can't hide what you can't inherit - you just HAVE a method with same name.
    Final class with regular method Can't sub-class anyway.   +1 Can't hide what you can't inherit.
    Constructor Constructor   +1 Can't hide what you can't inherit. Heck, as constructors, they couldn't even be named the same name anyway.
    Static initializer Static initializer   +1 Can't hide what you can't inherit.
    Instance initializer Instance initializer   +1 Can't hide what you can't inherit.

    So far the score is Subs 6 - Parents 8.

    Once you get the hang of it - start trying it with inner classes. Do you REALLY know what you get because of scope and what you get because of inheritance? Now try it with inner classes that are ALSO sub-classes of the Parent. Or you could try double inheritance using interfaces. For a real challenge use sub-interfaces with inner classes. The possibilities are endless.

    And the moral of all this is:
    - - Interfering with Inheritance causes "hiding".
    - - Interfering with Scope causes "shadowing".
    - - Interfering with Namespaces causes "obscuring".

    Copyright ? 2002. Cindy Glass. All rights reserved.

    Graphics by Pauline McNamara and Stephanie Grasson

    Next month (for sure) - "Stereotyping and pigeon-holing" - or "When is an Interface too tight"


    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 see your name on the Graduation Log.

    Gettin' them doggies...
    The past month has been a whirlwind of ol' timers coming back, and spurs being passed around like crazy! The list of active drivers shows around 18 drivers lately, the majority kickin' up dust in the OOP and Servlets corrals. Those doggies are the tough 'uns, so writin' em up and cleaning out the nits takes a bit longer.

    And a shiny spur goes to...
    The past month has been wilder than a Texas Twister when it comes to graduates!? Josue Cedeno wrangled his first graduation by passing through the Java Basics course.? Good job Josue!

    At the OOP corral, Rick Prevett and Sam Tilley blazed through the assignments and got themselves a second spur.? Y'all done real good!

    Back from being out on the range...
    We're glad to announce that Joel Cochran and Paul Ralph decided they had enough of the easy life and came back to the dirt and the grime that is the Cattle Drive.? Glad to have y'all back, we're planning on workin' ya hard!

    Saddle sore...
    Ronald Schindler has got his eye on the silver spur for OOP.? Keep wrastlin' with the code, Ronald, you'll be there in no time!

    Peter Gragert is serving up his final assignment in the Servlets section.? We'll save you a seat at the graduation saloon!

    And it's finally here, the instructor's solution to JDBC-4 has been completed.? Jason Adam and Lance Finney can see their JDBC spur on the horizon.

    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 and Jason Adam for their dedication and patience with the pesky vermin that always manage to make their way into those assignments.

    Tips for the Trail?
    While comments are good to have, and can help clarify parts of your code, too many comments can make a program unreadable.? Instead, focus on writing code that uses identifiers and expressions that explain themselves, and leave comments to the trickier parts of your program.? Like we've mentioned time and again, readability is one of the most important goals of the Cattle Drive, and for writing code in general!

    Written by Jason Adam and Pauline McNamara


    Return to Top
    Book Review of the Month

    EJB Design Patterns: Advanced Patterns, Processes, and Idioms
    by Floyd Marinescu, Ed Roman
    Take the most popular J2EE resource - theserverside.com. Have the people who actually built the site using J2EE technologies throw in the best practices as EJB design patterns. Open the stage for hundreds of developers to read, critique and say yea or nay. Painstakingly update the pattern repository incorporating the developer feedback. Repeat this process for nearly a year. What do you get? The best catalog of EJB design patterns ever written - by developers for developers.

    Let?s face it. Many of the standard GOF design patterns are hard to apply in the EJB world. The learning curve is steep and the mistakes can be very expensive. This book gives you everything you need to design, develop, deploy and maintain industry strength J2EE applications using EJB technology.

    The book is divided into two parts. Part one - EJB Pattern Language is a repository of the true and the tried strategies such as Transaction and Persistence Patterns, Session Facade, and Message Facade, JDBC for reading, Versioning etc. Part Two - titled "Best Practices for EJB Design and Implementation" is a collection of idioms, tips, do's and dont's specific to EJB projects.

    This book is a great working reference, every EJB developer must have on the bookshelf. For those who have just begun their journey through the J2EE land, getting started with "Mastering Enterprise JavasBeans-2nd Edition by Ed Roman" provides the required background.

    The book comes with a nifty chart for quick reference. The implementations can be downloaded freely from theserverside.com.

    (Ajith Kallambella - sheriff - April 2002)

    More info at Amazon.com More info at Amazon.co.uk

    Chosen by Cindy Glass and Madhav Lakkapragada

    Return to Top
    May Giveaways

    Starting Date Book Author(s) Publisher JavaRanch Forum
    May 7 Building Scalable and High-Performance Java(TM) Web Applications Using J2EE(TM) Technology Greg Barish Addison Wesley J2EE and EJB
    May 14 Instant Wireless Java with J2ME Paul Tremblett Osborne/McGraw-Hill Java 2 Micro Edition
    May 21 Professional Java Servlets 2.3 Simon Brown
    Sam Dalton
    Wrox Press Servlets
    May 28 WebLogic@Whiz- BEA WebLogic Certification Test Simulator Pradeep Chopra Whizlabs Product Certifications

    For more information on JavaRanch's Giveaways see Book Promotion Page.

    Return to Top
    May News

    JavaRanch moved to a Dedicated Server using Orion as the J2EE engine. Conversion was extremely smooth thanks to the good people at EJIP.NET.

    Announcing new Bartenders!
    If you look at our main Saloon page, you will see a several new names. Yes, I'm pleased to announce that we've added a number of new Bartenders here at JavaRanch:

    Jose Botella
    Simon Brown
    Michael Matola
    Michael Pearson
    Ilja Preuss
    Rob Ross
    Jessica Sant
    Dirk Schreckmann
    Mark Spritzler

    These folks were all selected based on their history of helpful, informative posts, and we're very happy to have them join us. Welcome, all!

    Also, you'll notice that many of the other bartender names are moving around to different forums, as we've given everyone a chance to move around and try new things if they wish. So, if you see that your favorite forum has a new bartender, please make him/her welcome there. Enjoy...

    As always JavaRanch is dedicated to providing you with the best source of information on Java Programming and Engineering.

    by Carl Trusiak


    Return to Top

    Managing Editor: Carl Trusiak

    Comments or suggestions for JavaRanch's NewsLetter can be sent to the NewsLetter Staff
    For advertising opportunities contact NewsLetter Advertising Staff