This section of the documentation gives some indication about how to create a SOAP 1.2 endpoint which matches the IHE specifications. Those constraints have to be followed when implementing an IHE actor acting as a webservice responder.
The next sections refer to the code snippet below (extract from PatientManager tool) which defines the PDQ Supplier service (PDQV3)
@Stateless @Name("PDQSupplierService") @WebService(portName = "PDQSupplier_Port_Soap12", name = "PDQSupplier_PortType", targetNamespace = "urn:ihe:iti:pdqv3:2007", serviceName = "PDQSupplier_Service") @SOAPBinding(parameterStyle = ParameterStyle.BARE) @Addressing(enabled = true, required = true) @BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING) @RespectBinding(enabled = true) @GenerateInterface(value = "PDQSupplierServiceRemote", isLocal = false, isRemote = true) @HandlerChain(file = "soap-handler.xml") public class PDQSupplierService implements PDQSupplierServiceRemote { private static Logger log = LoggerFactory.getLogger(PDQSupplierService.class); private static final String HL7_NS = "urn:hl7-org:v3"; @Resource private WebServiceContext context; @WebMethod(operationName = "PDQSupplier_PRPA_IN201305UV02", action = "urn:hl7-org:v3:PRPA_IN201305UV02") @WebResult(name = "PRPA_IN201306UV02", partName = "Body", targetNamespace = HL7_NS) @Action(input = "urn:hl7-org:v3:PRPA_IN201305UV02", output = "urn:hl7-org:v3:PRPA_IN201306UV02") public PRPAIN201306UV02Type findCandidatesQuery( @WebParam(name = "PRPA_IN201305UV02", partName = "Body", targetNamespace = HL7_NS) PRPAIN201305UV02Type request){ PDQV3QueryHandler queryHandler = new PDQV3QueryHandler(getDomainForPDQ(), Actor.findActorWithKeyword("PDS"), Actor.findActorWithKeyword("PDC"), Transaction.GetTransactionByKeyword("ITI-47"), getRemoteHostFromContext()); try { return queryHandler.handlePRPAIN201305UV02(request); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } }
Java code | WSDL | Value in the example |
serviceName attribute of @Webservice |
definitions@name service@name |
PDQSupplier_Service |
targetNamespace attribute of @Webservice | definitions@targetNamespace | urn:ihe:iti:pdqv3:2007 (prefixed as ns1 in the generated wsdl) |
name attribute of @Webservice |
portType@name bindings@type |
PDQSupplier_PortType |
portName attribute of @Webservice | service/port@name | PDQSupplier_Port_Soap12 |
You can use the RespectBindingFeature to control whether a JAX-WS implementation is required to respect the contents of a Web Services Description Language (WSDL) binding that is associated with an endpoint. In that case, use annotation @RespectBinding(enabled="true")
Java code | WSDL | Value in example |
name attribute of @WebResult name attribute of @WebParam |
message/part@element | PRPA_IN201305UV02 |
partName attribute of @WebResult partName attribute of @WebParam |
message/part@name | body (default) |
targetNamespace attribute of @WebResult targetNamespace attribute of @WebParam |
used as namespace in message/part@element | urn:hl7-org:v3 |
You can also use header attribute of @WebParam if the parameter is expected in the header of the SOAP envelop.
Note: ns1 is the namespace prefix for urn:hl7-org:v3
<wsdl:message name="PDQSupplier_QUQI_IN000003UV01_ContinueResponse"> <wsdl:part element="ns1:PRPA_IN201306UV02" name="body"></wsdl:part> </wsdl:message> <wsdl:message name="PDQSupplier_QUQI_IN000003UV01_Continue"> <wsdl:part element="ns1:QUQI_IN000003UV01" name="body"></wsdl:part> </wsdl:message> <wsdl:message name="PDQSupplier_QUQI_IN000003UV01_CancelResponse"> <wsdl:part element="ns1:MCCI_IN000002UV01" name="body"></wsdl:part> </wsdl:message> <wsdl:message name="PDQSupplier_PRPA_IN201305UV02"> <wsdl:part element="ns1:PRPA_IN201305UV02" name="Body"></wsdl:part> </wsdl:message> <wsdl:message name="PDQSupplier_QUQI_IN000003UV01_Cancel"> <wsdl:part element="ns1:QUQI_IN000003UV01_Cancel" name="body"></wsdl:part> </wsdl:message> <wsdl:message name="PDQSupplier_PRPA_IN201305UV02Response"> <wsdl:part element="ns1:PRPA_IN201306UV02" name="Body"></wsdl:part> </wsdl:message>
Typically, you will need to create one method per operation.
Java code | WSDL | Value in example |
operationName attribute of @WebMethod |
portType/operation@name binding/operation@name |
PDQSupplier_PRPA_IN201305UV02 |
action attribute of @WebMethod |
binding/operation/operation@soapAction |
urn:hl7-org:v3:PRPA_IN201305UV02 |
input attribute of @Action |
portType/operation/input@wsam:Action
portType/operation/input@wsaw:Action |
urn:hl7-org:v3:PRPA_IN201305UV02 |
output attribute of @Action |
portType/operation/output@wsam:Action portType/operation/output@wsaw:Action |
urn:hl7-org:v3:PRPA_IN201306UV02 |
<wsdl:portType name="PDQSupplier_PortType"> <wsdl:operation name="PDQSupplier_QUQI_IN000003UV01_Continue"> <wsdl:input message="tns:PDQSupplier_QUQI_IN000003UV01_Continue" name="PDQSupplier_QUQI_IN000003UV01_Continue" wsam:Action="urn:hl7-org:v3:QUQI_IN000003UV01_Continue" wsaw:Action="urn:hl7-org:v3:QUQI_IN000003UV01_Continue"></wsdl:input> <wsdl:output message="tns:PDQSupplier_QUQI_IN000003UV01_ContinueResponse" name="PDQSupplier_QUQI_IN000003UV01_ContinueResponse" wsam:Action="urn:hl7-org:v3:PRPA_IN201306UV02" wsaw:Action="urn:hl7-org:v3:PRPA_IN201306UV02"></wsdl:output> </wsdl:operation> <wsdl:operation name="PDQSupplier_PRPA_IN201305UV02"> <wsdl:input message="tns:PDQSupplier_PRPA_IN201305UV02" name="PDQSupplier_PRPA_IN201305UV02" wsam:Action="urn:hl7-org:v3:PRPA_IN201305UV02" wsaw:Action="urn:hl7-org:v3:PRPA_IN201305UV02"></wsdl:input> <wsdl:output message="tns:PDQSupplier_PRPA_IN201305UV02Response" name="PDQSupplier_PRPA_IN201305UV02Response" wsam:Action="urn:hl7-org:v3:PRPA_IN201306UV02" wsaw:Action="urn:hl7-org:v3:PRPA_IN201306UV02"></wsdl:output> </wsdl:operation> <wsdl:operation name="PDQSupplier_QUQI_IN000003UV01_Cancel"> <wsdl:input message="tns:PDQSupplier_QUQI_IN000003UV01_Cancel" name="PDQSupplier_QUQI_IN000003UV01_Cancel" wsam:Action="urn:hl7-org:v3:QUQI_IN000003UV01_Cancel" wsaw:Action="urn:hl7-org:v3:QUQI_IN000003UV01_Cancel"></wsdl:input> <wsdl:output message="tns:PDQSupplier_QUQI_IN000003UV01_CancelResponse" name="PDQSupplier_QUQI_IN000003UV01_CancelResponse" wsam:Action="urn:hl7-org:v3:MCCI_IN000002UV01" wsaw:Action="urn:hl7-org:v3:MCCI_IN000002UV01"></wsdl:output> </wsdl:operation> </wsdl:portType>
If the addressing tag can be present in the inbound message, add the @Addressing annotation and set its enabled attribute to "true".
If mustUnderstand="true" is expected, set the required attribute to "true".
In our example, we use the @HandlerChain annotation the one references an XML file. In the Java project, this file is located in EJBModule resources: resources/net/ihe/gazelle/simulator/pdqv3/pds/soap-hander.xml. It is import that the folder hierarchy matches the package where the Java class is defined (here net.ihe.gazelle.simulator.pdqv3.pds).
Below is its content:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"> <javaee:handler-chain> <javaee:handler> <javaee:handler-class>net.ihe.gazelle.simulator.common.ihewsresp.WSAddressingHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </javaee:handler-chains>
The WSAddressingHandler class referenced here is used to explicit the content of the addressing element in the SOAP header. This is required when you force the addressing using @Addressing(enabled="true", required="true").
You can define SOAP faults that will be returned by your webservice in specific use cases.
Add the following in the @Action annotation (example):
fault = { @FaultAction(className = NAVFault.class, value = "NAV"), @FaultAction(className = VERUNKFault.class, value = "VERUNK") }
You can declare as many @FaultAction as you need. The classes returned for those faults (NAVFault and VERUNKFault in our case) shall
Below, as example, is the implementation of the NAVFault Class.
@WebFault(name = "NAV", targetNamespace = "urn:ihe:iti:svs:2008") public class NAVFault extends SOAPFaultException { /** * */ private static final long serialVersionUID = 1L; public NAVFault(SOAPFault fault) { super(fault); try { getFault().setFaultCode(new QName("http://www.w3.org/2003/05/soap-envelope", "Sender")); getFault().appendFaultSubcode(new QName(SVS_NS, "NAV")); getFault().addFaultReasonText("Unknown value set", Locale.ENGLISH); } catch (SOAPException e) { log.error("Unable to build NAV SOAPFault"); e.getMessage(); } } }
Java code | WSDL | Value in example |
value attribute of @FaultAction name attribyte of @WebFault |
portType/operation/fault@Action | NAV |
className attribute of @FaultAction | portType/operation/fault@name | NAVFault |
targetNamespace attribute of @WebFault | used as namespace in message/part/element | urn:ihe:iti:svs:2008 |
In the same way as we use an handler to make the addressing header understandable by Jboss, we can define other handlers for operations to be performed
It allows you to perform some operations on the entire SOAP message.
Once your handler is ready, you can declare it in the handler.xml file. You can add as many <java:handlier-chain> elements as you need.
The easier way is to
The handle{Message|Fault|Inbound|Outbound} methods return a boolean indicating if the message shall be processed by the @WebService class. If the message shall not be processed and you want to send a Fault to the end user, simply throw a RuntimeException, its message will be used as fault reason in the returned response.
In order to make sure that the wsdl is correct and displays a URL in soap12:address/@location which is correct and reachable by the requester, the following changes shall be brought to the configuration (to be performed on each server inside a jboss installation):
In file ${JBOSS_HOME}/server/${JBOSS_SERVER}/deployers/jbossws.deployer/META-INF/stack-agnostic-jboss-beans.xml, replace
<property name="webServiceHost">${jboss.bind.address}</property>
by the following
<property name="webServiceHost">jbossws.undefined.host</property>
And make sure that the modifySOAPAddress property is set to true:
<property name="modifySOAPAddress">true</property>
You must restart your Jboss server to take the changes into account.
In order to make sure that the wsdl is correct and displays a URL in soap12:address/@location which is correct and reachable by the requester, the following changes shall be brought to the configuration (to be performed on each server inside a jboss installation):
In file standalone.xml of the jboss that you are running (file located at configuration/standalone.xml)
<wsdl-host>${jboss.bind.address}:0.0.0.0</wsdl-host>
by the following
<wsdl-host>jbossws.undefined.host</wsdl-host>
And make sure that the following property is set to true:
<modify-wsdl-address>true</modify-wsdl-address>
You must restart your Jboss server to take the changes into account.