The previous tutorial describes the general procedure for implementing a service. This section discusses some of the issues you may need to consider to create a production quality server.
The Agent servlet container is multithread-hot. The container creates threads as necessary to handle incoming requests, and multiple threads may enter a servlet at the same time. Objects supplied as parameters to the overridden virtual methods are guaranteed to be active in only a single thread. However, if the servlet maintains state, the servlet must take appropriate precautions. See Section 7.5, "Recovering Session State," in the HydraExpress Servlet Development Guide for details.
Because services created with HydraExpress run in multiple threads, it is recommended that you allocate objects on the heap rather than the stack, a general "best practice" for multithreaded applications. This prevents problems with thread stack size.
To run a secure service, the client must communicate with the service through an HTTPS transport. Any HydraExpress client can use the HTTPS transport supplied with HydraExpress.
To test a secure client against a Web service running in the Agent servlet container, just include in the WSDL or client implementation the path to the default port used for security, 8443, such as:
https://localhost:8443/dayofweek/DayofWeek/
For an example of creating a secure service, see Chapter 4, "Extending Your Web Service."
Using the HTTPS protocol results in a dependence on the OpenSSL third-party library.
HydraExpress handles exceptions through class rwsf::Exception or one of its derived classes, and SOAP faults through either an instance of the generic rwsf::SoapFaultException deriving from rwsf::SoapFault, or an instance of a specially-generated class if the fault was defined in the WSDL.
A WSDL file can define faults to be caught in the server and returned to the client. One or more faults may be defined for each operation defined in the WSDL. HydraExpress creates a class for each unique fault message type defined. Several faults can use the same message format, so messages are the critical definition needed by HydraExpress to create a mechanism for returning faults to the client.
If faults are defined in the WSDL, the server-side implementation code can detect problems and report them back to the client by throwing one of the defined fault messages.
This discussion reflects a traditional request-response operation. For a solicit-response operation, faults would be handled on the client and returned to the server.
For a discussion on exceptions for both the client and server, as well as WSDL-defined faults, see Section 8.3.1, "WSDL-Defined Faults," in the Introduction to HydraExpress.
HydraExpress allows you to implement your service as a standalone server, independent from the Agent servlet container. To take advantage of this feature, use the -standalone option when generating code in order to generate a sample standalone server.
For more information, see Section 23.3, "Standalone Server Implementations."
HydraExpress supports both client-side and server-side logging. Logging is implemented in the sample applications by default.
There are five logging levels:
None
Info
Warning
Error
Fatal
The default logging level is info, resulting in all messages reported.
See Chapter 12, "Web Service Logging," for detail on using or customizing loggers.
The server-side code generated by HydraExpress provides support for session management for the Web service. This session management depends on the session management capabilities of the Agent servlet container, which implements the session support defined in the HTTP protocol. For information on session management in the HydraExpress-generated classes, see Chapter 18.
The name of the sample server implementation generated by HydraExpress is based on the port name defined in the WSDL, with the addition of Imp, as in <portType>Imp. The simplest way to implement the Web service is to use the sample implementation directly, without changing the name of the implementation class or the files.
However, there may be circumstances in which you want to change the name of the class that implements the service.
As an example, let's suppose you wanted to change the name of the service implementation for the DayofWeek example from DayOfWeekPortTypeImp to MyDayofWeekImp. These are the changes you would need to make:
Rename the implementation files
DayOfWeekPortTypeImp.h => MyDayofWeekImp.h
DayOfWeekPortTypeImp.cpp => MyDayofWeekImp.cpp
In the renamed file MyDayofWeekImp.cpp, you would need to:
Change the include for the header:
#include MyDayofWeekImp.h
Change the macro that registers the implementation:
RWSF_DEFINE_MESSAGE_HANDLER(MyDayofWeekImp)
Change the method definition:
MyDayOfWeekImp::getDayOfWeek(...)
In the renamed file MyDayofWeekImp.h, you would need to:
Change the guard at the top of the file:
#ifndef MyDayOfWeekImp_h_
#define MyDayOfWeekImp_h_
Change the class declaration:
class MyDayOfWeekImp : public DayOfWeekPortTypeBase
In the makefile, search on the string DayOfWeekPortTypeImp and change it to MyDayOfWeekImp.
This would also be true of the debug makefile makefile_debug if you are using this, and of occurrences of DayOfWeekPortTypeImp in the MSVC project files if you are using those.
In the named object configuration file objects.xml, change the
naming-class element from DayOfWeekPortService.createDayOfWeekPortTypeImp to DayOfWeekPortService.createMyDayOfWeekImp.
Similar changes would need to be made if your application used notification classes and you wished to change the notification implementation.
In addition, if your application contained generated datatype classes (located in your code generation directory's codegen\data and include\data directories, you would need to:
change both the header and implementation file names to the new name
change any related includes in the renamed file MyDayofWeekImp.cpp
HydraExpress recognizes the wrapped document style. This feature allows you to generate an "unwrapped" interface for the proxy and server. Here are the WSDL requirements:
The input message definition must have a single part.
The part must be an element.
The element must have the same name as the related operation.
The element's complex type must have only a name attribute.
If you generate code using the -wrapped option, and your WSDL meets the above requirements, HydraExpress will "unwrap" the top level element, and treat each of the element's parts as arguments to the operation.
Both input and output elements are unwrapped, effectively removing the "wrapper" or top-level element from the generated interface.
Here is how this style looks in WSDL:
<wsdl:types> <schema ...> <element name="prevDay" type="sns:prevDay"/> //1 <complexType name="prevDay"> <sequence> <element name="Unit" type="xsd:string"/> <element name="identifier" type="xsd:int"/> </sequence> </complexType> </schema> </wsdl:types> ... <wsdl:message name="getBusinessDayRequest"> <wsdl:part name="input" element="sns:prevDay" /> //2 </wsdl:message> ... <wsdl:portType name="BusinessDayPortType"> <wsdl:operation name="prevDay"> //3 <wsdl:input message="tns:getBusinessDayRequest" /> <wsdl:output message="tns:getBusinessDayResponse" /> </wsdl:operation> </wsdl:portType>
//1 | The WSDL types element (or the XML Schema) defines a complex type with just one attribute, name, containing only elements, in this case the two elements Unit and identifier. |
//2 | The message's input part contains just a single part element, prevDay. |
//3 | The above element has the same name as the related operation, prevDay. |
You can generate code using the -wrapped option, as follows:
prompt>rwsfgen -wrapped -projectname Wrapped MyWSDL.wsdl
The resulting generated service operation method for the prevDay operation would not include the complex type sns::PreviousDay at all, instead using its elements Unit and identifier as parameters:
void BusinessDayPortTypeImp::prevDay( rwsf::CallInfo& callInfo, const std::string& Unit_in, int identifier_in) { ... }
By comparison, the same WSDL without the -wrapped option results in a generated service operation method that takes an input parameter of type sns::PreviousDay, as follows:
void BusinessDayPortTypeImp::prevDay(rwsf::CallInfo& callInfo, sns::PrevDay input_in);
Some WSDLs are cannot be used with the -wrapped option. If this is the case, the HydraExpress Agent will return an error:
WARNING: WSDL file: C:\RogueWave\Hydra\tutorials\webservices\DayOfWeek\DayO fWeek.wsdl is NOT eligible for wrapped style.
If you are certain that the data you are sending will not contain any special characters that must be escaped, you may get better performance in your applications by setting the property rwsf:doEscape to false.
This property controls whether or not the skeleton escapes content when writing responses. The default is true.
To reset this property on the server side, set the rwsf:doEscape property to false in the server-side configuration file <serviceName>handlers.xml. (For more information on <serviceName>handlers.xml, see Section 8.3.11.3.)
The default generated <serviceName>handlers.xml sets the property to true, as follows:
<configuration> <service name="DayOfWeekPortService"> <request-handlers> <handler name="http://localhost:8090/dayofweek/DayOfWeekSkeleton"> <property name="rwsf:doEscape" value="true" /> </handler> </request-handlers> ... </service> </configuration>
To turn off text escaping, just change the value to false:
<property name="rwsf:doEscape" value="false" />
The service implementation may access the URL for requests that arrive via HTTP or HTTPS from the rwsf::CallInfo object.
The service accesses the rwsf:requestURL in the same way it accesses a rwsf:SessionID.
rwsf::Attribute attr = callInfo.get("rwsf:RequestURL"); std::string temp; attr >> temp; std::string requestURL( temp.data() );
Among the generated files are a series of XML-based configuration files. Using the configuration files allows you to build a high degree of flexibility into your application. HydraExpress generates these configuration files automatically based on default values in the WSDL, so you don't have to deal with them unless you wish to customize your application.
The ability to use configuration files to link a transport with a service, or a service with a binding, effectively decouples these objects in your application. This allows you to change how your service works without changing your binding, or to use a new service with the same binding, or to change any number of other elements in your application without having to regenerate code or recompile.
The primary configuration files on the server side are
the transports configuration file: server-transports.xml
the servlet configuration file: servicecontextname_web.xml
the service chains configuration file: servicecontextname_handlers.xml
the named objects configuration file: servicecontextname_objects.xml
The server-transports.xml file includes all available transports for your service. This file is not generated, but rather copied from the template files in the <installdir>\conf\webservice directory each time the code generator is invoked. Use this file to configure additional transports that you have included in your application, or to customize existing transports.
For more information, see Chapter 11, "Dynamic Transports."
The <servicecontextname>_web.xml file includes servlet configuration information. This includes
a definition of the servlet
the name of the configuration file that defines the particular service (or services) that the servlet uses
other optional parameters
When you deploy your service, HydraExpress copies all config files, including this <servicecontextname>_web.xml file to the appropriate context directory in your <installdir>\apps\servlets directory.
For more information on this file and defining servlets, see Section 23.4, "Configuring the Servlet Used by the Service."
Following is an excerpt from dayofweek_web.xml.
<servlet-name>DayOfWeekPortService</servlet-name> <servlet-class>rwsf_webservice_servlet.createWebServiceServlet </servlet-class> ----- <web-apps> <servlet> <servlet-name>DayOfWeekPortService</servlet-name> 1 <servlet-class>rwsf_webservice_servlet.createWebServiceServlet </servlet-class> <init-param> <param-name>configFile</param-name> 2 <param-value>handlers.xml</param-value> </init-param> <init-param> <param-name>serviceName</param-name> 3 <param-value>DayOfWeekPortService</param-value> </init-param> <init-param> <param-name>wsdlFileName</param-name> 4 <param-value>DayOfWeek.wsdl</param-value> </init-param> </servlet> ... </web-apps>
//1 | The element servlet contains an attribute <servlet-name> identifying the name of the servlet that is the entry point to the service, DayOfWeekPortService, an instance of rwsf::WebServiceServlet, the class from which all Web services in HydraExpress are derived. |
//2 | DayOfWeekPortService is initialized using three initialization parameters, which are defined in <init-param> elements. The first is the configuration file, or configFile, for the servlet, identified as <servicecontextname>_handlers.xml. This configuration file defines the message handler chains for the service. (The file <servicecontextname>_handlers.xml is located in both the code generation directory and in the deployment directory for the service: RWSF_HOME\apps\servlets\<servicecontextname>.) |
//3 | Defines the service name to use in <servicecontextname>_handlers.xml. Note that the serviceName attribute DayofWeekPortService above maps to the name attribute in the <servicecontextname>_handlers.xml file. (See Section 8.3.11.3.) |
//4 | The optional parameter wsdlFileName simply specifies the name of the WSDL file that defined this service. If the parameter is specified, an HTTP GET request sent to the service's URL (the SOAP address in the WSDL) returns the WSDL file. (The WSDL file is located in the directory RWSF_HOME\apps\servlets\<servicecontextname>.) |
The excerpt here shows the default configuration elements contained in the generated <servicecontextname>_web.xml file. Other optional configuration elements may be added to define session timeout values and to configure error pages.
For more information on setting a timeout value for your session, see Section 18.4, "Configuring Session Timeouts." For more information, on error pages, see Section 4.10, "Configure Error Pages," in the HydraExpress Servlet Development Guide.
The configuration file <serviceName>handlers.xml is generated as part of the server side components. It chains together the handlers for this service and contains initialization parameters. For more information on handlers, see Chapter 14, "SOAP Message Handler SDK."
Following is an excerpt from dayofweek_handlers.xml:
<service name="DayOfWeekPortService"> 1 <request-handlers> 2 <handler name="http://localhost:8090/dayofweek/DayOfWeekSkeleton"> </request-handlers> <transport-handlers> </transport-handlers> <service-endpoint name="http://localhost:8090/dayofweek/DayOfWeek"/> 3 <response-handlers> </response-handlers> <fault-handlers> </fault-handlers> </service>
//1 | The attribute name identifies the name of the service, and maps from the serviceName attribute in web.xml. When the servlet identified in web.xml receives a message, it passes it to the DayofWeekPortService to handle. |
//2 | The request is passed to any transport handlers that might exist (none in this case) and then on to any bindings defined in the request-handlers element. (In this example, there is just one binding -- the SOAP message handle.) The request is then dispatched to the server implementation. |
//3 | The element service-endpoint identifies the service implementation and its location. |
The element transport-handlers should not be confused with the transport itself. An optional transport handler might add message encryption, compression, or special encoding. A transport handler manipulates the request before passing it to the request handlers, the response after any response handlers, and just before it is sent by the transport.
The generated <servicecontextname>_objects.xml file defines all the objects that need to be created in order to use this service. When instantiated, these objects are instances of rwsf::NamedObject.
An object configuration file client-objects.xml is also generated on the client side for the message pattern notification. See Section 9.2 for more information.
This <servicecontextname>_objects.xml file is loaded by the Agent servlet container at startup. The server first creates the objects and then stores them together in a context object specific to this service, or an instance of rwsf::NamingContext. This context object acts like a global registry and instantiates each object. The Agent servlet container then loads the relevant <servicecontextname>_web.xml for the context. At this point, all the context's objects are instantiated, the service knows which binding to use, and the service is ready to start receiving requests.
In the case of DayofWeek, the required objects are the server implementation and the skeleton, or binding. The file also defines a global log for this message.
Each <servicecontextname>_objects.xml file contains a series of named objects, or <naming-obj> elements. Each element contains two sub-elements as a key-value pair: a <naming-name> and its value, a <naming-class>.
Following is the dayofweek_objects.xml file generated for this service:
<?xml version="1.0" encoding="ISO-8859-1"?> <objects> <!-- First register the user's actual service implementation. If the name of the implementation class changes, make sure to also make the change here. --> <naming-obj> 1 <naming-name>http://localhost:8090/dayofweek/DayOfWeek</naming-name> <naming-class>DayOfWeekPortService.createDayOfWeekPortTypeImp </naming-class> </naming-obj> <naming-obj> 2 <naming-name>http://localhost:8090/dayofweek/DayOfWeekSkeleton </naming-name> <naming-class>DayOfWeekPortService.createDayOfWeekBindingSkeleton </naming-class> </naming-obj> </objects>
//1 | Defines the service implementation. |
//2 | Defines the binding. |
The <servicecontextname>_objects.xml file may also contains any special initialization parameters
If an object is considered by HydraExpress to be a handler (such as a Web service which is a "request handler"), its initialization parameters must be included in the <serviceName>handlers.xml configuration file rather than the objects.xml file. See Section 14.6 for more information.
For more information on named objects, see Chapter 16, "Named Objects."
© Copyright Rogue Wave Software, Inc. All Rights Reserved. All Rights Reserved. Rogue Wave is a registered trademark of Rogue Wave Software, Inc. in the United States and other countries. HydraExpress is a trademark of Rogue Wave Software, Inc. All other trademarks are the property of their respective owners.
Contact Rogue Wave about documentation or support issues.