Eingehende Webservices
Grundlagen
Dieser Artikel behandelt die Möglichkeit für externe Systeme, per Webservice mit CURSOR-CRM (auch EVI und TINA) zu kommunizieren.
Herstellen der Verbindung
WSDL
Das System stellt drei WSDLs zur Verfügung.
WSDL zum Starten und Fortsetzen von BPM-Prozessen
Muster: httsp://<SERVER>:X8443/<VERSION>-ejb/ProcessWebService?wsdl
Beispiel: https://localhost:18443/soap/ProcessWebService?wsdl
Beispiele zur XML-Syntax für die an CRM zu sendenden Nachrichten sind in der BPM-Dokumentation zu finden (Abschnitt Web Service).
WSDL für sonstige Funktionen
Muster: https://<SERVER>:X8443/soap/CrmWebService?wsdl
Beispiel: https://localhost:18443/soap/CrmWebService?wsdl
Anmeldung
Jeder Aufruf eines Webservices erfordert eine eigene Anmeldung (HTTP-Basic-Access), wodurch sich auch die Rechte auf die Daten von CURSOR-CRM/EVI ändern können.
Hierzu müssen Benutzerkürzel und Passwort (Klartext) übergeben werden.
Es muss also ein bestehender CRM-Benutzer verwendet werden. Es wird empfohlen, einen eigenen Benutzer dafür anzulegen.
Technik
Webservices werden über SessionBeans bereitgestellt. Die SessionBean wird über die EJB-Spezifikation als Service-Endpont deklariert. Die öffentlichen Methoden der SessionBean werden für die Verwendung im Webservice entsprechend der EJB 3.1 Spezifikation freigegeben.
Konfiguration in CURSOR-CRM/EVI
Um die Bereitstellung von Webservices zu zentralisieren, wurden die SessionBeans WebServiceControllerBean
(alte Version) und CrmWebServiceBean
(neue Version) erstellt. Alle Webservices werden über diese Klassen bereitgestellt. Die WSDL Definition wird durch den JBoss Applikationsserver generiert und unter URL https://<SERVER>:X8443/soap/WebServiceController?wsdl
bzw. https://<SERVER>:X8443/soap/CrmWebService?wsdl
zur Verfügung gestellt.
Jeder Aufruf eines Webservices erfordert eine eigene Anmeldung (HTTP-Basic-Access), wodurch sich auch die Rechte auf die Daten von CURSOR-CRM/EVI ändern können. Hierzu müssen Benutzerkürzel (in Großbuchstaben) und Passwort (als "Hashwert") aus der Datenbank übergeben werden.
Listing: WebServiceController.wsdl für Konfiguration der Methode String importEntities(String)
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="WebServiceController" targetNamespace="http://de.cursor.jevi.server.web/" xmlns:tns="http://de.cursor.jevi.server.web/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/>
<message name="importEntities">
<part name="xmlEntity" type="xsd:string"/>
</message>
<message name="importEntitiesResponse">
<part name="result" type="xsd:string"/>
</message>
<portType name="WebServiceControllerService">
<operation name="importEntities" parameterOrder="xmlEntity">
<input message="tns: importEntities"/>
<output message="tns:importEntitiesResponse"/>
</operation>
</portType>
<binding name="WebServiceControllerServiceBinding" type="tns:WebServiceControllerService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="importEntities">
<soap:operation soapAction=""/>
<input>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://de.cursor.jevi.server.web/"/>
</input>
<output>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://de.cursor.jevi.server.web/"/>
</output>
</operation>
</binding>
<service name="WebServiceController">
<port name="WebServiceControllerServicePort" binding="tns:WebServiceControllerServiceBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>
Logging von WebServices
Eingehende und ausgehende Daten an den bereitgestellten Webservices können in der Log-Datei WebServices.log im JBoss ausgegeben werden.
Das Log-Level ist für "de.cursor.jevi.server.web.webservice" auf INFO (oder DEBUG) zu stellen.
cursor-standalone.xml
<logger category="de.cursor.jevi.server.web.webservice" use-parent-handlers="false">
<level name="INFO"/>
<handlers>
<handler name="WEBSERVICE"/>
</handlers>
</logger>
Die ist auch mittels des CLI-Interfaces möglich:
Konsole
> bin/jboss-logging.bat webservice true
Nach der Aktivierung können sämtlich Ein- und Ausgaben kontrolliert werden.
Aufruf - Eingang
2014-11-20 08:16:24,581 INFO [de.cursor.jevi.common.web.webservice.interceptor.WebServiceInLogggingInterceptor] ============= SOAP Message ==============
2014-11-20 08:16:24,581 INFO [de.cursor.jevi.common.web.webservice.interceptor.WebServiceInLogggingInterceptor] Inbound Message
----------------------------
ID: 1
Address: https://SERVER:18443/soap/ProcessWebService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml;charset=UTF-8
Headers: {accept-encoding=[gzip,deflate], Authorization=[Basic ABCDEFG=], connection=[Keep-Alive], Content-Length=[2497], content-type=[text/xml;charset=UTF-8], host=[SERVER:18443], SOAPAction=["startProcess"], user-agent=[Apache-HttpClient/4.1.1 (java 1.5)]}
Payload: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://web.process.cursor.de/" xmlns:var="http://variable.process.common.jevi.cursor.de/">
<soapenv:Header/>
<soapenv:Body>
...
</soapenv:Envelope>
--------------------------------------
Rückgabe - Ausgang
2014-11-20 08:24:06,679 INFO [de.cursor.jevi.common.web.webservice.interceptor.WebServiceOutLoggingInterceptor] ============= SOAP Response ==============
2014-11-20 08:24:06,679 INFO [de.cursor.jevi.common.web.webservice.interceptor.WebServiceOutLoggingInterceptor] Outbound Message
----------------------------
ID: 1
Encoding: UTF-8
Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:ae412396-af94-4ffc-95f1-31154c29a097"; start="<root.message@cxf.apache.org>"; start-info="text/xml"
Headers: {}
Payload: --uuid:ae412396-af94-4ffc-95f1-31154c29a097
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns3:startProcessResponse xmlns:ns3="http://web.process.cursor.de/" xmlns:ns2="http://variable.process.common.jevi.cursor.de/">
<ns2:processWebResult>
...
</soap:Body></soap:Envelope>
--uuid:ae412396-af94-4ffc-95f1-31154c29a097--
--------------------------------------
Webservices in CURSOR-CRM/EVI
WebServiceController
search
Methodenname | search |
---|---|
Eingabeparameter | xmlSearch von Typ String (XML-Suchen-Import) |
Ausgabeparameter | String (XML-Import-Plugin) |
WSDL | WebServiceController?wsdl |
Beschreibung | Zum Ausführen einer Suche muss diese als vollständige XML-Beschreibung dem Webservice übergeben werden. Jedes Eingabe- und Ausgabefeld muss definiert sein. Jedes Feld ('Condition') enthält eine Funktion zur Einschränkung der Parameter, wobei hier der Datentyp richtig angegeben sein muss. Das Encoding und der DocType können auch entfallen. |
Listing: Geschäftspartnersuche mit Einschränkung des Kurznamens beginnt mit CURSORListing: Geschäftspartnersuche mit Einschränkung des Kurznamens beginnt mit CURSOR
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE CreateComplexSearch PUBLIC "ComplexSearchCreator.dtd" "ComplexSearchCreator.dtd">
<CreateComplexSearch>
<ExtendedSearch PK="" PlainKey="" Description="" QuickSearch="false" IgnoreCase="true">
<Query>
<SubQuery operator="de.cursor.jevi.common.search.Operator$AndOperator" relationName="" entityName="Customer" subQueryInSearchResult="true" useExists="false" useOuterJoin="false" optionalQuery="false" breakQuery="false" hint="">
<Condition searchResultField="true" listKey="false" defaultSearchValue="false">
<AttributeName>MatchCode.Customer</AttributeName>
<Function functionClassName="de.cursor.jevi.common.search.function.LikeFunction" numberOfParameters="1">
<Parameter parameterClassName="java.lang.String" parameterValue="CURSOR" />
</Function>
</Condition>
<Condition searchResultField="true" listKey="false" defaultSearchValue="false">
<AttributeName>CustomerNo1.Customer</AttributeName>
<Function functionClassName="de.cursor.jevi.common.search.function.NoConditionFunction" numberOfParameters="0">
<FunctionProperty functionPropertyName="TableName" functionPropertyDataType="java.lang.String" functionPropertyValue="" />
</Function>
</Condition>
<Condition ...>
...
</Condition>
</SubQuery>
</Query>
</ExtendedSearch>
</CreateComplexSearch>
Da man diese XML-Struktur kaum manuell erzeugen kann, ist es sehr hilfreich, die Suche zunächst in CURSOR-CRM/EVI zu erstellen und sie nach XML zu exportieren (siehe nachfolgende Abbildung). Damit später Parameter an die Suche übergeben werden können, sollte man diese bereits in der Suche durch auffällige Vorgabewerte kennzeichnen und dann im XML über eine Textersetzung austauschen (z.B. CURSOR durch ADAC ersetzen).
Als Ergebnis liefert die Suche alle AttributeContainer als XML zurück. Hierfür wird die Struktur wie beim XML-Import-Plugin von CURSOR-CRM/EVI verwendet und ist dadurch wieder importierbar. Falls die Ergebnismenge den Primärschlüssel enthält, wird dieser als identifyingField=true
markiert.
Listing: Ergebnis der Geschäftspartner-Suche, verwendbar für XML-Import
<?xml version="1.0" encoding="ISO-8859-1"?>
<Entries>
<Entry entity="Customer" createIfNotFound="false">
<Field name="Pk.Customer" identifyingField="true" value="ged-personpk1#customer1" />
<Field name="Name1.Customer" value="CURSOR Software AG" />
<Field name="CustTypeKey.Customer" value="?" />
<Field name="PersontypeKey.Customer" value="U" />
<Field name="CustomerNo1.Customer" value="1" />
<Field name="MatchCode.Customer" value="CURSOR GIESSEN" />
</Entry>
<Entry entity="Customer" createIfNotFound="false">
<Field name="Pk.Customer" identifyingField="true" value="ged-personpk1#customer2" />
<Field name="MatchCode.Customer" value="CURSOR ENTWICKLUNG" />
<Field name="Name1.Customer" value="CURSOR Software AG" />
<Field name="CustTypeKey.Customer" value="?" />
<Field name="PersontypeKey.Customer" value="U" />
<Field name="CustomerNo1.Customer" value="1" />
</Entry>
</Entries>
searchXML
Methodenname | searchXML |
---|---|
Eingabeparameter | xmlSearch von Typ String (XML-Suchen-Import) |
Ausgabeparameter | String (XML-Struktur, welche grundsätzlich für den XML-Import der zweiten Generation verwendbar ist) |
WSDL | WebServiceController?wsdl |
Beschreibung | Bei diesen Webservice handelt es sich um Weiterentwicklungen der bisherigen "search"-Webservices. Dem Service "searchXML" wird die XML-Definition einer Suche übergeben (Beispiel: s.o.) und als Ergebnis liefert er eine XML-Struktur, die an die XML-Import Schnittstelle der zweiten Generation übergeben werden kann (importXML, s.u.). |
searchXMLWithParams
Methodenname | searchXMLWithParams |
---|---|
Eingabeparameter | Suchname, Eingabeparameter |
Ausgabeparameter | String (XML-Struktur, welche grundsätzlich für den XML-Import der zweiten Generation verwendbar ist) |
WSDL | WebServiceController?wsdl |
Beschreibung | Bei "searchXMLWithParams" wird ein Suchname gefolgt von beliebig vielen Eingabeparametern übergeben. Alle Parameter-Templates innerhalb der angegebenen Suche werden der Reihenfolge nach (von oben nach unten) durch die übergebenen Parameter ersetzt. Als Ergebnis kommt ebenfalls eine Struktur für den XML-Import zurück. |
Diese Suchmethoden searchXML
und searchXMLWithParams
liefern die XML-Importstruktur immer mit einer Standardkonfiguration. In allen Einträgen sind die Optionen CREATE / NO_SEARCH gesetzt. Wenn diese Struktur also 1:1 an den Import geschickt wird, werden die entsprechenden Sätze erneut angelegt.
Die Struktur soll vielmehr als Grundgerüst dienen und muss für andere Operationen gemäß der Dokumentation der XML-Importschnittstelle angepasst werden. Dabei müssen speziell die Attribute action und found angepasst werden. Sobald nach einem Datensatz gesucht werden soll (z.B. bei einem Update oder einer Zuordnung), müssen dementsprechend auch einige Field-Einträge als identifyingField gekennzeichnet werden.
XML-Import V.1.0
CURSOR Software AG empfiehlt die Verwendung der Methoden searchXML
und importXML
.
Methodenname | importXML |
---|---|
Eingabeparameter | xmlEntries vom Typ String |
Ausgabeparameter | String (Logausgaben) |
WSDL | WebServiceController?wsdl |
Beschreibung | Alle Informationen zur XML-Struktur und den Möglichkeiten des XML-Imports entnehmen Sie bitte dem Kapitel XML Import-Schnittstelle der zweiten Generation. |
Listing: Neuanlage einer Aktivität mit Nachschlagen eines Ansprechpartners
<Entry entity="Activity" action="CREATE" found="ZERO">
<Field name="Subject.Activity" identifyingField="true">
<FieldValue>
<stringValue>Bestätigung</stringValue>
</FieldValue>
</Field>
<Field name="DelegatedBy.Activity" identifyingField="false">
<FieldValue>
<stringValue>BNE</stringValue>
</FieldValue>
</Field>
<Field name="DelegatedTo.Activity">
<FieldValue>
<stringValue>BNE</stringValue>
</FieldValue>
</Field>
<Field name="ActStatusKey.Activity">
<FieldValue>
<stringValue>O</stringValue>
</FieldValue>
</Field>
<Field name="ActTypeKey.Activity">
<FieldValue>
<stringValue>TELEIN</stringValue>
</FieldValue>
</Field>
<Field name="StartDate.Activity">
<FieldValue>
<dateTimeValue>2008-04-20T17:00:00</dateTimeValue>
</FieldValue>
</Field>
<Field name="EndDate.Activity">
<FieldValue>
<dateTimeValue>2008-04-27T17:00:00</dateTimeValue>
</FieldValue>
</Field>
<EntityLookupField name="DefaultContactPerson.Activity">
<Field name="FirstName.ContactPerson" identifyingField="true">
<FieldValue>
<stringValue>Cornelia</stringValue>
</FieldValue>
</Field>
<Field name="LastName.ContactPerson" identifyingField="true">
<FieldValue>
<stringValue>Altenhain</stringValue>
</FieldValue>
</Field>
<Field name="CustomerKey.ContactPerson" identifyingField="true">
<FieldValue>
<stringValue>CURSOR GIESSEN</stringValue>
</FieldValue>
</Field>
</EntityLookupField>
</Entry>
Entitäten-Import (XML-Import V.0.5)
Methodenname | importEntities |
---|---|
Eingabeparameter | xmlEntity vom Typ String (XML-Import-Plugin) |
Ausgabeparameter | String (Logausgaben) |
WSDL | WebServiceController?wsdl |
Beschreibung | Der Import von Entitäten unterstützt die Neuanlage, die Änderung von Daten und Verknüpfung mit abhängigen Entitäten. Jede Entität wird im Entry-Tag definiert. Falls mehrere Daten parallel bearbeitet werden sollen, können auch mehrere Entry-Tag aufgenommen werden. Zu jedem Eintrag kann die Option createIfNotFound gesetzt werden, um die Neuanlage zu steuern. Jede Entität hat spezielle Felder, die eindeutig sein müssen oder zwingend anzugeben sind. Diese Optionen können aus den Feldeigenschaften der einzelnen Felder eingesehen werden. Die eindeutigen Felder werden intern zum Laden eines Satzes verwendet, um dessen Daten zu ändern. Für einige Felder gibt es ein spezielles Format:
|
Listing: Import am Beispiel von Aktivitäten und abhängigem Vertrag
<?xml version="1.0" encoding="ISO-8859-1"?>
<Entries testRun="false">
<Entry entity="Activity" createIfNotFound="true">
<Field name="Subject.Activity" value="Präsentation" />
<Field name="Text.Activity" value="Sehr geehrte Damen und Herren" />
<Field name="ActtypeKey.Activity" value="ERINNERUNG" />
</Entry>
<Entry entity="Activity" createIfNotFound="true">
<Field name="Subject.Activity" value="Angebot versendet" />
<Field name="Text.Activity value="Anbei das Angebot bzgl. der Webservice-Schnittstelle" />
<Field name="ActtypeKey.Activity" value="MAILAUS" />
<Entry entity="Contract" createIfNotFound="false" >
<Field name="ContNo.Contract" identifyingField="true" value="00001003" />
<Field name="ProducttypeKey.Contract" identifyingField="true" value="TELE" />
<Field name="ContTitle.Contract" value="XXXX" />
</Entry>
</Entries>
Entitäten-Import und Transformation (XML-Import V.0.5)
Methodenname | importAndTransformEntities |
---|---|
Eingabeparameter |
|
Ausgabeparameter | String (Logausgaben) |
WSDL | WebServiceController?wsdl |
Beschreibung | Damit der Aufrufer des Webservices nicht an die Struktur des XML-Import-Plugins gebunden ist, kann eine beliebige XML-Struktur an die Methode übergeben werden. Dies ermöglicht den Aufruf des Webservices ohne spezielle Anpassung im aufrufenden Programm. Für die korrekte Übergabe sorgt ein XSLT-Skript. Dieses Skript muss für jedes Format separat erstellt werden. Das Zielformat ist wiederum das Import-XML-Format für die Methode Der Parameter |
INSERT
INTO PropertyMapper
VALUES('WebserviceXSLT-Pk', 'WebserviceXSLT-ID', null,
'<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="xml" encoding="UTF-8"/> ... </xsl:stylesheet>',
'SYSTEM', '', 1, GETDATE(), 'TECH_USER', GETDATE(), 'TECH_USER', null, null, null);
Dieser dieser Menüpunkt beschreibt die Einbindung externer Webservices innerhalb CURSOR-CRM, so dass diese beispielsweise in Maskenskripten angesprochen werden können.
CrmWebService
importXML
Methodenname | importXML |
---|---|
Eingabeparameter | Objekt vom Typ |
Ausgabeparameter | Objekt vom Typ |
WSDL | CrmWebService?wsdl |
Beschreibung | Dieser Service ist das Äquivalent zu Alle Informationen zur XML-Struktur und den Möglichkeiten des XML-Imports entnehmen Sie bitte dem Kapitel XML Import-Schnittstelle der zweiten Generation. |
Request - importXML
def parameters = proxy.create("de.cursor.jevi.server.web.ImportXML")
def entries = proxy.create("de.cursor.jevi.server.web.Entries")
parameters.setEntries(entries)
def entry = proxy.create("de.cursor.jevi.server.web.Entry")
entry.setAction(proxy.create("de.cursor.jevi.server.web.Action").CREATE)
entry.setFound(proxy.create("de.cursor.jevi.server.web.Found").ZERO)
entry.setEntity("Customer")
entries.getEntry().add(entry)
def fieldPk = proxy.create("de.cursor.jevi.server.web.Field")
fieldPk.setName("Pk.Customer")
fieldPk.setIdentifyingField(true)
def fieldvaluePk = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvaluePk.setStringValue("123")
fieldPk.setFieldValue(fieldvaluePk)
entry.getField().add(fieldPk)
def field1 = proxy.create("de.cursor.jevi.server.web.Field")
field1.setName("MatchCode.Customer")
field1.setIdentifyingField(false)
def fieldvalue1 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue1.setStringValue("CUSTOMER MATCHCODE")
field1.setFieldValue(fieldvalue1)
entry.getField().add(field1)
def field2 = proxy.create("de.cursor.jevi.server.web.Field")
field2.setName("CustomerNo1.Customer")
field2.setIdentifyingField(false)
def fieldvalue2 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue2.setStringValue("001")
field2.setFieldValue(fieldvalue2)
entry.getField().add(field2)
def field3 = proxy.create("de.cursor.jevi.server.web.Field")
field3.setName("CustomerNo2.Customer")
field3.setIdentifyingField(false)
def fieldvalue3 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue3.setStringValue("001")
field3.setFieldValue(fieldvalue3)
entry.getField().add(field3)
def field4 = proxy.create("de.cursor.jevi.server.web.Field")
field4.setName("Name1.Customer")
field4.setIdentifyingField(false)
def fieldvalue4 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue4.setStringValue("Customer")
field4.setFieldValue(fieldvalue4)
entry.getField().add(field4)
def field5 = proxy.create("de.cursor.jevi.server.web.Field")
field5.setName("Name2.Customer")
field5.setIdentifyingField(false)
def fieldvalue5 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue5.setStringValue("Inc.")
field5.setFieldValue(fieldvalue5)
entry.getField().add(field5)
def field8 = proxy.create("de.cursor.jevi.server.web.Field")
field8.setName("Website.Customer")
field8.setIdentifyingField(false)
def fieldvalue8 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue8.setStringValue("http://www.example.org")
field8.setFieldValue(fieldvalue8)
entry.getField().add(field8)
def field22 = proxy.create("de.cursor.jevi.server.web.Field")
field22.setName("CustTypeKey.Customer")
field22.setIdentifyingField(false)
field22.setClearUnknown(true)
def fieldvalue22 = proxy.create("de.cursor.jevi.server.web.ValidFieldValue")
fieldvalue22.setStringValue("INTERESSENT")
field22.setFieldValue(fieldvalue22)
entry.getField().add(field22)
def config = proxy.create("de.cursor.jevi.server.web.Config")
config.setTransactionTime(300)
config.setTestRun(false)
entries.setConfig(config)
def result = proxy.importXML(parameters)
Response - importXML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:importXMLResponse xmlns:ns2="http://job.xml.common.jevi.cursor.de/" xmlns:ns3="http://web.server.jevi.cursor.de/" xmlns:ns4="http://de.cursor.jevi.common.globalvariables/" xmlns:ns5="http://de.cursor.jevi.common.customizing/" xmlns:ns6="http://binding.search.common.jevi.cursor.de/" xmlns:ns7="http://de.cursor.jevi.common.keys/" xmlns:ns8="http://variable.process.common.jevi.cursor.de/">
<return success="true">
<Config transactionTime="300" testRun="false"/>
<Entry action="CREATE" found="ZERO" entity="Customer">
<CompletionType>SUCCESS</CompletionType>
<ModificationType>NO_MODIFICATION</ModificationType>
<PrimaryKeys>
<Pk value="123"/>
</PrimaryKeys>
<Field name="Pk.Customer" identifyingField="true">
<FieldValue>
<stringValue>123</stringValue>
</FieldValue>
</Field>
</Entry>
</return>
</ns3:importXMLResponse>
</soap:Body>
</soap:Envelope>
searchXML
Methodenname | searchXML |
---|---|
Eingabeparameter | Objekt vom Typ |
Ausgabeparameter | Objekt vom Typ |
WSDL | CrmWebService?wsdl |
Beschreibung | Dieser Service ist das Äquivalent zu |
Request searchXML
def parameters = proxy.create("de.cursor.jevi.server.web.SearchXML")
def search = proxy.create("de.cursor.jevi.server.web.SearchXML\$Search")
parameters.search = search
def extendedSearch = proxy.create("de.cursor.jevi.common.search.binding.ExtendedSearch")
search.extendedSearch.add(extendedSearch)
extendedSearch.setPK("")
extendedSearch.setPlainKey("GeneratedDefaultSearchForActivity")
extendedSearch.setDescription("")
extendedSearch.setQuickSearch(false)
extendedSearch.setIgnoreCase(true)
def query = proxy.create("de.cursor.jevi.common.search.binding.Query")
extendedSearch.query.add(query)
def subQuery = proxy.create("de.cursor.jevi.common.search.binding.SubQuery")
query.setSubQuery(subQuery)
subQuery.setOperator("de.cursor.jevi.common.search.Operator\$AndOperator")
subQuery.setEntityName("Activity")
subQuery.setSubQueryInSearchResult(true)
subQuery.setRelationName("")
def condition = proxy.create("de.cursor.jevi.common.search.binding.Condition")
subQuery.getConditionOrSubQuery().add(condition)
condition.setAttributeName("Subject.Activity")
condition.setSearchResultField(true)
def function = proxy.create("de.cursor.jevi.common.search.binding.Function")
condition.setFunction(function)
function.setFunctionClassName("de.cursor.jevi.common.search.function.LikeFunction")
function.setNumberOfParameters(1)
def functionParameter = proxy.create("de.cursor.jevi.common.search.binding.Parameter")
function.getParameterOrParameterLookupVO().add(functionParameter)
functionParameter.setParameterValue("v")
functionParameter.setParameterClassName("java.lang.String")
def result = proxy.searchXML(parameters)
Response - searchXML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:searchXMLResponse xmlns:ns2="http://job.xml.common.jevi.cursor.de/" xmlns:ns3="http://web.server.jevi.cursor.de/" xmlns:ns4="http://de.cursor.jevi.common.globalvariables/" xmlns:ns5="http://de.cursor.jevi.common.customizing/" xmlns:ns6="http://binding.search.common.jevi.cursor.de/" xmlns:ns7="http://de.cursor.jevi.common.keys/" xmlns:ns8="http://variable.process.common.jevi.cursor.de/">
<return success="false">
<Config transactionTime="300" version="1.0" testRun="false"/>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung angenommen</stringValue>
</FieldValue>
</Field>
</Entry>
</return>
</ns3:searchXMLResponse>
</soap:Body>
</soap:Envelope>
searchXMLWithParams
Methodenname | searchXMLWithParams |
---|---|
Eingabeparameter | Objekt vom Typ |
Ausgabeparameter | Objekt vom Typ |
WSDL | CrmWebService?wsdl |
Beschreibung | Dieser Service ist das Äquivalent zu searchXMLWithParams aus |
Request - searchXMLWithParams
def parameters = proxy.create("de.cursor.jevi.server.web.SearchXMLWithParams")
parameters.searchName = "ActivitiesWithParams"
parameters.getParams().add("v") // Template "Subject.Activity"
parameters.getParams().add("A") // Template "ActStatusKey.Activity
def result = proxy.searchXMLWithParams(parameters)
Response - searchXMLWithParams
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:searchXMLWithParamsResponse xmlns:ns2="http://job.xml.common.jevi.cursor.de/" xmlns:ns3="http://web.server.jevi.cursor.de/" xmlns:ns4="http://de.cursor.jevi.common.globalvariables/" xmlns:ns5="http://de.cursor.jevi.common.customizing/" xmlns:ns6="http://binding.search.common.jevi.cursor.de/" xmlns:ns7="http://de.cursor.jevi.common.keys/" xmlns:ns8="http://variable.process.common.jevi.cursor.de/">
<return success="false">
<Config transactionTime="300" version="1.0" testRun="false"/>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung</stringValue>
</FieldValue>
</Field>
<Field name="ActStatusKey.Activity" fieldType="PK" identifyingField="false">
<FieldValue>
<stringValue>S_ACTSTATUS-A</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung angenommen</stringValue>
</FieldValue>
</Field>
<Field name="ActStatusKey.Activity" fieldType="PK" identifyingField="false">
<FieldValue>
<stringValue>S_ACTSTATUS-A</stringValue>
</FieldValue>
</Field>
</Entry>
</return>
</ns3:searchXMLWithParamsResponse>
</soap:Body>
</soap:Envelope>
searchXMLOrderBy
Methodenname | searchXMLOrderBy |
---|---|
Eingabeparameter | Objekt vom Typ |
Ausgabeparameter | Objekt vom Typ |
WSDL | CrmWebService?wsdl |
Beschreibung | Dieser Service ist eine Erweiterung zu |
Request - searchXMLOrderBy
def parameters = proxy.create("de.cursor.jevi.server.web.SearchXMLOrderBy")
def search = proxy.create("de.cursor.jevi.server.web.SearchXMLOrderBy\$Search")
parameters.search = search
def extendedSearch = proxy.create("de.cursor.jevi.common.search.binding.ExtendedSearch")
search.extendedSearch.add(extendedSearch)
extendedSearch.setPK("")
extendedSearch.setPlainKey("GeneratedDefaultSearchForActivity")
extendedSearch.setDescription("")
extendedSearch.setQuickSearch(false)
extendedSearch.setIgnoreCase(true)
def query = proxy.create("de.cursor.jevi.common.search.binding.Query")
extendedSearch.query.add(query)
def subQuery = proxy.create("de.cursor.jevi.common.search.binding.SubQuery")
query.setSubQuery(subQuery)
subQuery.setOperator("de.cursor.jevi.common.search.Operator\$AndOperator")
subQuery.setEntityName("Activity")
subQuery.setSubQueryInSearchResult(true)
subQuery.setRelationName("")
def condition = proxy.create("de.cursor.jevi.common.search.binding.Condition")
subQuery.getConditionOrSubQuery().add(condition)
condition.setAttributeName("Subject.Activity")
condition.setSearchResultField(true)
def function = proxy.create("de.cursor.jevi.common.search.binding.Function")
condition.function = function
function.setFunctionClassName("de.cursor.jevi.common.search.function.LikeFunction")
function.setNumberOfParameters(1)
def functionParameter = proxy.create("de.cursor.jevi.common.search.binding.Parameter")
function.getParameterOrParameterLookupVO().add(functionParameter)
functionParameter.setParameterValue("a")
functionParameter.setParameterClassName("java.lang.String")
def searchOrder = proxy.create("de.cursor.jevi.server.web.SearchOrder")
parameters.searchOrder = searchOrder
def searchOrderEntry = proxy.create("de.cursor.jevi.server.web.SearchOrderEntry")
searchOrder.searchOrderEntry.add(searchOrderEntry)
searchOrderEntry.attributeName = "Subject.Activity"
searchOrderEntry.order = proxy.create("de.cursor.jevi.server.web.OrderType").ASCENDING
searchOrderEntry.searchSortOrder = proxy.create("de.cursor.jevi.server.web.OrderType").ASCENDING
def result = proxy.searchXMLOrderBy(parameters)
Response - searchXMLOrderBy
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:searchXMLOrderByResponse xmlns:ns2="http://job.xml.common.jevi.cursor.de/" xmlns:ns3="http://web.server.jevi.cursor.de/" xmlns:ns4="http://de.cursor.jevi.common.globalvariables/" xmlns:ns5="http://de.cursor.jevi.common.customizing/" xmlns:ns6="http://binding.search.common.jevi.cursor.de/" xmlns:ns7="http://de.cursor.jevi.common.keys/" xmlns:ns8="http://variable.process.common.jevi.cursor.de/">
<return success="false">
<Config transactionTime="300" version="1.0" testRun="false"/>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Anfrage #123 Aktivität zu Täpper bei Somentec</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Anfrage #123 Aktivität zu Täpper bei Somentec</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Anfrage #133 Aktivität zu Täpper bei ENTEGA</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Anfrage #133 Aktivität zu Täpper bei TäPPER HANS</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Angebot</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Angebotsversand</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Antwort von Meyer mit Link zum VL</stringValue>
</FieldValue>
</Field>
</Entry>
</return>
</ns3:searchXMLOrderByResponse>
</soap:Body>
</soap:Envelope>
searchXMLWithParamsOrderBy
Methodenname | searchXMLWithParamsOrderBy |
---|---|
Eingabeparameter | Objekt vom Typ |
Ausgabeparameter | Objekt vom Typ |
WSDL | CrmWebService?wsdl |
Beschreibung | Dieser Service ist eine Erweiterung zu |
Request - searchXMLWithParamsOrderBy
def parameters = proxy.create("de.cursor.jevi.server.web.SearchXMLWithParamsOrderBy")
parameters.searchName = "ActivitiesWithParams"
parameters.getParams().add("v") // Template "Subject.Activity"
parameters.getParams().add("A") // Template "ActStatusKey.Activity"
def searchOrder = proxy.create("de.cursor.jevi.server.web.SearchOrder")
parameters.searchOrder = searchOrder
def searchOrderEntry = proxy.create("de.cursor.jevi.server.web.SearchOrderEntry")
searchOrder.searchOrderEntry.add(searchOrderEntry)
searchOrderEntry.attributeName = "Subject.Activity"
searchOrderEntry.order = proxy.create("de.cursor.jevi.server.web.OrderType").ASCENDING
searchOrderEntry.searchSortOrder = proxy.create("de.cursor.jevi.server.web.OrderType").ASCENDING
def result = proxy.searchXMLWithParamsOrderBy(parameters)
Response - searchXMLWithParamsOrderBy
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:searchXMLWithParamsOrderByResponse xmlns:ns2="http://job.xml.common.jevi.cursor.de/" xmlns:ns3="http://web.server.jevi.cursor.de/" xmlns:ns4="http://de.cursor.jevi.common.globalvariables/" xmlns:ns5="http://de.cursor.jevi.common.customizing/" xmlns:ns6="http://binding.search.common.jevi.cursor.de/" xmlns:ns7="http://de.cursor.jevi.common.keys/" xmlns:ns8="http://variable.process.common.jevi.cursor.de/">
<return success="false">
<Config transactionTime="300" version="1.0" testRun="false"/>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung</stringValue>
</FieldValue>
</Field>
<Field name="ActStatusKey.Activity" fieldType="PK" identifyingField="false">
<FieldValue>
<stringValue>S_ACTSTATUS-A</stringValue>
</FieldValue>
</Field>
</Entry>
<Entry action="CREATE" found="NO_SEARCH" entity="Activity">
<Field name="Subject.Activity" fieldType="VALUE" identifyingField="false">
<FieldValue>
<stringValue>Vertragsverlaengerung angenommen</stringValue>
</FieldValue>
</Field>
<Field name="ActStatusKey.Activity" fieldType="PK" identifyingField="false">
<FieldValue>
<stringValue>S_ACTSTATUS-A</stringValue>
</FieldValue>
</Field>
</Entry>
</return>
</ns3:searchXMLWithParamsOrderByResponse>
</soap:Body>
</soap:Envelope>
Rest-Schnittstelle zum Dokumenten-Handling
Über Webservice Aufrufe ist es möglich, Dokumente vom CRM-System zu erhalten und an das CRM-System zu übergeben.
Service-Doku https://carmenbig.cursor.de:18443/rest/doc/v1/services/document
Aktion | Method | URL | Parameter | Ergebnis |
---|---|---|---|---|
anlegen | POST | https://server.baseUrl/rest/api/document/v1/documents?documentName=<documentName> | documentName QUERY, document FORM-DATA | {"documentPk":"fvvvvvuov9tr21bsa6pc7qDo","name":"Doppelter-Haupt_AP.jpg","updateDate":"2017-10-13T07:12:21Z","size":163938} DocumentMetaDataDTO |
lesen | GET | https://server.baseUrl/rest/api/document/v1/documents/<documentPk>/read | documentPk PATH | binary |
bearbeiten | GET | https://server.baseUrl/rest/api/document/v1/documents/<documentPk>/edit | documentPk PATH | binary |
zurückgeben | POST | https://server.baseUrl/rest/api/document/v1/documents/<documentPk>/return?documentName=<documentName> | documentName QUERY, document FORM-DATA | {"documentPk":"fvvvvvuov9tr21bsa6pc7qDo","name":"Doppelter-Haupt_AP.jpg","updateDate":"2017-10-13T07:12:21Z","size":163938}DocumentMetaDataDTO |
verwerfen | GET | https://server.baseUrl/rest/api/document/v1/documents/<documentPk>/discard | dokumentPk PATH | {"documentPk":"fvvvvvuov9tr21bsa6pc7qDo","name":"Doppelter-Haupt_AP.jpg","updateDate":"2017-10-13T07:12:21Z","size":163938} DocumentMetaDataDTO |
Webservice zur Entgegennahme von Zeitereignissen
Zeitereignisse können per Rest-Service im CRM abgelegt werden.
URL:
/rest/api/event/v1/events
Dokumentation:
/rest/doc/v1/services/event
Dem Aufruf als POST können der Typ des Ereignisses, optional der Zeitpunkt (Standard ist der Zeitpunkt des Aufrufs) und ein Payload im JSON-Format mitgegeben werden. Der eventType
ist eine fachliche Kennung des Zeitereignisses und sollte wie folgt aufgebaut sein:
<Top-Level-Domain>.<Second-Level-Domain>.<fachliche Kennung> → de.cursor.portalAccess, org.company.calculation
{
"eventType" : "domain.myEventType",
"payload" : {
"key1" : "stringValue",
"key2" : 1987,
"key3" : true
}
}
Zur Vereinfachung des Aufrufs wurde eine alternativer Service-URL zur Verfügung gestellt:
URL: /rest/api/event/v1/events/type/{eventType}
Beispiel: /rest/api/event/v1/events/type/domain.myEventType
{
"key1" : "stringValue",
"key2" : 1987,
"key3" : true
}
Über zusätzliche Parameter in der URL, können weitere Optionen den Services mitgeliefert werden. (Beispiel: /rest/api/event/v1/events/type/domain.myEventType?async=true¶llel=true&retryCount=10&retryPeriode=300)
dateTime: Das Datum des Ereignisses wird im Standard aus der aktuellen Systemzeit ermittelt. Mit diesem Parameter kann das Datum im ISO-8601 Format überschrieben werden.
async: Kann mit dem Wert "true" aktiviert werden. Das Ereignis wird in einer Warteschlage eingereiht und geprüft. Die Verarbeitung des Ereignisses läuft asynchron ab.
parallel: Bei der asynchronen Ausführung der Ereignisse bleibt die Reihenfolge zwischen Service-Aufruf und Ausführung stabil. Um die Verarbeitung der Ereignisse zu beschleunigen, kann die Verarbeitung parallelisiert werden. Die Reihenfolge bei diesen Ereignissen ist dann nicht mehr gesichert.
retryCount: Schlägt die Ausführung in der process()-Methode fehl, kann das Ereignis zu einem späteren Zeitpunkt erneut gestartet werden. Die Anzahl bestimmt die maximalen Wiederholungen, solange diese ebenfalls fehlschlagen. Nach welcher Wartezeit die Wiederholung stattfindet muss in der Variable
retryPeriod
definiert werden. Die Wiederholungen werden immer asynchron ausgeführt. Jede Wiederholung führt zu einem eigenen Zeitereignissatz, welche miteinander verknüpft sind.retryPeriod: Die minimale Wartezeit in Sekunden muss zeitgleich mit dem
retryCount
spezifiziert werden.
Nur bei der asynchronen Verarbeitung kann eine Reihenfolge der Ereignisse gesteuert werden. Bei der synchronen, direkten Verarbeitung werden einzelne Service-Aufrufe nicht gegenseitig synchronisiert.
Mit diesem Aufruf wird ein Zeitereignis in der Ablage erzeugt. Die Daten können durch den Administrator eingesehen werden.
Jedes Ereignis kann über die Methode C0Event.check()
geprüft werden. Die Prüfung findet auch bei der asynchronen Verarbeitung direkte beim Aufruf des Services statt.
Die Weiterverarbeitungslogik der Ereignisse wird in der Skriptbibliothek synchron unter C0Event.process()
angestoßen.
Es ist darauf zu achten, dass die Prüfung von Ereignissen sehr performant umgesetzt wird. Überschreitet die Prüfung eines Ereignisses eine Zeitspanne von 50ms erfolgt eine Meldung an den Systemadministrator.
Die Methoden der Skriptbibliothek können in der Kundenschicht übersteuert und erweitert werden.
SC0Event
@BpmScript @Released @Override
private Boolean check(String eventType, Map<String, Object> payloadRequest, Map<String, Object> payloadResponse)
{
// ...
return super.check(eventType, payloadRequest, payloadResponse);
}
@BpmScript @Released @Override
private void process(String eventPk, Date eventDate, String eventType, Map<String,Object> payloadRequest, Map<String,Object> payloadResponse)
{
// ...
super.process(eventPk, eventDate, eventType, payloadRequest, payloadResponse);
}
In der Skriptklasse kann auf den Typ des Zeitereignisses reagiert werden, um kundenspezifische Verarbeitungslogiken anzustoßen. Der Typ kann vom Aufrufer frei gewählt oder aber im Schlüsselbereich "Zeitereignis Typ" vordefiniert werden. Der Aufruf enthält folgende Parameter:
eventPk
: Der Primärschlüssel für den Datensatz des ZeitereignisseseventDate
: Der Zeitpunkt des ZeitereignisseseventType
: Der Schlüssel zur Auswahl der LogikpayloadRequest
: Eine Map mit dem Werten aus dem Payload des Rest-ServicespayloadResponse
: Eine Map für die Rückgabe von Ergebnissen der Logiken an den Aufrufer
Eine vordefinierte Logik ist der Typ "CreateActivity
". Dieser erlaubt die schnelle Neuanlage von Aktivitäten durch den Service. Die Felder der Aktvität können im Payload vorbelegt werden.
CreateActivity: Request
{
"eventType": "CreateActivity",
"payload": {
"myfield1": "myValue1",
"Activity": {
"Subject": "Mein Betreff",
"Text.Activity": "<html><body><p>Mein Text</p></boby></html>",
"StartDate": "2020-02-02T20:20:20+01:00", // optional
"EndDate": "2020-02-02T20:35:20+01:00", // optional
"ActStatusKey": "S_ACTTYPE-E",
"ActTypeKey": "S_ACTSTATUS-O",
"DelegatedBy": "ged-personpkTechUser#TechUser",
"DelegatedTo": "ged-personpkTechUser#TechUser",
"IsTaskRead": true,
"ContactCosts": 1,
"FreeNumber1": 1.23456789
}
}
}
Die Definition der Aktivität muss hierfür unter dem Wert "Activity" abgelegt sind. Für die Wandung von Map in IContainer wird auf die Methode WorkSpaceScriptUtils.converToIContainer()
zurückgegriffen. Das Start- und Endedatum werden im Standard mit dem Zeitpunkt des Ereignisses vorbelegt. Als Ergebnis wird der Primärschlüssel des Ereignisses und der Aktivität zurückgeliefert.
CreateActivity: Response
{
"id": "fvvvvvucldhsc1e11vhi8iEvAr",
"status": "DONE",
"payload": {
"Activity": {
"Pk.Activity": "1vlq1uh1e11vhideAc"
}
}
}
Wird eine Verknüpfung zwischen Zeitereignis und Aktivität gewünscht so muss ein C2-Nachschlagefeld in die Aktivität ausgenommen und die Logik in der Skriptmethode übersteuert werden
C0Event.createEntity
entityMap.put("C2EventKey", eventPk); //Neues C2-Feld in Aktivitäten aufnehmen
super.createEntity(eventPk, eventDate, entityName, entityMap, payloadResponse)
Dynamische Ansprache unterschiedlicher URLs in Webservices
Ein Webservice-Aufruf im CRM-System kann dynamisch so genutzt werden, dass man mit einem Webservice unterschiedliche URLs ansprechen kann.
Um beispielsweise die Webservice-Konfiguration zwischen Entwicklungs-, QS- und Produktiv-System unterscheiden zu können, kann die WSDL-Definition nun in eine Globale Variable ausgelagert werden.
Es ist möglich die URL zur WSDL-Definition zu hinterlegen wie auch die gesamte XML-Definition im XML-Format, in der die URL zu Service hinterlegt ist.
Bei der Neuanlage eines Webservices kann nun optional zur URL bzw. Datei auch die Globale Variable ausgewählt werden. Der Verfügbarkeitsprüfung und die Service-Erstellung können danach wie gewohnt durchgeführt werden.
Nach dem Customizing-Transport der Globalen Variable und der Webservice-Definition muss in den anderen Systemen nur noch die URL in der Globalen Variable des Services angepasst werden.