Showing posts with label Microsoft SAP Integration. Show all posts
Showing posts with label Microsoft SAP Integration. Show all posts

Tuesday, 6 September 2011

Fix - Duet Enterprise/SharePoint 2010 Exception - "System.ServiceModel.QuotaExceededException: The size necessary to buffer the XML content exceeded the buffer quota."

When attempting to establish communications between SAP and SharePoint (using the Duet Enterprise Claims provider) today at a new client, I encountered the following exception in SharePoint (as per the ULS logs):

InnerException 1: System.ServiceModel.QuotaExceededException: The size necessary to buffer the XML content exceeded the buffer quota. Server stack trace:

at System.ServiceModel.Channels.BufferedOutputStream.WriteCore(Byte[] buffer, Int32 offset, Int32 size)
at System.Xml.XmlStreamNodeWriter.FlushBuffer()
at System.Xml.XmlBinaryNodeWriter.FlushBuffer()
at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset)
at System.Xml.XmlBinaryNodeWriter.UnsafeWriteText(Char* chars, Int32 charCount)
at System.Xml.XmlBinaryNodeWriter.WriteText(Char[] chars, Int32 offset, Int32 count)
at System.Xml.XmlBaseWriter.WriteChars(Char[] chars, Int32 offset, Int32 count)
at System.Xml.XmlBinaryWriter.WriteTextNode(XmlDictionaryReader reader, Boolean attribute)
at System.Xml.XmlDictionaryWriter.WriteNode(XmlDictionaryReader reader, Boolean defattr)
at System.ServiceModel.Channels.ReceivedFault.CreateFault12Driver(XmlDictionaryReader reader, Int32 maxBufferSize, EnvelopeVersion version)
at System.ServiceModel.Channels.MessageFault.CreateFault(Message message, Int32 maxBufferSize)
at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.TryGetSecurityFaultException(Message faultMessage, Exception& faultException)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown
at [0]:
at Microsoft.SharePoint.BusinessData.SystemSpecific.Wcf.WcfSystemUtility.Execute(Object[] args)
at Microsoft.SharePoint.BusinessData.SystemSpecific.Wcf.WcfSystemUtility.ExecuteStatic(IMethodInstance methodInstance, ILobSystemInstance lobSystemInstance, Object[] args, IExecutionContext context)
at Microsoft.SharePoint.BusinessData.Runtime.DataClassRuntime.ExecuteInternalWithAuthNFailureRetry(ISystemUtility systemUtility, IMethodInstance methodInstanceToExecute, IMethod methodToExecute, ILobSystemInstance lobSystemInstance, ILobSystem lobSystem, IParameterCollection nonReturnParameters, Object[] overrideArgs)
at Microsoft.SharePoint.BusinessData.Runtime.DataClassRuntime.ExecuteInternal(IDataClass thisDataClass, ILobSystemInstance lobSystemInstance, ILobSystem lobSystem, IMethodInstance methodInstanceToExecute, IMethod methodToExecute, IParameterCollection nonReturnParameters, Object[]& overrideArgs)

I also received the following exception immediately after the above exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ServiceModel.QuotaExceededException: The size necessary to buffer the XML content exceeded the buffer quota. Server stack trace:

at System.ServiceModel.Channels.BufferedOutputStream.WriteCore(Byte[] buffer, Int32 offset, Int32 size)
at System.Xml.XmlStreamNodeWriter.FlushBuffer()
at System.Xml.XmlBinaryNodeWriter.FlushBuffer()
at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset)
at System.Xml.XmlBinaryNodeWriter.UnsafeWriteText(Char* chars, Int32 charCount)
at System.Xml.XmlBinaryNodeWriter.WriteText(Char[] chars, Int32 offset, Int32 count)
at System.Xml.XmlBaseWriter.WriteChars(Char[] chars, Int32 offset, Int32 count)
at System.Xml.XmlBinaryWriter.WriteTextNode(XmlDictionaryReader reader, Boolean attribute)
at System.Xml.XmlDictionaryWriter.WriteNode(XmlDictionaryReader reader, Boolean defattr)
at System.ServiceModel.Channels.ReceivedFault.CreateFault12Driver(XmlDictionaryReader reader, Int32 maxBufferSize, EnvelopeVersion version)
at System.ServiceModel.Channels.MessageFault.CreateFault(Message message, Int32 maxBufferSize)
at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.TryGetSecurityFaultException(Message faultMessage, Exception& faultException)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown
at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at BCSServiceProxy.IWXManageCustomerIn.FindCustomerByElements(FindCustomerByElementsRequest request)
at BCSServiceProxy.WXManageCustomerInClient.BCSServiceProxy.IWXManageCustomerIn.FindCustomerByElements(FindCustomerByElementsRequest request)
at BCSServiceProxy.WXManageCustomerInClient.FindCustomerByElements(BPCCustGetAll BPCCustGetAll) -
-- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Microsoft.SharePoint.BusinessData.SystemSpecific.Wcf.WcfSystemUtility.Execute(Object[] args)

With my experiences with SAP, buffer overflows normally indicate that a large Java stack trace is coming back from SAP and it overloads the WCF client buffer which is expecting a normal SOAP response.

To see the real exception, I tried to use Wireshark (http://www.wireshark.org/)- but our setup had (not by choice) Netweaver and SharePoint on the same box - and Wireshark cannot listen to localhost traffic.

As usual, the Fiddler Tool HTTP proxy (http://www.fiddler2.com/fiddler2/) came to the rescue- as it can actually listen to traffic occurring between applications on the same machine. When running Fiddler and listening to the https traffic, it popped up with an exception regarding certificate errors:

Session #24: The remote server (ausyd-a-sh1) presented a certificate that did not validate, due to RemoteCertificateChainErrors.

SUBJECT: CN=ausyd-a-sh1, OU=I0020310622, OU=SAP Web AS, O=SAP Trust Community, C=DE
ISSUER: CN=ausyd-a-sh1, OU=I0020310622, OU=SAP Web AS, O=SAP Trust Community, C=DE
EXPIRES: 1/01/2038 11:00:01 AM
I went to the server url in Internet Explorer and sure enough there was a certificate error occurring when going to the URL of the SAP server - at https://ausyd-a-sh1:8001/


Basically this exception was occurring because the SAP SSL certificate was not in the Trusted Root Certification Authorities store in Windows. To import it, I just opened mmc at a command prompt and added the certificates Snap-In (via File - Add/Remove Snap In... - for the Local Computer.

In Certificates (Local Computer), I went to Trusted Root Certification Authorities - Certificates and Imported the SAP Certificate in.

However, I kept getting the same exception. On closer inspection, the thumbprint of the SharePoint Security Token Service (STS) certificate was not the same as what had been imported into SAP - this was becuase SharePoint had been reinstalled - which changes the SharePoint STS certificate (i.e. SharePoint STS certificates are install specific).

I then received the following Exception - which was related to user mappings in SAP:

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail


To resolve this last exception:
1) We made sure SharePoint:: was used to prefix the name in SAP (in the VUSEREXITID table) - this prefix would normally be added through the Duet Active Directory user import job (specific to Duet Enterprise) - but we don't have AD in our environment.
2) We then updated the STS certificate in the SAP Certificate store (using SAP transaction /nstrust)
3) Used the /nsaml2 transaction to update the certificate used there as well.

This resolved all our SAP to SharePoint communication issues.

DDK

Wednesday, 15 June 2011

Duet Enterprise Integration - Fix for "System.ArgumentException: An item with the same key has already been added due to Extra Content Type Headers" in SharePoint 2010 ULS Logs

If you're not aware, Duet Enterprise (http://www.duet.com/) is an integration framework and infrastructure for providing SAP to SharePoint integration - of task lists, SAP workflows, SAP reports, SAP security and SAP data (through SharePoint BCS).

I recently had an issue with a Duet Enterprise deployment for one of my clients. When retrieving data, the out of the box web parts were failing and dropping the following Exception in the SharePoint 2010 ULS Logs:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentException: An item with the same key has already been added. Server stack trace:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at System.Xml.ContentTypeHeader.ParseValue()
at System.Xml.ContentTypeHeader.get_MediaType()
at System.Xml.XmlMtomReader.ReadRootContentTypeHeader(ContentTypeHeader header, Encoding[] expectedEncodings, String expectedType)
at System.Xml.XmlMtomReader.Initialize(Stream stream, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize)
at System.Xml.XmlMtomReader.SetInput(Stream stream, Encoding[] encodings, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize, OnXmlDictionaryReaderClose onClose)
at System.Xml.XmlDictionaryReader.CreateMtomReader(Byte[] buffer, Int32 offset, Int32 count, Encoding[] encodings, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize, OnXmlDictionaryReaderClose onClose)
at System.ServiceModel.Channels.MtomMessageEncoder.MtomBufferedMessageData.TakeXmlReader()
at System.ServiceModel.Channels.BufferedMessageData.GetMessageReader()
at System.ServiceModel.Channels.BufferedMessage..ctor(IBufferedMessageData messageData, RecycledMessageState recycledMessageState, Boolean[] understoodHeaders)
at System.ServiceModel.Channels.MtomMessageEncoder.ReadMessage(ArraySegment`1 buffer, BufferManager bufferManager, String contentType)
at System.ServiceModel.Channels.HttpInput.DecodeBufferedMessage(ArraySegment`1 buffer, Stream inputStream)
at System.ServiceModel.Channels.HttpInput.ReadBufferedMessage(Stream inputStream)
at System.ServiceModel.Channels.HttpInput.ParseIncomingMessage(Exception& requestException)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)

at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown
at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at BCSServiceProxy.IWXManageCustomerIn.FindCustomerByElements(FindCustomerByElementsRequest request)
at BCSServiceProxy.WXManageCustomerInClient.BCSServiceProxy.IWXManageCustomerIn.FindCustomerByElements(FindCustomerByElementsRequest request)
at BCSServiceProxy.WXManageCustomerInClient.FindCustomerByElements(BPCCustGetAll BPCCustGetAll) -


-- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Microsoft.SharePoint.BusinessData.SystemSpecific.Wcf.WcfSystemUtility.Execute(Object[] args)


I immediately thought that it was something wrong with the data. Because Duet uses HTTPS & SSL for all communications between SAP Netweaver 7.02+ and SharePoint 2010, I had to decrypt the traffic to work out what was going on. Running the calls on the SAP site returned blank results. To get WireShark (http://www.wireshark.org/) decrypting the traffic we had to generate a certificate in SAP with a private key. We had to get the key out in PKCS#12 format and then convet it to the .PEM format that wireshark accepts - similar to what is desribed here:
http://htluo.blogspot.com/2009/01/decrypt-https-traffic-with-wireshark.html
We used "sapgenpse" in SAP to generate the certificate with the private key - as per the screenshot below:
Once the key was set in WireShark preferences, you will see many of the packets being processed being marked as green. You can then right click on them and choose "Follow SSL Stream" on your web request - and you will see the decrypted version of your HTTPS traffic.

Turns out, the issue was with SAP retuning duplicate Content headers in the response to SharePoint BCS (as per the screenshot).

There is a SAP Note to fix this very issue - "SAP Note 1539888 - Composite Note -Duet Enterprise Installation Wizard". You will need a SAP Service Market place to log on to get this note.

DDK

Duet Enterprise - [ERROR] - "Cannot Merge Contents of LobSystem (External System) with Name 'Account' as it appears to be different from preexisting LobSystem in the current load context." - Fix

When using Duet Enterprise (for SAP and SharePoint 2010 Integration) you may receive the following exception when importing Duet Business Connectivity Service Models (as per http://technet.microsoft.com/en-us/library/gg394164.aspx):

"Duet Enterprise - [ERROR] - Cannot Merge Contents of LobSystem (External System) with Name 'Account' as it appears to be different from preexisting LobSystem in the current load context.".

This may occur even after you've used Duet to remove all of the models with the commandline DuetConfig.exe /RemoveBDC.

You can fix this issue by going to:

This may occur even after you've used Duet to remove all of the models with the commandline DuetConfig.exe /RemoveBDC.

SharePoint Central Administration - Your Business Connectivity Services (BCS) Service Application - Manage
Then:

1) Choose "Models" in the "View" dropdown and remove any Duet models if they still exist
2) Choose "External Systems" and Remove any Duet External systems (this is the external system referred to in the error).


You should now successfully be able to import the Duet Enterprise 1.0 out of the box Models.

DDK

Thursday, 13 January 2011

Duet Enterprise Released and Available for Download on MSDN!


After a long wait, Duet Enterprise 1.0, Microsoft and SAP's premier framework for SAP integration with SharePoint 2010 and Office 2010 has finally been released on MSDN. At Oakton, I've been doing SAP to SharePoint integration for the last 18 months so a framework and guidance like this is quite exciting.

If you have an MSDN subscription, you can get it from here:
https://msdn.microsoft.com/en-us/subscriptions/securedownloads/default.aspx
Also note that this is not the ONLY way to integrate and alternatives like just using out of the box SharePoint 2010 BCS (Business Connectivity Services), or the Biztalk SAP Adapters Packs are the way to go. It really depends on your requirements. Duet Enterprise provides a few sample scenarios as part of the framework to get you started as a model for integration guidance.

Here is a high level architecture diagram in case you're completely unfamiliar with the product (it requires and utilizes SharePoint 2010 BCS under the covers) :


Basically, Duet Enterprise gives you a head-start in terms of plumbing when you start a new SAP-SharePoint Integration project. It includes the following:
  1. A framework for building SAP-SharePoint-Office Integration Scenarios
  2. SAP Workflow Integration from SharePoint (though this is not using SAP BPM)
  3. Some Pre-canned Site Templates for certain scenarios like retrieving Customer Information through SharePoint
  4. Components to allow retrieval of SAP reports from SharePoint
  5. Duet Enterprise Document collaboration functionality
  6. Communication Monitoring and Support Components to make troubleshooting easier.
  7. A security framework for authorization purposes.
There are a couple of known issues with the release (not absolute showstoppers for my clients, but you should be aware of them):

Known Issue (1):


Aggregator Web Part will not work across the Web Apps having different Authentication
Description: Aggregator Web Part is deliverable of the Duet Workflow Feature. This Web Part will provide a support to view the Task for particular user across the Site Collection and Web App.


Current Limitation to the web Part - This Web Part cannot support the Scenario of Web Part is created in one type of authenticated Web App and the Rootsite Parameter pointing to the different Web App which if different authentication. Eg: Web Part placed in Win Claims based Web App can pull/show the task assigned to the User under another rootsite created in another win claim Web App but it will not work for the rootsite created in Win Legacy Web App and then the Page on which Web Part is placed will not accessible.


Viewing the Tasks from the Cross Auth Web App in aggregator web part is not Supported


Workaround: To Fix the Page which is caused by cross authentication issue in Aggregator Web Part in the Page is to remove the web Part through SPD and then add the Aggregator web Part and point only to the rootsite accessible and same authenticated.


Known Issue (2):
Task with attachments cannot be created and assigned to users in Turkish environment.


The following languages are included in this product: Arabic, Chinese-Simplified, Chinese-Traditional, English, French, German, Hebrew, Italian, Japanese, Portuguese-Brazil, Russian, Spanish, and Turkish.


Microsoft will be having some official launch announcements soon and I'll be covering Duet in more detail in later blog posts.

DDK

Tuesday, 16 November 2010

Exception when connecting WCF client to SAP WS-Reliable Messaging enabled web service - "Invalid WS-RM message. There are no WS-RM headers within SOAP message."

One of the SAP Business Process Management (SAP BPM) WSDLs consumed by a SharePoint web part was re-created by a member of our development team yesterday. My .NET client application then refused to operate with the new SAP SOAP endpoints and began to spit out the following error in the SOAP response (as captured by WireShark):

<SOAP-ENV:Envelope xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Invalid WS-RM message. There are no WS-RM headers within SOAP message.</faultstring>
<faultactor>Server</faultactor>
<detail>
<yq1:com.sap.engine.services.wsrm.exceptions.ReliableMessagingException xmlns:yq1="http://sap-j2ee-engine/error">Invalid WS-RM message. There are no WS-RM headers within SOAP message.</yq1:com.sap.engine.services.wsrm.exceptions.ReliableMessagingException>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

WS-RM (WS-Reliable messaging) is a protocol that allows messages to be transferred reliably between nodes that implement this protocol in the presence of software component, system, or network failures.

MSDN has a brief mention of the potential problem here:
http://msdn.microsoft.com/en-us/library/ff710229.aspx
As described in the article:

"Both products also support WS-ReliableMessaging 1.0. However, the implementations are not interoperable. Do not use WS-ReliableMessaging 1.0 when exchanging messages between SAP and .NET Framework."
In fact, even though SAP and WCF both support WS-ReliableMessaging 1.0, you cannot use it - it will just give you an error like the above. You can either turn WS-RM off or use version 1.1 of WS-ReliableMessaging for your SAP to .NET WCF communications.

DDK

Monday, 23 August 2010

"The one-way operation returned a non-null message with Action=''." Error when calling a SAP Web Service from a MS .NET WCF Client

I received the following exception today when attempting to call a new service in SAP PI (Process Integration), the equivalent of MS Biztalk in the SAP world:

The one-way operation returned a non-null message with Action=''.

Using WireShark, I looked in on the HTTP traffic:



Looking closely, it turns out that SAP returns an Empty SOAP body in the response (ie NOT just a blank string/nothing) as per the screenshot below:


As discussed here http://www.go4answers.com/Example/exception-while-making-way-call-wcf-56416.aspx   , there is a hotfix for the issue that can be found at  http://forums.sdn.sap.com/message.jspa?messageID=9031931 
- however the simplest fix is to handle the System.ServiceModel.ProtocolException and effectively ignore this exception.

//Execute call
            var client = WCFClientFactory.CreateClient(CredentialType.PI);
            try
            {
                client.SI_EmpSave_Async_Out(empObj);
            }
            catch (System.ServiceModel.ProtocolException ex) //Just handle this specific exception. 
            //Otherwise bubble up
            {
                if (!ex.Message.Equals("The one-way operation returned a non-null message with Action=''."))
                    throw;
            }

DDK

Tuesday, 13 July 2010

Fix for WCF Client Proxy deserialization issue (related to svcutil.exe) when referencing Non-Microsoft Services (e.g. SAP services from SharePoint) - "Unable to generate a temporary class (result=1)."

When creating a client proxy for the SAP Service Registry (so I could dynamically set endpoints for my other WCF client calls), I had the following issue today when running a unit test:

Test method DDK.UnitTest.UDDIProxyTest.GetEndPointBasicTest threw exception: System.ServiceModel.CommunicationException: There was an error in serializing body of message findServiceDefinitionsRequest: 'Unable to generate a temporary class (result=1).

error CS0030: Cannot convert type 'DDK.BusinessService.UDDIRegistrySearchProxy.classificationPair[]' to 'DDK.BusinessService.UDDIRegistrySearchProxy.classificationPair'

This error is a result of issues with .NET commandline tools wsdl.exe or svcutil.exe incorrectly creating multidimensional arrays in the strongly typed proxy class (Reference.cs), as per screenshot below:


Cause:
This problem occurs when the svcutil.exe or the Web Services Description Language Tool (Wsdl.exe) are used to generate the client information. When you publish a schema that contains nested nodes that have the maxOccurs attribute set to the "unbounded" value, these tools create multidimensional arrays in the generated datatypes.cs file. Therefore, the generated Reference.cs file contains incorrect types for the nested nodes.

The problem and fix is described in the following kb articles:
http://support.microsoft.com/kb/891386 and 
http://support.microsoft.com/kb/326790/en-us

The fix is to basically change the multi-dimensional array in the Reference.cs file related to your service reference to a single dimension.
e.g.

classificationPair[] [] 
instead becomes
classificationPair[] 

Note that you will of course need to update all parameter references in the Reference.cs file to this multi-dimensional array, not just the original declarations.
DDK

Wednesday, 5 May 2010

Error when creating Purchase Order using SAP BAPI_PO_CREATE1 Error - "Material purchase order" is not allowed"

If you get the following error when attempting to create a Purchase Order with Project/WBS related line items:

No instance of object type PurchaseOrder has been created. External reference: ; Purchase order still contains faulty items ; "Material purchase order" is not allowed


The issue may be that the Project (to which you are trying to allocate the cost) is either unapproved or Inactive. Check the status of the WBS or Project that you have chosen and try again.

DDK

Thursday, 29 April 2010

www.ddkonline.com featured in SAP official documentation!

I was referred to the following article by the SAP Project tech lead in my current project. Funny part is that this blog is actually one of the references detailed at the end of the article! It was interesting to see the look on his face!

Ahhhh, it's nice to be appreciated sometimes - even though I'm still feel like a bit of a Microsoft foreigner in the SAP world -

http://www.sdn.sap.com/irj/scn/index?rid=/library/uuid/40dbb54f-5c28-2d10-ad94-f812a548dc07




DDK

Thursday, 18 February 2010

"javax.naming.Name is an interface, and JAXB can't handle interfaces" - Deployment Issues when using Eclipse to Deploy a JavaBean Web Service in to SAP Java AS

Hit an issue today when deploying an Enterprise JavaBen (EJB) web service today via Eclipse. I was exposing the SAP UWL through the UWL API - but it wouldn't deploy. I kept getting the following error in the deployment trace:
[com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
javax.naming.Name is an interface, and JAXB can't handle interfaces.]




Looking at all my @WebMethod annotations, there didn't seem to be any obvious problem:

@WebMethod(operationName="Test", exclude=false)
public int Test() throws UWLException, NamingException
{
IUser user = com.sap.security.api.UMFactory.getAuthenticator().getLoggedInUser();

I examined the Eclipse debug trace to about line 100, I found an issue - that there was a problem with serialization:
Unable to generate serialization framework for web service UWLFacadeService

Turns out the deployment exception was really trying to tell me that "NamingException" is not serializable - and so cannot be surfaced through a Web Service. I changed my method to handle the NamingException errors internally (via try..catching them), and removed the throws statement on the Method. Without the requirement to serialize the exception, the deployment errors for my test EJB web service went away!


......

Wednesday, 17 February 2010

SAP Purchase Order Creation Error via BAPI - "In case of account assignment, please enter acc. assignment data for item" Some of the quirks of BAPI_PO_CREATE1 (via a custom BAPI)

I have been calling the SAP BAPI "BAPI_PO_CREATE1" indirectly via a custom BAPI created by one of the other guys in my development team. However, I have found out some things about this the hard way:
  1. If you haven't applied all the most recent SAP fixes to your install since mid 2009 til now (Feb 2010), you will be in a world of pain as it is IMPOSSIBLE to post a service item entry (ItemCat = "D" against a purchase order header). You will always get the following error (aka error 06 436):
    "'In case of account assignment, please enter acc. assignment data for item'"
    I spent hours butting my head against the wall because of this obscure bug when trying to call our custom BAPI. Some basic details are in the SAP Note 1373051 below (As per the image, German is considered to be the 'Master' language'!)  I believe this problem would also occur with the SAP Purchase Order Enterprise services as they call the BAPI directly.

  2. As described in https://forums.sdn.sap.com/thread.jspa?threadID=877029, to get the BAPI_PO_CREATE1 to work at all with a service item (Item Category = 'D' (Service) and Spend type of 'K' (Cost Centre), you need to create all the correct entries in the following BAPI tables:
    a) POITEM
    b) POITEMX (Change Flags - must have entries for all columns that have data)
    c) POACCOUNT
    d) POACCOUNTX (Change Flags - must have entries for all columns that have data)
    e) POSERVICES (This doesn't have a matching change flag table) - which goes into the ESLL table in SAP)
    f) POSRVACCESSVALUES

    If you don't put this information in, you will get the error "Please maintain services or limits"

    As described in the link:

    For service items: POITEM-PCKG_NO = ‘0000000001’. (assign package no as a dummy number)

    Set PCKG_NO flag in POITEMX table as ‘X’.

    Package No is the link that connect POITEM table to POACCOUNT table through tables POSERVICES and POSRVACCESSVALUES.

    Set POACCOUNT-SERIAL_NO to ‘01’.

    Set same PCKG_NO to ‘0000000001’ in POSERVICES table. Maintain two entries in POSERVICES table like this:

    WA_POSERVICES-PCKG_NO = ‘0000000001’.
    WA_POSERVICES-LINE_NO = ‘0000000001’.
    WA_POSERVICES-OUTL_IND = ‘X’.
    WA_POSERVICES-SUBPCKG_NO = ‘0000000003’. (Dummy No.)
    WA_POSERVICES-QUANTITY = ‘100.000’.
    WA_POSERVICES-BASE_UOM = ‘EA’.
    WA_POSERVICES-PRICE_UNIT = ‘1’.
    WA_POSERVICES-GR_PRICE = ‘100.000’. 
    WA_POSERVICES-SHORT_TEXT = ‘SERVICE TEST’.
    APPEND WA_POSERVICES TO IT_POSERVICES.
    
    WA_POSERVICES- PCKG_NO = ‘0000000003’.
    WA_POSERVICES- LINE_NO = ‘0000000002’.
    WA_POSERVICES-QUANTITY = ‘10.000’.
    WA_POSERVICES- BASE_UOM = ‘EA’.
    WA_POSERVICES--PRICE_UNIT = ‘1’.
    WA_POSERVICES-GR_PRICE = ‘100.000’. 
    WA_POSERVICES-SHORT_TEXT = ‘SERVICE 1’.
    WA_POSERVICES-MATL_GROUP = ‘0012’.
    APPEND WA_POSERVICES TO IT_POSERVICES.
    
    Set PCKG_NO as SUB_PCKG_NO in table POSRVACCESSVALUES this:
    WA_POSRVACCESSVALUES-PCKG_NO = ‘0000000003’.
    WA_POSRVACCESSVALUES-LINE_NO = ‘0000000002’.
    WA_POSRVACCESSVALUES-SERNO_LINE = ‘01’.
    WA_POSRVACCESSVALUES-SERIAL_NO = ‘01’.
    WA_POSRVACCESSVALUES-QUANTITY = ’10.000’.
    APPEND WA_POSRVACCESSVALUES TO IT_ POSRVACCESSVALUES.
  3. Also note that if you get errors relating to "Cost Object XXXXXX does not exist on DD.MM.YYYY", then you have likely set the value of "CostObject" on the Account Parameter. Depending on your config, it is typically OK to just leave this blank, and feed your cost object information into the relevant column on the Account Table (Asset/Cost Centre/Project etc).
PS. Why not use Enterprise Services rather than the custom BAPIs? I've more recently learnt that the official best way to do some of these changes (rather than writing custom BAPIs) is to EXTEND the Enterprise Service Extensions (according to Jan from SAP who visited us recently). However there is scant documentation on this so is only really in the realm of "Internal SAP Employees Only" development.

I've found that SAP Enterprise Services are even more complex and are more difficult to customize than the simpler BAPI mechanisms we have established here at my current client. Custom BAPIs have the main disadvantage of duplicating some datatypes (there are global datatypes defined in SAP Enterprise Services) and an increased difficulty in data type governance and consolidation that this entails.

Monday, 1 February 2010

Fix for Error: "The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via." when calling Web Service in SAP

Today I got an exception after using the default bindings generated by the .NET svcutil.exe tool. This occurred as the tool incorrectly detected that it should be using an httpsTransport, and we didn’t have https internally. This was because the SAP wsdl supports both https and http bindings.

To get around this issue, I changd the binding to use 'security mode="TransportCredentialOnly"':

<binding name="Binding_T_HTTPS_A_HTTP_Z_CONTRACT_FIND_CE_ERP">
          <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
              messageVersion="Soap11" writeEncoding="utf-8">
            <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          </textMessageEncoding>
          <httpsTransport manualAddressing="false" maxBufferPoolSize="524288"
              maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Basic"
              bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
              keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous"
              realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
              useDefaultWebProxy="true" requireClientCertificate="false" />
        </binding>

I changed it to use a basic authentication proxy, and it works (not secure – but this is for an internal dev box, not using https). This suits my purpose:

<basicHttpBinding>
        <binding name="SAPBasicHttpBinding" closeTimeout="00:01:00"
  openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
  allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
  maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
  messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
  useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="TransportCredentialOnly" >
            <transport clientCredentialType="Basic" proxyCredentialType="Basic"
              realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>

Wednesday, 23 December 2009

Steps for Setting up an SAP Enterprise Service to be consumed from an external application such as SharePoint

  1. Go to SAP GUI
  2. Run transaction /nsproxy. This will show what services are available to expose as endpoints, including your own custom created services (otherwise you have to download or create it).
  3. In transaction /nsproxy, search for your service by clicking the binoculars icon e.g. the EmployeeBasicDataByEmployeeQueryResponse_In Service in the Enterprise Service repository. Alternatively you can browse for the Service you are looking for (as pictured above).
  4. Note that there are no service endpoints defined if you go to the WSDL tab when viewing the service details.
  5. Go to transaction /nsoamanager in SAP GUI
  6. Go to the “Application And Scenario Communication” Tab ->; “Single Service Administration”.
  7. Search for the name of the service as found in sproxy (e.g. EmployeeBasicDataByEmployeeQueryResponse_In)
  8. Select the Service in the search results and click “Apply Selection”. Notice that there are no Bindings Listed if you click on “Display Selected Binding’s WSDL”
  9. Click the "Configurations" tab and Click the "Create Service" button. Enter a service name e.g. EmployeeBasicDataByEmployeeQueryResponse_In_Service, and a description

  10. Also enter a binding name e.g. EmployeeBasicDataByEmployeeQueryResponse_In_Binding, and Click “Apply Settings”
  11. A new set of tabs should appear at the bottom of your screen. Enter details for the provider security
  12. Click “Save”
  13. The service is not activated yet. Even if you try and open the service with SOAP UI at this stage, you will just get errors such as "“Logon Error Message. What has happened? URL http://servername:serverport/sap/bc/srt/xip/sap/ecc_employeebasicdbyemployeeqr/120/employeebasicdatabyemployeequery/employeebasicdatabyemployeequeryresponse_in_binding call was terminated because the corresponding service is not available."
  14. To activate the service, go to transaction /nsicf in SAP GUI
  15. Search for the service e.g. put “EMPLOYEE*” in the service name. This should find your service e.g. “EmployeeBasicDataByEmployeeQuery”
  16. Right click on the service at the bottom level and click Activate, and click on the “Yes” button (yes it is odd that there are 2 – I don’t know the difference).
  17. You should be able to call the web service through SOAP UI or any other web services client or tool (e.g. Visual Studio .NET). Make sure you enter a username and password under the “Auth” tab of the request (if using SOAP UI 3.0).
  18. DONE!

Thursday, 17 December 2009

CX_ST_MATCH_ELEMENT:XSLT exception.System expected element 'MyTableName' SOAP Fault when calling a SAP BAPI remotely via Web Service

If you receive the following error when calling a BAPI in SAP (e.g. from a .NET WCF client or using SOAPUI):

CX_ST_MATCH_ELEMENT:XSLT exception.System expected element 'MyTableName'

Then you have not initialized your variables correctly. In SOAPUI (http://www.soapui.org/), you cannot remove the elements even though instances are marked as "Zero or many". Instead, you have to pad it with empty elements. You cannot have zero elements in the table (even though the XSD seemingly permits it) otherwise the SAP Web service will spit the dummy.





By the same token, you must initialize all your arrays/lists in .NET:
e.g.

try
{
CostObjectSearchService.Z_Bapi_Cost_Object_FindClient client = new CostObjectSearchService.Z_Bapi_Cost_Object_FindClient();
CostObjectSearchService.ZBapiCostObjectFind request = new CostObjectSearchService.ZBapiCostObjectFind();

request.ICompanyCode = "2000";
request.ISpendType = "P";
request.ICostObject = costObjectMatchString;

//Return Object initialization (for Asset, Cost Centre or Work Breakdown Structure (WBS)
// - otherwise returns error from SAP Web Service
// XSLT CX_ST_MATCH_ELEMENT:XSLT exception.System expected element 'MyTableName'
request.TTypeAAsset = new CostObjectSearchService.Zfanla[0];
request.TTypeOkhCostCentre = new CostObjectSearchService.Zfcsks[0];
request.TTypePWbs = new CostObjectSearchService.Zfprps[0];
CostObjectSearchService.ZBapiCostObjectFindResponse response = client.ZBapiCostObjectFind(request);

Wednesday, 16 December 2009

SAP ABAP and Case sensitivity of the LIKE clause Versus the CS (Contains String) Keyword

I had an issue today with a custom BAPI (using SAP's proprietary language ABAP) that would just NOT return values that I knew were in the PRPS.POST1 field. It turned out that it was a case sensitivity problem - as the LIKE clause in SAP is case sensitive. Typically users don't care about case at all and expect that a search for "Test" will return all upper case and lower case variations (e.g. Test, TEst, TEST, etc).

Unfortunately there is no equivalent of the UPPER() statement in standard SQL Syntax. There are 2 workarounds that I'm aware of:
  1. If you are lucky, there is a field with the same name defined for text fields which has all upper case characters and suffixed with "U". For example, the upper case equivalent of POST1 is the POSTU field. You can then do case insensitive searches on the field by forcing your input parameter to upper case.
  2. Use the CS (Contains String) Function instead of the LIKE clause within your select.....endselect

See below for details of a real-world example of how to use the CS clause for case-insensitive searches:

The below custom BAPI function accepts a spend type (e.g. Capital Expenditure (CAPEX), Operational Expenditure (OPEX)) company code parameter and a wildcard search field and then populates different objects based on the spend type. For example, a Spend type of OPEX will return a list of Cost Centres into the T_TYPE_OKH_COST_CENTRE Table as an output, a spend type of Asset will return a list of assets into the the T_TYPE_A_ASSET structure in the output.

Sample Search BAPI Code (ABAP)


FUNCTION Z_BAPI_COST_OBJECT_FIND.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(I_SPEND_TYPE) TYPE EKPO-KNTTP
*" VALUE(I_COMPANY_CODE) TYPE PRPS-PBUKR
*" VALUE(I_COST_OBJECT) TYPE CHAR_50
*" TABLES
*" T_TYPE_P_WBS STRUCTURE ZFPRPS
*" T_TYPE_OKH_COST_CENTRE STRUCTURE ZFCSKS
*" T_TYPE_A_ASSET STRUCTURE ZFANLA
*" EXCEPTIONS
*" EXCEPTION
*"----------------------------------------------------------------------

tables: prps, csks, anla.
data: projdesc(50) TYPE C.

*The below statements use a CS statement rather than the like clause of SQL
*because LIKE is case sensitive in SAP - unfortunately there is no equivalent
* of UPPER() same as in Native SQL Server.

* Get Cost Object of type WBS
* and POSKI LIKE I_COST_OBJECT."
* We need to do a cast
if I_SPEND_TYPE = 'P'.
select * from PRPS
where BELKZ = 'X'
and PBUKR = I_COMPANY_CODE.
* and ( POSID LIKE I_COST_OBJECT or POST1 LIKE I_COST_OBJECT ).
move PRPS-POST1 to projdesc.

if PRPS-TADAT IS INITIAL and
( PRPS-POSID CS I_COST_OBJECT
or projdesc CS I_COST_OBJECT ).
move PRPS-PSPNR to T_TYPE_P_WBS-PSPNR.
move PRPS-POSID to T_TYPE_P_WBS-POSID.
move PRPS-POST1 to T_TYPE_P_WBS-POST1.
append T_TYPE_P_WBS.
endif.
endselect.

* Get Cost Object of type Cost Centre for Retail, Office and Industrial
elseif I_SPEND_TYPE = 'O'.
select * from CSKS
where BUKRS = I_COMPANY_CODE
and ( ABTEI = 'RETAIL'
or ABTEI = 'OFFICE'
or ABTEI = 'INDUSTRIAL'
or ABTEI = '12' ).

if CSKS-NAME1 CS I_COST_OBJECT
or CSKS-KOSTL CS I_COST_OBJECT.

move CSKS-KOKRS to T_TYPE_OKH_COST_CENTRE-KOKRS.
move CSKS-KOSTL to T_TYPE_OKH_COST_CENTRE-KOSTL.
move CSKS-DATBI to T_TYPE_OKH_COST_CENTRE-DATBI.

append T_TYPE_OKH_COST_CENTRE.
endif.

endselect.

* Get Cost Object of type Cost Centre for Marketing
elseif I_SPEND_TYPE = 'K'.
select * from CSKS
where BUKRS = I_COMPANY_CODE
* and ABTEI = 'MARKETING'.
and ABTEI = '12'.

*Conditionally
if CSKS-NAME1 CS I_COST_OBJECT
or CSKS-KOSTL CS I_COST_OBJECT.
move CSKS-KOKRS to T_TYPE_OKH_COST_CENTRE-KOKRS.
move CSKS-KOSTL to T_TYPE_OKH_COST_CENTRE-KOSTL.
move CSKS-DATBI to T_TYPE_OKH_COST_CENTRE-DATBI.
append T_TYPE_OKH_COST_CENTRE.
endif.
endselect.

* Get Cost Object of type Cost Centre for Head Office
elseif I_SPEND_TYPE = 'H'.
select * from CSKS
where BUKRS = I_COMPANY_CODE
and ( ABTEI = 'CORPORATE'
or ABTEI = '12' ).

move CSKS-KOKRS to T_TYPE_OKH_COST_CENTRE-KOKRS.
move CSKS-KOSTL to T_TYPE_OKH_COST_CENTRE-KOSTL.
move CSKS-DATBI to T_TYPE_OKH_COST_CENTRE-DATBI.

append T_TYPE_OKH_COST_CENTRE.

endselect.

* Get Cost Object of type Asset
elseif I_SPEND_TYPE = 'A'.
select * from ANLA
where BUKRS = I_COMPANY_CODE
and ( ANLKL = '13000000'
or ANLKL = '14000000' ).

if ANLA-ANLN1 CS I_COST_OBJECT or
ANLA-TXT50 CS I_COST_OBJECT.
move ANLA-BUKRS to T_TYPE_A_ASSET-BUKRS.
move ANLA-ANLN1 to T_TYPE_A_ASSET-ANLN1.
move ANLA-ANLN2 to T_TYPE_A_ASSET-ANLN2.

append T_TYPE_A_ASSET.
endif.
endselect.

endif.

ENDFUNCTION.


Additional Notes

1) Spaces DO matter around parentheses - the following will NOT work:

INCORRECT:
select * from PRPS
where BELKZ = 'X'
and PBUKR = I_COMPANY_CODE
and (POSKI LIKE I_COST_OBJECT or POST1 = I_COST_OBJECT).
endselect.

CORRECT:
The following is correct syntax
select * from PRPS
where BELKZ = 'X'
and PBUKR = I_COMPANY_CODE
and ( POSKI LIKE I_COST_OBJECT or POST1 = I_COST_OBJECT ).
endselect.



2) All statements should end with a period. (ie similar to the use of the semicolon as a line delimiter in C#)