views:

45

answers:

2

Well this is an old issue I've been dealing with and still no solution, so trying a new approach.

How can I send th SOAP response early (Before script execution ends)?

These issues are cause when the ACK file is not sent before 30 seconds as the process takes longer to complete then the allotted time.

flush() not working, get this error:

org.xml.sax.SAXParseException: XML document structures must start and end within the same entity.

without the flush() I get this

org.xml.sax.SAXParseException: Premature end of file.

The script process can takeover 180 seconds to complete and the server waiting for the response only wait for about 30 seconds before timing out (Which cause the above error).

any thoughts as to how I can fix this?

Here is some of the code: This is how I accept and send the ACK file for the imcoming SOAP request

$data = 'php://input';
$content = file_get_contents($data);

if($content) {
    respond('true');
} else {
    respond('false');
}

The respond function

function respond($tf) {
    $ACK = <<<ACK
<?xml version = "1.0" encoding = "utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
    <soapenv:Body>
        <notifications xmlns="http://soap.sforce.com/2005/09/outbound"&gt;
            <Ack>$tf</Ack>
        </notifications>
    </soapenv:Body>
</soapenv:Envelope>
ACK;

    print trim($ACK); 
}

PHP uses a single thread processing approach and will not send back the ACK file until the thread has completed it's processing. Is there some way to close the socket after the ACK submission and continue the processing so I don't get these timeout issues on the sending server?

+1  A: 

a) (optional) you can use set_time_limit() for increase the time limit of the execution time.

b) You must increase the time to the wsdl client object, for example :

$clientwsdl->setOpt('timeout', 300); // if you are using PEAR:SOAP.

Most wsdl class allow to define the timeout.

c) and not, you can't response early using SOAP, not at least using one call. Take in consideration that SOAP return a XML, so a partial XML usually is invalid (it miss the closing tag).

d) Alternate, you can use other method than SOAP, for example reading a url:

$fp=fopen("http://www.mysite.com/url.php","r");

where url.php return the columns (or some kind of value without using xml :

30|50|70|80|20 
30|50|70|80|20
30|50|70|80|20
magallanes
Thanks for the input, having trouble trying to set the setOpt() and as for the other fopen method I cant change from XML
Phill Pafford
+1  A: 

As far as I know we can't do anything about the 30 second limit and the output won't be flushed before the script finishes. Can you try splitting your processing logic into 2 pieces?

  1. "Listener" script that accepts messages, logs the job to be done and sends ACK instantly, then quits.
  2. Actual processing on your side which might take longer.

The "job to be done" could be a newly spawned process (check out the comments for the pcntl_fork() function but they don't look too promising to me) or some way to store the data in file or database and periodically process it with another script, for example scheduled to run every 5 minutes?

If 60 seconds would somehow save you, you could rewrite your outbound message into a callout from Apex. Basically you then write your own SOAP envelope and send it to any http address. You could put it in a trigger. See here for limitations of this approach though.


Other ranting:

  1. I don't think your ACK really "matters" to salesforce. Outbound messages are just notifications. Do you rely somewhere else in your application on Ack = true/false? If not - listener that blindly sends ack=true & schedules the job might really be the way to go ;)
  2. From the other queston I understood you basically just need to store updates in DB on your side. You realize you shouldn't use OM for audit purposes, right? (Link, search for "audit").
  3. Wouldn't it be simpler to make PHP the active side? make it query Salesforce for [SELECT Id, Name FROM Account WHERE LastModifiedDate > :lastTimeYouQueried]? That way you can take all the time you want to process the results :)
eyescream