+ Start a Discussion
Chirag MehtaChirag Mehta 

Apex Callouts to Apex Web Service (Automated S2S), Possible ?

Note : Following is a small development being carried out to demystify salesforce apex webservice and callout features, please correct me if you find my understanding to be incorrect.

 

I am trying to integrate two Salesforce Orgs using Apex. Apex supports both callouts and webservices, then why not leverage both to achieve a 360 connectivity between two Salesforce instances.

 

Org1 : Create a Apex Web Service, Generate its WSDL

global class AccountPlan {

webservice String area;
webservice String region;

//Define an object in apex that is exposed in apex web service
global class Plan {
webservice String name;
webservice Integer planNumber;
webservice Date planningPeriod;
webservice Id planId;
}

webservice static Plan createAccountPlan(Plan vPlan) {

//A plan maps to the Account object in salesforce.com.
//So need to map the Plan class object to Account standard object
Account acct = new Account();
acct.Name = vPlan.name;
acct.AccountNumber = String.valueOf(vPlan.planNumber);
insert acct;

vPlan.planId=acct.Id;
return vPlan;
}

}

 

Org2:  Consume above generated WSDL to generate following Apex Class viz.,AccountPlanClasses

public class AccountPlanClasses {
public class LogInfo {
public String category;
public String level;
private String[] category_type_info = new String[]{'category','http://soap.sforce.com/schemas/class/TCSSAAS/AccountPlan','LogCategory','1','1','false'};
private String[] level_type_info = new String[]{'level','http://soap.sforce.com/schemas/class/TCSSAAS/AccountPlan','LogCategoryLevel','1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/TCSSAAS/AccountPlan','true','false'};
private String[] field_order_type_info = new String[]{'category','level'};
}
public class AllowFieldTruncationHeader_element {
............................
............................

 

Now lets try to connect to Org1 from Org2

AccountPlanClasses.Plan plan = new AccountPlanClasses.Plan();
plan.name = 'Chirag';
plan.planNumber = 111;
AccountPlanClasses.AccountPlan a = new AccountPlanClasses.AccountPlan();
a.createAccountPlan(plan);

 

Execution of above code runs into following error

WebService returned a SOAP Fault: INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session faultcode=sf:INVALID_SESSION_ID faultactor= 

 

I to feel that somewhere I am missing User Credentials to be supplied, but I dont know exactly where. Please guide me to complete this 360 connectivity between two orgs using Apex.

Message Edited by Chirag Mehta on 05-05-2009 06:13 AM
Message Edited by Chirag Mehta on 05-12-2009 07:52 PM
Best Answer chosen by Admin (Salesforce Developers) 
AlexPHPAlexPHP

Hey guys, I've been researching this topic of communicating between multiple Salesforce instances in the past days.

 

With the idea TLF had in mind, here is the extracted parts of the Partner WSDL that I used, which only have the essential parts needed for the login to work:

 

<?xml version="1.0" encoding="UTF-8"?> <definitions targetNamespace="urn:partner.soap.sforce.com" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fns="urn:fault.partner.soap.sforce.com" xmlns:tns="urn:partner.soap.sforce.com" xmlns:ens="urn:sobject.partner.soap.sforce.com"> <types> <schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:partner.soap.sforce.com"> <import namespace="urn:sobject.partner.soap.sforce.com"/> <!-- Our simple ID Type --> <simpleType name="ID"> <restriction base="xsd:string"> <length value="18"/> <pattern value='[a-zA-Z0-9]{18}'/> </restriction> </simpleType> <complexType name="GetUserInfoResult"> <sequence> <element name="accessibilityMode" type="xsd:boolean"/> <element name="currencySymbol" type="xsd:string" nillable="true"/> <element name="orgDefaultCurrencyIsoCode" type="xsd:string" nillable="true"/> <element name="organizationId" type="tns:ID"/> <element name="organizationMultiCurrency" type="xsd:boolean"/> <element name="organizationName" type="xsd:string"/> <element name="profileId" type="tns:ID"/> <element name="roleId" type="tns:ID" nillable="true"/> <element name="userDefaultCurrencyIsoCode" type="xsd:string" nillable="true"/> <element name="userEmail" type="xsd:string"/> <element name="userFullName" type="xsd:string"/> <element name="userId" type="tns:ID"/> <element name="userLanguage" type="xsd:string"/> <element name="userLocale" type="xsd:string"/> <element name="userName" type="xsd:string"/> <element name="userTimeZone" type="xsd:string"/> <element name="userType" type="xsd:string"/> <element name="userUiSkin" type="xsd:string"/> </sequence> </complexType> <complexType name="LoginResult"> <sequence> <element name="metadataServerUrl" type="xsd:string" nillable="true"/> <element name="passwordExpired" type="xsd:boolean" /> <element name="serverUrl" type="xsd:string" nillable="true"/> <element name="sessionId" type="xsd:string" nillable="true"/> <element name="userId" type="tns:ID" nillable="true"/> <element name="userInfo" type="tns:GetUserInfoResult" minOccurs="0"/> </sequence> </complexType> <!-- Login Message Types --> <element name="login"> <complexType> <sequence> <element name="username" type="xsd:string"/> <element name="password" type="xsd:string"/> </sequence> </complexType> </element> <element name="loginResponse"> <complexType> <sequence> <element name="result" type="tns:LoginResult"/> </sequence> </complexType> </element> <!-- Header Elements --> <element name="SessionHeader"> <complexType> <sequence> <element name="sessionId" type="xsd:string"/> </sequence> </complexType> </element> <element name="LoginScopeHeader"> <complexType> <sequence> <element name="organizationId" type="tns:ID"/> <element name="portalId" type="tns:ID" minOccurs="0"/> </sequence> </complexType> </element> <element name="CallOptions"> <complexType> <sequence> <element name="client" type="xsd:string" nillable="true"/> <element name="defaultNamespace" type="xsd:string" nillable="true"/> </sequence> </complexType> </element> </schema> </types> <!-- Header Message --> <message name="Header"> <part element="tns:LoginScopeHeader" name="LoginScopeHeader"/> <part element="tns:SessionHeader" name="SessionHeader"/> <part element="tns:CallOptions" name="CallOptions"/> <part element="tns:QueryOptions" name="QueryOptions"/> <part element="tns:AssignmentRuleHeader" name="AssignmentRuleHeader"/> <part element="tns:MruHeader" name="MruHeader"/> <part element="tns:EmailHeader" name="EmailHeader"/> <part element="tns:UserTerritoryDeleteHeader" name="UserTerritoryDeleteHeader"/> <part element="tns:DebuggingHeader" name="DebuggingHeader"/> <part element="tns:DebuggingInfo" name="DebuggingInfo"/> </message> <!-- Fault Messages --> <message name="ApiFault"> <part name="fault" element="fns:fault"/> </message> <message name="LoginFault"> <part name="fault" element="fns:LoginFault"/> </message> <!-- Method Messages --> <message name="loginRequest"> <part element="tns:login" name="parameters"/> </message> <message name="loginResponse"> <part element="tns:loginResponse" name="parameters"/> </message> <!-- Soap PortType --> <portType name="Soap"> <operation name="login"> <documentation>Login to the Salesforce.com SOAP Api</documentation> <input message="tns:loginRequest"/> <output message="tns:loginResponse"/> <fault message="tns:LoginFault" name="LoginFault"/> <fault message="tns:UnexpectedErrorFault" name="UnexpectedErrorFault"/> <fault message="tns:InvalidIdFault" name="InvalidIdFault"/> </operation> </portType> <!-- Soap Binding --> <binding name="SoapBinding" type="tns:Soap"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="login"> <soap:operation soapAction=""/> <input> <soap:header use="literal" message="tns:Header" part="LoginScopeHeader"/> <soap:header use="literal" message="tns:Header" part="CallOptions"/> <soap:body parts="parameters" use="literal"/> </input> <output> <soap:body use="literal"/> </output> <fault name="LoginFault"> <soap:fault name="LoginFault" use="literal"/> </fault> <fault name="UnexpectedErrorFault"> <soap:fault name="UnexpectedErrorFault" use="literal"/> </fault> <fault name="InvalidIdFault"> <soap:fault name="InvalidIdFault" use="literal"/> </fault> </operation> </binding> <!-- Soap Service Endpoint --> <service name="SforceService"> <documentation>Sforce SOAP API</documentation> <port binding="tns:SoapBinding" name="Soap"> <soap:address location="https://www.salesforce.com/services/Soap/u/11.1"/> </port> </service> </definitions>

 

This modified/extracted version of the Partner WSDL should be accepted cleanly by the Apex generator.

 

After doing so, you should be able to call the login operation and obtain the session id of that login and pass it into your webservice as a SessionHeader element as such:

 

 

// new instance of the partner SOAP class partnerSoapSforceCom.Soap myPartnerSoap = new partnerSoapSforceCom.Soap(); // call login partnerSoapSforceCom.LoginResult partnerLoginResult = myPartnerSoap.login('your_username', 'your_password'); // from here, the session id can be accessed via partnerLoginResult.sessionId // now construct a SessionHeader element for your webservice and set the session id webserviceSOAP.SessionHeader_element webserviceSessionHeader = new webserviceSOAP.SessionHeader_element(); webserviceSessionHeader.sessionId = partnerLoginResult.sessionId; // now construct a new instance of your webservice class and set the SessionHeader webserviceSOAP.webserviceClass myWebservice = new webserviceSOAP.webserviceClass(); myWebservice.SessionHeader = webserviceSessionHeader; // at this point you should be connected to your webservice and be able to call methods against it myWebservice.doStuff();

 

What I'm concerned about are the Apex Callout Governor Limits (you can read about these in the Apex reference).

 

Your feedback and input into how fesiable using the Apex Callout to Apex Webservice approach is vs Salesforce to Salesforce would be appreciated!

 

 

All Answers

HarmpieHarmpie
Interesting case. I would expect you to need to make a login call to the second org, and pass along the obtained session Id to the subsequent callouts. You could do so with the input- and outputHttpHeaders. Please lt us know how far you get with this :)
withoutmewithoutme
why not use Salesforce to salesforce?
Chirag MehtaChirag Mehta

S2S involves manual intervention of clicking button to forward record to another Org.Let me take this reply to state what exactly is in my mind and what i am trying to develop.

 

Business Scenario

Suppose a firm is using two Salesforce Orgs for two diff process implementation, due to some reasons it wasnt possible to get both these process implementation on a single org. But there's a functionality which need to be replicated on both org, for instance say you want a particular record to be created on second org too as soon as created on first org.

 

Solutions Possible

Approach 1 : Use S2S

Approach 2:  Apex Callouts to Apex Web Service

 

Apporach 2 is more preferred as it wouldnt require any manual intervention at all. I am trying to build this new feature as this will make our S2S more strong with two options

  • Use exisiting S2S
  • Use Advance S2S which requires coding and will help you automate part of your S2S process


I feel this to be a cool new feature/app which will be helpful in S2S implementation.
Community commuters, what say ?

Chirag MehtaChirag Mehta

Harmpie, Thanks !

 

Can you share that how can we make a login call to the second org? To my knowledge there's no API alike to login in APEX language.

 

Unless i login to second org, i wont be able to fetch session Id. 

 

 

 

TLFTLF
I think you would need to consume the Partner WSDL and generate Apex stubs from it in order to be able to log in to the org that is exposing your web service and get the session ID. Unfortunately, I don't think it is going to be possible. I've tried using the "Generate from WSDL" on the Partner WSDL before and it doesn't work. You'll get errors about complex types not being supported. Also, even if you get past that, you'll probably hit the 100,000 character limit on Apex scripts.
Chirag MehtaChirag Mehta

Are you stating me to perform something like(In Org1)

a. Grab Partner WSDL which will generate Apex Stubs mainly Login Apex Stub

b. Grab my custom webservice WSDL which holds API to create records

 

Now in my second Org I will consume above both WSDL to make first a login call and then custom method call which will perform insertion in Org1.

 

Also please confirm on following ?

1. Will Partner WSDL contain definition of my custom defined webservice too? Ans : No

2. Unable to consume Partner WSDL due to big Apex File Size generation, cant we generate only Login Stub ? Ans: Edit Partner WSDL to contain only XML definition of login and dependant classes and then try to generate Apex Stub

TLFTLF

Regarding #1, you are correct. The Partner WSDL will not contain your custom defined webservice. You'll still have to parse the WSDL generated from your webservice class and generate an Apex class from that WSDL to call your webservice. Keep in mind that your custom webservice cannot return Salesforce object types, nor can it take Salesforce object types as parameters. Only basic types like string, integer, etc, are supported by the Salesforce WSDL parser.

 

Regarding #2, I'm not sure if you will be able to strip down the Partner WSDL file enough to be able to generate an Apex class from it. I started down that path. I had to strip out type definitions for Salesforce object types like Contact, Account, etc, since the WSDL parser doesn't know how to handle these. Once I got the WSDL to parse, then I ran into the 100,000 character Apex script limitation. I really didn't pursue it any farther than that. 

Chirag MehtaChirag Mehta

Regarding #1

 

I got the solution. It goes as follows ...

 

Step1 : Login to SFDC, using Enterprise/Partner WSDL and obtain SessionId

binding1 = (SoapBindingStub) new SforceServiceLocator().getSoap();
loginResult = binding1.login("username","PwdToken");
SessionHeader sh = new SessionHeader();
sh.setSessionId(loginResult.getSessionId());

 

Step2 : Set above fetched SessionId as the sessionHeader on your Apex Webservice WSDL stub and then call the required methods.

binding2 = (checkForLeadNumberBindingStub) new checkForLeadNumberServiceLocator().getcheckForLeadNumber();
binding2.setHeader(new checkForLeadNumberServiceLocator().getServiceName().getNamespaceURI(), "SessionHeader", sh);
binding2.extractLeadId("123456","123456");

 

So that's it, you are all done! nJoy!!

 

Note : binding1 and binding2 are pointers to Enterprise WSDL and Apex Webservice WSDL respectively.

private SoapBindingStub binding;
private checkForLeadNumberBindingStub binding2;

 

Message Edited by Chirag Mehta on 05-24-2009 09:45 PM
MATTYBMEMATTYBME

Chirag,

 

Are you saying you got this to work? What was the full implementation in the end, for both Org1 and Org2? I just posted a thread http://community.salesforce.com/sforce/board/message?board.id=general_development&thread.id=30405 

because now that Summer '09 release is out we want to set up an automated sharing of Cases between orgs rather than having to manually share each Case. 

 

Would what you have acheived be a viable solution?

 

Thanks,

 

Mattybme

Chirag MehtaChirag Mehta

No, it wasn't solution for Apex Callouts to Apex Web Service. I am still waiting from bloggers/commuters to respond to a solution for Apex Callouts to Apex Web Service.

 

 Apex Callouts to Apex Web Service is nothing other than Automated S2S. We are trying to develop something called as automated Salesforce 2 Salesforce using 2 classy features of Salesforce 1) Apex Callouts & 2) Apex Web Service, but unable to find a perfect n complete solution.

 

The solution mentioned by me was solution on part1 question posted below my reply.

Regarding #1, you are correct. The Partner WSDL will not contain your custom defined webservice. You'll still have to parse the WSDL generated from your webservice class and generate an Apex class from that WSDL to call your webservice. Keep in mind that your custom webservice cannot return Salesforce object types, nor can it take Salesforce object types as parameters. Only basic types like string, integer, etc, are supported by the Salesforce WSDL parser.

 

 

 

 

metaforcemetaforce

Hi Chirag

 

Can you please post the entire process that you followed to achieve this?

 

And if possible, the complete code on both the orgs please.

 

Thanks

 

metaforce

 

Chirag MehtaChirag Mehta

Hi,

 

Exactly what you are looking for ?

 

Right now two topics discussion  is being carried on the same post, so just clarify me what you are looking to achieve. What is your business requirement ?

 

Thanks 

metaforcemetaforce

Hi Chirag 

 

Thanks for your response. 

 

Here's my business scenario. I have two orgs:

 

Org 1: where my master data resides

 

Org 2: custom application is being developed, which I plan to launch commercially on Appexchange and will be distributed to customers

 

Org 2 application has a requirement to lookup master data from Org 1 through web services, but am not able to do that.

 

Can you please let me know the steps of entire process that you followed to successfully call Org 1 web service from Org 2 and the code that you have written in both the Orgs. Let me know if the code is too large to be posted here, in which case I can send you my email Id in a private message.

 

 

Thanks.

 

TLFTLF

I'm going to chime in here because I have experimented with trying to do exactly what you are describing. I can shed some light into the challenges you will face.

 

The challenge first is that you indicate that Org 2 must look up master data from Org 1 through web services. In order to achieve this, you must write Apex code on that resides on Org 1 that will be exposed as a web service method. You indicate that you are trying to develop a commercial app that you will distribute to customers. This means that you would have to deploy code to both Org 1 (the code that implements and exposes the web services) and Org 2 (the code that calls these web services). Writing the code to expose your web services from Org 1 is the relatively easy part, as implementing Apex web services is fairly well documented.

 

The second challenge is that Org 2 must somehow log in to Org 1 in order to use the web services. This is where I (and I think Chirag too) got stuck. Salesforce provides the Partner WSDL document that can be used to generate code that allows you to log in and use Salesforce as a web service. The process for this is relatively well documented for PHP and Java code. However, doing it using Apex code is not documented as far as I know. What I tried to do, is to use the Salesforce WSDL parser against this Partner WSDL document to parse the WSDL and generate Apex code that would allow you to log in to one Salesforce org from another and make web service calls. This is where things fell apart. The Salesforce WSDL parser cannot parse the Salesforce Partner WSDL for a couple of reasons:

 

  1. The Partner WSDL contains complex type definitions for objects like "Contact" and "Account", etc. The WSDL parser does not know how to parse these complex types. I edited the Partner WSDL to removed all the custom type definitions from the Partner WSDL. All I was really interested in was the WSDL definitions that would allow me to log in. This got me past this problem.
  2. Once I was able to parse the WSDL, however, the next step is to generate Apex stubs from this parsed WSDL document that would allow you to call the web services from Apex code. The Apex stub generation failed, because the generated Apex script exceeded 100,000 characters (a limit imposed by Salesforce itself). This is where I threw in the towel on the problem and have not come back to it.
Chirag MehtaChirag Mehta
Yes brother, can you drop your email id so we can connect and resolve out. Thanks!
Chirag MehtaChirag Mehta

Hi TLF,

 

Sorry unaware of your name - referring you with your community nickname. Thanks for doucmenting what was in our minds, but why did you stopped.. you seemed to be on success ...

 

Lets give a try as together and i think we can get a new thing .. Automated S2S using Apex Web Services and Callouts. if needed, lets connect either on email or call, what say?

 

I got stuck in some other important work, so i couldnt carry on. Anyhow I am sure it will work, lest try together.

 

 

 

Message Edited by Chirag Mehta on 06-13-2009 11:49 AM
TLFTLF

I stopped because my requirements changed and I too got diverted other tasks. Unfortunately, I don't have a lot of time to get involved in this right now.

 

By the way, I went back and tried parsing partner WSDL document and generating the Apex stubs. It seems that the Summer 09 release of Salesforce gets you a bit closer to your goal. I didn't get any parser errors complaining about lack of support for complex types. After parsing, it tried to generate three Apex classes; faultPartnerSoapSforceCom, sobjectPartnerSoapSforceCom and partnerSoapSforceCom. The first two classes were generated successfully, but the third (and probably most important) class failed code generation because of the 100,000 character limitation on the size of Apex scripts. My only suggestion would be to edit the partner WSDL to remove some of the complexType definitions that you probably won't need like "EmptyRecyleBinResult" and "UndeleteResult" to try to get the generated WSDL down below the 100,000 limit. Sorry I can't be more helpful than this.

AlexPHPAlexPHP

Hey guys, I've been researching this topic of communicating between multiple Salesforce instances in the past days.

 

With the idea TLF had in mind, here is the extracted parts of the Partner WSDL that I used, which only have the essential parts needed for the login to work:

 

<?xml version="1.0" encoding="UTF-8"?> <definitions targetNamespace="urn:partner.soap.sforce.com" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fns="urn:fault.partner.soap.sforce.com" xmlns:tns="urn:partner.soap.sforce.com" xmlns:ens="urn:sobject.partner.soap.sforce.com"> <types> <schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:partner.soap.sforce.com"> <import namespace="urn:sobject.partner.soap.sforce.com"/> <!-- Our simple ID Type --> <simpleType name="ID"> <restriction base="xsd:string"> <length value="18"/> <pattern value='[a-zA-Z0-9]{18}'/> </restriction> </simpleType> <complexType name="GetUserInfoResult"> <sequence> <element name="accessibilityMode" type="xsd:boolean"/> <element name="currencySymbol" type="xsd:string" nillable="true"/> <element name="orgDefaultCurrencyIsoCode" type="xsd:string" nillable="true"/> <element name="organizationId" type="tns:ID"/> <element name="organizationMultiCurrency" type="xsd:boolean"/> <element name="organizationName" type="xsd:string"/> <element name="profileId" type="tns:ID"/> <element name="roleId" type="tns:ID" nillable="true"/> <element name="userDefaultCurrencyIsoCode" type="xsd:string" nillable="true"/> <element name="userEmail" type="xsd:string"/> <element name="userFullName" type="xsd:string"/> <element name="userId" type="tns:ID"/> <element name="userLanguage" type="xsd:string"/> <element name="userLocale" type="xsd:string"/> <element name="userName" type="xsd:string"/> <element name="userTimeZone" type="xsd:string"/> <element name="userType" type="xsd:string"/> <element name="userUiSkin" type="xsd:string"/> </sequence> </complexType> <complexType name="LoginResult"> <sequence> <element name="metadataServerUrl" type="xsd:string" nillable="true"/> <element name="passwordExpired" type="xsd:boolean" /> <element name="serverUrl" type="xsd:string" nillable="true"/> <element name="sessionId" type="xsd:string" nillable="true"/> <element name="userId" type="tns:ID" nillable="true"/> <element name="userInfo" type="tns:GetUserInfoResult" minOccurs="0"/> </sequence> </complexType> <!-- Login Message Types --> <element name="login"> <complexType> <sequence> <element name="username" type="xsd:string"/> <element name="password" type="xsd:string"/> </sequence> </complexType> </element> <element name="loginResponse"> <complexType> <sequence> <element name="result" type="tns:LoginResult"/> </sequence> </complexType> </element> <!-- Header Elements --> <element name="SessionHeader"> <complexType> <sequence> <element name="sessionId" type="xsd:string"/> </sequence> </complexType> </element> <element name="LoginScopeHeader"> <complexType> <sequence> <element name="organizationId" type="tns:ID"/> <element name="portalId" type="tns:ID" minOccurs="0"/> </sequence> </complexType> </element> <element name="CallOptions"> <complexType> <sequence> <element name="client" type="xsd:string" nillable="true"/> <element name="defaultNamespace" type="xsd:string" nillable="true"/> </sequence> </complexType> </element> </schema> </types> <!-- Header Message --> <message name="Header"> <part element="tns:LoginScopeHeader" name="LoginScopeHeader"/> <part element="tns:SessionHeader" name="SessionHeader"/> <part element="tns:CallOptions" name="CallOptions"/> <part element="tns:QueryOptions" name="QueryOptions"/> <part element="tns:AssignmentRuleHeader" name="AssignmentRuleHeader"/> <part element="tns:MruHeader" name="MruHeader"/> <part element="tns:EmailHeader" name="EmailHeader"/> <part element="tns:UserTerritoryDeleteHeader" name="UserTerritoryDeleteHeader"/> <part element="tns:DebuggingHeader" name="DebuggingHeader"/> <part element="tns:DebuggingInfo" name="DebuggingInfo"/> </message> <!-- Fault Messages --> <message name="ApiFault"> <part name="fault" element="fns:fault"/> </message> <message name="LoginFault"> <part name="fault" element="fns:LoginFault"/> </message> <!-- Method Messages --> <message name="loginRequest"> <part element="tns:login" name="parameters"/> </message> <message name="loginResponse"> <part element="tns:loginResponse" name="parameters"/> </message> <!-- Soap PortType --> <portType name="Soap"> <operation name="login"> <documentation>Login to the Salesforce.com SOAP Api</documentation> <input message="tns:loginRequest"/> <output message="tns:loginResponse"/> <fault message="tns:LoginFault" name="LoginFault"/> <fault message="tns:UnexpectedErrorFault" name="UnexpectedErrorFault"/> <fault message="tns:InvalidIdFault" name="InvalidIdFault"/> </operation> </portType> <!-- Soap Binding --> <binding name="SoapBinding" type="tns:Soap"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="login"> <soap:operation soapAction=""/> <input> <soap:header use="literal" message="tns:Header" part="LoginScopeHeader"/> <soap:header use="literal" message="tns:Header" part="CallOptions"/> <soap:body parts="parameters" use="literal"/> </input> <output> <soap:body use="literal"/> </output> <fault name="LoginFault"> <soap:fault name="LoginFault" use="literal"/> </fault> <fault name="UnexpectedErrorFault"> <soap:fault name="UnexpectedErrorFault" use="literal"/> </fault> <fault name="InvalidIdFault"> <soap:fault name="InvalidIdFault" use="literal"/> </fault> </operation> </binding> <!-- Soap Service Endpoint --> <service name="SforceService"> <documentation>Sforce SOAP API</documentation> <port binding="tns:SoapBinding" name="Soap"> <soap:address location="https://www.salesforce.com/services/Soap/u/11.1"/> </port> </service> </definitions>

 

This modified/extracted version of the Partner WSDL should be accepted cleanly by the Apex generator.

 

After doing so, you should be able to call the login operation and obtain the session id of that login and pass it into your webservice as a SessionHeader element as such:

 

 

// new instance of the partner SOAP class partnerSoapSforceCom.Soap myPartnerSoap = new partnerSoapSforceCom.Soap(); // call login partnerSoapSforceCom.LoginResult partnerLoginResult = myPartnerSoap.login('your_username', 'your_password'); // from here, the session id can be accessed via partnerLoginResult.sessionId // now construct a SessionHeader element for your webservice and set the session id webserviceSOAP.SessionHeader_element webserviceSessionHeader = new webserviceSOAP.SessionHeader_element(); webserviceSessionHeader.sessionId = partnerLoginResult.sessionId; // now construct a new instance of your webservice class and set the SessionHeader webserviceSOAP.webserviceClass myWebservice = new webserviceSOAP.webserviceClass(); myWebservice.SessionHeader = webserviceSessionHeader; // at this point you should be connected to your webservice and be able to call methods against it myWebservice.doStuff();

 

What I'm concerned about are the Apex Callout Governor Limits (you can read about these in the Apex reference).

 

Your feedback and input into how fesiable using the Apex Callout to Apex Webservice approach is vs Salesforce to Salesforce would be appreciated!

 

 

This was selected as the best answer
Chirag MehtaChirag Mehta

Alex,

 

Thanks to complete successfully and post what me and rest other users where trying to do. TLF, I think now we have got a cut-down WSDL which we were talking.

 

We always had that idea to cut short the WSDL and generate the stubs, but we were unable to do so due to some other prioriry works. Anyhow thanks to Alex to complete the pending task and post a complete reply

 

Thanks to everyone to make this post complete on Automating S2S.

 

 

Suzan2009Suzan2009

excellent!!!!

 

Thanks for your post. That works for mine app.

 

I was struggling with the Login related to S2S.

 

Suzan

anu08anu08

Hi All,

 

I am Also suffer from same Situation (Exchange Data from Sales force Organization 1 to Sales force Organization 2 )

I wrote Apex class for SF Org1 has  TestWebService class.

I want  get data from SF Org1 to SF Org2 by using webservice methods in Apex class in SF Org1  and that class generates WSDL file and upload WSDL file into SF Org2 and it generate Stub class from WSDL file.

 

Apex class for SF Org1 has one web method below

 

global class TestWebService{   
    webService static String testWebMethod(String testname ){
        return 'Hello '+testname;
    }
}

 

And Generated WSDL file from above class called TestWebService

 

<?xml version="1.0" encoding="UTF-8"?>
<!--
 Web Services API : TestWebService
-->
<definitions targetNamespace="http://soap.sforce.com/schemas/class/TestWebService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://soap.sforce.com/schemas/class/TestWebService">
 <types>
  <xsd:schema elementFormDefault="qualified" targetNamespace="http://soap.sforce.com/schemas/class/TestWebService">
   <xsd:element name="DebuggingInfo">
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name="debugLog" type="xsd:string"/>
     </xsd:sequence>

 

 

I deployed wsdl  file into Another SF Org2  it generates Stub class from above wsdl file called TestWebServiceStub

 

 

    
public classs public TestWebServiceStub {
class TestWebService {
        public String endpoint_x = 'https://ap1-api.salesforce.com/services/Soap/class/TestWebService';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public TestWebServiceStub.AllowFieldTruncationHeader_element AllowFieldTruncationHeader;
        public TestWebServiceStub.DebuggingInfo_element DebuggingInfo;
        public TestWebServiceStub.CallOptions_element CallOptions;
        public TestWebServiceStub.DebuggingHeader_element DebuggingHeader;
        public TestWebServiceStub.SessionHeader_element SessionHeader;
        private String AllowFieldTruncationHeader_hns = 'AllowFieldTruncationHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/schemas/class/TestWebService';
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/schemas/class/TestWebService';
        private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/schemas/class/TestWebService', 'TestWebServiceStub'};
        public String testWebMethod(String testname) {
            TestWebServiceStub.testWebMethod_element request_x = new TestWebServiceStub.testWebMethod_element();
            TestWebServiceStub.testWebMethodResponse_element response_x;
            request_x.testname = testname;
            Map<String, TestWebServiceStub.testWebMethodResponse_element> response_map_x = new Map<String, TestWebServiceStub.testWebMethodResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/schemas/class/TestWebService',
              'testWebMethod',
              'http://soap.sforce.com/schemas/class/TestWebService',
              'testWebMethodResponse',
              'TestWebServiceStub.testWebMethodResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
    }

 And I wrote controller in SF Org2 ,  Access  SF Org1 from TestWebService class  in that method testWebMethod(String testname) 

 

Controller in SF Org2 

 

 TestWebServiceStub.TestWebService testStub = new TestWebServiceStub.TestWebService(); 

testStub.inputHttpHeaders_x = new Map<String, String>();

 

testStub.inputHttpHeaders_x.put('Authorization','Basic YXJ1bmFAYm9kaHRyZWUuY29tOmJvZGh0cmVlMTIzVW9RSFJPVFBaWjRWcko3NUZrWFg0TUJEeA==');  

 

Here I Provided Encode 63 bit code is ---        username:passwordsecurtytoken        

 

 

String nameresp = testStub.testWebMethod('Hi Hello ');

 

 

 

 

I got I Exception Called  InValid SessionHeader and System.callout Exception

 

Can u any give a solutions for this

 

Thanks 

Vasu

 

 


 

anu08anu08

Hi All,

 

I am Also suffer from same Situation (Exchange Data from Sales force Organization 1 to Sales force Organization 2 )

I wrote Apex class for SF Org1 has  TestWebService class.

I want  get data from SF Org1 to SF Org2 by using webservice methods in Apex class in SF Org1  and that class generates WSDL file and upload WSDL file into SF Org2 and it generate Stub class from WSDL file.

 

Apex class for SF Org1 has one web method below

 

global class TestWebService{   
    webService static String testWebMethod(String testname ){
        return 'Hello '+testname;
    }
}

 

And Generated WSDL file from above class called TestWebService

 

<?xml version="1.0" encoding="UTF-8"?>
<!--
 Web Services API : TestWebService
-->
<definitions targetNamespace="http://soap.sforce.com/schemas/class/TestWebService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://soap.sforce.com/schemas/class/TestWebService">
 <types>
  <xsd:schema elementFormDefault="qualified" targetNamespace="http://soap.sforce.com/schemas/class/TestWebService">
   <xsd:element name="DebuggingInfo">
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name="debugLog" type="xsd:string"/>
     </xsd:sequence>

 

 

I deployed wsdl  file into Another SF Org2  it generates Stub class from above wsdl file called TestWebServiceStub

 

 

    
public classs public TestWebServiceStub {
class TestWebService {
        public String endpoint_x = 'https://ap1-api.salesforce.com/services/Soap/class/TestWebService';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public TestWebServiceStub.AllowFieldTruncationHeader_element AllowFieldTruncationHeader;
        public TestWebServiceStub.DebuggingInfo_element DebuggingInfo;
        public TestWebServiceStub.CallOptions_element CallOptions;
        public TestWebServiceStub.DebuggingHeader_element DebuggingHeader;
        public TestWebServiceStub.SessionHeader_element SessionHeader;
        private String AllowFieldTruncationHeader_hns = 'AllowFieldTruncationHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/schemas/class/TestWebService';
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/schemas/class/TestWebService';
        private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/schemas/class/TestWebService';
        private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/schemas/class/TestWebService', 'TestWebServiceStub'};
        public String testWebMethod(String testname) {
            TestWebServiceStub.testWebMethod_element request_x = new TestWebServiceStub.testWebMethod_element();
            TestWebServiceStub.testWebMethodResponse_element response_x;
            request_x.testname = testname;
            Map<String, TestWebServiceStub.testWebMethodResponse_element> response_map_x = new Map<String, TestWebServiceStub.testWebMethodResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/schemas/class/TestWebService',
              'testWebMethod',
              'http://soap.sforce.com/schemas/class/TestWebService',
              'testWebMethodResponse',
              'TestWebServiceStub.testWebMethodResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
    }

 And I wrote controller in SF Org2 ,  Access  SF Org1 from TestWebService class  in that method testWebMethod(String testname) 

 

Controller in SF Org2 

 

 TestWebServiceStub.TestWebService testStub = new TestWebServiceStub.TestWebService(); 

testStub.inputHttpHeaders_x = new Map<String, String>();

 

testStub.inputHttpHeaders_x.put('Authorization','Basic YXJ1bmFAYm9kaHRyZWUuY29tOmJvZGh0cmVlMTIzVW9RSFJPVFBaWjRWcko3NUZrWFg0TUJEeA==');  

 

Here I Provided Encode 63 bit code is ---        username:passwordsecurtytoken        

 

 

String nameresp = testStub.testWebMethod('Hi Hello ');

 

 

 

 

I got I Exception Called  InValid SessionHeader and System.callout Exception

 

Can u any give a solutions for this

 

Thanks 

Vasu

 

 


 

TLFTLF
Take a look at the earlier post from AlexPHP (page 2 of 3). I think you need to use his WSDL in order to be able to log in to org1 from org2. Once you log in, you'll be able to get the session ID and insert it into the session header of your web service class. Also, you'll need to make sure you have enabled 'https://ap1-api.salesforce.com' as a remote site in your security settings for org2.
GuyClairboisGuyClairbois

Great post Alex, this works excellent.

I use this to test my own webservices from 1 sandbox to another. That way I don't have to create a client webserver.

VenkatesanVenkatesan

hi Alex I have implemented the same that described in the post in my sandbox but i am getting error

as mentioned

 

Web service callout failed: Unable to parse callout response. Apex type not found for element urn:partner.soap.sforce.com=sandbox

 

An unexpected error has occurred. Your development organization has been notified.

 

please help me to solve this error

 

i have changed the   <soap:address location=https://test.salesforce.com/services/Soap​/u/20.0

 

<script type="text/javascript">// function bodyOnLoad() { setFocusOnLoad(); if (typeof(startSessionTimer)!='undefined') {startSessionTimer(); }; if (typeof(ActivityReminder)!='undefined') {ActivityReminder.initialize([], false, true);}; if ((window.sfdcPage) && (sfdcPage.executeOnloadQueue)){sfdcPage.executeOnloadQueue();}; if(Sidebar.prototype.theSidebar) {Sidebar.prototype.theSidebar.sizeToBody(true);}; new GhostTextInputWrapperElement('phSearchInput', 'Search All... \u25be', '', 'headerSearchBoxGhostText', 'phSearchForm'); new UnifiedSearchAutoCompleteElement("phSearchInput","/_ui/common/search/client/ui/UnifiedSearchAutoCompleteServlet",1,{},true,null,"phSearchForm",["div","searchOwner","asPhrase","sen"],"searchScopeDialog","Search Options","/unifiedsearch/unifiedSearchScoping.apexp?scopeDest=UNIFIED","false"); new UnifiedSearchButton("searchButtonContainer", "phSearchButton", "headerSearchRightRoundedCornerMouseOver"); } function bodyOnBeforeUnload() { if ((window.sfdcPage) && (sfdcPage.executeOnBeforeUnloadQueue)){sfdcPage.executeOnBeforeUnloadQueue();}; } function bodyOnFocus() { closePopup(); } function bodyOnUnload() { } // </script>
Coco_SydneyCoco_Sydney

Thanks for this great post, this solved my issue! 

kprkpr

Hi All,

 

I have a similar situation where I need trigger firing in org1 to update data in org2.

 

I am trying to do callouts to org2, but there is already a SSO established between the two orgs.

 

How do I make use of the established connection instead of trying to log in again?

 

Thanks in advance

kpr

streetstreet

Hi Chirag,

 

Can u brief the steps to follow to develop the same.

 

I have the same requirement to do. N me new to the concept.

 

What has to done in two org;s(Org1, org2), and how to execute.

 

 

streetstreet

can u post complete code and steps for executing the same. As i have the same requiremen

 

How to execute the same, need code too!!!

Srikanth44Srikanth44

Can u Please send me hte webservice code.

my mail id :  salesforceglobal@gmail.com

TLFTLF

While I can't say I've actually done it, seems to me that it would be much easier to achieve this using Apex REST web services, rather than SOAP-based web services now that Apex REST is generally available. This would preclude the need to have to work mentioned by AlexPHP in his post. The step of trying to generate Apex stubs from the partner WSDL so that you can log in and invoke your SOAP web service is unnecessary with REST web services.

Anil SahuAnil Sahu

CAn you plz share the code?

appreciated in advance

Anil SahuAnil Sahu

I hv the same issue.

i dont want to use S2S(Connection), but i wana communicate using webservices(between twon instance).

 

Plz help..

jinnijinni

Hi

 

Thanks AlexPHP for the wonderful solution for connecting two salesforce orgs using callouts and webservices.

I was doing something similar, got stuck somewhere and then the solution u provided by parsing cut down partner wsdl helped me

I thought everything is done and was checking whether its working fine but its not

I am getting the following error:

Web service callout failed: Unable to parse callout response. Apex type not found for element urn:partner.soap.sforce.com=sandbox

 

Please help me to resolve this issue.

Thanks.

riffindusriffindus

Hi AlexPHP/chirag

 

I have a question on the approach used here for SF webservice

 

if i have to use SF webservice from other system also we need to provide username, password in the header.

 

Providing username & password to any other third party to use it as input in the request they form is not acceptable in any organisation. 

 

Please clarify me, how this can be done.

TLFTLF

You probably should be looking at the Salesforce REST API and/or Apex REST web services, which utilize OAuth rather than username/password authentication.

Karna_ShivaKarna_Shiva
Hi all,

I am facing the below issue, can some please help on this.

System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: Element {http://soap.sforce.com/schemas/class/AccountInsert}SessionId invalid at this location faultcode=soapenv:Client faultactor=

more detail, plz link the below link
https://developer.salesforce.com/forums/ForumsMain?id=906F0000000BIiBIAW