Vault Java SDK Upgrade to Java 17

Over the next several releases, Veeva is upgrading the Vault Java SDK compilation from Java 8 to Java 17. Java 17 includes optimizations that can improve performance and efficiency compared to Java 8, allowing for reduced resource usage.

Existing integrations are not affected as long as developers do not update or modify existing integration code. When Java 17 is enabled, Vault compiles code deployments with Java 17 instead of Java 8. If no code is deployed, there is no behavior change. Updating or modifying an existing integration requires deployment, which means that updating an existing integration compiled with Java 8 will cause the integration to recompile with Java 17.

Until 25R2, Admins can disable Java 17 to allow developers to modify existing integrations and deploy with Java 8 compilation. After 25R2, Admins can no longer disable Java 17 compilation.

We recommend developers test their code using Java 17 as soon as possible, which will allow developers to modify existing integrations if needed after 25R2. After 25R2, all code will compile with Java 17. At this time, existing integrations compiled with Java 8 cannot be modified without changing compilation from Java 8 to Java 17.

The upgrade to Java 17 may require code changes to keep custom integrations working with Java 17 compilation. To help with this migration, we’ve created a list of solutions to common Java 17 compilation errors.

Release Dates

Limited Release Dates

General Release Dates

How to Enable Java 17

The upgrade to Java 17 may require code changes. We recommend developers enable Java 17 and test their code as soon as possible.

When enabled, the Use Java 17 Compilation and Runtime setting causes Vault code deployments to compile with Java 17. Enabling Java 17 compilation in your Vault does not automatically test your code for compatibility, as existing code is not recompiled. To test your code for compatibility with Java 17, you must enable Java 17 in your Vault and then redeploy any existing custom code to recompile with Java 17.

To enable Java 17:

  1. Navigate to Admin > Settings > General Settings.
  2. Click Edit.
  3. Select the Use Java 17 Compilation and Runtime checkbox.
  4. Click Save.

When Java 17 is enabled:

How to Temporarily Disable Java 17

The upgrade to Java 17 may require code changes. If your organization needs more time to make these changes, you can temporarily disable Java 17. Disabling Java 17 allows your developers to modify your existing integrations without upgrading to Java 17 yet. Organizations must upgrade by 25R2 (25R1.2), at which time the option to disable Java 17 will no longer be available.

To disable Java 17:

  1. Navigate to Admin > Settings > General Settings.
  2. Click Edit.
  3. Clear the Use Java 17 Compilation and Runtime checkbox.
  4. Click Save.

When Java 17 is disabled:

How to Redeploy Existing Integration Code

When evaluating custom code for compatibility with Java 17, developers may want to start by making no changes and redeploying existing code as-is in a sandbox Vault. This will evaluate if code changes are needed for the upgrade to Java 17 compilation.

To redeploy existing code without making changes:

  1. Create a sandbox Vault. Verify that Java 17 is enabled in your sandbox Vault.
  2. Export a VPK from your production Vault.
  3. Import the VPK to your sandbox Vault with the replace_all deployment option.
  4. Deploy the imported VPK to your sandbox Vault. Code that cannot compile with Java 17 will fail to deploy.

If code incompatibilities are found, developers should deploy code changes with the Maven Plugin. For example, developers may need to deploy changes for common Java 17 compilation errors.

POM Update to Test Java 17

To test your code with Java 17 locally, you must make the following change to your POM file:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
 <!-- Source will still be Java 8 -->
        <source>1.8</source>
     <!-- compile using Java 17 -->
        <target>17</target>
    </configuration>
</plugin>

Common Java 17 Compilation Errors

The following solutions can help you fix common issues you may encounter when upgrading to Java 17.

Can’t find your issue? Let us know in the Vault for Developers community on Veeva Connect.

Java Record Class Error

Summary of Issue:

Affected Code Example

The following example code demonstrates the Java Record Class error:

package com.veeva.vault.custom.triggers;

import com.veeva.vault.sdk.api.data.*;

@RecordTriggerInfo(object = "vsdk_hello_world__c", name="vsdk_hello_world__c", events = {RecordEvent.BEFORE_INSERT})
public class HelloWorld implements RecordTrigger {

    public void execute(RecordTriggerContext recordTriggerContext) {
        LogService logService = ServiceLocator.locate(LogService.class);

// is this java.lang.Record, or com.veeva.vault.sdk.api.data.Record? 
        Record record = recordTriggerContext.getRecordChanges().get(0).getNew();
    }
}

Compilation Error

Compiling the affected code example produces the following error:

MESSAGE "[type=INVALID_DATA,message=javasdk/src/main/java/com/veeva/vault/custom/triggers/HelloWorld.java:51: error: reference to Record is ambiguous
        Record record = recordTriggerContext.getRecordChanges().get(0).getNew();
        ^
  both interface com.veeva.vault.sdk.api.data.Record in com.veeva.vault.sdk.api.data and class java.lang.Record in java.lang match

Solution

Provide a specific import statement for the Record class.

package com.veeva.vault.custom.triggers;

import com.veeva.vault.sdk.api.data.Record;
import com.veeva.vault.sdk.api.data.RecordChange;
import com.veeva.vault.sdk.api.data.RecordEvent;
import com.veeva.vault.sdk.api.data.RecordTrigger;
import com.veeva.vault.sdk.api.data.RecordTriggerContext;
import com.veeva.vault.sdk.api.data.RecordTriggerInfo;

@RecordTriggerInfo(object = "vsdk_hello_world__c", name="vsdk_hello_world__c", events = {RecordEvent.BEFORE_INSERT})
public class HelloWorld implements RecordTrigger {

    public void execute(RecordTriggerContext recordTriggerContext) {
        LogService logService = ServiceLocator.locate(LogService.class);

// Now it is clear this is com.veeva.vault.sdk.api.data.Record
        Record record = recordTriggerContext.getRecordChanges().get(0).getNew();
    }
}

Underscore Keyword Error

Summary of Issue:

Affected Code Example

The following example code demonstrates the Underscore Keyword error:

package com.veeva.vault.custom.triggers;

import com.veeva.vault.sdk.api.data.Record;
import com.veeva.vault.sdk.api.data.RecordChange;
import com.veeva.vault.sdk.api.data.RecordEvent;
import com.veeva.vault.sdk.api.data.RecordTrigger;
import com.veeva.vault.sdk.api.data.RecordTriggerContext;
import com.veeva.vault.sdk.api.data.RecordTriggerInfo;

@RecordTriggerInfo(object = "vsdk_hello_world__c", name="vsdk_hello_world__c", events = {RecordEvent.BEFORE_INSERT})
public class HelloWorld implements RecordTrigger {

    public void execute(RecordTriggerContext recordTriggerContext) {
        LogService logService = ServiceLocator.locate(LogService.class);

// Cannot use underscore (_) as identifier
        Record _ = recordTriggerContext.getRecordChanges().get(0).getNew();

// Cannot use underscore (_) as identifier
        List<BigDecimal> bgdList = VaultCollections.newList();
        bgdList.stream().forEach(_ -> _.add(new BigDecimal(14)));
    }
}

Compilation Error

Compiling the affected code example produces the following error:

java: as of release 9, '_' is a keyword, and may not be used as an identifier

java: '_' used as an identifier
  (use of '_' as an identifier is forbidden for lambda parameters)

Solution

Replace all identifiers using an underscore (_) with a different, non-keyword String.

package com.veeva.vault.custom.triggers;

import com.veeva.vault.sdk.api.data.Record;
import com.veeva.vault.sdk.api.data.RecordChange;
import com.veeva.vault.sdk.api.data.RecordEvent;
import com.veeva.vault.sdk.api.data.RecordTrigger;
import com.veeva.vault.sdk.api.data.RecordTriggerContext;
import com.veeva.vault.sdk.api.data.RecordTriggerInfo;

@RecordTriggerInfo(object = "vsdk_hello_world__c", name="vsdk_hello_world__c", events = {RecordEvent.BEFORE_INSERT})
public class HelloWorld implements RecordTrigger {

    public void execute(RecordTriggerContext recordTriggerContext) {
        LogService logService = ServiceLocator.locate(LogService.class);

// Replaced underscore (_) identifier with non-keyword String
        Record record = recordTriggerContext.getRecordChanges().get(0).getNew();

// Replaced underscore (_) identifier with non-keyword String
        List<BigDecimal> bgdList = VaultCollections.newList();
        bgdList.stream().forEach(bd -> bd.add(new BigDecimal(14)));
    }
}