Skip to content

Instantly share code, notes, and snippets.

@mdmyr
Created May 28, 2021 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdmyr/6f0e01bf93fd43d93d247c07ef7af853 to your computer and use it in GitHub Desktop.
Save mdmyr/6f0e01bf93fd43d93d247c07ef7af853 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:bes-external-call.informatica.mdm"
xmlns:exc="urn:bes-external-call.informatica.mdm">
<xsd:complexType name="ExternalCallRequest">
<xsd:annotation>
<xsd:documentation>External Web Service should accept this type as request (message part for soap operation input)</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="header" type="exc:Header" maxOccurs="1"/>
<xsd:element name="trigger" type="exc:Trigger" maxOccurs="1"/>
<xsd:element name="serviceCall" type="exc:ServiceCall" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name=" ">
<xsd:annotation>
<xsd:documentation>External Web Service should return this type as request (message part for soap operation output)</xsd:documentation>
</xsd:annotation>
<xsd:choice>
<xsd:element name="serviceCall" type="exc:ServiceCall" maxOccurs="1"/>
<xsd:element name="serviceError" type="exc:ServiceError" maxOccurs="1"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="Header">
<xsd:annotation>
<xsd:documentation>Information about ORS, user, his roles, etc</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="orsId" type="xsd:string" maxOccurs="1"/>
<xsd:element name="user" type="xsd:string" maxOccurs="1"/>
<xsd:element name="role" type="xsd:string" maxOccurs="unbounded"/>
<xsd:element name="reposVersion" type="xsd:string" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Trigger">
<xsd:annotation>
<xsd:documentation>Explains context for external web service call: Business Entity that is being processed by Hub, processing phase.</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="businessEntity" type="xsd:string" maxOccurs="1"/>
<xsd:element name="servicePhase" type="exc:ServicePhase" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="ServicePhase">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="WriteCO.BeforeEverything"/>
<xsd:enumeration value="WriteCO.BeforeValidate"/>
<xsd:enumeration value="WriteCO.AfterValidate"/>
<xsd:enumeration value="WriteCO.AfterEverything"/>
<xsd:enumeration value="WriteView.BeforeEverything"/>
<xsd:enumeration value="WriteView.BeforeValidate"/>
<xsd:enumeration value="WriteView.AfterValidate"/>
<xsd:enumeration value="WriteView.AfterEverything"/>
<xsd:enumeration value="MergeCO.BeforeEverything"/>
<xsd:enumeration value="MergeCO.AfterEverything"/>
<xsd:enumeration value="PreviewMergeCO.BeforeEverything"/>
<xsd:enumeration value="PreviewMergeCO.AfterEverything"/>
<xsd:enumeration value="ReadCO.BeforeEverything"/>
<xsd:enumeration value="ReadCO.AfterEverything"/>
<xsd:enumeration value="ReadView.BeforeEverything"/>
<xsd:enumeration value="ReadView.AfterEverything"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="ServiceCall">
<xsd:annotation>
<xsd:documentation>This class contains actual Business Entity data and parameters that we passed to service.</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="objectXml" type="xsd:string" maxOccurs="1"/>
<xsd:element name="parameter" type="exc:Parameter" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ServiceError">
<xsd:sequence>
<xsd:element name="code" type="xsd:string" maxOccurs="1"/>
<xsd:element name="message" type="xsd:string" maxOccurs="1"/>
<xsd:element name="detailsXml" type="xsd:string" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Parameter">
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="stringValue" type="xsd:string"/>
<xsd:element name="intValue" type="xsd:int"/>
<xsd:element name="doubleValue" type="xsd:double"/>
<xsd:element name="dateValue" type="xsd:dateTime"/>
<xsd:element name="booleanValue" type="xsd:boolean"/>
<xsd:element name="xmlValue" type="xsd:string"/>
</xsd:choice>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:schema>
<wsdl:definitions name="CustomLogicService"
targetNamespace="http://cs.sample.mdm.informatica.com/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://cs.sample.mdm.informatica.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:bes-external-call.informatica.mdm"
xmlns:exc="urn:bes-external-call.informatica.mdm">
<xsd:complexType name="ExternalCallRequest">
<xsd:sequence>
<xsd:element name="header" type="exc:Header" maxOccurs="1"/>
<xsd:element name="trigger" type="exc:Trigger" maxOccurs="1"/>
<xsd:element name="serviceCall" type="exc:ServiceCall" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ExternalCallResponse">
<xsd:choice>
<xsd:element name="serviceCall" type="exc:ServiceCall" maxOccurs="1"/>
<xsd:element name="serviceError" type="exc:ServiceError" maxOccurs="1"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="Password">
<xsd:sequence>
<xsd:element name="password" type="xsd:string" maxOccurs="1"/>
<xsd:element name="encrypted" type="xsd:boolean" maxOccurs="1" default="false"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Header">
<xsd:sequence>
<xsd:element name="orsId" type="xsd:string" maxOccurs="1"/>
<xsd:element name="user" type="xsd:string" maxOccurs="1"/>
<xsd:element name="role" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="password" type="exc:Password" minOccurs="0" maxOccurs="1"/>
<xsd:element name="payload" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="reposVersion" type="xsd:string" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Trigger">
<xsd:sequence>
<xsd:element name="businessEntity" type="xsd:string" maxOccurs="1"/>
<xsd:element name="servicePhase" type="xsd:string" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ServiceCall">
<xsd:sequence>
<xsd:element name="objectXml" type="xsd:string" maxOccurs="1"/>
<xsd:element name="parameter" type="exc:Parameter" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ServiceError">
<xsd:sequence>
<xsd:element name="code" type="xsd:string" maxOccurs="1"/>
<xsd:element name="message" type="xsd:string" maxOccurs="1"/>
<xsd:element name="detailsXml" type="xsd:string" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Parameter">
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="stringValue" type="xsd:string"/>
<xsd:element name="intValue" type="xsd:int"/>
<xsd:element name="doubleValue" type="xsd:double"/>
<xsd:element name="dateValue" type="xsd:dateTime"/>
<xsd:element name="booleanValue" type="xsd:boolean"/>
<xsd:element name="xmlValue" type="xsd:string"/>
</xsd:choice>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:schema>
<xsd:schema targetNamespace="http://cs.sample.mdm.informatica.com/"
xmlns:exc="urn:bes-external-call.informatica.mdm">
<xsd:import namespace="urn:bes-external-call.informatica.mdm"/>
<xsd:element name="validate" type="exc:ExternalCallRequest"/>
<xsd:element name="validateResponse" type="exc:ExternalCallResponse"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="validate">
<wsdl:part name="parameters" element="tns:validate"/>
</wsdl:message>
<wsdl:message name="validateResponse">
<wsdl:part name="parameters" element="tns:validateResponse"/>
</wsdl:message>
<wsdl:portType name="CustomLogicService">
<wsdl:operation name="validate">
<wsdl:input message="tns:validate"/>
<wsdl:output message="tns:validateResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CustomLogicServicePortBinding" type="tns:CustomLogicService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="validate">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CustomLogicService">
<wsdl:port name="CustomLogicServicePort" binding="tns:CustomLogicServicePortBinding">
<soap:address location="http://localhost:8080/bes-external-call/customLogicService"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
package com.informatica.mdm.sample.cs;
import java.util.Map;
import com.informatica.mdm.cs.CallContext;
import com.informatica.mdm.cs.client.CompositeServiceClient;
import com.informatica.mdm.spi.cs.StepException;
import com.informatica.mdm.spi.externalcall.CustomLogic;
import com.informatica.mdm.spi.externalcall.CustomLogicFactory;
import com.informatica.mdm.spi.externalcall.ServicePhase;
import com.informatica.mdm.spi.externalcall.Trigger;
import com.informatica.mdm.spi.externalcall.ExternalCallRequest;
import commonj.sdo.DataObject;
import commonj.sdo.helper.HelperContext;
public class CustomLogicFactoryImpl implements CustomLogicFactory {
public static final String PERSON = "Person";
private static final CustomLogic EMPTY_LOGIC = new EmptyLogic();
private CompositeServiceClient besClient;
public (CompositeServiceClient besClient) {
this.besClient = besClient;
}
@Override
public CustomLogic create(ExternalCallRequest externalCallRequest) throws StepException {
throw new StepException("CallContext is required!");
}
@Override
public CustomLogic create(ExternalCallRequest externalCallRequest, CallContext callContext) throws StepException {
// we interested in "Person" Business Entity only and can handle just few servcie phases
Trigger trigger = externalCallRequest.getTrigger();
String businessEntity = trigger.getBusinessEntity();
ServicePhase phase = trigger.getServicePhase();
switch (phase) {
case WRITE_CO_BEFORE_VALIDATE:
if (PERSON.equals(businessEntity)) {
return new ValidatePerson(besClient, callContext);
}
break;
case PREVIEW_MERGE_CO_BEFORE_EVERYTHING:
case MERGE_CO_BEFORE_EVERYTHING:
if (PERSON.equals(businessEntity)) {
return new MergePerson();
}
break;
default:
//
}
return EMPTY_LOGIC; // this one will do nothing
}
private static class EmptyLogic implements CustomLogic {
public static final DataObject OBJECT = null;
@Override
public DataObject process(HelperContext helperContext, DataObject dataObject, Map<String, Object> map,
Map<String, Object> map1) throws StepException {
return OBJECT;
}
}
}
package com.informatica.mdm.sample.cs;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.xml.transform.Source;
import javax.xml.ws.Provider;
import javax.xml.ws.Service.Mode;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;
import com.informatica.mdm.cs.client.CompositeServiceClient;
import com.informatica.mdm.spi.externalcall.CustomLogicFactory;
import com.informatica.mdm.spi.externalcall.ExternalCallProcessor;
import com.siperian.sif.client.EjbSiperianClient;
import com.siperian.sif.client.SiperianClient;
/**
* Web service implementation.
* It must accept {urn:bes-external-call.informatica.mdm}ExternalCallRequest as operation input
* and return {urn:bes-external-call.informatica.mdm}ExternalCallResponse as output
*/
@WebServiceProvider(
targetNamespace = "http://cs.sample.mdm.informatica.com/",
serviceName = "CustomLogicService",
portName = "CustomLogicServicePort",
wsdlLocation = "WEB-INF/wsdl/custom-logic-service.wsdl"
)
@ServiceMode(Mode.PAYLOAD)
public class CustomLogicService implements Provider<Source> {
@Override
public Source invoke(Source request) {
CompositeServiceClient compositeServiceClient = createCompositeServiceClient();
CustomLogicFactory customLogicFactory = new CustomLogicFactoryImpl(compositeServiceClient);
String appName = "<trusted user>"; // replace with proper application user name
// create processor instance ond let it do the job.
// all we need to provide is a custom logic factory implementation.
ExternalCallProcessor externalCallProcessor =
new ExternalCallProcessor(compositeServiceClient, appName, customLogicFactory);
return externalCallProcessor.invoke(request);
}
private static CompositeServiceClient createCompositeServiceClient() {
InputStream resourceAsStream = CustomLogicService.class.getResourceAsStream("/bes-client.properties");
Properties config = new Properties();
try {
config.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
return CompositeServiceClient.newCompositeServiceClient(config);
}
}
package com.informatica.mdm.sample.cs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.informatica.mdm.cs.CallContext;
import com.informatica.mdm.cs.api.CompositeServiceException;
import com.informatica.mdm.cs.client.CompositeServiceClient;
import com.informatica.mdm.sdo.cs.base.ValidationError;
import com.informatica.mdm.sdo.cs.base.ValidationErrors;
import com.informatica.mdm.spi.cs.StepException;
import com.informatica.mdm.spi.externalcall.CustomLogic;
import commonj.sdo.DataObject;
import commonj.sdo.helper.DataFactory;
import commonj.sdo.helper.HelperContext;
import mdm.informatica.cs_ors.ReadEntity;
/**
* This is piece of logic for validating "Person" Business Entity.
* All we do here is force user to provide at least one Address for the Person.
*/
public class ValidatePerson implements CustomLogic {
private CompositeServiceClient besClient;
private CallContext callContext;
public ValidatePerson(CompositeServiceClient besClient, CallContext callContext) {
this.besClient = besClient;
this.callContext = callContext;
}
@Override
public DataObject process(HelperContext helperContext, DataObject inputSdo,
Map<String, Object> inParams,
Map<String, Object> outParams) throws StepException {
try {
// make sure "Person" SDO has at least one "Address" item
List<?> list = inputSdo.getList("Person/Addresses/item");
if ((list == null) || list.isEmpty()) {
DataFactory dataFactory = helperContext.getDataFactory();
String personId = inputSdo.getString("Person/rowidObject");
if(personId != null) { // check if address already exist
DataObject readEntity = dataFactory.create(ReadEntity.class);
DataObject personFilter = readEntity.createDataObject("parameters").createDataObject("coFilter").createDataObject("object");
personFilter.setString("name", "Person");
personFilter.createDataObject("key").setString("rowid", personId);
DataObject addressFilter = personFilter.createDataObject("object");
addressFilter.setString("name", "Addresses");
addressFilter.createDataObject("pager").setInt("recordsToReturn", 1);
DataObject readEntityReturn = besClient.process(callContext, readEntity);
List existingList = readEntityReturn.getList("object/Person/Addresses/item");
if(existingList != null && !existingList.isEmpty()) {
return null;
}
}
List<ValidationError> errorList = new ArrayList<>();
ValidationError error = (ValidationError) dataFactory.create(ValidationError.class);
error.setCode("CUSTOM-00001");
error.setMessage("Person must have at least one Address");
error.setField(Collections.singletonList("Person.Addresses"));
errorList.add(error);
ValidationErrors errors = (ValidationErrors) dataFactory.create(ValidationErrors.class);
errors.setError(errorList);
throw new StepException((DataObject) errors, "SIP-50022");
}
} catch (CompositeServiceException e) {
throw new StepException(e);
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment