Just to not forget: Microsoft has a useful list of all updates concerning BizTalk. Please do not forget (like it happened to me) to scroll down until you reach the BizTalk Adapter Pack updates. They are published separately.
Please open the BizTalk Update Site with Firefox - I'm not joking, IE 10 was hiding the Adapter Pack 2013 updates due to an html error.
Random pitfalls (and their solutions) in EAI technologies such as BizTalk
Monday, June 2, 2014
Friday, May 30, 2014
WCF SAP Adapter XmlReaderParsingException
If you run into this exception:
then you should check the IDoc-XML carefully. Since the generated XSD itself does not necessarily check for maxLength (it might only define xsd:string), the IDoc-XML might successfully validate against the schema. However, during run-time the WCF SAP adapter checks the field length against the IDoc specification and fails.
You can find the real field length actually in the flat file attribute pos_length.
Also keep in mind that the length value is doubled (at least, in our case). The exception message says that a length of 2 is allowed, but actually only 1 is allowed. I suspect that there is some encoding reason behind.
Solution: You can enrich the generated XSD by using all pos_length values in MaxLength. This way the error will be seen earlier.
BTW, if you transfer IDoc as a flat file and thus use the Flat file-Assembler, you won't run into that problem since the assembler just cuts the overlength. ;-)
A message sent to adapter "WCF-Custom" on send port "SENDPORT" with URI "sap://CLIENT=123;LANG=EN;@a/ABC/00?GWSERV=DEF&RfcSdkTrace=True&AbapDebug=False" is suspended.
Error details: Microsoft.ServiceModel.Channels.Common.XmlReaderParsingException: The length of the value for the field exceeds the allowed value. Value: 4 Field: TDSPRAS Allowed value: 2
then you should check the IDoc-XML carefully. Since the generated XSD itself does not necessarily check for maxLength (it might only define xsd:string), the IDoc-XML might successfully validate against the schema. However, during run-time the WCF SAP adapter checks the field length against the IDoc specification and fails.
You can find the real field length actually in the flat file attribute pos_length.
Also keep in mind that the length value is doubled (at least, in our case). The exception message says that a length of 2 is allowed, but actually only 1 is allowed. I suspect that there is some encoding reason behind.
Solution: You can enrich the generated XSD by using all pos_length values in MaxLength. This way the error will be seen earlier.
BTW, if you transfer IDoc as a flat file and thus use the Flat file-Assembler, you won't run into that problem since the assembler just cuts the overlength. ;-)
Thursday, May 22, 2014
SAP-Adapter Error "The segment or group definition E2IDKU5 was not found in the IDoc metadata."
During runtime, we received the following error from the WCF SAP adapter:
Microsoft.ServiceModel.Channels.Common.XmlReaderGenerationException ist aufgetreten.
HResult=-2146233087
Message=The segment or group definition E2IDKU5 was not found in the IDoc metadata. The UniqueId of the IDoc type is: IDOCTYP/3/PEXR2002//701. For Receive operations, the SAP adapter does not support unreleased segments.
Source=Microsoft.Adapters.SAP
StackTrace:
bei Microsoft.Adapters.SAP.TypedIdocBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
The reason was that the IDoc contained the element name E2IDKU5 although its segment definition was E2IDKU5001.
So, whenever you run into a similar message, it's worth to check the SAP transaction WE60 to check the segment definition of the element.
Microsoft.ServiceModel.Channels.Common.XmlReaderGenerationException ist aufgetreten.
HResult=-2146233087
Message=The segment or group definition E2IDKU5 was not found in the IDoc metadata. The UniqueId of the IDoc type is: IDOCTYP/3/PEXR2002//701. For Receive operations, the SAP adapter does not support unreleased segments.
Source=Microsoft.Adapters.SAP
StackTrace:
bei Microsoft.Adapters.SAP.TypedIdocBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
The reason was that the IDoc contained the element name E2IDKU5 although its segment definition was E2IDKU5001.
So, whenever you run into a similar message, it's worth to check the SAP transaction WE60 to check the segment definition of the element.
Labels:
Biztalk,
WCF SAP adapter,
WE60,
XmlReaderGenerationException
Monday, May 12, 2014
Typical Problems Making SAP BAPI Calls
This is an uncomplete list of typical problems when you run BAPI calls.
1. If you get an empty result from SAP, you have to pass a stub of the result that you expect (read http://blogs.msdn.com/b/adapters/archive/2008/01/10/when-i-execute-bapis-i-don-t-get-back-any-data-help.aspx for more information).
2. If you get this exception
If you are aware of (1) and (2), a working BAPI Call would be:
1. If you get an empty result from SAP, you have to pass a stub of the result that you expect (read http://blogs.msdn.com/b/adapters/archive/2008/01/10/when-i-execute-bapis-i-don-t-get-back-any-data-help.aspx for more information).
2. If you get this exception
Microsoft.ServiceModel.Channels.Common.XmlReaderGenerationException: An error occurred when trying to convert the byte array [30-00-30-00-30-00-30-00-30-00-30-00-30-00-30-00] of RFCTYPE RFCTYPE_DATE with length 8 and decimals 0 to XML format. Parameter/field name: CLEAR_DATE Error message: Die Parameter "Year", "Month", und "Day" beschreiben eine nicht darstellbare DateTime.you might want to create the Biztalk BAPI schema with enableSafeTyping=true. Also use this option during run-time. You can then use dates in the form of YYYYMMDD.
If you are aware of (1) and (2), a working BAPI Call would be:
<ns0:GETSTATEMENT xmlns:ns0="http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS3007/"> <ns0:COMPANYCODE>xxx</ns0:COMPANYCODE> <ns0:CUSTOMER>xxx</ns0:CUSTOMER> <ns0:DATE_FROM>20140101</ns0:DATE_FROM> <ns0:DATE_TO>20140505</ns0:DATE_TO> <ns0:NOTEDITEMS>X</ns0:NOTEDITEMS> <ns0:LINEITEMS /> </ns0:GETSTATEMENT>Thanks, Mirko!
Wednesday, April 9, 2014
Read IDocs with WCF-SAP-Adapter that contain Elements that are not attached to a SAP Release
There are basis types that contain elements that are not attached to any release; by design, you cannot retrieve their schema with the Consume Adapter Wizard like it is explained here neither could you get the IDocs on run-time. In both cases, you receive an error like this:
Error while retrieving or generating the WSDL. Adapter message: Details: ErrorCode=RFC_EXCEPTION. ErrorGroup=RFC_ERROR_APPLICATION_EXCEPTION. SapErrorMessage=SEGMENT_UNKNOWN. AdapterErrorMessage=Error returned by RfcCallReceiveEx while calling RFC: IDOCTYPE_READ_COMPLETE..
When you anyway have to get the IDocs and your SAP colleagues cannot change the basis type's setting, you still can read the IDocs "weakly-typed", i.e. with the setting ReceiveIdocFormat=String.
First, you have to get the general ReceiveIdoc schema:
Tipp: Use WE60 in SAP to find the element that is not set to a release! Maybe your SAP administrator can set this element to a release, and then you can retrieve the schema.
Error while retrieving or generating the WSDL. Adapter message: Details: ErrorCode=RFC_EXCEPTION. ErrorGroup=RFC_ERROR_APPLICATION_EXCEPTION. SapErrorMessage=SEGMENT_UNKNOWN. AdapterErrorMessage=Error returned by RfcCallReceiveEx while calling RFC: IDOCTYPE_READ_COMPLETE..
When you anyway have to get the IDocs and your SAP colleagues cannot change the basis type's setting, you still can read the IDocs "weakly-typed", i.e. with the setting ReceiveIdocFormat=String.
First, you have to get the general ReceiveIdoc schema:
Second, you have to change the setting in the WCF-Adapter and set ReceiveIDocFormat to from "Types" to "String". A good explanation of how to receive idocs in this "raw format" is written by Mustansir and Gordie, as well as on MSDN.
The drawback of this solution is that you now receive the IDocs in a flat file format and no longer in good-readable and handable XML - until you add a flatfile pipeline.
Tipp: Use WE60 in SAP to find the element that is not set to a release! Maybe your SAP administrator can set this element to a release, and then you can retrieve the schema.
Tuesday, April 1, 2014
Visual Studio's Consume Adapter Wizard does not show the SAP IDoc you search
There are several reasons, if you can't see an SAP IDoc in the "Consume Adapter Wizard"-List.
First, their might be a resitriction in SAP: You could analyse this in se37 calling IDOCTYPE_READ_COMPLETE. Thanks for the help, Patrick!
Second, you might search for a basic type but the wizard only list message types (and one level deeper, its basis type). Thanks, Christian!
Third, the IDoc search in the wizard does not search for basic types, so you really have to find out what message type uses this basic type.
You can find a good explanation on IDoc opertions on MSDN.
First, their might be a resitriction in SAP: You could analyse this in se37 calling IDOCTYPE_READ_COMPLETE. Thanks for the help, Patrick!
Second, you might search for a basic type but the wizard only list message types (and one level deeper, its basis type). Thanks, Christian!
Third, the IDoc search in the wizard does not search for basic types, so you really have to find out what message type uses this basic type.
You can find a good explanation on IDoc opertions on MSDN.
Labels:
Biztalk,
Consume Adapter Wizard,
SAP,
WCF adapter,
WCF LOB adapter
Monday, February 17, 2014
Request-Response for WCF-Custom Receive Location Timed Out
In case you get the following error
...then your Receive Location with a WCF-Custom adapter and SAP-Binding run into a time-out while sending the reply to the SAP system. In this case, you might accidently have used a transactional RFC connection, that happens if you use a Request Response Receive Location instead of a One-Way Receive Location (for example, because Visual Studio created you a binding with Request Response RL).
To solive this, you could either use a Request Response RL and send the response by your own, or you simply switch to a One-Way RL.
A request-response for the "WCF-Custom" adapter at receive location "sap://CLIENT=xxx;LANG=DE;@d/ABCDEF?ListenerDest=VWXYZ&RfcSdkTrace=True&AbapDebug=False" has timed out before a response could be delivered.
...then your Receive Location with a WCF-Custom adapter and SAP-Binding run into a time-out while sending the reply to the SAP system. In this case, you might accidently have used a transactional RFC connection, that happens if you use a Request Response Receive Location instead of a One-Way Receive Location (for example, because Visual Studio created you a binding with Request Response RL).
To solive this, you could either use a Request Response RL and send the response by your own, or you simply switch to a One-Way RL.
Monday, January 27, 2014
WCF LOB Adapter Exception "Melden Sie sich mit einem Dialogbenutzer an"
If you receive the folgenden error message in the SAP WCF dialog:
If you set AbapDebug to "true", you have to log on with a Dialog user.
...you might have set AbapDebug to "true", like I did in my WCF URI "sap://CLIENT=800;LANG=DE;@A/sap.host.de/00?ListenerDest=ABC&ListenerProgramId=DEF&RfcSdkTrace=False&AbapDebug=True"Connecting to the LOB system has failed.Details: ErrorCode=RFC_OK. ErrorGroup=RFC_ERROR_LOGON_FAILURE. SapErrorMessage=Melden Sie sich mit einem Dialogbenutzer an. AdapterErrorMessage=..
If you set AbapDebug to "true", you have to log on with a Dialog user.
Friday, January 24, 2014
WCF LOB Adapter Exception: "Connect from SAP gateway to RFC server failed"
If the WCF LOB Adapter Pack receives a RFC_ERROR_COMMUNICATION from SAP saying "Connect from SAP gateway to RFC server failed", you have to double check that all network settings are correct so that send and receive are possible. In our case that NATting of the response did not reach us, while we could track our call successfully on the firewall.
Other reasons might be SAP configuration or missing entries in services file.
Other reasons might be SAP configuration or missing entries in services file.
Thursday, November 14, 2013
Encrypt XML Content with X.509 Certificates
On MSDN, there is some help how to encrypt XML elements with certificates, for example here: How To: Encrypt XML Element with X.509 Certificates
However, what is missing, is advice about how to encrypt XML content, i.e. in the famous XML example
<root>
<creditcard>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</creditcard>
</root>
you might not want to decrypt the whole <creditcard> element, but rather its two subnodes <number> and <expiry>. This is necessary when the http://www.w3.org/2001/04/xmlenc#Content standard is used.
I just reuse the code from the how-to-article and add specific lines for the content-encryption:
For Encryption, you use this method:
public static XmlDocument Encrypt(XmlDocument doc, string elementToEncryptName, X509Certificate2 cert)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("doc");
if (elementToEncryptName == null)
throw new ArgumentNullException("elementToEncryptName");
if (cert == null)
throw new ArgumentNullException("cert");
////////////////////////////////////////////////
// Find the specified element in the XmlDocument
// object and create a new XmlElemnt object.
////////////////////////////////////////////////
XmlElement elementToEncrypt = doc.GetElementsByTagName(elementToEncryptName)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
//////////////////////////////////////////////////
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
//////////////////////////////////////////////////
// Create a 256 bit Rijndael key.
RijndaelManaged sessionKey = new RijndaelManaged();
sessionKey.KeySize = 128;
// Set RSA crypto provider to certificate's public key
RSACryptoServiceProvider alg = (RSACryptoServiceProvider) cert.PublicKey.Key;
// Encrypt the content of the XML element
EncryptedXml eXml = new EncryptedXml();
byte[] encryptedElement = eXml.EncryptData(Encoding.UTF8.GetBytes(elementToEncrypt.InnerXml), sessionKey);
////////////////////////////////////////////////
// Construct an EncryptedData object and populate
// it with the desired encryption information.
////////////////////////////////////////////////
EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementContentUrl;
//edElement.Id = encryptionElementId;
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES128Url);
// Encrypt the session key and add it to an EncryptedKey element.
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, alg, true);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSAOAEPUrl);
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
//// Create a new KeyInfo element.
KeyInfo ki = new KeyInfo();
ki.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.EndCertOnly));
// Add the KeyInfoName element to the
// EncryptedKey object.
ek.KeyInfo = ki;
// Add the encrypted element data to the
// EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement;
////////////////////////////////////////////////////
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
////////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, true);
sessionKey.Clear();
return doc;
}
The decryption can be taken from the MSDN example:
public static XmlDocument Decrypt(XmlDocument doc)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("doc");
// Decrypt the XML document, with the help of the private key from the certification store
EncryptedXml exml = new EncryptedXml(doc);
exml.DecryptDocument();
return doc;
}
The calling code will be:
X509Certificate2 cert = new X509Certificate2("xxx.pfx", "xxx");
var encrypted = Encrypt(xmlDoc, "creditcard", cert);
var decrypted = Decrypt(encrypted);
However, what is missing, is advice about how to encrypt XML content, i.e. in the famous XML example
<creditcard>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</creditcard>
</root>
you might not want to decrypt the whole <creditcard> element, but rather its two subnodes <number> and <expiry>. This is necessary when the http://www.w3.org/2001/04/xmlenc#Content standard is used.
I just reuse the code from the how-to-article and add specific lines for the content-encryption:
For Encryption, you use this method:
public static XmlDocument Encrypt(XmlDocument doc, string elementToEncryptName, X509Certificate2 cert)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("doc");
if (elementToEncryptName == null)
throw new ArgumentNullException("elementToEncryptName");
if (cert == null)
throw new ArgumentNullException("cert");
////////////////////////////////////////////////
// Find the specified element in the XmlDocument
// object and create a new XmlElemnt object.
////////////////////////////////////////////////
XmlElement elementToEncrypt = doc.GetElementsByTagName(elementToEncryptName)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
//////////////////////////////////////////////////
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
//////////////////////////////////////////////////
// Create a 256 bit Rijndael key.
RijndaelManaged sessionKey = new RijndaelManaged();
sessionKey.KeySize = 128;
// Set RSA crypto provider to certificate's public key
RSACryptoServiceProvider alg = (RSACryptoServiceProvider) cert.PublicKey.Key;
// Encrypt the content of the XML element
EncryptedXml eXml = new EncryptedXml();
byte[] encryptedElement = eXml.EncryptData(Encoding.UTF8.GetBytes(elementToEncrypt.InnerXml), sessionKey);
////////////////////////////////////////////////
// Construct an EncryptedData object and populate
// it with the desired encryption information.
////////////////////////////////////////////////
EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementContentUrl;
//edElement.Id = encryptionElementId;
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES128Url);
// Encrypt the session key and add it to an EncryptedKey element.
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, alg, true);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSAOAEPUrl);
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
//// Create a new KeyInfo element.
KeyInfo ki = new KeyInfo();
ki.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.EndCertOnly));
// Add the KeyInfoName element to the
// EncryptedKey object.
ek.KeyInfo = ki;
// Add the encrypted element data to the
// EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement;
////////////////////////////////////////////////////
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
////////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, true);
sessionKey.Clear();
return doc;
}
The decryption can be taken from the MSDN example:
public static XmlDocument Decrypt(XmlDocument doc)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("doc");
// Decrypt the XML document, with the help of the private key from the certification store
EncryptedXml exml = new EncryptedXml(doc);
exml.DecryptDocument();
return doc;
}
The calling code will be:
X509Certificate2 cert = new X509Certificate2("xxx.pfx", "xxx");
var encrypted = Encrypt(xmlDoc, "creditcard", cert);
var decrypted = Decrypt(encrypted);
Subscribe to:
Posts (Atom)