Java tutorial
/** * Mule Salesforce Connector * * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.modules.salesforce; import com.sforce.async.AsyncApiException; import com.sforce.async.AsyncExceptionCode; import com.sforce.async.BatchInfo; import com.sforce.async.BatchRequest; import com.sforce.async.BatchResult; import com.sforce.async.JobInfo; import com.sforce.async.OperationEnum; import com.sforce.async.RestConnection; import com.sforce.soap.partner.Connector; import com.sforce.soap.partner.DeleteResult; import com.sforce.soap.partner.DescribeGlobalResult; import com.sforce.soap.partner.DescribeSObjectResult; import com.sforce.soap.partner.EmptyRecycleBinResult; import com.sforce.soap.partner.GetDeletedResult; import com.sforce.soap.partner.GetUpdatedResult; import com.sforce.soap.partner.GetUserInfoResult; import com.sforce.soap.partner.LeadConvert; import com.sforce.soap.partner.LeadConvertResult; import com.sforce.soap.partner.LoginResult; import com.sforce.soap.partner.PartnerConnection; import com.sforce.soap.partner.QueryResult; import com.sforce.soap.partner.SaveResult; import com.sforce.soap.partner.UpsertResult; import com.sforce.soap.partner.fault.ApiFault; import com.sforce.soap.partner.sobject.SObject; import com.sforce.ws.ConnectionException; import com.sforce.ws.ConnectorConfig; import com.sforce.ws.MessageHandler; import com.sforce.ws.transport.SoapConnection; import org.apache.log4j.Logger; import org.mule.api.ConnectionExceptionCode; import org.mule.api.annotations.Configurable; import org.mule.api.annotations.Connect; import org.mule.api.annotations.ConnectionIdentifier; import org.mule.api.annotations.Disconnect; import org.mule.api.annotations.InvalidateConnectionOn; import org.mule.api.annotations.Processor; import org.mule.api.annotations.Source; import org.mule.api.annotations.ValidateConnection; import org.mule.api.annotations.display.FriendlyName; import org.mule.api.annotations.display.Placement; import org.mule.api.annotations.param.ConnectionKey; import org.mule.api.annotations.param.Default; import org.mule.api.annotations.param.Optional; import org.mule.api.callback.SourceCallback; import org.springframework.util.StringUtils; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Map; /** * The Salesforce Connector will allow to connect to the Salesforce application. Almost every operation that can be * done via the Salesforce's API can be done thru this connector. This connector will also work if your Salesforce * objects are customized with additional fields or even you are working with custom objects. * <p/> * Integrating with Salesforce consists of web service calls utilizing XML request/response setup * over an HTTPS connection. The technical details of this connection such as request headers, * error handling, HTTPS connection, etc. are all abstracted from the user to make implementation * quick and easy. * * @author MuleSoft, Inc. */ @org.mule.api.annotations.Connector(name = "sfdc", schemaVersion = "4.0", friendlyName = "Salesforce") public class SalesforceModule { private static final Logger LOGGER = Logger.getLogger(SalesforceModule.class); /** * SalesForce SOAP endpoint */ @Configurable @Optional @Default("https://login.salesforce.com/services/Soap/u/23.0") @Placement(group = "Salesforce endpoint") private URL url; /** * Proxy host */ @Configurable @Optional @Placement(group = "Proxy settings") private String proxyHost; /** * Proxy port */ @Configurable @Optional @Default("80") @Placement(group = "Proxy settings") private int proxyPort = -1; /** * Proxy username */ @Configurable @Optional @Placement(group = "Proxy settings") private String proxyUsername; /** * Proxy password */ @Configurable @Optional @Placement(group = "Proxy settings") private String proxyPassword; /** * Bayeux client */ private SalesforceBayeuxClient bc; /** * Partner connection */ private PartnerConnection connection; /** * REST connection to the bulk API */ private RestConnection restConnection; /** * Login result */ private LoginResult loginResult; /** * Adds one or more new records to your organization's data. * <p/> * <p class="caution"> * IMPORTANT: When you map your objects to the input of this message processor keep in mind that they need * to match the expected type of the object at Salesforce. * <p/> * Take the CloseDate of an Opportunity as an example, if you set that field to a string of value "2011-12-13" * it will be sent to Salesforce as a string and operation will be rejected on the basis that CloseDate is not * of the expected type. * <p/> * The proper way to actually map it is to generate a Java Date object, you can do so using Groovy expression * evaluator as <i>#[groovy:Date.parse("yyyy-MM-dd", "2011-12-13")]</i>. * </p> * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:create} * * @param objects An array of one or more sObjects objects. * @param type Type of object to create * @return An array of {@link SaveResult} if async is false * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_create.htm">create()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<SaveResult> create(@Placement(group = "Type") @FriendlyName("sObject Type") String type, @Placement(group = "sObject Field Mappings") List<Map<String, Object>> objects) throws Exception { return Arrays.asList(connection.create(toSObjectList(type, objects))); } /** * Adds one or more new records to your organization's data. * <p/> * This call uses the Bulk API. The creation will be done in asynchronous fashion. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:create-bulk} * * @param objects An array of one or more sObjects objects. * @param type Type of object to create * @return A {@link BatchInfo} that identifies the batch job. {@link http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_reference_batchinfo.htm} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_create.htm">createBatch()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public BatchInfo createBulk(@Placement(group = "Type") @FriendlyName("sObject Type") String type, @Placement(group = "sObject Field Mappings") List<Map<String, Object>> objects) throws Exception { return createBatchAndCompleteRequest(createJobInfo(OperationEnum.insert, type), objects); } private BatchInfo createBatchAndCompleteRequest(JobInfo jobInfo, List<Map<String, Object>> objects) throws SoapConnection.SessionTimedOutException { try { BatchRequest batchRequest = restConnection.createBatch(jobInfo); batchRequest.addSObjects(toAsyncSObjectList(objects)); return batchRequest.completeRequest(); } catch (AsyncApiException e) { if (e.getExceptionCode() == AsyncExceptionCode.InvalidSessionId) { throw new SoapConnection.SessionTimedOutException(e.getMessage(), e); } } return null; } private JobInfo createJobInfo(OperationEnum op, String type) throws AsyncApiException { return createJobInfo(op, type, null); } private JobInfo createJobInfo(OperationEnum op, String type, String externalIdFieldName) throws AsyncApiException { JobInfo jobInfo = new JobInfo(); jobInfo.setOperation(op); jobInfo.setObject(type); if (externalIdFieldName != null) { jobInfo.setExternalIdFieldName(externalIdFieldName); } return restConnection.createJob(jobInfo); } /** * Adds one new records to your organization's data. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:create-single} * {@sample.java ../../../doc/mule-module-sfdc.java.sample sfdc:create-single} * * @param object SObject to create * @param type Type of object to create * @return An array of {@link SaveResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_create.htm">create()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public SaveResult createSingle(@Placement(group = "Type") @FriendlyName("sObject Type") String type, @Placement(group = "sObject Field Mappings") Map<String, Object> object) throws Exception { SaveResult[] saveResults = connection.create(new SObject[] { toSObject(type, object) }); if (saveResults.length > 0) { return saveResults[0]; } return null; } @ValidateConnection public boolean isConnected() { if (restConnection != null) { if (connection != null) { if (loginResult != null) { if (loginResult.getSessionId() != null) { return true; } } } } return false; } /** * Returns the session id for the current connection * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-session-id} * * @return the session id for the current connection */ @Processor @ConnectionIdentifier public String getSessionId() { if (connection != null) { if (loginResult != null) { return loginResult.getSessionId(); } } return null; } /** * End the current session * * @throws Exception */ @Disconnect public synchronized void destroySession() { if (bc != null) { if (bc.isConnected()) { bc.disconnect(); } } if (connection != null && loginResult != null) { try { connection.logout(); loginResult = null; connection = null; } catch (ConnectionException ce) { LOGGER.error(ce); } } } /** * Updates one or more existing records in your organization's data. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:update} * * @param objects An array of one or more sObjects objects. * @param type Type of object to update * @return An array of {@link SaveResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_update.htm">update()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<SaveResult> update(@Placement(group = "Type") @FriendlyName("sObject Type") String type, @Placement(group = "Salesforce sObjects list") List<Map<String, Object>> objects) throws Exception { return Arrays.asList(connection.update(toSObjectList(type, objects))); } /** * Updates one or more existing records in your organization's data. * <p/> * This call uses the Bulk API. The creation will be done in asynchronous fashion. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:update-bulk} * * @param objects An array of one or more sObjects objects. * @param type Type of object to update * @return A {@link BatchInfo} that identifies the batch job. {@link http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_reference_batchinfo.htm} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_create.htm">createBatch()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public BatchInfo updateBulk(@Placement(group = "Type") @FriendlyName("sObject Type") String type, @Placement(group = "Salesforce sObjects list") List<Map<String, Object>> objects) throws Exception { return createBatchAndCompleteRequest(createJobInfo(OperationEnum.update, type), objects); } /** * <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_upsert.htm">Upserts</a> * an homogeneous list of objects: creates new records and updates existing records, using a custom field to determine the presence of existing records. * In most cases, prefer {@link #upsert(String, String, List)} over {@link #create(String, List)}, * to avoid creating unwanted duplicate records. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:upsert} * * @param externalIdFieldName Contains the name of the field on this object with the external ID field attribute * for custom objects or the idLookup field property for standard objects. * @param type the type of the given objects. The list of objects to upsert must be homogeneous * @param objects the objects to upsert * @return a list of {@link UpsertResult}, one for each passed object * @throws Exception if a connection error occurs * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_upsert.htm">upsert()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<UpsertResult> upsert(@Placement(group = "Information") String externalIdFieldName, @Placement(group = "Information") @FriendlyName("sObject Type") String type, @Placement(group = "Salesforce sObjects list") List<Map<String, Object>> objects) throws Exception { return Arrays.asList(connection.upsert(externalIdFieldName, toSObjectList(type, objects))); } /** * <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_upsert.htm">Upserts</a> * an homogeneous list of objects: creates new records and updates existing records, using a custom field to determine the presence of existing records. * In most cases, prefer {@link #upsert(String, String, List)} over {@link #create(String, List)}, * to avoid creating unwanted duplicate records. * <p/> * This call uses the Bulk API. The creation will be done in asynchronous fashion. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:upsert-bulk} * * @param externalIdFieldName Contains the name of the field on this object with the external ID field attribute * for custom objects or the idLookup field property for standard objects. * @param type the type of the given objects. The list of objects to upsert must be homogeneous * @param objects the objects to upsert * @return A {@link BatchInfo} that identifies the batch job. {@link http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_reference_batchinfo.htm} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_create.htm">createBatch()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public BatchInfo upsertBulk( @Placement(group = "Information", order = 1) @FriendlyName("sObject Type") String type, @Placement(group = "Information", order = 2) String externalIdFieldName, @Placement(group = "Salesforce sObjects list") List<Map<String, Object>> objects) throws Exception { return createBatchAndCompleteRequest(createJobInfo(OperationEnum.upsert, type, externalIdFieldName), objects); } /** * Access latest {@link BatchInfo} of a submitted {@link BatchInfo}. Allows to track execution status. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:batch-info} * * @param batchInfo the {@link BatchInfo} being monitored * @return Latest {@link BatchInfo} representing status of the batch job result. * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_get_info.htm">getBatchInfo()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public BatchInfo batchInfo(BatchInfo batchInfo) throws Exception { return restConnection.getBatchInfo(batchInfo.getJobId(), batchInfo.getId()); } /** * Access {@link BatchResult} of a submitted {@link BatchInfo}. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:batch-result} * * @param batchInfo the {@link BatchInfo} being monitored * @return {@link BatchResult} representing result of the batch job result. * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_get_results.htm">getBatchResult()</a> * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_asynch/Content/asynch_api_batches_interpret_status.htm">BatchInfo status</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public BatchResult batchResult(BatchInfo batchInfo) throws Exception { return restConnection.getBatchResult(batchInfo.getJobId(), batchInfo.getId()); } /** * Retrieves a list of available objects for your organization's data. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:describe-global} * * @return A {@link DescribeGlobalResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describeglobal.htm">describeGlobal()</a> * @since 4.0 */ @Processor public DescribeGlobalResult describeGlobal() throws Exception { return connection.describeGlobal(); } /** * Retrieves one or more records based on the specified IDs. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:retrieve} * * @param type Object type. The sp ecified value must be a valid object for your organization. * @param ids The ids of the objects to retrieve * @param fields The fields to return for the matching objects * @return An array of {@link SObject}s * @throws Exception */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<Map<String, Object>> retrieve( @Placement(group = "Information", order = 1) @FriendlyName("sObject Type") String type, @Placement(group = "Ids to Retrieve") List<String> ids, @Placement(group = "Fields to Retrieve") List<String> fields) throws Exception { String fiedsCommaDelimited = StringUtils.collectionToCommaDelimitedString(fields); SObject[] sObjects = connection.retrieve(fiedsCommaDelimited, type, ids.toArray(new String[ids.size()])); List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); if (sObjects != null) { for (SObject sObject : sObjects) { result.add(sObject.toMap()); } } return result; } /** * Executes a query against the specified object and returns data that matches the specified criteria. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:query} * * @param query Query string that specifies the object to query, the fields to return, and any conditions for * including a specific object in the query. For more information, see Salesforce Object Query * Language (SOQL). * @return An array of {@link SObject}s * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_query.htm">query()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<Map<String, Object>> query(@Placement(group = "Query") String query) throws Exception { QueryResult queryResult = connection.query(query); List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); while (queryResult != null) { for (SObject object : queryResult.getRecords()) { result.add(object.toMap()); } if (queryResult.isDone()) { break; } queryResult = connection.queryMore(queryResult.getQueryLocator()); } return result; } /** * Executes a query against the specified object and returns the first record that matches the specified criteria. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:query-single} * * @param query Query string that specifies the object to query, the fields to return, and any conditions for * including a specific object in the query. For more information, see Salesforce Object Query * Language (SOQL). * @return A single {@link SObject} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_query.htm">query()</a> * @since 4.1 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public Map<String, Object> querySingle(@Placement(group = "Query") String query) throws Exception { SObject[] result = connection.query(query).getRecords(); if (result.length > 0) { return result[0].toMap(); } return null; } /** * Converts a Lead into an Account, Contact, or (optionally) an Opportunity. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:convert-lead} * * @param leadId ID of the Lead to convert. Required. For information on IDs, see ID Field Type. * @param contactId ID of the Contact into which the lead will be merged (this contact must be * associated with the specified accountId, and an accountId must be specified). * Required only when updating an existing contact.IMPORTANT if you are converting * a lead into a person account, do not specify the contactId or an error will result. * Specify only the accountId of the person account. If no contactID is specified, * then the API creates a new contact that is implicitly associated with the Account. * To create a new contact, the client application must be logged in with sufficient * access rights. To merge a lead into an existing contact, the client application * must be logged in with read/write access to the specified contact. The contact * name and other existing data are not overwritten (unless overwriteLeadSource is * set to true, in which case only the LeadSource field is overwritten). * For information on IDs, see ID Field Type. * @param accountId ID of the Account into which the lead will be merged. Required * only when updating an existing account, including person accounts. * If no accountID is specified, then the API creates a new account. To * create a new account, the client application must be logged in with * sufficient access rights. To merge a lead into an existing account, * the client application must be logged in with read/write access to the * specified account. The account name and other existing data are not overwritten. * For information on IDs, see ID Field Type. * @param overWriteLeadSource Specifies whether to overwrite the LeadSource field on the target Contact object * with the contents of the LeadSource field in the source Lead object (true), or * not (false, the default). To set this field to true, the client application * must specify a contactId for the target contact. * @param doNotCreateOpportunity Specifies whether to create an Opportunity during lead conversion (false, the * default) or not (true). Set this flag to true only if you do not want to create * an opportunity from the lead. An opportunity is created by default. * @param opportunityName Name of the opportunity to create. If no name is specified, then this value * defaults to the company name of the lead. The maximum length of this field is * 80 characters. If doNotCreateOpportunity argument is true, then no Opportunity * is created and this field must be left blank; otherwise, an error is returned. * @param convertedStatus Valid LeadStatus value for a converted lead. Required. * To obtain the list of possible values, the client application queries the * LeadStatus object, as in: * Select Id, MasterLabel from LeadStatus where IsConverted=true * @param sendEmailToOwner Specifies whether to send a notification email to the owner specified in the * ownerId (true) or not (false, the default). * @return A list of {@link LeadConvertResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_convertlead.htm">convertLead()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public LeadConvertResult convertLead(String leadId, String contactId, @Optional String accountId, @Optional @Default("false") Boolean overWriteLeadSource, @Optional @Default("false") Boolean doNotCreateOpportunity, @Optional String opportunityName, String convertedStatus, @Optional @Default("false") Boolean sendEmailToOwner) throws Exception { LeadConvert leadConvert = new LeadConvert(); leadConvert.setLeadId(leadId); leadConvert.setContactId(contactId); leadConvert.setAccountId(accountId); leadConvert.setOverwriteLeadSource(overWriteLeadSource); leadConvert.setDoNotCreateOpportunity(doNotCreateOpportunity); if (opportunityName != null) { leadConvert.setOpportunityName(opportunityName); } leadConvert.setConvertedStatus(convertedStatus); leadConvert.setSendNotificationEmail(sendEmailToOwner); LeadConvert[] list = new LeadConvert[1]; list[0] = leadConvert; return connection.convertLead(list)[0]; } /** * The recycle bin lets you view and restore recently deleted records for 30 days before they are * permanently deleted. Your organization can have up to 5000 records per license in the Recycle Bin at any * one time. For example, if your organization has five user licenses, 25,000 records can be stored in the * Recycle Bin. If your organization reaches its Recycle Bin limit, Salesforce.com automatically removes * the oldest records, as long as they have been in the recycle bin for at least two hours. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:empty-recycle-bin} * * @param ids Array of one or more IDs associated with the records to delete from the recycle bin. * Maximum number of records is 200. * @return A list of {@link EmptyRecycleBinResult} * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_emptyrecyclebin.htm">emptyRecycleBin()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<EmptyRecycleBinResult> emptyRecycleBin(@Placement(group = "Ids to Delete") List<String> ids) throws Exception { return Arrays.asList(connection.emptyRecycleBin(ids.toArray(new String[] {}))); } /** * Deletes one or more records from your organization's data. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:delete} * * @param ids Array of one or more IDs associated with the objects to delete. * @return An array of {@link DeleteResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_delete.htm">delete()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public List<DeleteResult> delete(@Placement(group = "Ids to Delete") List<String> ids) throws Exception { return Arrays.asList(connection.delete(ids.toArray(new String[] {}))); } /** * Retrieves the list of individual records that have been created/updated within the given timespan for the specified object. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-updated-range} * * @param type Object type. The specified value must be a valid object for your organization. * @param startTime Starting date/time (Coordinated Universal Time (UTC)not local timezone) of the timespan for * which to retrieve the data. The API ignores the seconds portion of the specified dateTime value ' * (for example, 12:30:15 is interpreted as 12:30:00 UTC). * @param endTime Ending date/time (Coordinated Universal Time (UTC)not local timezone) of the timespan for * which to retrieve the data. The API ignores the seconds portion of the specified dateTime value * (for example, 12:35:15 is interpreted as 12:35:00 UTC). If it is not provided, the current * server time will be used. * @return {@link GetUpdatedResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getupdatedrange.htm">getUpdatedRange()</a> */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public GetUpdatedResult getUpdatedRange( @Placement(group = "Information") @FriendlyName("sObject Type") String type, @Placement(group = "Information") @FriendlyName("Start Time Reference") Calendar startTime, @Placement(group = "Information") @FriendlyName("End Time Reference") @Optional Calendar endTime) throws Exception { if (endTime == null) { Calendar serverTime = connection.getServerTimestamp().getTimestamp(); endTime = (Calendar) serverTime.clone(); if (endTime.getTimeInMillis() - startTime.getTimeInMillis() < 60000) { endTime.add(Calendar.MINUTE, 1); } } return connection.getUpdated(type, startTime, endTime); } /** * Retrieves the list of individual records that have been deleted within the given timespan for the specified object. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-deleted-range} * {@sample.java ../../../doc/mule-module-sfdc.java.sample sfdc:get-deleted-range} * * @param type Object type. The specified value must be a valid object for your organization. * @param startTime Starting date/time (Coordinated Universal Time (UTC)not local timezone) of the timespan for * which to retrieve the data. The API ignores the seconds portion of the specified dateTime value ' * (for example, 12:30:15 is interpreted as 12:30:00 UTC). * @param endTime Ending date/time (Coordinated Universal Time (UTC)not local timezone) of the timespan for * which to retrieve the data. The API ignores the seconds portion of the specified dateTime value * (for example, 12:35:15 is interpreted as 12:35:00 UTC). If not specific, the current server * time will be used. * @return {@link GetDeletedResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getdeletedrange.htm">getDeletedRange()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public GetDeletedResult getDeletedRange( @Placement(group = "Information") @FriendlyName("sObject Type") String type, @Placement(group = "Information") @FriendlyName("Start Time Reference") Calendar startTime, @Placement(group = "Information") @FriendlyName("End Time Reference") @Optional Calendar endTime) throws Exception { if (endTime == null) { Calendar serverTime = connection.getServerTimestamp().getTimestamp(); endTime = (Calendar) serverTime.clone(); if (endTime.getTimeInMillis() - startTime.getTimeInMillis() < 60000) { endTime.add(Calendar.MINUTE, 1); } } return connection.getDeleted(type, startTime, endTime); } /** * Describes metadata (field list and object properties) for the specified object. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:describe-sobject} * * @param type Object. The specified value must be a valid object for your organization. For a complete list * of objects, see Standard Objects * @return {@link DescribeSObjectResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describesobject.htm">describeSObject()</a> * @since 4.0 */ @Processor(name = "describe-sobject") @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public DescribeSObjectResult describeSObject( @Placement(group = "Type") @FriendlyName("sObject Type") String type) throws Exception { return connection.describeSObject(type); } /** * Retrieves the list of individual records that have been deleted between the range of now to the duration before now. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-deleted} * * @param type Object type. The specified value must be a valid object for your organization. * @param duration The amount of time in minutes before now for which to return records from. * @return {@link GetDeletedResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getdeleted.htm">getDeleted()</a> * @since 4.2 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public GetDeletedResult getDeleted(@Placement(group = "Information") @FriendlyName("sObject Type") String type, @Placement(group = "Information") int duration) throws Exception { Calendar serverTime = connection.getServerTimestamp().getTimestamp(); Calendar startTime = (Calendar) serverTime.clone(); Calendar endTime = (Calendar) serverTime.clone(); endTime.add(Calendar.MINUTE, duration); return getDeletedRange(type, startTime, endTime); } /** * Retrieves the list of individual records that have been updated between the range of now to the duration before now. * <p/> * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-updated} * * @param type Object type. The specified value must be a valid object for your organization. * @param duration The amount of time in minutes before now for which to return records from. * @return {@link GetDeletedResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getupdated.htm">getUpdated()</a> */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public GetUpdatedResult getUpdated(@Placement(group = "Information") @FriendlyName("sObject Type") String type, @Placement(group = "Information") int duration) throws Exception { Calendar serverTime = connection.getServerTimestamp().getTimestamp(); Calendar startTime = (Calendar) serverTime.clone(); Calendar endTime = (Calendar) serverTime.clone(); endTime.add(Calendar.MINUTE, duration); return getUpdatedRange(type, startTime, endTime); } /** * Creates a topic which represents a query that is the basis for notifying * listeners of changes to records in an organization. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:publish-topic} * * @param name Descriptive name of the push topic, such as MyNewCases or TeamUpdatedContacts. The * maximum length is 25 characters. This value identifies the channel. * @param description Description of what kinds of records are returned by the query. Limit: 400 characters * @param query The SOQL query statement that determines which records' changes trigger events to be sent to * the channel. Maximum length: 1200 characters * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/pushtopic.htm">Push Topic</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public void publishTopic(@Placement(group = "Information") String name, @Placement(group = "Information") String query, @Placement(group = "Information") @Optional String description) throws Exception { QueryResult result = connection.query("SELECT Id FROM PushTopic WHERE Name = '" + name + "'"); if (result.getSize() == 0) { SObject pushTopic = new SObject(); pushTopic.setType("PushTopic"); pushTopic.setField("ApiVersion", "23.0"); if (description != null) { pushTopic.setField("Description", description); } pushTopic.setField("Name", name); pushTopic.setField("Query", query); SaveResult[] saveResults = connection.create(new SObject[] { pushTopic }); if (!saveResults[0].isSuccess()) { throw new SalesforceException(saveResults[0].getErrors()[0].getStatusCode(), saveResults[0].getErrors()[0].getMessage()); } } else { SObject pushTopic = result.getRecords()[0]; if (description != null) { pushTopic.setField("Description", description); } pushTopic.setField("Query", query); SaveResult[] saveResults = connection.update(new SObject[] { pushTopic }); if (!saveResults[0].isSuccess()) { throw new SalesforceException(saveResults[0].getErrors()[0].getStatusCode(), saveResults[0].getErrors()[0].getMessage()); } } } /** * Retrieves personal information for the user associated with the current session. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:get-user-info} * * @return {@link GetUserInfoResult} * @throws Exception * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getuserinfo.htm">getUserInfo()</a> * @since 4.0 */ @Processor @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class) public GetUserInfoResult getUserInfo() throws Exception { return connection.getUserInfo(); } /** * Subscribe to a topic. * <p/> * {@sample.xml ../../../doc/mule-module-sfdc.xml.sample sfdc:subscribe-topic} * * @param topic The name of the topic to subscribe to * @param callback The callback to be called when a message is received * @api.doc <a href="http://www.salesforce.com/us/developer/docs/api_streaming/index_Left.htm">Streaming API</a> * @since 4.0 */ @Source public void subscribeTopic(String topic, final SourceCallback callback) { getBayeuxClient().subscribe("/topic" + topic, new SalesforceBayeuxMessageListener(callback)); } /** * Creates a new Salesforce session * * @param username Username used to initialize the session * @param password Password used to authenticate the user * @param securityToken User's security token * @throws ConnectionException if a problem occurred while trying to create the session */ @Connect public synchronized void connect(@ConnectionKey String username, String password, String securityToken) throws org.mule.api.ConnectionException { ConnectorConfig connectorConfig = createConnectorConfig(url, username, password + securityToken, proxyHost, proxyPort, proxyUsername, proxyPassword); connectorConfig.addMessageHandler(new MessageHandler() { @Override public void handleRequest(URL endpoint, byte[] request) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Sending request to " + endpoint.toString()); LOGGER.debug(new String(request)); } } @Override public void handleResponse(URL endpoint, byte[] response) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Receiving response from " + endpoint.toString()); LOGGER.debug(new String(response)); } } }); try { connection = Connector.newConnection(connectorConfig); } catch (ConnectionException e) { throw new org.mule.api.ConnectionException(ConnectionExceptionCode.UNKNOWN, null, e.getMessage(), e); } reconnect(); try { String restEndpoint = "https://" + (new URL(connectorConfig.getServiceEndpoint())).getHost() + "/services/async/23.0"; connectorConfig.setRestEndpoint(restEndpoint); restConnection = new RestConnection(connectorConfig); } catch (AsyncApiException e) { throw new org.mule.api.ConnectionException(ConnectionExceptionCode.UNKNOWN, e.getExceptionCode().toString(), e.getMessage(), e); } catch (MalformedURLException e) { throw new org.mule.api.ConnectionException(ConnectionExceptionCode.UNKNOWN_HOST, null, e.getMessage(), e); } } public void reconnect() throws org.mule.api.ConnectionException { try { LOGGER.debug("Creating a Salesforce session using " + connection.getConfig().getUsername()); loginResult = connection.login(connection.getConfig().getUsername(), connection.getConfig().getPassword()); if (loginResult.isPasswordExpired()) { try { connection.logout(); } catch (ConnectionException e) { LOGGER.error(e.getMessage(), e); } String username = connection.getConfig().getUsername(); connection = null; throw new org.mule.api.ConnectionException(ConnectionExceptionCode.CREDENTIALS_EXPIRED, null, "The password for the user " + username + " has expired"); } LOGGER.debug("Session established successfully with ID " + loginResult.getSessionId() + " at instance " + loginResult.getServerUrl()); connection.getSessionHeader().setSessionId(loginResult.getSessionId()); connection.getConfig().setServiceEndpoint(loginResult.getServerUrl()); connection.getConfig().setSessionId(loginResult.getSessionId()); } catch (ConnectionException e) { if (e instanceof ApiFault) { throw new org.mule.api.ConnectionException(ConnectionExceptionCode.UNKNOWN, ((ApiFault) e).getExceptionCode().name(), ((ApiFault) e).getExceptionMessage(), e); } else { throw new org.mule.api.ConnectionException(ConnectionExceptionCode.UNKNOWN, null, e.getMessage(), e); } } } protected SObject[] toSObjectList(String type, List<Map<String, Object>> objects) { SObject[] sobjects = new SObject[objects.size()]; int s = 0; for (Map<String, Object> map : objects) { sobjects[s] = toSObject(type, map); s++; } return sobjects; } private SObject toSObject(String type, Map<String, Object> map) { SObject sObject = new SObject(); for (String key : map.keySet()) { sObject.setType(type); sObject.setField(key, map.get(key)); } return sObject; } protected com.sforce.async.SObject[] toAsyncSObjectList(List<Map<String, Object>> objects) { com.sforce.async.SObject[] sobjects = new com.sforce.async.SObject[objects.size()]; int s = 0; for (Map<String, Object> map : objects) { sobjects[s] = toAsyncSObject(map); s++; } return sobjects; } private com.sforce.async.SObject toAsyncSObject(Map<String, Object> map) { com.sforce.async.SObject sObject = new com.sforce.async.SObject(); for (String key : map.keySet()) { if (map.get(key) != null) { sObject.setField(key, map.get(key).toString()); } else { sObject.setField(key, null); } } return sObject; } /** * Retrieve host of proxy * * @return The host of the configured proxy */ public String getProxyHost() { return proxyHost; } /** * Set proxy host * * @param proxyHost Proxy host to set */ public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; } /** * Retrieve proxy port * * @return Configured proxy port */ public int getProxyPort() { return proxyPort; } /** * Set proxy port * * @param proxyPort Proxy port to set */ public void setProxyPort(int proxyPort) { this.proxyPort = proxyPort; } /** * Retrieve proxy username * * @return Configured proxy username */ public String getProxyUsername() { return proxyUsername; } /** * Set proxy username * * @param proxyUsername Proxy username to set */ public void setProxyUsername(String proxyUsername) { this.proxyUsername = proxyUsername; } /** * Retrieve proxy password * * @return Proxy password */ public String getProxyPassword() { return proxyPassword; } /** * Set proxy password * * @param proxyPassword Proxy password to set */ public void setProxyPassword(String proxyPassword) { this.proxyPassword = proxyPassword; } public URL getUrl() { return url; } /** * Set Salesforce endpoint. * * @param url Web service endpoint */ public void setUrl(URL url) { this.url = url; } /** * Create connector config * * @param endpoint Salesforce endpoint * @param username Username to use for authentication * @param password Password to use for authentication * @param proxyHost * @param proxyPort * @param proxyUsername * @param proxyPassword * @return */ protected ConnectorConfig createConnectorConfig(URL endpoint, String username, String password, String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) { ConnectorConfig config = new ConnectorConfig(); config.setUsername(username); config.setPassword(password); config.setAuthEndpoint(endpoint.toString()); config.setServiceEndpoint(endpoint.toString()); config.setManualLogin(true); config.setCompression(false); if (proxyHost != null) { config.setProxy(proxyHost, proxyPort); if (proxyUsername != null) { config.setProxyUsername(proxyUsername); } if (proxyPassword != null) { config.setProxyPassword(proxyPassword); } } return config; } public PartnerConnection getConnection() { return connection; } public LoginResult getLoginResult() { return loginResult; } public SalesforceBayeuxClient getBayeuxClient() { try { if (bc == null) { bc = new SalesforceBayeuxClient(this); if (!bc.isHandshook()) { bc.handshake(); } } } catch (MalformedURLException e) { LOGGER.error(e.getMessage()); } return bc; } protected void setConnection(PartnerConnection connection) { this.connection = connection; } protected void setLoginResult(LoginResult loginResult) { this.loginResult = loginResult; } protected void setRestConnection(RestConnection restConnection) { this.restConnection = restConnection; } protected void setBayeuxClient(SalesforceBayeuxClient bc) { this.bc = bc; } }