When you upload and run a pipeline with a script node that uses a
WebReference2
object to invoke a WSDL file, Salesforce B2C Commerce
automatically generates classes from the WSDL using Apache CXF.
The generated classes are automatically added to the webreferences2
package.
You use the generated classes in a B2C Commerce script to call the web service
operations and process any response from the web service.
B2C Commerce implements SOAP web services through the
dw.ws
package port
,
webreference2
, and WSUtil
classes.
Overview
The diagram below assumes you are using the dw.ws
package to implement web services.
For additional information about transport-layer and application-layer security, see also Web Service Security.
B2C Commerce also maintains a legacy implementation that uses the
webreferences
object and Axis 1.4 to generate classes
from the WSDL. This legacy implementation isn't recommended for
development of new web services. However, it does support RPC-style web
services, while the current implementation does not. See also Current vs. Legacy Web Service
Implementation
Integrating a web service into your storefront is relatively straightforward.
HelloWorld.wsdl
, then the other files
in the directory must be named
HelloWorld.wsdl.properties
, and
HelloWorld.jks
.underscoreBinding=asCharInWord
property to control
code generation. To direct Webreferences2 to use the
underscoreBinding
property, create a properties file
in the same directory as the WSDL file. The name of the property file
is <wsdl_name>.wsdl.properties
, where
<wsdl_name>
is the name of the WSDL
file.webreferences2
folder in your cartridge.webreferences
folder in your cartridge.Add a service definition to your service registry script for the SOAP web service.
{
initServiceClient: function() {
// Storing this in a custom attribute so it can be used later
this.webReference = webreferences.LuhnChecker;
// The return here is the 'svc.serviceClient' in subsequent calls
return webReference.getDefaultService();
}
createRequest: function(svc:SOAPService, param1) {
var requestObject = new this.webReference.CheckCC();
requestObject.setCardNumber(param1);
return requestObject;
},
execute: function(svc:SOAPService, requestObject) {
return svc.serviceClient.checkCC(requestObject);
},
parseResponse: function(svc:SOAPService, responseObject) {
var responseWrapper = {};
responseWrapper.type = responseObject.checkCCResult.cardType;
responseWrapper.valid = responseObject.checkCCResult.cardValid;
return responseWrapper;
}
}
The ServiceDefinition must specify:
initServiceClient callback to get:
dw.ws
package:WebReference2
objectdw.rpc
package:This callback must also set the serviceClient property for the Service.
The webreference/webreference2 object is required to generate the classes for the WSDL, which you must use to call the web service. When you have created this callback you can call the pipeline to generate the API for the WSDL. You might want to skip to the steps where you develop the pipeline and run it, so that you have the WSDL API available, and then finish the task of creating the service definition.
dw.ws
package:CheckFraud.WSDL
service and a
pkcs12 keystore type, the keystore must be named
CheckFraud.pkcs12
.In your B2C Commerce script that invokes the web service, create a request and response security configuration. The security configuration is a HashMap, whose first element defines the actions you want B2C Commerce to take: whether to add a timestamp, encrypt the message, sign the message, or other actions. The other elements in the HashMap assign a value to constants defined for the WSUtil class.
Create a separate security configuration for
the request messages and the response messages and
pass them both into the
setWSSecurityConfig(port : Object,
requestConfigMap : Map, responseConfigMap :
Map)
class to set the request and response
security configuration for the web service.
Example:
// define a map with all the secrets
var secretsMap : Map = new HashMap();
secretsMap.put("myclientkey", "ckpass");
secretsMap.put("myservicekey", "ckpass");
secretsMap.put("username", "password");
var requestCfg : Map = new HashMap();
// define the ws actions to be performed - in this case add a username token, timestamp,
// sign and encrypt the message
requestCfg.put(WSUtil.WS_ACTION, WSUtil.WS_USERNAME_TOKEN + " " +
WSUtil.WS_TIMESTAMP + " " +
WSUtil.WS_SIGNATURE + " " +
WSUtil.WS_ENCRYPT);
requestCfg.put(WSUtil.WS_USER, "username");
requestCfg.put(WSUtil.WS_PASSWORD_TYPE, WSUtil.WS_PW_DIGEST );
requestCfg.put(WSUtil.WS_SIG_DIGEST_ALGO, "http://www.w3.org/2001/04/xmlenc#sha256" );
// define signature properties
// the keystore file has the basename of the WSDL file and the
// file extension based on the keystore type (for example, HelloWorld.jks).
// The keystore file has to be placed beside the WSDL file.
requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myclientkey");
requestCfg.put(WSUtil.WS_SIGNATURE_USER, "myclientkey");
// define enrcryption properties
requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myservicekey");
requestCfg.put(WSUtil.WS_ENCRYPTION_USER, "myservicekey");
requestCfg.put(WSUtil.WS_SIGNATURE_PARTS, "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body");
requestCfg.put(WSUtil.WS_ENCRYPTION_PARTS,"{Element}{" + WSU_NS + "}Timestamp;"+"{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
// set the secrets for the callback
requestCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);
var responseCfg : Map = new HashMap();
// define the ws actions to be performed for the response
responseCfg.put(WSUtil.WS_ACTION, WSUtil.WS_TIMESTAMP + " " +
WSUtil.WS_SIGNATURE + " " +
WSUtil.WS_ENCRYPT);
// define signature properties
responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myservicekey");
responseCfg.put(WSUtil.WS_SIGNATURE_USER, "myservicekey");
// define decryption properties
responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myclientkey");
responseCfg.put(WSUtil.WS_ENCRYPTION_USER, "myclientkey");
// set the secrets for the callback
responseCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);
// set the security
WSUtil.setWSSecurityConfig(port, requestCfg, responseCfg);
dw.rpc
package:CheckFraud.WSDL
service and a
pkcs12 keystore type, the keystore must be named
CheckFraud.pkcs12
.static
setWSSecurityConfig(port : Object, requestConfigMap :
Map, responseConfigMap : Map) : void
; SOAPUtil.setWSSecurityConfig
method
to configure the actions on the serverwebreferences2
object. When the classes are
generated, however, you can call the script using an OCAPI
hook. However, if the WSDL changes and you need to generate a
new version of the classes, you must run the pipeline
again.www.mycompany.com/default/Pipeline-Start
webreferences2
or webreferences
object. When the classes are generated, however, you can call the
script using an OCAPI hook. However, if the WSDL changes and you need
to generate a new version of the classes, you must run the pipeline
again.WebReference2
script class generation process uses the name of the WSDL service and
port elements to create a class representing the service or port. If
the service or port name contains an underscore character, the
generated class name might or might not contain the underscore
character based on naming rules used in the code generation process.
Regardless of this, calling
WebReferences2.getDefaultService()
or
WebReferences2.getService(String, String)
resolves
the service and port element names to the corresponding script
classes.B2C Commerce maintains the legacy implementation of web services for backwards compatibility.
Differences in implementing services:
webreferences
folder instead of the webreferences2
folder.The legacy web service implementation supports the following standards:
webreferences
folder, the web service
framework uses HTTP 1.0 when making the SOAP request by default. Some web
services don't allow HTTP 1.0, so there is now a property you can set to
direct the web services framework to use HTTP 1.1. The property is named
'http_version
'. To use this property, first create a
properties file and put it in the same webreferences
folder as your WSDL file. The properties file must have the same name as
your WSDL file, but with a .properties extension. For example, if your
WSDL file is named PayPal.wsdl
, the properties file is
PayPal.wsdl.properties
. Inside the properties file, add
the following: http_version=1.1
For the web
services framework to pick up your changes, you must upload the WSDL file
and the properties file to your server.If you are making doc-style
web service calls from WSDL files in the webreferences2
folder, the web service framework uses HTTP 1.1 when making the SOAP
request by default. The properties file ignores any
http_version
set in the properties file.
dw.rpc.Stub
and supporting
classes, using the default namespace
webreferences.<wsdl_file_name>.
webreferences
object, folder, and file names,
instead of webreferences2
, but the information is is
equally applicable to JAX-WS as JAX-RPC web services and the associated
webreferences2
object, folder, and file
names.If your web service WSDL has many different types with
the same name, compilation errors can occur because the type classes are
all put in the same namespace package. To resolve this issue, you can
specify that you want B2C Commerce to generate a namespace-aware
Stub
and supporting classes, by including a properties
file in the same location as your WSDL file. In this properties file, you
specify the property as follows:
namespace=true
For B2C Commerce to apply namespace support, the properties file name must be specified as follows:
<wsdl_file_name>.wsdl.properties
For
example, if your WSDL file is HelloWorld.wsdl
, the
properties file must be HelloWorld.wsdl.properties
and it
must be placed in the same webreferences
directory as the
WSDL file itself.
In your B2C Commerce script file, create objects
using the WebReference class. For example, if the namespace for
HelloWorld.wsdl
is com.test.wsdl
,
creating objects for the namespace requires the qualified name to be:
var webRef : WebReferences = webreference.HelloWorld;
var request : new webRef.com.test.wsdl.HelloRequest();
var svc = webRef.getDefaultService();
var response = webRef.hello(request);
Q: I am receiving an error that there is a duplicate file name but, the file is working fine in my SoapUI program. The error I am seeing is: “Script exception in line 44:org.mozilla.javascript.EcmaError: ReferenceError: Failed to load and compile WSDL for… Reason: org.mozilla.javascript.EcmaError: ReferenceError: Error while generating intermediate code for webservice. Reason: org.apache.axis.wsdl.toJava.DuplicateFileException: Duplicate file name.... Hint: you might have mapped two namespaces with elements of the same name to the same package name."
A: Duplicate Names can now be supported with the addition of a properties file with the same name and in the same directory of the WSDL which contains namespace=true. Example: foo.wsdl and foo.wsdl.properties. If you use this method the scripts calling them will need to use the fully qualifying name when creating objects or the response objects of the types dependent on this.
Q: I am seeing an error that references security headers and is similar to the following: "com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC....."
A: The request requires a username and password token in the request header. Make sure when constructing this request that the correct credentials (as provided by the web service being connected to) are included.
Q: I am seeing an error that references 'FaultMessage', however, everything looks syntactically correct. What is wrong and how can I fix it?
_Elemen
t' suffix. What this
means is that, for a WSDL element 'fmt_FaultMessage
'
there is a class with the name
'Fmt_FaultMessage_Element.java
' created (when executing
wsdl2java). The WSDL and code to reference this must be adjusted
accordingly. To do this, modify the WSDL to change all references to (in
this example) 'fmt_FaultMessage
': <xsd:element name="fmt_FaultMessage"> <wsdl:message name="fmt_FaultMessage">
Q: I am seeing an error that the WSDL file can't be located in B2C Commerce, but I know the WSDL file is there. The error is: "Script exception in line 42:org.mozilla.javascript.EcmaError: ReferenceError: WSDL file for webreference2 'example' doesn't exist"
A: Make sure the that WSDL
file is in the cartridge’s webreferences2
directory and
make sure the pipeline executing/calling it's in the same cartridge (as
the WSDL file).
Q: I am having difficulty invoking a method that expects an ArrayOf type as a parameter, how can I get this to work?
A: To pass an ArrayOf structure as a parameter to a method in
a script, one should follow the following conventions (keeping in mind
that the exact implementation will vary depending on your specific
code/logic): request.setSomething([theArray]);
or
request.setSomething(new Array(x))
;
Q: I am trying to integrate with a web service that has fields defined as dateTime and keep getting this error: "Cannot convert [Calendar id=19206138] to java.util.Calendar "
A: All variables have setter methods. Salesforce recommends always using setter methods. However, when using primitive types it isn't necessary, but by using setters you will always be safe.
Q: I am running into some data-type specific issues. Which data-types are not supported?
<element name="NewOrderResponse">
<complexType>
<sequence>
<element name="return" type="ns:NewOrderResponseElement" minOccurs="1" maxOccurs="1"/>
</sequence>
</complexType>
</element>
response = service.newOrder(newOrder);
var result = response.['_return'];
With an underscore in the name, you can access all the properties in the object.
Errors shown for names without an Underscore
Attempting to access the element causes some of the following errors:
Example 1:
response = service.newOrder(newOrder); var result = response.return;
Throws
the following error:org.mozilla.javascript.EvaluatorException: missing
response = service.newOrder(newOrder);
Throws
the following error:var result = response.['return']; Unknown dynamic property 'return' for class