Java Action Activities for Custom Blob Documents

Last modified: September 30, 2026

Introduction

Java Actions can have Custom Blob Documents as a parameter. You can link the Java Action directly to a document type when the type is registered. This allows the user to drag a Custom Blob Document from the App Explorer directly into a microflow, and a new Java Action Activity is automatically generated with that exact Blob Document as the parameter value for the Java Action.

Prerequisites

Registering a Custom Blob Document with a Java Action

If the Java Action that contains a Blob Document type as its parameter already exists in your extension, you can use its qualified name during the registration call of your Blob Document type. The registration method triggers when the app opens and extensions are loaded, linking the two.

 async loaded(componentContext) {
    const studioPro = getStudioProApi(componentContext);
    await studioPro.app.model.customBlobDocuments.registerDocumentType<PersonInfo>({
        type: personDocumentType,
        readableTypeName: 'Person',
        defaultContent: {
            firstName: '',
            lastName: '',
            age: 0,
            email: ''
        },
        javaActionQualifiedName: 'MyModule.MyJavaAction'
    });

    ...
}

If you want to create the Java Action that has your new Blob Document Type as a parameter at the same time as registering the document, you can do so as shown below. However, be aware that the Java Action will be created every time your extension gets loaded. This code below is a simple example to show how to create a Java Action and assign its parameter types to a Blob Document type.


 async loaded(componentContext) {
    const studioPro = getStudioProApi(componentContext);

    const moduleName = "MyFirstModule";
    const javaActionName = "MyJavaAction";
    
    await createJavaActionWithBlobDocumentParameter(studioPro, moduleName, javaActionName, personDocumentType, "Person");

    await studioPro.app.model.customBlobDocuments.registerDocumentType<PersonInfo>({
        type: personDocumentType,
        readableTypeName: 'Person',
        defaultContent: {
            firstName: '',
            lastName: '',
            age: 0,
            email: ''
        },
        javaActionQualifiedName: `${moduleName}.${javaActionName}`
    });

    ...
}

async function createJavaActionWithBlobDocumentParameter(studioPro: StudioProApi, moduleName: string, javaActionName: string, customDocumentTypeName: string, customDocumentReadableTypeName: string) {
    const module = await studioPro.app.model.modules.getModule(moduleName);

    if (!module) {
        throw new Error(`Module was not found.`);
    }

    const javaActions = studioPro.app.model.javaActions;

    const javaAction = await javaActions.createUnit(module.$ID, {
        name: javaActionName
    });

    const parameterType = await javaActions.createElement<CodeActions.CustomBlobDocumentParameterType>(
        "CodeActions$CustomBlobDocumentParameterType"
    );

    parameterType.customDocumentTypeName = customDocumentTypeName;
    parameterType.customDocumentReadableTypeName = customDocumentReadableTypeName;

    const parameter = await javaActions.createElement<JavaActions.JavaActionParameter>("JavaActions$JavaActionParameter", {
        name: "document"
    });

    parameter.actionParameterType = parameterType;

    javaAction.actionParameters.push(parameter);

    await javaActions.save(javaAction);

    return javaAction;
}

Consistency Checks for Lost Action and Parameter Types

Define a Sample Type That Keeps Track of the Java Action Name

Add this type next to your other document content types, for example in src/model/PersonInfo.ts. It represents the contents of the document that tracks the Java Action linked to it:

export type JavaActionDocument = {
    javaActionQualifiedName: string | undefined;
    renamedJavaActionQualifiedName?: string | undefined;
    someValue?: string | undefined;
};

Write the Consistency Check

Add the following to your extension's entry point, for example src/main/index.ts. It defines the error codes and the getConsistencyCheck function that validates a JavaActionDocument:

const withJavaActionDocumentType = "myextension.JavaActionDocument";

const wrongActionParameterErrorCode = "WRNJAP";
const noJavaActionErrorCode = "NOJAA";
const wrongNamedJavaActionErrorCode = "WRNJAA";
const reservedErrorCodes = [wrongActionParameterErrorCode, noJavaActionErrorCode, wrongNamedJavaActionErrorCode];

async function getConsistencyCheck(studioPro: StudioProApi) {
    return async (data: JavaActionDocument) => {
        const errors: ConsistencyError[] = [];

        if (!data.javaActionQualifiedName || data.javaActionQualifiedName.trim().length === 0) {
            errors.push({
                errorCode: noJavaActionErrorCode,
                errorDescription: `The Document of type ${withJavaActionDocumentType} must have a java action associated with it.`,
                severity: "error",
                elementText: "Parameter"
            });
        }

        const [action] = await studioPro.app.model.javaActions.loadAll(unit => {
            const name = `${unit.moduleName}.${unit.name}`;
            return name === data.javaActionQualifiedName || name === data.renamedJavaActionQualifiedName;
        });

        const dependentElementIds: string[] = [];

        if (!action) {
            errors.push({
                errorCode: noJavaActionErrorCode,
                errorDescription: `The Document of type ${withJavaActionDocumentType} must have a java action associated with it.`,
                severity: "error",
                elementText: "Parameter"
            });

            return {
                errors,
                dependentElementIds
            };
        } else dependentElementIds.push(action.$ID); // track the JavaAction as a dependency of this document.

        if (data.renamedJavaActionQualifiedName && data.renamedJavaActionQualifiedName !== data.javaActionQualifiedName) {
            errors.push({
                errorCode: wrongNamedJavaActionErrorCode,
                errorDescription: `The Java action was renamed from ${data.javaActionQualifiedName} to ${data.renamedJavaActionQualifiedName}.`,
                severity: "error",
                elementText: "Name"
            });

            return {
                errors,
                dependentElementIds
            };
        }

        const blobDocumentParameters = action.actionParameters.filter(
            parameter => parameter.actionParameterType.$Type === "CodeActions$CustomBlobDocumentParameterType"
        );

        const correctTypeParameter = blobDocumentParameters.filter(
            parameter =>
                parameter.actionParameterType.$Type === "CodeActions$CustomBlobDocumentParameterType" &&
                parameter.actionParameterType.customDocumentTypeName === withJavaActionDocumentType
        );

        const wrongTypeParameter = blobDocumentParameters.filter(
            parameter =>
                parameter.actionParameterType.$Type === "CodeActions$CustomBlobDocumentParameterType" &&
                parameter.actionParameterType.customDocumentTypeName !== withJavaActionDocumentType
        );

        if (correctTypeParameter.length !== 1 || wrongTypeParameter.length > 0) {
            errors.push({
            errorCode: wrongActionParameterErrorCode,
            errorDescription: `The Java Action "${data.javaActionQualifiedName}" must have a single parameter of type ${withJavaActionDocumentType}.`,
            severity: "error",
            elementText: "Parameter"
        });
        }

        return {
            errors,
            dependentElementIds
        };
    };
}

Register the Document Type with Its Consistency Check

Add the following code inside the loaded function in src/main/index.ts, after registering the Person document type. Do this by building a ConsistencyCheckRegistration and passing it to registerDocumentType, the same way you registered the Person document type earlier:

const consistencyCheckRegistration: ConsistencyCheckRegistration<JavaActionDocument> = {
    check: await getConsistencyCheck(studioPro),
    reservedErrorCodes
};

await studioPro.app.model.customBlobDocuments.registerDocumentType<JavaActionDocument>({
    type: withJavaActionDocumentType,
    readableTypeName: "Java Action Document",
    defaultContent: {
        javaActionQualifiedName: undefined
    },
    consistencyCheckRegistration
});

Tracking Java Action Renamed or Re-Added with Same Name After Deletion

Using events from studioPro.app.projectChanges, you can track when a Java Action is renamed or re-added with the same name:

studioPro.app.projectChanges.addEventListener("elementsRenamed", async ({ elements }) => {
   const javaActionsRenamed = elements.filter(element => element.documentType === "JavaActions$JavaAction");

   const javaActionBlobDocuments = await studioPro.app.model.customBlobDocuments.getDocumentsOfType(withJavaActionDocumentType);
   for (const doc of javaActionBlobDocuments) {
       const d = await studioPro.app.model.customBlobDocuments.getDocumentById<JavaActionDocument>(doc.id);

       if ("document" in d && d.document) {
           for (const javaActionRenamed of javaActionsRenamed) {
               // renamed JavaAction's old name matches our JavaAction, so we track the new name.
               if (javaActionRenamed.oldName.qualifiedName === d.document.contents.javaActionQualifiedName) {
                   d.document.contents.renamedJavaActionQualifiedName = javaActionRenamed.newName.qualifiedName;

                   // always save the document so that the consistency checks run again
                   await studioPro.app.model.customBlobDocuments.updateDocumentContent(d.document.$ID, d.document.contents);
               }

               // renamed JavaAction new name matches our name, we can stop tracking the rename
               if (javaActionRenamed.newName.qualifiedName === d.document.contents.javaActionQualifiedName) {
                   d.document.contents.renamedJavaActionQualifiedName = undefined;

                   // always save the document so that the consistency checks run again
                   await studioPro.app.model.customBlobDocuments.updateDocumentContent(d.document.$ID, d.document.contents);
               }                
           }
       }
   }
});

studioPro.app.projectChanges.addEventListener("documentAdded", async ({ document }) => {
   const javaActionDocuments = await studioPro.app.model.customBlobDocuments.getDocumentsOfType(withJavaActionDocumentType);

   for (const doc of javaActionDocuments) {
       const d = await studioPro.app.model.customBlobDocuments.getDocumentById<JavaActionDocument>(doc.id);

       if ("document" in d && d.document) {
           const javaAction = (await studioPro.app.model.javaActions.loadAll(ja => ja.$ID === document.documentId)).find(
               ja => ja.$ID === document.documentId
           );

           if (javaAction) {
               const qualifiedName = (javaAction as JavaActions.JavaAction & { $QualifiedName: string }).$QualifiedName;

               // new JavaAction is in fact our own
               if (d.document.contents.javaActionQualifiedName === qualifiedName) {
                   d.document.contents.javaActionQualifiedName = qualifiedName;
                   d.document.contents.renamedJavaActionQualifiedName = undefined;
                    
                   // trigger the change to run consistency checks again, since this new
                   // action is probably missing the required parameters of the correct type.
                   await studioPro.app.model.customBlobDocuments.updateDocumentContent(d.document.$ID, d.document.contents);
               }
           }
       }
   }
});

Limitations

A Custom Blob Document and Java Action relationship is one-to-one. There can only be one Java Action per document type. If an extension tries to link a Java Action that is already linked to another type, the API will throw an error.

It is recommended to write some consistency checks to detect when the Java Action is renamed or deleted, or when its parameter types change. Add the javaActionQualifiedName property to the Custom Blob Document contents so it is included in the document data when the consistency checks run.