What is SOAP? How about IBM SOAP4J

What is SOAP? How about IBM SOAP4J

Brett McLaughlin Translation by Ilya Chekmenev

SOAP is the Simple Object Access Protocol. If you have never heard of it before, then you must live in the middle of nowhere, far from civilization. It has become the latest fashion in web programming, and an integral part of web services, which are used with such fanaticism in web developments of the latest generation. If you've heard of Microsoft's .NET, or peer-to-peer "revolution," then you've heard of technologies that rely on SOAP (even if you don't know what it is). There is not one, but two SOAP implementations from Apache and Microsoft, which have thousands of pages dedicated to them on their MSDN support site (http://msdn.microsoft.com/).

In this article I will tell you what SOAP is and why it is such an important part in the development of the web programming paradigm. This will help you skip the fundamentals and get straight into working with the SOAP toolkit. Then I'll give a quick overview of existing SOAP projects and dive into Apache's implementation. This article is not intended to provide a complete picture of SOAP; my book, Java & XML, 2nd Edition, fills in many of the gaps. You will find answers to many of the questions that arose after reading this article in the book.

Introduction

First you need to understand what SOAP is. You can read the full (and quite lengthy) W3C opinion at http://www.w3.org/TR/SOAP. Then, having figured it out and discarded all the husk, you will understand that SOAP is just a protocol. It is a simple protocol (no need to write a new one to use it) based on the idea that at some point in a distributed architecture there is a need to exchange information. In addition, for systems in which there is a possibility of overloads and difficulties in processing processes, this protocol is very advantageous in that it is lightweight and requires a minimum amount of resources. Finally, it allows all operations to be carried out over HTTP, which makes it possible to bypass such tricky things as firewalls and protect yourself from listening using sockets on an incredible number of ports. The main thing is that you realize this, and everything else is details.

Of course, you would like to know these details, and I will not ignore them. There are three basic components to the SOAP specification: a SOAP envelope, a set of encryption rules, and a means of interaction between the request and response. Let's think of a SOAP message as a regular letter. Do you still remember those ancient things in envelopes with a postage stamp and the address written on the front? This analogy will help you understand the concept of SOAP as an "envelope" more clearly. Figure 12-1 depicts SOAP processes in the form of this analogy.

Figure 12-1. SOAP Message Process

Keep this picture in mind and let's look at the three components of the SOAP specification. I'll talk briefly about each of them, giving examples that best represent the concept. These three key components make SOAP so important and meaningful. Error handling, support for various encryptions, parameter serialization, and the fact that SOAP works over HTTP in most cases make it more attractive than other solutions for distributed protocols. SOAP provides a high degree of interoperability with other applications, which I covered in more detail in my book. For now, I want to focus on the core elements of SOAP.

Envelope

A SOAP envelope is similar to a regular letter envelope. It contains information about the message that will be encrypted in the main SOAP section, including information about the recipient and sender, as well as information about the message itself. For example, the SOAP envelope header may indicate how the message should be processed. Before an application begins processing a message, it examines information about the message, including whether it can process the message at all. Unlike the situation with standard XML-RPC calls (remember? XML-RPC messages, encryption, etc., everything is combined into a single XML fragment), with SOAP the ongoing processing occurs in order to learn something about the message. A typical SOAP message may also include an encryption style that will assist the recipient in processing the message. Example 12-1 shows a SOAP envelope that ends with an encoding specification.

Example 12-1: SOAP Envelope

Soapbox http://www-106.ibm.com/developerworks/library/x-soapbx1.html

As you can see, the encryption is set inside the envelope, which allows the application to determine (using the attribute value encodingStyle), whether it can read the incoming message located in the element Body. Make sure the SOAP envelope namespace is correct, or the SOAP servers that receive your message will report a version mismatch error and you will not be able to communicate with them.

Encryption

The second important element of SOAP is the ability to encrypt custom data types. With RPC (and XML-RPC), encryption can only be performed on predefined data types that are supported in the XML-RPC toolkit you downloaded. Encrypting other types of data requires you to modify the RPC server and client yourself. With SOAP, an XML schema can be used quite easily to specify new data types (using the structure complexType, discussed in Chapter 2 of my book), and these new types can be represented in XML as part of the main section of SOAP. Thanks to XML Schema integration, you can encrypt any type of data in a SOAP message by logically describing it in an XML Schema.

Call

The best way to understand how a SOAP call works is to compare it to something you're familiar with, such as XML-RPC. If you recall, the XML-RPC call looks similar to the code snippet presented in Example 12-2.

Example 12-2. Call to XML-RPC

// Specifying the XML processor (parser) to use XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser"); // Specifying the server to which the connection is made XmlRpcClient client = new XmlRpcClient("http://rpc.middleearth.com"); // Creating parameters Vector params = new Vector(); params.addElement(flightNumber); params.addElement(numSeats); params.addElement(creditCardType); params.addElement(creditCardNum); // Request Boolean boughtTickets = (Boolean)client.execute("ticketCounter.buyTickets", params); // Process the response

I created a simple program for ordering airline tickets. Now take a look at Example 12-3, which demonstrates a SOAP call.

Example 12-3. Call to SOAP

// Creating parameters Vector params = new Vector(); params.addElement(new Parameter("flightNumber", Integer.class, flightNumber, null)); params.addElement(new Parameter("numSeats", Integer.class, numSeats, null)); params.addElement(new Parameter("creditCardType", String.class, creditCardType, null)); params.addElement(new Parameter("creditCardNumber", Long.class, creditCardNum, null)); // Creating a Call object Call call = new Call(); call.setTargetObjectURI("urn:xmltoday-airline-tickets"); call.setMethodName("buyTickets"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); call.setParams(params); // Call Response res = call.invoke(new URL("http://rpc.middleearth.com"), ""); // Process the response

As you can see, the actual call represented by the object Call, memory resident. It allows you to specify the call target, call method, encryption style, parameters, and many other parameters not presented in this example. This is a more flexible mechanism than the XML-RPC method, allowing you to explicitly specify a set of different parameters that are implicitly defined in XML-RPC. Later in this article, you'll learn more about the call process, including how SOAP handles invalid requests, the error hierarchy, and, of course, the call results returned.

After such a brief introduction, you already know enough to be interested in this funny thing. Now let me introduce you to the SOAP implementation I'm going to use. I will explain the reasons why I chose it and look at some code examples.

Settings

Now that you've learned the basics of the concept, it's time for the fun part: programming. To do this, you will need a convenient project or product, which is easier to find than it might seem at first glance. If you need a Java project that provides SOAP capabilities, you don't have to look long to find one. There are two groups of products: commercial and free. As in my book, I will avoid mentioning commercial products. This is not at all because they are bad (on the contrary, some of them are excellent), but because I would like any reader to be able to try any of the examples given. This is due to accessibility, which many commercial products do not have. You must pay to use them, or temporarily use them for a limited period of time after downloading.

Thus, we smoothly approached open source projects. From this area I can name only one product: Apache SOAP. It is located at http://xml.apache.org/soap and provides SOAP toolkit for Java. At the time of writing, version 2.2 was released, which you can download from the Apache website. It is this version that I will use in the examples for this article.

Other Alternatives

Before moving on to installing and configuring Apache SOAP, I will answer a few questions that might have crept into your mind. I think I have explained quite clearly the reasons why I do not use commercial products. However, you may be thinking of some other open source or related projects that you might like to use, and you're surprised I haven't commented on them.

What about IBM SOAP4J?

First on the list of alternatives is an implementation from IBM: SOAP4J. IBM's work formed the basis of the Apache SOAP project, just as IBM's XML4J grew into what is now known as the Apache Xerces XML parser project. It is assumed that the IBM implementation will be redesigned, merging with Apache SOAP. Much the same thing happened with IBM's XML4J: now it only provides packaging in Xerces. This only highlights the trends - large manufacturers often support and use OpenSource projects, in this case both projects (Apache and IBM) use the same code base .

Is Microsoft out of the game?

Of course no. Microsoft and its SOAP implementation, as well as the entire .NET movement (discussed in more detail in my book), are quite significant. I really wanted to spend most of my time looking at Microsoft's SOAP implementation in detail, but it only supports COM objects and it doesn't support Java. For these reasons, such a description could not be included in an article about Java and XML. However, Microsoft (despite all the complaints we, as developers, have about this company) has done important work in the field of web services, and you will make a mistake if you dismiss it without thinking, guided only by raw emotions. If you have a need to work with COM or Visual Basic components, I highly recommend that you try using the Microsoft SOAP toolkit, available at http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp ?contentid=28000523 along with many other SOAP resources.

What is Axis?

Those of you who follow Apache activities must have heard of Apache Axis. Axis is a next-generation SOAP toolkit also developed under the Apache XML umbrella. SOAP (a specification, not a specific implementation), which has been developing rapidly and radically lately, is very difficult to follow. Trying to create a version of SOAP that fully meets current requirements as they evolve is also quite challenging. As a result, the current version of Apache SOAP offers a solution limited by its design. Having decided that it was not worth trying to completely redesign the existing tool, the Apache developers began creating a project based on the new code. Thus Axis was born. SOAP's name also changed, first from SOAP to XP and then to XMLP. Then the specification name was dropped from the name of the new SOAP and the name "Axis" was born. But now it looks like the W3C is going back to the name of the SOAP specification (version 1.2 or 2.0), so things may still change and there will be even more confusion!

Think of IBM SOAP4J as an architecture?1 of the SOAP toolkit. What about Apache SOAP (discussed in this article) as an architecture?2. And Axis represents the ?3 architecture, a new generation architecture. This project uses SAX while Apache SOAP is DOM based. In addition, Axis, unlike Apache SOAP, provides a more user-friendly approach to user interaction. After listing these advantages, you might be wondering why I didn't choose Axis as my subject of study. It would just be a little premature. Currently, only version 0.51 of Axis is being prepared for release. This is not a beta yet, or even an alpha version. I'd love to talk about the new Axis features, but you'd have no chance of convincing your management that you can use sub-alpha open source software for your critical system needs. So I decided to focus on something that you are real you can use already Today- Apache SOAP. I think that by the time the final version of Apache Axis is released, I will update this material in the next edition of my book. Until then, let's focus on the solution that is already available.

Installation

There are two possible forms of SOAP installation. The first is to start a SOAP client using the SOAP API to communicate with a server that can accept SOAP messages. The second way is to run a SOAP server that can receive messages from a SOAP client. In this section I have described both procedures.

Client

To use the SOAP client, you need to first download Apache SOAP, available at http://xml.apache.org/dist/soap. I downloaded version 2.2 in binary format (from the subdirectory version-2.2). Then you must unzip the contents of the archive into a directory on your computer. In my case it was the directory javaxml2 (c:\javaxml2 on my Windows computer /javaxml2 on my Mac OS X computer). As a result, the files were unzipped into /javaxml2/soap-2_2. You will also need to download the JavaMail package available from Sun http://java.sun.com/products/javamail/. It will be required to support the SMTP transfer protocol used by Apache SOAP. Then download the Java Beans Activation Framework (JAF), also available from Sun http://java.sun.com/products/beans/glasgow/jaf.html. Based on the assumption that you already have Xerces or another XML parser installed and ready to use.

Note: Make sure your XML parser is JAXP compliant and uses the namespace correctly. Your parser most likely meets these requirements. If you are having problems, it is best to revert to using Xerces.

Note: Use the latest versions of Xerces. Version 1.4 and higher will do. There are a number of bugs with SOAP and Xerces 1.3(.1), so I advise against using this combination.

Unzip the JavaMail and JAF packages and then include their jar files in your classpath as well as the library soap.jar. Each of these jar files must be located either in the root directory of the corresponding program, or in a subdirectory /lib. When finished your variable classpath should look something like this:

$ echo $CLASSPATH /javaxml2/soap-2_2/lib/soap.jar:/javaxml2/lib/xerces.jar: /javaxml2/javamail-1.2/mail.jar:/javaxml2/jaf-1.0.1/activation.jar

For Windows it will look like this:

c:\>echo %CLASSPATH% c:\javaxml2\soap-2_2\lib\soap.jar;c:\javaxml2\lib\xerces.jar; c:\javaxml2\javamail-1.2\mail.jar;c:\javaxml2\jaf-1.0.1\activation.jar

And finally add the directory javaxml2/soap-2_2/ in your classpath to run SOAP examples. I've described the setup for several examples in this chapter.

Server

To create a SOAP-compatible set of server components, you first need a servlet engine. As in previous chapters, I used Apache Tomcat (available at http://jakarta.apache.org/) as an example for this chapter. You will need to add everything the client needs in classpath server. The easiest way to do this is to reset soap.jar, activation.jar And mail.jar, as well as your parser, into your servlet engine's libraries directory. For Tomcat, this is the /lib directory, which contains libraries for automatic loading. If you want to provide support for scripts (which are not discussed in this chapter, but are in the Apache SOAP examples), you need to put bsf.jar(available at http://oss.software.ibm.com/developerworks/projects/bsf) and js.jar(available at http://www.mozilla.org/rhino/) to the same directory.

Note: If you're using Xerces with Tomcat, you'll need to repeat the trick I covered in Chapter 10. Rename parser.jar V z_parser.jar, A jaxp.jar V z_jaxp.jar to make sure that xerces.jar and the included version of JAXP is loaded before any other parser or JAXP implementation.

Then restart your servlet engine, after which you are ready to write SOAP server components.

Router Servlet and Admin Client

Apart from the basic operations, Apache SOAP includes a router servlet as well as an admin client. Even if you don't intend to use them, I recommend that you install them to test that SOAP is installed correctly. This process depends on which servlet engine you are using, so I will limit the installation process to Tomcat. Installation instructions for some other servlet engines can be found at http://xml.apache.org/soap/docs/index.html.

Installation under Tomcat is very simple: just take the file soap.war from directory soap-2_2/webapps and drop it into the directory $TOMCAT_HOME/webapps- and that’s it! To check the installation, enter the address in your browser http://localhost:8080/soap/servlet/rpcrouter. You should receive a response similar to that shown in Figure 12-2.

Figure 12-2. Router RPC Servlet

Although the message appears to be an error message, it indicates that everything is working correctly. You should get the same response if you point your browser to the administrator's client address: http://localhost:8080/soap/servlet/messagerouter.

To finish testing the server and client, make sure you have followed all instructions completely. Then run the following Java class as shown below to support your servlet URL for the RPC router servlet:

C:\>java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter list Deployed Services:

You should get an empty list of services as shown above. If you receive any messages, please review the long list of possible errors available at http://xml.apache.org/soap/docs/trouble/index.html. This is the most comprehensive list of problems you might encounter. If you receive an empty list, this means that the setup is complete and you are ready to start looking at the examples given in this chapter.

Let's get started

There are three main stages in writing any SOAP-based systems. Having listed them, I will briefly discuss each of them:

  • Choosing between SOAP-RPC and SOAP messages;
  • Writing or gaining access to a SOAP service;
  • Writing or accessing a SOAP client.

The first step is to choose whether you will use SOAP for RPC calls (in which a remote procedure is executed on the server), or messages (in which the client simply sends pieces of information to the server). I discuss these processes in detail below. Once you have made this decision, you will need to access or create your own service. Of course, since we're all Java professionals, this chapter covers how to create your own. And finally, you need to write a client for this service, that's all!

RPC or Messaging?

Your first task has nothing to do with programming and is more of a design nature. You need to choose whether you will use the RPC or messages service. We will assume that you are familiar with RPC (for example, by reading one of the chapters of my book). The client executes a remote procedure on the server and then receives a response. In this scenario, SOAP acts as an enhanced XML-RPC system that provides better error handling and transfer of complex data types over the network. You're already familiar with this concept, and since RPC systems are easier to write in SOAP, I'll start with them. This article describes how to create an RPC service, an RPC client, and put the system into action.

Another way of working SOAP is based on message exchange. Instead of performing remote procedures, it is used only for exchanging information. As you can guess, this is a powerful tool that does not require the client to know the individual methods of any server. It also makes modeling of remote systems more isolated by allowing data packets (packets in the figurative sense, not in the network sense) to be sent to other systems. At the same time, other systems do not need to know about the operations that were performed with this data. This style is more complex than RPC programming, so I will not describe it here. You will find it in my book, along with other details of business-to-business interaction. First, get acquainted with SOAP-RPC programming.

Like most design problems, making this decision is up to you. Analyze your application and try to determine why you need to use SOAP. If you have a server and a set of clients that perform specific business functions on demand, then RPC is more suitable for you. In complex systems in which data exchange is more than just performing specific business functions on demand, the use of SOAP messages is much preferable.

RPC service

Now that the formalities are over, it is time to act. As you know, in RPC you will need classes whose methods will be executed remotely.

Code snippets

I'll start by looking at some code snippets for the server. These fragments are classes with methods that are executed for RPC clients. I used code from my book as examples. Instead of using simple classes, I chose a more complex example to demonstrate the capabilities of SOAP as clearly as possible. So, I used the CD class as an example. First we define the element map for each non-standard parameter type. For attribute encodingStyle, at least in Apache SOAP 2.2. you must provide the value http://schemas.xmlsoap.org/soap/encoding/ . This is currently the only supported encoding. You need to specify the namespace for the user-defined type and then the class name prefixed with the namespace for that type. In our case, for these purposes I used a fictitious namespace and a simple prefix " x". Then using the attribute javaType, set the real name of the Java class (for this case - javaxml2.CD). And finally, kuralesil with attributes java2XMLClassName And xml2JavaClassName. With their help, a class is specified that is converted from Java to XML and vice versa. I used the amazingly handy BeanSerializer class, also included with Apache SOAP. If your custom parameter is in JavaBean format, this serializer and deserializer will save you from having to write your own. You need a class with a default constructor (remember, for the CD class I defined a simple, parameterless constructor), and publish all the data of this class using methods setXXX And getXXX. Because class CD perfectly satisfies all these requirements, BeanSerializer works perfect.

Note: What class CD meets requirements BeanSerializer. doesn't matter much. Most classes are easily converted to this format. Therefore, I advise avoiding writing your own serializers and deserializers. This is an extra headache (nothing complicated, but too painstaking) and I recommend that you save your energy and use bean conversion in your custom parameters. In many cases, bean conversions only require a default constructor (no parameters) in your class.

Now let's recreate jar file and reinstall our service:

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter xml/CDCatalogDD.xml

Attention: If you leave your servlet engine running and rehost a service at the same time, you will need to restart the servlet engine to enable the new classes for the SOAP service and rehost the service.

Now all that remains is to modify the client to use new classes and methods. Example 12-10 contains a modified version of the client class CDAdder. Changes made to the previous version are highlighted.

Example 12-10: Updated CDAdder class

package javaxml2; import java.net.URL; import java.util.Vector; import org.apache.soap.Constants; import org.apache.soap.Fault; import org.apache.soap.SOAPException; import org.apache.soap.encoding.SOAPMappingRegistry; import org.apache.soap.encoding.soapenc.BeanSerializer; import org.apache.soap.rpc.Call; import org.apache.soap.rpc.Parameter; import org.apache.soap.rpc.Response; import org.apache.soap.util.xml.QName; public class CDAdder( public void add(URL url, String title, String artist, String label) throws SOAPException ( System.out.println("Adding a CD with the title "" + title + "" artist "" + artist + "" studio " + label); CD cd = new CD(title, artist, label); // Create a call object Call Call call = new Call(); call.setSOAPMappingRegistry(registry); call.setTargetObjectURI("urn:cd-catalog"); call.setMethodName("addCD"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); // Setting parameters Vector params = new Vector(); params.addElement(new Parameter("cd", CD.class, cd, null)); call.setParams(params); // Processing the Invoke call Response response; response = call.invoke(url, ""); if (!response.generatedFault()) ( System.out.println("Add CD completed successfully."); ) else ( Fault fault = response.getFault(); System.out.println(Error: " + fault.getFaultString ()); ) ) public static void main(String args) ( if (args.length != 4) ( System.out.println("Template: java javaxml2.CDAdder " + "\"[CD Title]\" \"[Artist Name]\ " \"[Studio CD]\""); return; ) try ( // URL of the SOAP server to which the connection is made URL url = new URL(args); // Get values ​​for new CD String title = args; String artist = args; String label = args; // Add the CD CDAdder adder = new CDAdder(); adder.add(url, title, artist, label); ) catch (Exception e) ( e.printStackTrace(); ) ) )

The only really interesting change is in class mapping CD:

// Map this type so it can be used with SOAP SOAPMappingRegistry registry = new SOAPMappingRegistry(); BeanSerializer serializer = new BeanSerializer(); registry.mapTypes(Constants.NS_URI_SOAP_ENC, new QName("urn:cd-catalog-demo", "cd"), CD.class, serializer, serializer);

This is how a user parameter can be encoded and transmitted over the network. I have already told you how the class BeanSerializer can be used to process parameters in JavaBean format, such as class CD. I used a placement descriptor to indicate these to the server, although now I need to tell the client to use this serializer and deserializer. This function is performed by the class SOAPMappingRegistry. Method mapTypes() takes the encrypted string (again, it is better to use a constant for this NS_URI_SOAP_ENC), and information about the parameter type for which special serialization should be used. The QName is specified first. This is why the strange namespace was used in the hosting descriptor. You need to provide the same URN here, as well as the element's local name (for this example "CD"), then the Java object Class class that will be serialized ( CD.class) and finally an instance of the class for serialization and deserialization. For this example, both cases will involve an instance BeanSerializer. Once all these settings have been entered into the registry, notify the object Call using the method setSOAPMapping-Registry().

You can run this class as shown earlier, adding a CD, and everything should work as expected:

C:\javaxml2\build>java javaxml2.CDAdder http://localhost:8080/soap/servlet/rpcrouter "Tony Rice" "Manzanita" "Sugar Hill" Adding a CD titled "Tony Rice" by Sugar Hill's "Manzanita" Successfully adding a CD.

I left the class modification CDLister for you. Everything is produced according to the same template. To test yourself, you can refer to the example files for my book, which already contain these updated classes.

Note: You can decide that because the class CDLister does not directly interact with the object CD(returned by method list() the type matters Hashtable), then you don't need to make any changes. However, the returned class Hashtable contains object instances CD. If SOAP doesn't know how to deserialize them, the client will throw an error. In this case, to solve the problem you must specify in the object Call copy SOAPMappingRegistry.

Efficient error handling

Now that you've seen custom objects and made RPC calls and such, let me talk about a less exciting topic: error handling. With any network transaction, many failures can occur. The service does not start, there is an error in the server, an object cannot be found, classes are missing and many other problems. So far I have simply used the method fault.getString() to generate error messages. But this method may not always be useful. To see it in action, uncomment the constructor CDCatalog:

public CDCatalog() ( //catalog = new Hashtable(); // Create a directory addCD(new CD("Nickel Creek", "Nickel Creek", "Sugar Hill")); addCD(new CD("Let it Fall", "Sean Watkins", "Sugar Hill")); addCD(new CD("Aerial Boundaries", "Michael Hedges", "Windham Hill")); addCD(new CD("Taproot", "Michael Hedges", "Windham Hill")); )

Recompile it, restart the servlet engine and rehost it. This will result in an exception NullPointerException when the class constructor tries to add CD to uninitialized Hashtable. When starting the client, an error message will appear, but it will not be very informative:

(gandalf)/javaxml2/build$ java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcrouter View the current CD directory. Error: Unable to resolve target object: null

This is not at all the information that can help in identifying and correcting an error. Nevertheless, the framework copes with error handling properly. Do you remember DOMFaultListener, which you specified as the value of the element faultListener? The time has come for him to enter the game. Object returned in case of error Fault contains the DOM (Document Object Model) org.w3c.dom.Element with detailed information about the error. First add an import expression to your source code java.util.Iterator:

import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import org.apache.soap.Constants; import org.apache.soap.Fault; import org.apache.soap.SOAPException; import org.apache.soap.encoding.SOAPMappingRegistry; import org.apache.soap.encoding.soapenc.BeanSerializer; import org.apache.soap.rpc.Call; import org.apache.soap.rpc.Parameter; import org.apache.soap.rpc.Response; import org.apache.soap.util.xml.QName;

Now let's make changes to handle errors in the list() method:

if (!response.generatedFault()) ( Parameter returnValue = response.getReturnValue(); Hashtable catalog = (Hashtable)returnValue.getValue(); Enumeration e = catalog.keys(); while (e.hasMoreElements()) ( String title = (String)e.nextElement(); CD cd = (CD)catalog.get(title); System.out.println(" "" + cd.getTitle() + "" artist " + cd.getArtist() + " studios " + cd.getLabel()); ) ) else ( Fault fault = response.getFault(); System.out.println("Error: " + fault.getFaultString()); Vector entries = fault.getDetailEntries(); for (Iterator i = entries.iterator(); i.hasNext();) ( org.w3c.dom.Element entry = (org.w3c.dom.Element)i.next(); System.out.println(entry .getFirstChild().getNodeValue()); ) )

Using method getDetailEntries() you get access to the SOAP service and the raw data server supporting the problem. The code re-processes them (usually there is only one element, but it requires close attention) and intercepts the DOM Element, contained in each entry. Essentially, here's the XML you're working with:

SOAP-ENV:Server.BadTargetObjectURI Cannot resolve target: null This is what we want!

In other words, the Fault object gives you access to the part of the SOAP envelope that contains errors. In addition, Apache SOAP provides a Java stack trace when errors occur, providing the detailed information needed to correct them. Intercepting an element stackTrace and printing out the node value Text from this element your client can print the server stack trace. By compiling these changes and restarting the client you will get the following result:

C:\javaxml2\build>java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcr outer View the current CD directory. Error: Cannot resolve target: null java.lang.NullPointerException in javaxml2.CDCatalog.addCD(CDCatalog.java:24) in javaxml2.CDCatalog. (CDCatalog.java:14) in java.lang.Class.newInstance0(Native Method) in java.lang.Class.newInstance(Class.java:237)

It's not much better, but at least you can see some tidbits of information that an exception occurred NullPointerException and even find out the line numbers in the server classes in which this problem occurred. The result of these recent changes has given you a clear picture of the error handling problem. You should now check your server classes for errors. Yes, I almost forgot, before that don’t forget to change your class back CDCatalog to get rid of the errors we intentionally introduced for clarity!

  1. There is a lot of talk about running SOAP over other protocols such as SMTP (or even Jabber). The SOAP standard does not currently provide this, but similar capabilities may be added in the future. Therefore, do not be surprised if you encounter active discussions on this topic.
  • Tutorial

Hi all!
It so happened that recently I began to develop web services. But today the topic is not about me, but about how we can write our own XML Web Service based on the SOAP 1.2 protocol.

I hope that after reading the topic you will be able to:

  • write your own server implementation of a web application;
  • write your own client implementation of a web application;
  • write your own web service description (WSDL);
  • send the client arrays of the same type of data to the server.
As you might have guessed, all the magic will be done using PHP and the built-in SoapClient and SoapServer classes. Our rabbit will be a service for sending SMS messages.

1 Problem statement

1.1 Boundaries

At the beginning, I propose to deal with the result that we will achieve at the end of the topic. As announced above, we will write a service for sending SMS messages, and more precisely, we will receive messages from different sources via the SOAP protocol. After which, we will consider in what form they come to the server. The process of queuing messages for further sending to the provider, unfortunately, is beyond the scope of this post for many reasons.

1.2 What data will we change?

Great, we have decided on the boundaries! The next step that needs to be taken is to decide what data we will exchange between the server and the client. On this topic, I suggest not to split hairs for too long and immediately answer the main questions for yourself:
  • What minimum data must be sent to the server in order to send an SMS message to a subscriber?
  • What minimum data must be sent from the server to satisfy the client's needs?
Something tells me that for this you need to send the following:
  • mobile phone number and
  • text of the SMS message.
In principle, these two characteristics are enough to send, but I immediately imagine the case of an SMS with birthday greetings coming to you at 3 o’clock in the morning, or 4! At this moment, I will be very grateful to everyone for not forgetting about me! Therefore, we will also send to the server and
  • date of sending the SMS message.
The next thing I would like to send to the server is:
  • Message type.
This parameter is not mandatory, but it can be very useful to us if we quickly need to tell the boss how many of our clients we have “delighted” with our news, and also draw some beautiful statistics on this matter.

And yet, I forgot something! If we reflect a little more, it is worth noting that the client can send either one SMS message or a number of them to the server at a time. In other words, one data packet can contain from one to infinity messages.

As a result, we get that to send an SMS message we need the following data:

  • Mobile phone number,
  • SMS message text,
  • time of sending the SMS message to the subscriber,
  • message type.

We have answered the first question, now we need to answer the second question. And perhaps I’ll allow myself to mess around a little. Therefore, from the server we will send only Boolean data, the meaning of which has the following meaning:

  • TRUE – the packet successfully reached the server, passed authentication and queued for sending to the SMS provider
  • FALSE – in all other cases

This concludes the description of the problem statement! And finally, let's get down to the fun part - let's figure out what kind of strange beast this SOAP is!

2 What is SOAP?

In general, initially I did not plan to write anything about what SOAP is and wanted to limit myself to links to the w3.org website with the necessary specifications, as well as links to Wikipedia. But at the very end I decided to write a short note about this protocol.

And I will begin my story with the fact that this data exchange protocol belongs to a subset of protocols based on the so-called RPC (Remote Procedure Call) paradigm, the antipode of which is REST (Representational State Transfer). You can read more about this on Wikipedia; links to articles are at the very end of the topic. From these articles, we need to understand the following: “The RPC approach allows the use of a small number of network resources with a large number of methods and a complex protocol. With the REST approach, the number of methods and protocol complexity are strictly limited, which means the number of individual resources can be large.” That is, in relation to us, this means that in the case of the RPC approach on the site there will always be one input (link) to the service and what procedure to call to process incoming data we transfer along with the data, while with the REST approach in our The site has many inputs (links), each of which accepts and processes only certain data. If anyone reading knows how to explain the difference in these approaches even more simply, be sure to write in the comments!

The next thing we need to know about SOAP is that this protocol uses the same XML as a transport, which on the one hand is very good, because Our arsenal immediately includes the full power of a stack of technologies based on this markup language, namely XML-Schema - a language for describing the structure of an XML document (thanks Wikipedia!), which allows for automatic validation of data received by the server from clients.

And so, now we know that SOAP is a protocol used to implement remote procedure calls and it uses XML as a transport! If you read the article on Wikipedia, you can also learn from there that it can be used over any application-level protocol, and not just in combination with HTTP (unfortunately, in this topic we will only consider SOAP over HTTP). And you know what I like most about all this? If there are no guesses, then I’ll give a hint - SOAP!... Still no guesses?... Are you sure you read the article on Wikipedia?... In general, I won’t torture you further. Therefore, I’ll go straight to the answer: “SOAP (from the English Simple Object Access Protocol - simple protocol access to objects; up to specification 1.2)". The most remarkable thing about this line is in italics! I don’t know what conclusions you drew from all this, but I see the following - since this protocol cannot in any way be called “simple” (and apparently even w3 agrees with this), then from version 1.2 it stopped being decrypted somehow! And it became known as SOAP, just SOAP, period.

Well, okay, please excuse me, I got a little sidetracked. As I wrote earlier, XML is used as transport, and the packets that travel between the client and server are called SOAP envelopes. If you consider the general structure of the envelope, it will seem very familiar to you, because... resembles the structure of an HTML page. It has a main section - Envelop, which includes sections Header And Body, or Fault. IN Body data is transmitted and it is a mandatory section of the envelope, while Header is optional. IN Header authorization or any other data that is not directly related to the input data of the web service procedures may be transmitted. About Fault there is nothing special to tell, except that it comes to the client from the server in case of any errors.

This is where my review story about the SOAP protocol ends (we will look at the envelopes themselves and their structure in more detail when our client and server finally learn to run them at each other) and a new one begins - about the SOAP companion called WSDL(Web Services Description Language). Yes, yes, this is the very thing that scares most of us away from even trying to implement our API on this protocol. As a result, we usually reinvent our wheel with JSON as transport. So what is WSDL? WSDL is a language for describing web services and accessing them, based on the XML language (c) Wikipedia. If this definition does not make clear to you the entire sacred meaning of this technology, then I will try to describe it in my own words!

WSDL is designed to allow our clients to communicate normally with the server. To do this, the file with the extension “*.wsdl” describes the following information:

  • What namespaces were used?
  • What data schemas were used?
  • What types of messages does the web service expect from clients?
  • Which data belongs to which web service procedures,
  • What procedures does the web service contain?
  • How should the client call the web service procedures,
  • To which address should customer calls be sent?
As you can see, this file is the entire web service. By specifying the address of the WSDL file in the client, we will know everything about any web service! As a result, we do not need to know absolutely nothing about where the web service itself is located. All you need to know is the location of its WSDL file! We will soon find out that SOAP is not as scary as Russian proverbs make it out to be.

3 Introduction to XML-Schema

Now we know a lot about what SOAP is, what is inside it, and have an overview of the technology stack that surrounds it. Since, first of all, SOAP is a method of interaction between a client and a server, and XML markup language is used as a transport for it, in this section we will understand a little about how automatic data validation occurs using XML schemas.

The main task of the diagram is to describe the structure of the data that we are going to process. All data in XML schemas is divided into simple(scalar) and complex(structures) types. Simple types include the following types:

  • line,
  • number,
  • boolean value,
  • date of.
Something very simple that has no extensions inside. Their antipode is complex complex types. The simplest example of a complex type that comes to everyone’s mind is objects. For example, a book. The book consists of properties: author, Name, price, ISBN number etc. And these properties, in turn, can be either simple types or complex ones. And the task of the XML schema is to describe this.

I suggest not going far and writing an XML schema for our SMS message! Below is the xml description of the SMS message:

71239876543 Test message 2013-07-20T12:00:00 12
Our complex type diagram will look like this:


This entry reads as follows: We have a variable " message" type " Message" and there is a complex type called " Message", which consists of a sequential set of elements " phone" type string, « text" type string, « date" type dateTime, « type" type decimal. These types are simple and are already defined in the schema description. Congratulations! We just wrote our first XML Schema!

I think that the meaning of the elements " element" And " complexType"Everything has become more or less clear to you, so we won’t focus on them any more and let’s switch straight to the composer element" sequence" When we use the composer element " sequence“We inform you that the elements included in it must always be located in the sequence specified in the diagram, and all of them are mandatory. But don't despair! There are two more composer elements in XML schemas: " choice" And " all" Composer " choice" announces that there must be one of the elements listed in it, and the composer " all» – any combination of the listed elements.

As you remember, in the first section of the topic we agreed that from one to infinity SMS messages can be transmitted in a package. Therefore, I propose to understand how such data is declared in the XML schema. The general package structure might look like this:

71239876543 Test message 1 2013-07-20T12:00:00 12 71239876543 Test message N 2013-07-20T12:00:00 12
The diagram for such a complex type will look like this:


The first block contains the familiar declaration of the complex type “ Message" If you noticed, then in each simple type included in " Message", new clarifying attributes have been added " minOccurs" And " maxOccurs" As you might guess from the name, the first ( minOccurs) indicates that this sequence must contain at least one element of type " phone», « text», « date" And " type", while the next one ( maxOccurs) attribute declares to us that there is at most one such element in our sequence. As a result, when we write our own schemas for any data, we are given the widest choice in how to configure them!

The second block of the diagram declares the element " messageList" type " MessageList" It's clear that " MessageList" is a complex type that contains at least one element " message", but the maximum number of such elements is not limited!

4 Write your WSDL

Do you remember that WSDL is our web service? I hope you remember! As we write it, our little web service will run on it. Therefore, I suggest not to mess around.

In general, in order for everything to work correctly for us, we need to transfer a WSDL file with the correct MIME type to the client. To do this, you need to configure your web server accordingly, namely, set the MIME type for files with the “*.wsdl” extension to the following line:

Application/wsdl+xml
But in practice, I usually sent the HTTP header via PHP " text/xml»:

Header("Content-Type: text/xml; charset=utf-8");
and everything worked great!

I want to warn you right away that our simple web service will have a rather impressive description, so don’t be alarmed, because... Most of the text is obligatory water and, having written it once, you can constantly copy it from one web service to another!

Since WSDL is XML, you need to write about this directly in the very first line. The root element of the file should always be named " definitions»:


Typically, WSDL consists of 4-5 main blocks. The very first block is the definition of a web service or, in other words, the entry point.


It says here that we have a service called - “ SmsService" In principle, all the names in the WSDL file can be changed by you to whatever you want, because they play absolutely no role.

After this we announce that in our web service " SmsService" there is an entry point ("port") called " SmsServicePort" It is to this entry point that all requests from clients to the server will be sent. And indicate in the element “ address» link to the handler file that will accept requests.

Once we have defined the web service and specified the entry point for it, we need to bind supported procedures to it:


To do this, it lists which operations and in what form they will be called. Those. for port " SmsServicePort" a binding is defined under the name " SmsServiceBinding", which has a call type " rpc"and HTTP is used as the transmission protocol. Thus, we indicated here that we will make an RPC call over HTTP. After this we describe which procedures ( operation) are supported in the web service. We will support only one procedure – “ sendSms" Through this procedure our wonderful messages will be sent to the server! After the procedure has been declared, it is necessary to indicate in what form the data will be transmitted. In this case, it is indicated that standard SOAP envelopes will be used.

After that, we need to bind the procedure to messages:


To do this, we specify that our binding is of type " SmsServicePortType" and in the element " portType"with the name of the same type, we indicate the binding of procedures to messages. And so, the incoming message (from client to server) will be called “ sendSmsRequest", and outgoing (from server to client) " sendSmsResponse" Like all names in WSDL, the names of incoming and outgoing messages are arbitrary.

Now we need to describe the messages themselves, i.e. incoming and outgoing:


To do this we add the elements " message" with names " sendSmsRequest" And " sendSmsResponse" respectively. In them we indicate that the input should be an envelope whose structure corresponds to the data type " Request" After which an envelope is returned from the server containing the data type - “ Response».

Now we need to do just a little - add a description of these types to our WSDL file! And how do you think the WSDL describes incoming and outgoing data? I think that you have already understood everything a long time ago and told yourself that using XML schemas! And you will be absolutely right!


You can congratulate us! Our first WSDL has been written! And we are one step closer to achieving our goal.
Next, we'll look at what PHP provides us with for developing our own distributed applications.

5 Our first SOAP server

Earlier I wrote that to create a SOAP server in PHP we will use the built-in SoapServer class. In order for all further actions to happen the same way as for me, you will need to tweak your PHP a little. To be even more precise, you need to make sure that you have the “php-soap” extension installed. It is best to read how to install it on your web server on the official PHP website (see the list of references).

After everything has been installed and configured, we will need to create a file in the root folder of your hosting “ smsservice.php» with the following content:

setClass("SoapSmsGateWay"); //Start the server $server->handle();
I hope there is no need to explain what is above the line with the “ini_set” function. Because there it is determined which HTTP headers we will send from the server to the client and the environment is configured. In the line with “ini_set” we disable caching of the WSDL file so that our changes in it immediately take effect on the client.

Now we come to the server! As you can see, the entire SOAP server takes only three lines! On the first line, we create a new instance of the SoapServer object and pass the address of our WSDL description of the web service to its constructor. Now we know that it will be located in the root of the hosting in a file with the self-explanatory name “ smsservice.wsdl.php" In the second line, we tell the SOAP server which class needs to be pulled in order to process the envelope received from the client and return the envelope with the response. As you might have guessed, it is in this class that our only method will be described sendSms. On the third line we start the server! That's it, our server is ready! With which I congratulate us all!

Now we need to create the WSDL file. To do this, you can either simply copy its contents from the previous section, or take liberties and “template” it a little:

"; ?> /" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http:// schemas.xmlsoap.org/wsdl/http/" name="SmsWsdl" xmlns="http://schemas.xmlsoap.org/wsdl/"> /"> /smsservice.php" />
At this stage, we should be completely satisfied with the resulting server, because We can log the envelopes coming to it and then calmly analyze the incoming data. In order for us to receive anything on the server, we need a client. So let's get to it!

6 SOAP client on the way

First of all, we need to create a file in which we will write the client. As usual, we will create it in the root of the host and call it " client.php", and inside we will write the following:

messageList = new MessageList(); $req->messageList->message = new Message(); $req->messageList->message->phone = "79871234567"; $req->messageList->message->text = "Test message 1"; $req->messageList->message->date = "2013-07-21T15:00:00.26"; $req->messageList->message->type = 15; $client = new SoapClient("http://($_SERVER["HTTP_HOST"])/smsservice.wsdl.php", array("soap_version" => SOAP_1_2)); var_dump($client->sendSms($req));
Let's describe our objects. When we wrote the WSDL, it described three entities for the envelope incoming to the server: Request, MessageList And Message. Accordingly classes Request, MessageList And Message are reflections of these entities in our PHP script.

Once we have defined the objects, we need to create an object ( $req), which we will send to the server. After which come the two most cherished lines for us! Our SOAP client! Believe it or not, this is enough for our server to start receiving messages from the client, as well as for our server to successfully receive and process them! In the first of them, we create an instance of the SoapClient class and pass the address of the location of the WSDL file to its constructor, and in the parameters we explicitly indicate that we will work using the SOAP protocol version 1.2. On the next line we call the method sendSms object $client and immediately display the result in the browser.
Let's run it and see what we finally got!

The following object was returned to me from the server:

Object(stdClass) public "status" => boolean true
And this is great, because... Now we know for sure that our server is working and not only works, but can also return some values ​​to the client!

Now let's look at the log that we prudently keep on the server side! In its first part we see the raw data that arrived on the server:

79871234567 Test message 1 2013-07-21T15:00:00.26 15
This is the envelope. Now you know what it looks like! But it’s unlikely that we’ll be interested in looking at it all the time, so let’s deserialize the object from the log file and see if everything is fine:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1 " (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public "type" => string "15" (length=2)
As you can see, the object was deserialized correctly, for which I want to congratulate us all! Something more interesting awaits us next! Namely, we will send the client to the server not just one SMS message, but a whole pack (to be more precise, three)!

7 Sending complex objects

Let's think about how we can transfer a whole bunch of messages to the server in one packet? Probably the easiest way would be to organize an array inside the messageList element! Let's do this:

// create an object to send to the server $req = new Request(); $req->messageList = new MessageList(); $msg1 = new Message(); $msg1->phone = "79871234567"; $msg1->text = "Test message 1"; $msg1->date = "2013-07-21T15:00:00.26"; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = "79871234567"; $msg2->text = "Test message 2"; $msg2->date = "2014-08-22T16:01:10"; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = "79871234567"; $msg3->text = "Test message 3"; $msg3->date = "2014-08-22T16:01:10"; $msg3->type = 17; $req->messageList->message = $msg1; $req->messageList->message = $msg2; $req->messageList->message = $msg3;
Our logs indicate that the following packet was received from the client:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17
What nonsense, you say? And you will be right in a sense, because... As soon as we learned that an object left the client, it came to our server in absolutely the same form in the form of an envelope. True, SMS messages were not serialized in XML in the way we needed - they had to be wrapped in elements message, not in Struct. Now let's see in what form such an object comes to the method sendSms:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "Struct" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => string "17" (length=2)
What does this knowledge give us? Only that the path we chose is not correct and we did not receive an answer to the question - “How can we get the correct data structure on the server?” But I suggest not to despair and try to convert our array to the type an object:

$req->messageList->message = (object)$req->messageList->message;
In this case, we will receive another envelope:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17
Came into the method sendSms the object has the following structure:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => string "17" (length=2)
As for me, “the sum does not change from changing the places of the terms” (c). What BOGUS, What Struct– we have not yet achieved our goal! And to achieve it, we need to make sure that instead of these incomprehensible names our native one is displayed message. But the author does not yet know how to achieve this. Therefore, the only thing we can do is get rid of the extra container. In other words, we will now make sure that instead of message became BOGUS! To do this, change the object as follows:

// create an object to send to the server $req = new Request(); $msg1 = new Message(); $msg1->phone = "79871234567"; $msg1->text = "Test message 1"; $msg1->date = "2013-07-21T15:00:00.26"; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = "79871234567"; $msg2->text = "Test message 2"; $msg2->date = "2014-08-22T16:01:10"; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = "79871234567"; $msg3->text = "Test message 3"; $msg3->date = "2014-08-22T16:01:10"; $msg3->type = 17; $req->messageList = $msg1; $req->messageList = $msg2; $req->messageList = $msg3; $req->messageList = (object)$req->messageList;
What if we get lucky and the correct name comes up from the diagram? To do this, let's look at the envelope that arrived:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17
Yes, a miracle did not happen! BOGUS– we won’t win! Came to sendSms the object in this case will look like this:

Object(stdClass) public "messageList" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public " text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public "type" => string "15" (length =2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length=37) public "date" => string " 2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone" => string "79871234567" (length= 11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string " 17" (length=2)
As they say – “Almost”! On this (slightly sad) note, I propose to slowly wrap things up and draw some conclusions for ourselves.

8 Conclusion

Finally we got here! Let's figure out what you can do now:
  • you can write the WSDL file necessary for your web service;
  • you can easily write your own client that can communicate with the server via SOAP;
  • you can write your own server that communicates with the outside world via SOAP;
  • you can send arrays of the same type of objects to the server from your client (with some restrictions).
We also made some discoveries during our little research:
  • the native SoapClient class does not correctly serialize data structures of the same type in XML;
  • when serializing an array to XML it creates an extra element called Struct;
  • when serializing an object to XML it creates an extra element called BOGUS;
  • BOGUS less evil than Struct due to the fact that the envelope is more compact (extra namespaces are not added to the XML header of the envelope);
  • Unfortunately, the SoapServer class does not automatically validate the envelope data with our XML schema (perhaps other servers do not do this either).

I will not dwell on the question of what it is web services and why they are needed. There are a lot of articles on this topic on the Internet. I’ll just try to briefly show how simple it is to create a client for any web service in PHP.

Settings

For use SOAP in php you need to connect the SOAP module (included in the php5 distribution). Under Windows, this is done simply - you need to add (namely add, since this line is not just commented out there, it is missing altogether) in php.ini:
extension=php_soap.dll

Don't forget to restart the server if you have php installed as a module.


Creating a SOAP client from a WSDL document

Creating a SOAP client usually occurs by WSDL document, which is an XML document in a specific format that fully describes a particular web service. For details about WSDL, I refer you to the W3C consortium website - http://www.w3.org/TR/2005/WD-wsdl20-soap11-binding-20050510/.

The main thing you need to know in order to build a client for a web service is to know the URL of its WSDL document.
For example, let's take the "Currency Exchange Rate" web service from xmethods.com. The address of this web service, which allows you to receive exchange rates online, is http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl.

The second important point is that from the description of the web service it is necessary to obtain information about what methods this service provides, and what parameters we should pass to it as input values ​​(very similar to calling a regular PHP function or class method). Typically this information is contained in the description of the service on its website. Our web service for obtaining exchange rates provides the getRate() method, to which currency codes are passed as arguments.

And lastly, it is important to know what to expect as an answer: how many values, what type, etc. This can also be obtained from the description.
And as a result, the code turns out to be very simple and compact, almost elementary:

// Using the Web service
// "Currency Exchange Rate" from xmethods.com

// Creating a SOAP client from a WSDL document
$client = new SoapClient("http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl");

// Send a SOAP request and receive the result
$result = $client->getRate("us", "russia");

Echo 'Current dollar exchange rate: ', $result, ' rubles';
?>

As you can see from the code, you need to pass the URL of the WSDL document to the SoapClient class constructor and receive an object to work with the desired web service. Then a method of this object is called, the name of which is the same as the name of the web service method itself. This method returns the result we want.

So, this simple example illustrates the principle of building a SOAP client for web services in PHP. However, in a real application there is still a lot to take care of, in particular the fact that when the web service is called, it may be temporarily unavailable or return an error. It clearly suggests using a block try/catch/throw :)

Here at LeaseWeb, we work a lot with SOAP web-services to integrate our internal applications with each other. Especially during development and testing of our applications as we need the ability to practice with SOAP API’s.

$ curl -sS http://leaseweb.github.io/php-soap-client/installer | php

This will download the phar file to the current working directory and make it executable so you can use start using it right away by invoking:

$ ./soap_client

To install the latest master version you can get the source code directly from GitHub, package your own .phar file and install it - using GNU Make.
In order to be able to create the .phar file you need to have composer installed. To read more about composer refer to their excellent documentation.

# Install php soap client $ git clone https://github.com/LeaseWeb/php-soap-client.git $ cd php-soap-client $ composer.phar install $ make $ sudo make install

If you are getting a Failed to compile phar exception while running make you need to set phar.readonly = Off in your php.ini . On a development machine this is fine to do but please be aware of the security risks when setting phar.readonly to Off .

The above make install command will install the soap_client application to /usr/local/bin and make it executable so you can easily call it like this:

$ soap_client php-soap-client version 2.1.3 Usage: command Options: ... Available commands: call Call the remote service with the `method` specified and output the response to stdout. help Displays help for a command list Lists commands list-methods Get a list of available methods to call on the remote. request Generate an xml formatted SOAP request for the given method and output to stdout. wsdl Get the WSDL of a soap service.

From this point onwards we assume you have installed the soap_client.phar on your system in /usr/local/bin/soap_client and that the directory /urs/local/bin is in your $PATH .

Lets say we would like to see what methods are available on the remote service http://www.webservicex.net/ConvertTemperature.asmx. We could issue the following command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" list-methods

Which will output the following:

ConvertTemp

If you run the above command with the -vvv option you will get more verbose output.
In this case the only available method is ConvertTemp . Let’s see how a SOAP XML request looks like for this method:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp 0

If you want to make a SOAP request to the ConvertTemp method on the remote service use the call sub command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call --editor ConvertTemp

Notice the --editor option after the call sub command. If you use the --editor flag soap_client will open up the editor specified in your environment variable $EDITOR so you are able to modify the request XML before sending it.

If you issue the same request multiple times, you could save a soap request as a local XML file and pass it to /dev/stdin of the soap_client call command:

# Get the request xml and store it locally $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp > my_sample_request.xml # Now edit my_sample_request.xml # Now you can call the ConvertTemp method with this pre-prepared request $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call ConvertTemp< my_sample_request.xml

Since you will be repeating soap_client commands frequently in a short time while exploring a remote web service you can save yourself some time by setting an environment variable SOAPCLIENT_ENDPOINT that contains the URL to the WSDL. When this environment variable is set you can omit the --endpoint command line option. Let’s do this now and call the ConvertTemp method:

$ export SOAPCLIENT_ENDPOINT="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" $ soap_client call ConvertTemp< my_sample_request.xml

I wanted to know how much 107.6 degrees Fahrenheit is in Celsius, so my my_sample_request.xml contains:

$ cat my_sample_request.xml 107.6 degreeFahrenheit degreeCelsius

$ soap_client call ConvertTemp< my_sample_request.xml stdClass Object ( => 42)

The answer is 42.

If you rather see the responses in XML format you can use the --xml command line option:

$ soap_client call --xml ConvertTemp< my_sample_request.xml 42

This tutorial should give you enough information to get started with exploring, testing and/or developing SOAP API's.
In a future blog post, I will continue the topic of the php soap client. We are currently working on packing the .phar archive for the web.

Lyrical part.

Imagine that you have implemented or are implementing a certain system that should be accessible from the outside. Those. there is a certain server with which you need to communicate. For example a web server.

This server can perform many actions, work with the database, perform some third-party requests to other servers, do some calculations, etc. live and possibly develop according to the scenario known to him (i.e. according to the developers’ scenario). It is not interesting for a person to communicate with such a server, because he may not be able/want to provide beautiful pages with pictures and other user-friendly content. It is written and works to work and provide data when asked to it, without worrying that it is human readable, the client will deal with it himself.

Other systems, accessing this server, can already dispose of the data received from this server at their own discretion - process, accumulate, issue to their clients, etc.

Well, one of the options for communicating with such servers is SOAP. SOAP xml message exchange protocol.

Practical part.

A web service (this is the name of what the server provides and what clients use) makes it possible to communicate with the server with clearly structured messages. The fact is that the web service does not accept any data. The web service will respond with an error to any message that does not comply with the rules. The error, by the way, will also be in xml form with a clear structure (which is not true about the text of the message).

WSDL (Web Services Description Language). The rules by which messages are composed for the web service are also described using xml and also have a clear structure. Those. If a web service provides the ability to call a method, it must allow clients to know what parameters are used for this method. If the web service expects a string for Method1 as a parameter and the string should be named Param1, then these rules will be specified in the web service description.

Not only simple types, but also objects and collections of objects can be passed as parameters. The description of an object comes down to a description of each component of the object. If an object consists of several fields, then each field is described, its type, name (what are the possible values). Fields can also be of a complex type, and so on until the description of the types ends with simple ones - string, boolean, number, date... However, some specific types may turn out to be simple, it is important that clients can understand what values ​​they may contain.

For clients, it is enough to know the url of the web service; the wsdl will always be nearby, from which you can get an idea of ​​the methods and their parameters that this web service provides.

What are the advantages of all these bells and whistles:

  • In most systems, the description of methods and types occurs automatically. Those. the programmer on the server just needs to say that this method can be called through a web service, and the wsdl description will be generated automatically.
  • The description, which has a clear structure, is readable by any soap client. Those. whatever the web service, the client will understand what data the web service receives. Using this description, the client can build its own internal structure of object classes, the so-called. binding" and. As a result, the programmer using the web service has to write something like (pseudocode):

    NewUser:=TSoapUser.Create("Vasya","Pupkin","admin"); soap.AddUser(NewUser);

  • Automatic validation.

    • xml validation. xml must be well-formed. Invalid xml - immediately an error to the client, let him sort it out.
    • schema-validation. xml must have a certain structure. xml does not match the schema - immediately an error to the client, let him sort it out.
    • Data verification is carried out by the soap server so that data types and restrictions match the description.
  • Authorization and authentication can be implemented using a separate method. natively. or using http authorization.
  • Web services can work both via the soap protocol and via http, that is, through get requests. That is, if the parameters are simple data (without structure), then you can simply call the usual get www.site.com/users.asmx/GetUser?Name=Vasia or post. However, this is not everywhere and not always.
  • ... see on Wikipedia

There are also a lot of disadvantages:

  • Unreasonably large message size. Well, here the very nature of xml is such that the format is redundant, the more tags, the more useless information. Plus soap adds its redundancy. For intranet systems, the issue of traffic is less acute than for the internet, so soap for local networks is more in demand, in particular Sharepoint has a soap web service with which you can communicate with success (and some limitations).
  • Automatically changing the description of a web service can break all clients. Well, it’s like this for any system, if backward compatibility with old methods is not supported, everything will fall off...
  • Not a minus, but a drawback. All method calls must be atomic. For example, when working with a database, we can start a transaction, execute several queries, then rollback or commit. There are no transactions in soap. One request, one answer, the conversation is over.
  • Dealing with the description of what is on the server side (is everything described correctly?) and what is on the client (what was described to me here?) can be quite difficult. There were several times when I had to deal with the client side and convince the server programmer that his data was described incorrectly, but he couldn’t understand anything about it at all, because automatic generation and he shouldn’t, it’s a matter of software. And the error, naturally, was in the method code; the programmer simply did not see it.
  • Practice shows that web service developers are terribly far from the people who use these web services. In response to any request (valid from the outside), an incomprehensible error “Error 5. Everything is bad” may come. It all depends on the conscience of the developers :)
  • I'm sure I still don't remember something...

As an example, there is an open web service belavia:

  • http://86.57.245.235/TimeTable/Service.asmx - entry point, there is also a text description of methods for third-party developers.
  • http://86.57.245.235/TimeTable/Service.asmx?WSDL - wsdl description of methods and types of received and returned data.
  • http://86.57.245.235/TimeTable/Service.asmx?op=GetAirportsList - description of a specific method with an example of the type of xml request and xml response.

You can manually create and send a request like:

POST /TimeTable/Service.asmx HTTP/1.1 Host: 86.57.245.235 Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://webservices.belavia.by/GetAirportsList" ru

the answer will come:

HTTP/1.1 200 OK Date: Mon, 30 Sep 2013 00:06:44 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 4.0.30319 Cache-Control: private, max -age=0 Content-Type: text/xml; charset=utf-8 Content-Length: 2940

PS Previously, the Aeroflot web service was opened, but after 1C added soap support to 8ku, a bunch of 1C beta testers successfully installed it. Now something has changed there (I don’t know the address, you can look it up if you’re interested).
ZZY Disclaimer. He spoke at the everyday level. You can kick.

views