Salesforce Developers Blog

Do the Impossible in Apex with Salesforce Functions

Avatar for Nicolas Vanden BosscheNicolas Vanden Bossche
Apex developers have access to a lot of resources that can help them improve the quality of their development. They can learn about new features on Trailhead. They can see best-practice examples in Apex Recipes. They can collaborate in the Trailblazer Community and Salesforce Stack Exchange.
Do the Impossible in Apex with Salesforce Functions
October 11, 2022
Listen to this article
0:00 / 0:00

Apex developers have access to a lot of resources that can help them improve the quality of their development. They can learn about new features on Trailhead. They can see best-practice examples in Apex Recipes. They can collaborate in the Trailblazer Community and Salesforce Stack Exchange.

But there is one resource that has been lacking up until now. The number of available open source packages in the Salesforce space is limited compared to Java, JavaScript, or Python. Think of packages available through npm or Maven Central. Well, there’s good news: our situation has improved! Developers can now use these packages inside Apex code with the introduction of Salesforce Functions.

Salesforce Functions allows you to run Java or JavaScript code from Apex. This code runs on an elastic compute environment managed by Salesforce, allowing developers to focus on code quality rather than infrastructure or integration.

This article will give you an overview of how to set this up. We’ll use the example of a Java library for a web security standard that would be difficult to implement in Apex. This takes only a couple of hours using Salesforce Functions, whereas implementing the same logic in Apex would take many days, if not weeks.

Setting the scene

Let’s set the scene before we dive into the technical aspects. Imagine that we’re a customer who has been doing software development for a long time. We’ve built a set of Java applications with a layer of web services to communicate with other systems inside our landscape.

One of these endpoints is a SOAP web service that we need to call from Salesforce. The web service requires us to secure our messages using a well-known standard: WS-Security. While Apex supports calling out to a WS-Security endpoint, it requires us to manipulate the outgoing SOAP message. We can do this by using the Crypto library to change the XML, but then we quickly run into complex situations. After trial and error, it’s clear that we need another solution.

We could leverage an integration solution like MuleSoft to transform our message. Or we could change the logic on the web service to implement a security standard that is more compatible with Apex. Such solutions are not always available or desirable. Instead, we will tackle the problem using Salesforce Functions.

A nice cup of Java

Connecting to WS-Security-enabled endpoints is a piece of cake in Java. First, we import the Apache WSS4J package. It contains a Java library that implements the WS-Security standard. Next, we write a small Java method that takes our SOAP message as input. This method uses WSS4J to sign the message and then returns the output.

You can find the method below. Some security details are left out, but you can find the complete class on GitHub. We first convert the SOAP message to a Document object. Then we use WSS4J to create the security header, timestamp, and finally the signature. We end the method by adding everything to the Document object and returning it as a String.

private String signSoapMessage(String soapMessage) throws Exception {
    // Convert SOAP message to a Document object
    Document doc = toSOAPPart(soapMessage);
    
    // Create security header
    WSSecHeader wsSecHeader = new WSSecHeader(doc);
    wsSecHeader.setMustUnderstand(true);
    wsSecHeader.insertSecurityHeader();
    
    // Create timestamp
    WSSecTimestamp wsSecTimestamp = new WSSecTimestamp(wsSecHeader);
    wsSecTimestamp.setTimeToLive(3600);
    wsSecTimestamp.build();
    
    // Create the signature
    WSSecSignature wsSecSignature = new WSSecSignature(wsSecHeader);
    wsSecSignature.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
    wsSecSignature.setUserInfo("mykey", "security");
    wsSecSignature.setDigestAlgo(WSConstants.SHA512);
    wsSecSignature.setSigCanonicalization(WSConstants.C14N_EXCL_OMIT_COMMENTS);
    
    // Add the signature to the Document object and convert back into a String 
    Document signedDocument = wsSecSignature.build(crypto);
    String signedSoapMessage = XMLUtils.prettyDocumentToString(signedDocument);
    
    return signedSoapMessage;
}

We need to wrap the signing method into a nice and cozy Salesforce Functions blanket. This allows us to call the signing method from Apex. We implement the SalesforceFunction class and override the apply method.

public class WssecurityjavaFunction implements SalesforceFunction<FunctionInput, FunctionOutput> {
    [...]
    @Override
    public FunctionOutput apply(InvocationEvent<FunctionInput> event, Context context) throws Exception {
        // Retrieve Function input
        String soapMessageToSign = event.getData().getXmlBody();
        
        // Pass to signing method
        String signedSoapMessage = this.signSoapMessage(soapMessageToSign);
        
        // Return the output (this is what Apex will receive)
        return new FunctionOutput(signedSoapMessage);
    }
    [...]
}

And that’s it for the Java part of our implementation! We’ll leave the creation of the unit test out of scope for this article. We are now ready to deploy everything to Salesforce.

Deploying the function

We need to take care of a couple of things before we can deploy our function. We won’t go over these steps in detail as they are described in the Salesforce Functions guides linked with each item.

  • Make sure you have access to a Salesforce org where Salesforce Functions is enabled. This can be done by contacting your Salesforce Account Executive (AE) and requesting to try sample applications like the one in this article, Account Merge, or PDF Generation.
  • Set up a development environment and connect it to your Salesforce org. Create a compute environment linked to a sandbox or scratch org of that org.
  • Add the Salesforce DX project containing the function to Git.

Once we’ve done that, we can run the below command to deploy. After some time, the command will complete. We’re now ready to call our function from Apex!

sf deploy functions -o MyOrgAlias

Calling from Apex

The easiest way to call the function from Apex is by executing code anonymously from VS Code (or the Developer Console if you want to go old school). Below you can find a simplified example (full script).

// Instantiate Function through name reference
Functions.Function securityFunc = Functions.Function.get('functionswssecurity.wssecurityjava');

// Hardcoded input for educational purposes
String jsonInput = '{ "xmlBody" : "<soapenv:Envelope>...</soapenv:Envelope>" }';

// Invoke the Function and retrieve and deserialize response
Functions.FunctionInvocation invocation = securityFunc.invoke(jsonInput);
String jsonResponse = invocation.getResponse();
Map<String, Object> xmlResponse = (Map<String, Object>) JSON.deserializeUntyped(jsonResponse);

// Print out the signed SOAP message
System.debug('Xml retrieved: ' + xmlResponse.get('signedSoapMessage'));

When we run this code, a beautiful signed SOAP message appears that you can be proud of. We can now send to the WS-Security endpoint!

A SOAP message, signed according to the WS-Security standard.

Conclusion

Throughout this article, we saw how Salesforce Functions can empower Apex developers. We can unlock libraries and packages from other programming languages. This brings endless new possibilities to the platform!

We looked at a specific example: the WSS4J package. It allowed us to transform a SOAP message with a couple of lines of Java code. The output was easily accessed in Apex where we could continue processing.

This is a new and powerful tool in our tool-belt. At first glance, it seems to serve very specific use cases. When looking deeper, we see that gaining access to a wide range of code libraries can benefit us in many ways. Let it inspire you to also take a look at Salesforce Functions with a fresh perspective.

About the author

Nicolas Vanden Bossche is a Senior Program Architect at Salesforce and a Certified Technical Architect (CTA). He has over 9 years of delivery and technical consulting experience in the Salesforce ecosystem across Platform, Sales, Service, Marketing, and Commerce. He is passionate about helping people overcome their challenges so they can focus on what matters most to them.

More Blog Posts

Get AI-Powered Insights for Your Apex Code with ApexGuru

Get AI-Powered Insights for Your Apex Code with ApexGuru

ApexGuru is a GenAI-based feature in Scale Center, designed to fix anti-patterns and hotspots in Salesforce implementations.February 06, 2024

Ground Your Prompt Templates with Data Using Flow or Apex

Ground Your Prompt Templates with Data Using Flow or Apex

In this blog post, we’ll give you an overview of the different grounding techniques that you can use to customize prompt templates without limits.April 09, 2024