Getting started with Web Services Using Apache Axis
(written May 2002, for JavaRanch.com) 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. IntroductionWeb 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:
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 ServicesWeb 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.
Let's start by explaining a few of the acronyms:
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 AxisApache 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 ProtocolSOAP 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.
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:
If you inferred that much from the above, you got the major points. Time to move on. Getting startedIn the rest of this article, I will describe what you need to do to get started with Axis. Afterwards , you should be able to:
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 DependenciesFirst, 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:
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):
Downloading, Installing, and ConfigurationIn 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):
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 easyOkay, 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 servicesThe 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:
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 serviceTime 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:
Below is code for HelloClient.java that enables this:
Here, there are obviously a few more things to note. Let's take them one at a time.
Now, we can simply compile this client, specifically:
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:
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.
A more advanced exampleAs 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:
Developing the LoudServiceThe code for LoudService is just as simple as the code for the Hello service we developed earlier:
Developing the clientDeveloping 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).
We can then store this file in $AXIS_HOME/samples/loud and compile it with the Java compiler. Developing the deployment descriptorThe 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.
We name this descriptor deploy.wsdd and store it in $AXIS_HOME/samples/loud. Deployment and client accessNow 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:
ConclusionOf 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.4By Thomas PaulThe 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.
LoggersLogger 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. HandlersA Handler object controls how messages are published but not how they will appear. The API provides five Handler classes:
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:
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.
FormattersIn 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:
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:
See Listing 3 for a sample Formatter class.
FiltersThe 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);
ConclusionsThere 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 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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:
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).
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 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 The test for whether or not it works is this: 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 methods things get much more complicated: For it to compile the Subs methods must
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:
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 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. And a shiny spur goes to... 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...
Saddle sore... 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... Tips for the Trail?
Written by Jason Adam and Pauline McNamara
Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
For more information on JavaRanch's Giveaways see Book Promotion Page.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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!
Jose Botella 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