WebServiceHelper.java
/*
** Module : WebServiceHelper.java
** Abstract : A helper class to implement web service calls.
**
** Copyright (c) 2013-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description---------------------------------
** 001 CA 20131220 Created initial version.
** 002 GES 20140116 Some constants moved to a different class so the references were fixed.
** 003 MAG 20140625 Fix port name on ServiceClient constructor.
** Merge SOAP Envelope namespaces on response like on P4GL.
** 004 CA 20150209 Add wsdl-specified namespace support for input that is pre-formed XML passed
** via a string.
** 005 CA 20150416 Added isConnected - returns always true, as the web services do not use a
** permanent socket connection.
** 006 CA 20160404 Enhanced the appserver invocations, to allow implicit or explicit termination
** of requests being executing on a certain connection.
** 007 OM 20160323 Moved WebService invocation on client side.
** 008 CA 20190118 disconnect() must be aware if the FWD session is gone.
** 009 CA 20190628 Added invoke() API which uses a InvokeConfig instance (unsupported for this).
** 010 CA 20220514 The active session is now set for leaf sessions, too - this is required for a change in
** RemoteObject.obtainInstance, where first a check is done for an existing local proxy, and
** after that a network instance is obtained (a requirement for the server-side OS resources
** support, like memptr).
** 011 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
** 012 GBB 20240101 ConnectHelper renamed to ServerConnectHelper.
** 013 GBB 20240826 Moving WebService to osresource package.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.util.*;
import java.util.logging.*;
import com.goldencode.p2j.net.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.util.ControlFlowOps.ArgValidationErrors;
import com.goldencode.p2j.util.logging.*;
import com.goldencode.p2j.util.osresource.*;
import com.goldencode.util.*;
/**
* Provides a collection of APIs for establishing Progress-style connections to web-services and
* performing Progress-style invocation of service operations.
* <p>
* This class allows connections only to SOAP 1.1 services, to conform with Progress runtime.
* <p>
* Rules describing the validation of the web-service connect options and validation of the WSDL
* file:
* <ol>
* <li>the target WSDL file must be a well-formed WSDL file; local or remote URLs can be used.
* </li>
* <li>if <code>-Service</code> or <code>-Port</code>is specified, then <code>-Binding</code> and
* <code>-SOAPEndpoint</code> must not be specified (and viceversa).</li>
* <li>if <code>-Binding</code> is specified, then <code>-SOAPEndpoint</code> must be specified.
* </li>
* <li>if <code>-TargetNamespace</code> is specified, it must match the one at the WSDL document.
* </li>
* <li>if <code>-Service</code> is specified, then:
* <ul>
* <li>it must match (case-sensitively) a service defined in the WSDL document.</li>
* <li>if <code>-Port</code> is specified, then the port must be matched with one specified at
* the service in the WSDL document and it must have an associated SOAP 1.1. binding.</li>
* <li>if no <code>-Port</code> is specified, then there must exist only one SOAP 1.1.
* <code>(port, binding)</code> pair at the service configuration.</li>
* </ul>
* </li>
* <li>if <code>-Binding</code> is specified, then:
* <ul>
* <li>it must be defined in the WSDL document.</li>
* <li>it must be for SOAP 1.1.</li>
* <li>there must exist a service configured for this binding and its associated port.</li>
* </ul>
* </li>
* <li>else, if no <code>-Binding</code> or <code>-Service</code> is specified, then:
* <ul>
* <li>there must exist exactly only on service definied in the document.</li>
* <li>the service must have associated only one SOAP 1.1. port.</li>
* </ul>
* </li>
* </ol>
* <p>
* The {@link #CONNECT_OPTION_MAX_CONNECTIONS}, {@link #CONNECT_OPTION_CONNECTION_LIFETIME},
* {@link #CONNECT_OPTION_NO_SESSION_REUSE} and {@link #CONNECT_OPTION_NO_HOST_VERIFY} options are
* not yet implemented by {@link #connect}.
* <p>
* TODO: implementation for these options will not be trivial. There probably will be needed
* changes to AXIS2 core to allow persistent connections and to validate the SSL Host and reuse
* the SSL Session. Host's SSL certificate validation may be done separately (by establishing a
* connection just to make sure the target host is known), but SSL Session reuse is linked to
* AXIS2 code.
*/
public class WebServiceHelper
extends ServerHelper
{
/** Logger */
private static final CentralLogger LOG = CentralLogger.get(WebServiceHelper.class.getName());
/** Constant identifying the '-WSDL' connect option. */
static final String CONNECT_OPTION_WSDL = "-WSDL";
/** Constant identifying the '-WSDLUserid' connect option. */
static final String CONNECT_OPTION_WSDL_USERID = "-WSDLUserid";
/** Constant identifying the '-WSDLPassword' connect option. */
static final String CONNECT_OPTION_WSDL_PASSWORD = "-WSDLPassword";
/** Constant identifying the '-Service' connect option. */
static final String CONNECT_OPTION_SERVICE = "-Service";
/** Constant identifying the '-ServiceNamespace' connect option. */
static final String CONNECT_OPTION_SERVICE_NAMESPACE = "-ServiceNamespace";
/** Constant identifying the '-Port' connect option. */
static final String CONNECT_OPTION_PORT = "-Port";
/** Constant identifying the '-Binding' connect option. */
static final String CONNECT_OPTION_BINDING = "-Binding";
/** Constant identifying the '-BindingNamespace' connect option. */
static final String CONNECT_OPTION_BINDING_NAMESPACE = "-BindingNamespace";
/** Constant identifying the '-SOAPEndpoint' connect option. */
static final String CONNECT_OPTION_SOAP_ENDPOINT = "-SOAPEndpoint";
/** Constant identifying the '-SOAPEndpointUserid' connect option. */
static final String CONNECT_OPTION_SOAP_ENDPOINT_USER = "-SOAPEndpointUserid";
/** Constant identifying the '-SOAPEndpointPassword' connect option. */
static final String CONNECT_OPTION_SOAP_ENDPOINT_PASSWORD = "-SOAPEndpointPassword";
/** Constant identifying the '-TargetNamespace' connect option. */
static final String CONNECT_OPTION_TARGET_NAMESPACE = "-TargetNamespace";
/** Constant identifying the '-maxConnections' connect option. */
static final String CONNECT_OPTION_MAX_CONNECTIONS = "-maxConnections";
/** Constant identifying the '-connectionLifetime' connect option. */
static final String CONNECT_OPTION_CONNECTION_LIFETIME = "-connectionLifetime";
/** Constant identifying the '-nosessionreuse' connect option. */
static final String CONNECT_OPTION_NO_SESSION_REUSE = "-nosessionreuse";
/** Constant identifying the '-nohostverify' connect option. */
static final String CONNECT_OPTION_NO_HOST_VERIFY = "-nohostverify";
/** Constant identifying the '-pf' connect option. */
static final String CONNECT_OPTION_PF = "-pf";
/** The set of web-service connect options used by the legacy CONNECT method. */
private static final Set<String> knownConnectOptions;
static
{
knownConnectOptions = new HashSet<>();
knownConnectOptions.add(CONNECT_OPTION_WSDL);
knownConnectOptions.add(CONNECT_OPTION_WSDL_USERID);
knownConnectOptions.add(CONNECT_OPTION_WSDL_PASSWORD);
knownConnectOptions.add(CONNECT_OPTION_SERVICE);
knownConnectOptions.add(CONNECT_OPTION_SERVICE_NAMESPACE);
knownConnectOptions.add(CONNECT_OPTION_PORT);
knownConnectOptions.add(CONNECT_OPTION_BINDING);
knownConnectOptions.add(CONNECT_OPTION_BINDING_NAMESPACE);
knownConnectOptions.add(CONNECT_OPTION_SOAP_ENDPOINT);
knownConnectOptions.add(CONNECT_OPTION_SOAP_ENDPOINT_USER);
knownConnectOptions.add(CONNECT_OPTION_SOAP_ENDPOINT_PASSWORD);
knownConnectOptions.add(CONNECT_OPTION_TARGET_NAMESPACE);
knownConnectOptions.add(CONNECT_OPTION_CONNECTION_LIFETIME);
knownConnectOptions.add(CONNECT_OPTION_MAX_CONNECTIONS);
knownConnectOptions.add(CONNECT_OPTION_NO_SESSION_REUSE);
knownConnectOptions.add(CONNECT_OPTION_NO_HOST_VERIFY);
knownConnectOptions.add(CONNECT_OPTION_PF);
}
/** The target SOAP endpoint to which requests are sent. */
private final String soapEndpoint;
/** The context-local data of this class. */
private static final ContextLocal<WebService> localService =
new ContextLocal<WebService>()
{
protected WebService initialValue()
{
Class<?> cls = WebService.class;
if (!SessionManager.get().isLeaf())
{
return (WebService) RemoteObject.obtainNetworkInstance(cls);
}
else
{
return (WebService) RemoteObject.obtainLocalInstance(cls, true);
}
};
};
/** The counter for async requests. */
private int nextAsyncReqId = 0;
/** The client side context id for the web service associated with this helper. */
private int webServiceId = -1;
/**
* Instantiate a new helper.
*
* @param webServiceId
* The client side id of WebService implementation.
* @param soapEndpoint
* The target SOAP endpoint to which requests are sent.
*/
private WebServiceHelper(int webServiceId, String soapEndpoint)
{
this.webServiceId = webServiceId;
this.soapEndpoint = soapEndpoint;
}
/**
* Do a first level validation of parameters and send them to client to establish a new
* web-service connection using the connect options in the given map.
*
* @param opts
* A map with the connect options.
*
* @return A new {@link WebServiceHelper} instance or {@code null} if no connection
* was possible. Check the {@link ErrorManager} for details about any errors.
*/
static WebServiceHelper connect(Map<String, String> opts)
{
// wsdl target is required
String wsdlTarget = ServerConnectHelper.getString(opts, CONNECT_OPTION_WSDL, null);
WebServiceConnectOptions options = new WebServiceConnectOptions(wsdlTarget);
// wsdl user/passwd are optional
String wsdlUserid = ServerConnectHelper.getString(opts, CONNECT_OPTION_WSDL_USERID, null);
options.setWsdlUserid(wsdlUserid);
options.setWsdlPassword((wsdlUserid == null)
? null
: ServerConnectHelper.getString(opts, CONNECT_OPTION_WSDL_PASSWORD, ""));
// -Service and -Binding groups are mutually exclusive, but are optional
options.setServiceName(ServerConnectHelper.getString(opts, CONNECT_OPTION_SERVICE, null));
options.setServiceNamespace(
ServerConnectHelper.getString(opts, CONNECT_OPTION_SERVICE_NAMESPACE, null));
options.setPortName(ServerConnectHelper.getString(opts, CONNECT_OPTION_PORT, null));
options.setBindingName(ServerConnectHelper.getString(opts, CONNECT_OPTION_BINDING, null));
options.setBindingNamespace(
ServerConnectHelper.getString(opts, CONNECT_OPTION_BINDING_NAMESPACE, null));
options.setSoapEndpoint(ServerConnectHelper.getString(opts, CONNECT_OPTION_SOAP_ENDPOINT, null));
// soap user/passwd are optional
String soapEndpointUserid =
ServerConnectHelper.getString(opts, CONNECT_OPTION_SOAP_ENDPOINT_USER, null);
options.setSoapEndpointUserid(soapEndpointUserid);
options.setSoapEndpointPassword((soapEndpointUserid == null)
? null
: ServerConnectHelper.getString(opts, CONNECT_OPTION_SOAP_ENDPOINT_PASSWORD, ""));
// target NS is optional, used to validate the WSDL
options.setTargetNamespace(
ServerConnectHelper.getString(opts, CONNECT_OPTION_TARGET_NAMESPACE, null));
options.setMaxConnections(ServerConnectHelper.getInt(opts, CONNECT_OPTION_MAX_CONNECTIONS, 0));
options.setConnectionLifetime(
ServerConnectHelper.getInt(opts, CONNECT_OPTION_CONNECTION_LIFETIME, 300));
// related to ssl connections
options.setNoSessionReuse(opts.containsKey(CONNECT_OPTION_NO_SESSION_REUSE));
options.setNoHostVerify(opts.containsKey(CONNECT_OPTION_NO_HOST_VERIFY));
WebService webService = localService.get();
int webServiceId = webService.connect(options);
if (webServiceId >= 0)
{
return new WebServiceHelper(webServiceId, options.getSoapEndpoint());
}
return null;
}
/**
* Get the set of known options for web-service connections.
*
* @return See above.
*/
static Set<String> getKnownConnectOptions()
{
return knownConnectOptions;
}
/**
* Run the port type with the given name, only if the name matches (case-insensitively) with
* the {@link WebService#getPortTypeName(int) target port type} determined during connection.
*
* @param name
* The port type name.
*
* @return A {@link PortTypeWrapper} instance associated with the port type or {@code null}
* if the port type name does not match.
*/
public PersistentProcedure runPort(character name)
{
// validate the port type
String portTypeName = name.toStringMessage();
WebService webService = localService.get();
String webServicePortTypeName = webService.getPortTypeName(webServiceId);
// search is done case-insensitive
if (!webServicePortTypeName.equalsIgnoreCase(portTypeName))
{
final int[] errCodes = { 11754, 11755 };
final String[] errMsgs =
{
String.format("Failure in initializing Port Type %s (11754)", portTypeName),
String.format("Invalid WSDL PortType name %s (11755)", portTypeName)
};
ErrorManager.recordOrThrowError(errCodes, errMsgs, false, true);
return null;
}
return new PortTypeWrapper(portTypeName, this.getServer());
}
/**
* Get the sub-type of this web service connection.
*
* @return Always the <code>WEBSERVICE</code> string.
*/
@Override
public String getSubType()
{
return "WEBSERVICE";
}
/**
* Get the connection ID of this web service connection.
*
* @return The {@link #soapEndpoint}.
*/
@Override
public String getConnectionID()
{
return soapEndpoint;
}
/**
* Check if this is a web service.
*
* @return Always <code>true</code>.
*/
@Override
public boolean isWebService()
{
return true;
}
/**
* Check if this is a session-free connection.
*
* @return Always <code>true</code> for web service connections.
*/
@Override
public boolean isSessionFree()
{
return true;
}
/**
* Get the name of this server.
*
* @return Always the connected {@link #soapEndpoint}.
*/
@Override
public String getServerName()
{
return soapEndpoint;
}
/**
* Disconnect this server.
* <p>
* This is a no-op for web-services.
*
* @param disconnecting
* Flag indicating if the FWD client session is being terminated.
*/
@Override
public void disconnect(boolean disconnecting)
{
if (webServiceId < 0)
{
return;
}
if (!disconnecting)
{
WebService webService = localService.get();
webService.disconnect(webServiceId);
}
webServiceId = -1;
}
/**
* This implementation always returns true, as the connection to remote side is not permanent.
*
* @return Always <code>true</code>.
*/
@Override
public boolean isConnected()
{
return true;
}
/**
* Send a STOP condition to the specified async request.
*
* @param requestId
* The request where to send a STOP condition.
*/
@Override
public void sendStop(int requestId)
{
UnimplementedFeature.todo("WebServiceHelper.sendStop is not implemented.");
}
/**
* Send a STOP condition to the request currently being executed on this connection.
*
* @throws IllegalStateException
* If the appserver is not connected.
*/
@Override
public void sendStop()
{
UnimplementedFeature.todo("WebServiceHelper.sendStop is not implemented.");
}
/**
* Invoke a web service operation.
*
* @param timeout
* The maximum allowed time for this invocation to complete. In milliseconds.
* Use <code>0</code> to disable the timeout.
* @param asyncReq
* When not-null, an {@link AsyncRequestImpl} instance for the associated async
* request.
* @param name
* The name for the web service operation.
* @param h
* The port-type procedure in which to invoke the web service operation. May not
* be {@code null}.
* @param function
* <code>true</code> if this is a function call.
* @param dynamicFunction
* <code>true</code> if this is a DYNAMIC-FUNCTION call.
* @param superCall
* <code>true</code> if this is a RUN SUPER or SUPER() call.
* @param transactionDistinct
* Flag indicating if the TRANSACTION DISTINCT clause is in effect.
* @param modes
* A string representation of the modes of each parameter. May not be {@code null}.
* @param args
* The procedure's arguments.
*
* @return The result of this procedure/function invocation or {@code null} if the
* invocation could not be performed.
*
* @throws IllegalArgumentException
* If this is a super call, the argument modes are not specified or the passed
* procedure handle is {@code null} or not a {@link PortTypeWrapper} procedure.
*/
@Override
@SuppressWarnings("unchecked")
BaseDataType invoke(long timeout,
AsyncRequestImpl asyncReq,
character name,
handle h,
boolean function,
boolean dynamicFunction,
boolean superCall,
boolean transactionDistinct,
String modes,
Object... args)
{
if (asyncReq != null && asyncReq._isCancelled())
{
// if the procedure is cancelled, nothing else to do
asyncReq.setResult(new RemoteInvocationResult());
return new unknown();
}
if (superCall)
{
throw new IllegalArgumentException("superCall argument must always be false!");
}
if (h == null || !(h.get() instanceof PortTypeWrapper))
{
throw new IllegalArgumentException("The passed handle is not a port type procedure!");
}
if (modes == null || modes.length() != args.length)
{
throw new IllegalArgumentException("The parameter modes must always be specified!");
}
WebService webService = localService.get();
PortTypeWrapper portProc = (PortTypeWrapper) h.get();
String portTypeName = portProc.name().toStringMessage();
String opName = name.toStringMessage();
if (!webService.prepareInvoke(webServiceId, portTypeName, opName, modes, args))
{
// failure to prepare to call the web service. Eventual messages were already fed into
// ErrorManager
return new unknown();
}
String opNs = webService.getNamespace(webServiceId);
ConditionException condition = null;
Object[] outputParams = null;
// if SOAP call initialization was successful then we can invoke the request-header callback
character requestHeader = invokeRequestHeaderCallback(portProc, opName, opNs);
if (requestHeader == null)
{
// problems invoking the request-header callback, abort
webService.cleanupInvocation(webServiceId);
return new unknown();
}
if (asyncReq != null)
{
asyncReq.setRequestId(++nextAsyncReqId); // set the request ID
}
try
{
// now we are ready to invoke the web service from client side:
String cbHeader = requestHeader.isUnknown() ? null : requestHeader.toStringMessage();
if (!webService.invoke(webServiceId, opName, portTypeName, cbHeader))
{
// invocation failed!
return new unknown();
}
// invoke the response-header callback
if (portProc.getResponseHeaderProc() != null)
{
handle headerRef = new handle();
SOAPHeaderImpl header = new SOAPHeaderImpl();
headerRef.set(header);
String[] wsHeaderEntries = webService.getHeaderEntries(webServiceId);
if (wsHeaderEntries != null)
{
for (String wsEntry : wsHeaderEntries)
{
// first register it as a header entry
SOAPHeaderEntryImpl entry = new SOAPHeaderEntryImpl();
header.addHeaderEntry(entry);
entry.setSerialized(new longchar(wsEntry));
}
}
// even if there is no header, an empty SOAP Header resource is sent.
if (!invokeResponseHeaderCallback(portProc, headerRef, opName, opNs))
{
// problems invoking the response-header callback, abort
return new unknown();
}
}
outputParams = webService.getOutputValues(webServiceId);
}
catch (ConditionException ce)
{
if (asyncReq == null)
{
// if not an async request, let it unwind
throw ce;
}
condition = ce;
}
finally
{
webService.cleanupInvocation(webServiceId);
}
character res = new character();
if (outputParams != null)
{
// copy outputParams back to [args] for parameters with O modes
int j = 0;
for (int i = 0; i < args.length; i++)
{
if (modes.charAt(i) != 'I')
{
if (j >= outputParams.length)
{
if (LOG.isLoggable(Level.SEVERE))
{
LOG.log(Level.SEVERE,
"Incorrect number of output parameters received from WebService.");
}
break;
}
((BaseDataType)args[i]).assign((BaseDataType)outputParams[j++]);
}
}
if (outputParams.length == 1)
{
res.assign((BaseDataType) outputParams[0]);
}
}
if (asyncReq != null)
{
RemoteInvocationResult asyncResult = new RemoteInvocationResult();
asyncResult.setArguments(outputParams);
asyncResult.setModes(
StringHelper.repeatChar('I', outputParams == null ? 0 : outputParams.length));
asyncResult.setResult(res);
asyncResult.setError(condition);
asyncReq.setResult(asyncResult);
}
return res;
}
/**
* This API is not available for web services.
*
* @param timeout
* The maximum allowed time for this invocation to complete. In milliseconds.
* Use <code>0</code> to disable the timeout.
* @param asyncReq
* When not-null, an {@link AsyncRequestImpl} instance for the associated async
* request.
* @param cfg
* The invoke configuration.
*
* @return Always <code>unknown</code>.
*/
@Override
BaseDataType invoke(long timeout, AsyncRequestImpl asyncReq, InvokeConfig cfg)
{
UnimplementedFeature.unsupported("This API is not available for Web Services!");
return new unknown();
}
/**
* Invoke the response-header callback, if it was set for the specified procedure.
* <p>
* This will also validate the response-header callback and the associated context procedure.
*
* @param portProc
* The {@link PortTypeWrapper port-type procedure}.
* @param soapHeader
* A handle pointing to the {@link SOAPHeader} resource.
* @param opName
* The name of operation to be invoked.
* @param opNamespace
* The namespace of operation to be invoked.
*
* @return <code>true</code> if the callback was invoked and no error condition was raised.
*/
private boolean invokeResponseHeaderCallback(PortTypeWrapper portProc,
handle soapHeader,
String opName,
String opNamespace)
{
String procedure = portProc.getResponseHeaderProc();
handle context = portProc.getResponseHeaderContext();
if (procedure == null)
{
// nothing to invoke
return false;
}
if (!context._isValid() || context.getResource() instanceof PortTypeWrapper)
{
final String msg1 = "Could not process callback: " +
"invalid procedure handle for the callback.";
final String msg2 = "Error receiving Web Service Response: " +
"An error occurred processing SOAP Response Headers";
final int[] errCodes = { 11574, 11773 };
ErrorManager.recordOrThrowError(errCodes, new String[] {msg1, msg2}, false, true);
return false;
}
String modes = "III";
character operNS = new character(opNamespace);
character operName = new character(opName);
try
{
// validate the procedure name and parameters now. the modes are not validated.
ArgValidationErrors argErr =
ControlFlowOps.reachableInternalEntry(context, procedure, false, modes, false,
soapHeader, operNS, operName);
if (argErr == null || argErr != ArgValidationErrors.NO_ERROR)
{
showCallbackError(false, argErr, procedure);
return false;
}
ControlFlowOps.invokeInWithMode(procedure, context, null, soapHeader, operNS, operName);
return true;
}
catch (ErrorConditionException e)
{
final String msg = "Error receiving Web Service Response: " +
"An error occurred processing SOAP Response Headers";
ErrorManager.recordOrThrowError(11773, msg, false, true);
return false;
}
}
/**
* Invoke the request-header callback, if it was set for the specified procedure.
* <p>
* This will also validate the request-header callback and the associated context procedure.
*
* @param portProc
* The {@link PortTypeWrapper port-type procedure}.
* @param opName
* The name of operation to be invoked.
* @param opNamespace
* The namespace of operation to be invoked.
*
* @return an unknown {@link character} instance if nothing is needed to be invoked,
* {@code null} in case of any raised error conditions or the request-header
* XML payload serialized as text, in case of success.
*/
private character invokeRequestHeaderCallback(PortTypeWrapper portProc,
String opName,
String opNamespace)
{
String procedure = portProc.getRequestHeaderProc();
handle context = portProc.getRequestHeaderContext();
if (procedure == null)
{
// nothing to invoke
return new character();
}
if (!context._isValid() || context.getResource() instanceof PortTypeWrapper)
{
final int[] errCodes = { 11574, 11767 };
final String msg1 = "Could not process callback: " +
"invalid procedure handle for the callback";
final String msg2 = "Error sending Web Service Request: " +
"An error occurred processing SOAP Request Headers";
ErrorManager.recordOrThrowError(errCodes, new String[] { msg1, msg2 }, false, true);
return null;
}
String modes = "OIIO";
handle soapHeader = new handle();
character operNS = new character(opNamespace);
character operName = new character(opName);
logical deleteOnDone = new logical();
String header = null;
try
{
// validate the procedure name and parameters now. The modes are not validated.
ArgValidationErrors argErr =
ControlFlowOps.reachableInternalEntry(context, procedure, false, modes, false,
soapHeader, operNS, operName, deleteOnDone);
if (argErr == null || argErr != ArgValidationErrors.NO_ERROR)
{
showCallbackError(true, argErr, procedure);
return null;
}
ControlFlowOps.invokeInWithMode(procedure, context, null,
soapHeader, operNS, operName, deleteOnDone);
if (soapHeader._isValid())
{
// no error is shown if the handle doesn't reference a SOAP-header...
Object res = soapHeader.get();
if (res instanceof SOAPHeaderImpl)
{
SOAPHeaderImpl hdr = (SOAPHeaderImpl) res;
// get the header serialized to a string and return it.
if (hdr.valid())
{
header = hdr.asString();
}
}
if (deleteOnDone.booleanValue())
{
// the soap header and the underlying DOM XML Nodes (referring the header entries)
// get deleted
HandleOps.delete(soapHeader);
}
}
return new character(header);
}
catch (ErrorConditionException e)
{
final String msg = "Error sending Web Service Request: " +
"An error occurred processing SOAP Request Headers";
ErrorManager.recordOrThrowError(11767, msg, false, true);
return null;
}
}
/**
* Show a callback error for a failed callback validation.
*
* @param request
* When <code>true</code>, the request-header callback is invoked.
* @param err
* The validation errors for this callback. {@code null} specifies the callback
* procedure was not found in the specified context.
* @param procedure
* The procedure name for this callback.
*/
private void showCallbackError(boolean request, ArgValidationErrors err, String procedure)
{
final String reqMsg2 = "Error sending Web Service Request: " +
"An error occurred processing SOAP Request Headers";
final String respMsg2 = "Error receiving Web Service Response: " +
"An error occurred processing SOAP Response Headers";
final String msg2 = (request ? reqMsg2 : respMsg2);
final int errCode2 = (request ? 11767 : 11773);
final int errCode1;
final String msg1;
if (err != null)
{
if (err == ArgValidationErrors.INCORRECT_COUNT ||
err == ArgValidationErrors.UNEXPECTED_PARAM)
{
msg1 = String.format("Callback error: " +
"mismatched number of parameters passed to procedure %s",
procedure);
errCode1 = 10511;
}
else
{
msg1 = String.format("%s-HEADER sent procedure %s mismatched parameters",
(request ? "REQUEST" : "RESPONSE"),
procedure);
errCode1 = 10512;
}
}
else
{
msg1 = null;
errCode1 = -1;
}
if (msg1 != null)
{
ErrorManager.recordOrThrowError(new int[] { errCode1, errCode2 },
new String[] { msg1, msg2 },
false, true);
}
else
{
ErrorManager.recordOrThrowError(errCode2, msg2, false, true);
}
}
}