Developer Reference

BeanShell Scripting Reference

BeanShell is a lightweight Java scripting language that gives FirstSpirit developers powerful capabilities for automation, content manipulation, and workflow customization. This reference covers the execution model, the Access API essentials, common use-case recipes, best practices, and troubleshooting.

AI generated Heads up: This documentation was generated with AI and may contain inaccuracies or out-of-date details. Always verify critical information independently before relying on it.

Overview

BeanShell is a lightweight Java scripting language that provides FirstSpirit developers with powerful capabilities for automation, content manipulation, and workflow customization.

BeanShell scripts execute within the FirstSpirit server context and have full access to the FirstSpirit Access API, making them ideal for administrative tasks, content processing, and integration scenarios.

What is BeanShell?

BeanShell is a Java-like scripting language that runs in the Java Runtime Environment. In FirstSpirit, BeanShell scripts can be executed in various contexts:

  • Executable Components — Run scripts from the ServerManager or SiteArchitect
  • Workflows — Automate workflow transitions and actions
  • Scheduled Tasks — Execute scripts at defined intervals
  • Template Code — Dynamic content generation in templates

Quick Start

Here's a simple BeanShell script that prints all page references in the PageStore:

//!BeanShell
import de.espirit.firstspirit.access.store.pagestore.*;

pageStore = context.getProject().getUserService().getStore(Store.Type.PAGESTORE);
pages = pageStore.getStoreRoot().getChildren(Page.class, true);

for (Page page : pages) {
    context.logInfo("Page: " + page.getDisplayName(context.getProject().getMasterLanguage()));
}

Key Benefits

  • No Compilation Required — Scripts are interpreted at runtime
  • Full API Access — Complete access to FirstSpirit Access API
  • Context Awareness — Automatic access to execution context
  • Java Compatibility — Use Java syntax and libraries
  • Rapid Development — Quick iteration without deployment cycles

Getting Started

Creating a BeanShell Script

BeanShell scripts can be created in several locations within FirstSpirit:

  1. ServerManager → Tools → Execute Script
  2. Executable Components in project configuration
  3. Workflow Scripts in workflow definitions
  4. Templates using $CMS_SET()$ or format templates

Script Header

Every BeanShell script should start with the BeanShell comment marker:

//!BeanShell
The //!BeanShell header is required for FirstSpirit to recognize the file as a BeanShell script. Without it, the script will not execute.

Basic Script Structure

//!BeanShell
import de.espirit.firstspirit.access.*;
import de.espirit.firstspirit.access.store.*;

// Access the context (automatically available)
project = context.getProject();
user = context.getUser();

// Your script logic here
context.logInfo("Script started by: " + user.getLogin());

// Return values (optional)
return true;

Execution Contexts

BeanShell scripts run in different contexts depending on where they're executed:

ContextAvailable ObjectsUse Cases
Executablecontext, project, userAdministrative tasks, content manipulation
Workflowcontext, workflowable, transitionWorkflow automation, validation
Templatecontext, #global, #itemDynamic content generation
Scheduledcontext, projectPeriodic maintenance, reporting

Core Concepts

The Context Object

The context object is automatically available in all BeanShell scripts and provides access to the execution environment.

//!BeanShell
// Get project
project = context.getProject();

// Get current user
user = context.getUser();

// Logging
context.logInfo("Information message");
context.logWarning("Warning message");
context.logError("Error message");

// Check environment
isWebEdit = context.is(BaseContext.Env.WEBEDIT);
isSiteArchitect = context.is(BaseContext.Env.PREVIEW);

Context Methods

MethodDescriptionExample
getProject()Get current projectcontext.getProject()
getUser()Get executing usercontext.getUser()
logInfo(msg)Log info messagecontext.logInfo("Done")
logError(msg)Log error messagecontext.logError("Failed")
is(Env)Check environmentcontext.is(Env.WEBEDIT)
requireSpecialist()Get agent/servicecontext.requireSpecialist(LockService.TYPE)

Accessing Stores

FirstSpirit organizes content in stores. Access them through the project:

//!BeanShell
import de.espirit.firstspirit.access.store.*;

project = context.getProject();
userService = project.getUserService();

// Get stores
pageStore = userService.getStore(Store.Type.PAGESTORE);
mediaStore = userService.getStore(Store.Type.MEDIASTORE);
siteStore = userService.getStore(Store.Type.SITESTORE);
contentStore = userService.getStore(Store.Type.CONTENTSTORE);
templateStore = userService.getStore(Store.Type.TEMPLATESTORE);

// Access store roots
pageRoot = pageStore.getStoreRoot();
mediaRoot = mediaStore.getStoreRoot();

Working with Store Elements

Store elements are the building blocks of FirstSpirit projects:

//!BeanShell
import de.espirit.firstspirit.access.store.pagestore.*;

// Find element by UID
element = pageStore.getStoreElement("homepage");

if (element != null) {
    // Get basic properties
    uid = element.getUid();
    id = element.getId();
    name = element.getName();
    displayName = element.getDisplayName(project.getMasterLanguage());

    // Check element type
    if (element instanceof Page) {
        page = (Page) element;
        formData = page.getFormData();
    }
}

Using Agents and Services

Agents provide specialized functionality through the Access API:

//!BeanShell
import de.espirit.firstspirit.agency.*;

// GenerationAgent - for generating pages
generationAgent = context.requireSpecialist(GenerationAgent.TYPE);

// QueryAgent - for searching content
queryAgent = context.requireSpecialist(QueryAgent.TYPE);
hits = queryAgent.answer("fs.type = Page", null);

// LockService - for locking elements
lockService = context.requireSpecialist(LockService.TYPE);
lockService.setLock(element);

// ReleaseAgent - for release operations
releaseAgent = context.requireSpecialist(ReleaseAgent.TYPE);
releaseAgent.releaseStoreElement(element, recursive);

Common Use Cases

Example 1: Find and Update Pages

Find all pages with a specific template and update a form field:

//!BeanShell
import de.espirit.firstspirit.access.store.pagestore.*;
import de.espirit.firstspirit.forms.*;

pageStore = context.getProject().getUserService().getStore(Store.Type.PAGESTORE);
pages = pageStore.getStoreRoot().getChildren(Page.class, true);

int updated = 0;
for (Page page : pages) {
    formData = page.getFormData();

    // Check if page uses specific template
    if (page.getTemplate().getUid().equals("news_page")) {
        // Update form field
        form = formData.getForm();
        authorField = form.findEditor("pt_author");

        if (authorField != null) {
            formData.get(page.getLanguageInfo().getLanguage(), "pt_author").set("Admin");
            page.setFormData(formData);
            page.save();
            updated++;
        }
    }
}

context.logInfo("Updated " + updated + " pages");

Example 2: Export Media Files

Export all images from MediaStore to the file system:

//!BeanShell
import de.espirit.firstspirit.access.store.mediastore.*;
import de.espirit.firstspirit.io.*;
import java.io.*;

mediaStore = context.getProject().getUserService().getStore(Store.Type.MEDIASTORE);
media = mediaStore.getStoreRoot().getChildren(Media.class, true);

exportDir = new File("C:/export/media");
exportDir.mkdirs();

for (Media mediaItem : media) {
    if (mediaItem instanceof MediaFile) {
        file = (MediaFile) mediaItem;

        // Get binary data
        fileHandle = file.getFile("original");
        if (fileHandle != null) {
            // Copy to export directory
            outputFile = new File(exportDir, file.getUid() + "_" + fileHandle.getFileName());
            FileOutputStream fos = new FileOutputStream(outputFile);
            FileUtil.stream(fileHandle.load(), fos);
            fos.close();

            context.logInfo("Exported: " + file.getUid());
        }
    }
}

Example 3: Generate Content Report

Create a CSV report of all content in the ContentStore:

//!BeanShell
import de.espirit.firstspirit.access.store.contentstore.*;
import de.espirit.firstspirit.access.store.templatestore.*;
import java.io.*;

contentStore = context.getProject().getUserService().getStore(Store.Type.CONTENTSTORE);
datasets = contentStore.getStoreRoot().getChildren(Content2.class, true);

reportFile = new File("C:/reports/content_report.csv");
writer = new PrintWriter(new FileWriter(reportFile));
writer.println("UID,Name,Template,EntityType,RecordCount");

for (Content2 content : datasets) {
    schema = content.getSchema();
    uid = content.getUid();
    name = content.getDisplayName(context.getProject().getMasterLanguage());
    template = schema.getDisplayName(context.getProject().getMasterLanguage());
    entityType = schema.getEntityType().getName();

    // Count datasets
    dataProvider = content.getDataProvider();
    count = dataProvider.getDatasets().size();

    writer.println(uid + "," + name + "," + template + "," + entityType + "," + count);
}

writer.close();
context.logInfo("Report generated: " + reportFile.getAbsolutePath());

Example 4: Workflow Validation

Validate content before workflow transition:

//!BeanShell
import de.espirit.firstspirit.access.store.pagestore.*;

// workflowable is automatically available in workflow context
element = workflowable.getElement();

if (element instanceof Page) {
    page = (Page) element;
    formData = page.getFormData();
    form = formData.getForm();

    // Check required fields
    titleField = formData.get(page.getLanguageInfo().getLanguage(), "pt_title");

    if (titleField == null || titleField.get() == null || titleField.get().isEmpty()) {
        context.logError("Title field is required!");
        return false;  // Prevent transition
    }
}

return true;  // Allow transition

Example 5: Bulk Release Pages

Release all pages in a specific folder to live:

//!BeanShell
import de.espirit.firstspirit.access.store.pagestore.*;
import de.espirit.firstspirit.agency.*;

// Get folder
pageStore = context.getProject().getUserService().getStore(Store.Type.PAGESTORE);
folder = pageStore.getStoreElement("news");

if (folder instanceof PageFolder) {
    // Get ReleaseAgent
    releaseAgent = context.requireSpecialist(ReleaseAgent.TYPE);

    // Get all pages in folder
    pages = folder.getChildren(Page.class, true);

    for (Page page : pages) {
        // Check if page has changes
        if (page.hasChangedMetaData() || page.hasChangedReleaseData()) {
            // Release page (recursive = false)
            releaseAgent.releaseStoreElement(page, false);
            context.logInfo("Released: " + page.getUid());
        }
    }
}

Access API Essentials

FormData Manipulation

Working with form data in pages and content:

//!BeanShell
import de.espirit.firstspirit.forms.*;

page = pageStore.getStoreElement("mypage");
formData = page.getFormData();
language = page.getLanguageInfo().getLanguage();

// Read form field
titleField = formData.get(language, "pt_title");
title = titleField.get();

// Write form field
titleField.set("New Title");

// Work with different field types
// Text field
textValue = formData.get(language, "pt_text").get();

// Number field
numberValue = formData.get(language, "pt_count").get();

// Date field
dateValue = formData.get(language, "pt_date").get();

// Option (select/radio)
optionValue = formData.get(language, "pt_option").get();

// Save changes
page.setFormData(formData);
page.save();

Reference Handling

Working with references (links to other elements):

//!BeanShell
import de.espirit.firstspirit.access.store.*;

page = pageStore.getStoreElement("mypage");
formData = page.getFormData();
language = page.getLanguageInfo().getLanguage();

// Get reference field (e.g., FS_REFERENCE)
refField = formData.get(language, "pt_linked_page");

// Check if reference is set
if (refField != null && refField.get() != null) {
    referencedElement = refField.get();
    context.logInfo("Linked to: " + referencedElement.getUid());

    // Set new reference
    newPage = pageStore.getStoreElement("other_page");
    refField.set(newPage);
}

// Work with InlineReferences (multiple references)
inlineRefField = formData.get(language, "pt_related_pages");
if (inlineRefField != null) {
    references = inlineRefField.get();
    for (ref : references) {
        element = ref.getReferencedElement();
        context.logInfo("Related: " + element.getUid());
    }
}

Dataset Operations

Working with ContentStore datasets:

//!BeanShell
import de.espirit.firstspirit.access.store.contentstore.*;

contentStore = context.getProject().getUserService().getStore(Store.Type.CONTENTSTORE);
content2 = contentStore.getStoreElement("products");

// Get schema and entity type
schema = content2.getSchema();
entityType = schema.getEntityType();

// Get data provider
dataProvider = content2.getDataProvider();

// Create new dataset
dataset = dataProvider.createDataset();
language = project.getMasterLanguage();

// Set field values
dataset.set("ps_name", language, "New Product");
dataset.set("ps_price", language, 99.99);
dataset.set("ps_active", language, true);

// Save dataset
dataProvider.save(dataset);

// Query datasets
allDatasets = dataProvider.getDatasets();
for (ds : allDatasets) {
    name = ds.get("ps_name", language);
    context.logInfo("Product: " + name);
}

Generation and Deployment

Trigger generation programmatically:

//!BeanShell
import de.espirit.firstspirit.agency.*;
import de.espirit.firstspirit.generate.*;

generationAgent = context.requireSpecialist(GenerationAgent.TYPE);

// Get generation context for specific page
page = pageStore.getStoreElement("homepage");
generationContext = generationAgent.getGenerationContext(page);

// Generate page
generationContext.generate();

// Check generation status
if (generationContext.successful()) {
    context.logInfo("Generation successful");

    // Deploy generated files (if deployment configured)
    generationContext.deploy();
} else {
    context.logError("Generation failed");
}

Best Practices

Error Handling

Always use try-catch blocks for robust scripts:

//!BeanShell
try {
    element = pageStore.getStoreElement("mypage");

    if (element == null) {
        context.logWarning("Element not found");
        return false;
    }

    // Process element
    element.save();

} catch (Exception e) {
    context.logError("Error: " + e.getMessage());
    e.printStackTrace();
    return false;
}

return true;

Locking Elements

Always lock elements before modifying:

//!BeanShell
import de.espirit.firstspirit.agency.*;

lockService = context.requireSpecialist(LockService.TYPE);

try {
    // Lock element
    lockService.setLock(element);

    // Modify element
    formData = element.getFormData();
    // ... make changes ...
    element.save();

} finally {
    // Always release lock
    lockService.releaseLock(element);
}

Performance Optimization

For large-scale operations, use efficient iteration:

//!BeanShell
// BAD - loads all children into memory
children = folder.getChildren();
for (child : children) {
    // process
}

// GOOD - uses iterator
iterator = folder.getChildrenIterator();
while (iterator.hasNext()) {
    child = iterator.next();
    // process
}

Logging Best Practices

Use appropriate log levels:

//!BeanShell
// Info - normal operation
context.logInfo("Processing " + count + " pages");

// Warning - non-critical issues
context.logWarning("Element '" + uid + "' not found, skipping");

// Error - critical failures
context.logError("Failed to save element: " + e.getMessage());

// Debug - detailed information (check log level settings)
if (context.getLogLevel() == LogLevel.DEBUG) {
    context.logInfo("DEBUG: Element state = " + element.getState());
}

Transaction Handling

Use transactions for atomic operations:

//!BeanShell
import de.espirit.firstspirit.agency.*;

transactionAgent = context.requireSpecialist(TransactionAgent.TYPE);

try {
    transactionAgent.beginTransaction();

    // Multiple operations that should be atomic
    page1.save();
    page2.save();
    content.save();

    transactionAgent.commitTransaction();
    context.logInfo("Transaction committed successfully");

} catch (Exception e) {
    transactionAgent.rollbackTransaction();
    context.logError("Transaction rolled back: " + e.getMessage());
}

Memory Management

For large datasets, process in batches:

//!BeanShell
import de.espirit.firstspirit.access.store.contentstore.*;

content2 = contentStore.getStoreElement("large_dataset");
dataProvider = content2.getDataProvider();

// Process in batches
batchSize = 100;
offset = 0;
language = project.getMasterLanguage();

while (true) {
    // Get batch
    datasets = dataProvider.getDatasets(offset, batchSize);

    if (datasets.isEmpty()) {
        break;
    }

    // Process batch
    for (dataset : datasets) {
        // Process individual dataset
        name = dataset.get("ps_name", language);
        context.logInfo("Processing: " + name);
    }

    offset += batchSize;
}

Troubleshooting

Common Errors

ErrorCauseSolution
NullPointerExceptionElement not found or field is nullAdd null checks before accessing objects
AccessDeniedExceptionUser lacks permissionsCheck user rights or execute as admin
ElementDeletedExceptionElement was deletedRefresh references or check element state
LockExceptionElement is locked by another userCheck locks with LockService
ClassCastExceptionWrong type assumptionUse instanceof before casting

Debugging Tips

Enable Detailed Logging

//!BeanShell
// Print variable values
context.logInfo("Variable value: " + myVariable);

// Print object type
context.logInfo("Object class: " + element.getClass().getName());

// Print stack trace for debugging
try {
    // code
} catch (Exception e) {
    e.printStackTrace();
    context.logError("Error: " + e.getMessage());
}

Check Element State

//!BeanShell
element = pageStore.getStoreElement("mypage");

context.logInfo("UID: " + element.getUid());
context.logInfo("ID: " + element.getId());
context.logInfo("State: " + element.getState());
context.logInfo("Locked: " + element.isLocked());
context.logInfo("Has changes: " + element.hasChangedMetaData());

Verify Permissions

//!BeanShell
import de.espirit.firstspirit.access.*;

user = context.getUser();
element = pageStore.getStoreElement("mypage");

// Check permissions
permission = element.getPermission(user);
canRead = permission.isAllowed(Permission.READ);
canChange = permission.isAllowed(Permission.CHANGE);
canDelete = permission.isAllowed(Permission.DELETE);

context.logInfo("Can read: " + canRead);
context.logInfo("Can change: " + canChange);
context.logInfo("Can delete: " + canDelete);

Script Execution Issues

BeanShell scripts must be executed with appropriate user permissions. Admin tasks require Admin user context.

Common execution issues:

  • Script doesn't start: Check for syntax errors, verify //!BeanShell header
  • Permission denied: Execute as user with sufficient rights
  • Elements not found: Verify UIDs and store paths
  • Changes not saved: Ensure .save() is called and no exceptions occurred
  • Locks preventing modification: Check and release locks with LockService

Variable Scope Issues

BeanShell has different scoping rules than Java:

//!BeanShell
// GOOD - explicit typing for clarity
String myVariable = "value";
int count = 0;

// ACCEPTABLE - dynamic typing
myVariable = "value";  // BeanShell infers type

// AVOID - can cause issues with method overloading
result = element.getFormData();  // Type unclear

FirstSpirit 5.2 Compatibility

For scripts that need to run on FS 5.2 and newer versions:

//!BeanShell
// Check FirstSpirit version
version = context.getProject().getVersion();
context.logInfo("FirstSpirit version: " + version);

// Use version-specific code when needed
if (version.startsWith("5.2")) {
    // FS 5.2 compatible code
} else {
    // Newer API features
}

Advanced Topics

Dynamic Class Loading

Load Java classes dynamically in BeanShell:

//!BeanShell
import java.lang.reflect.*;

// Load custom class from project lib
classLoader = context.getProject().getClassLoader();
customClass = classLoader.loadClass("com.example.CustomHelper");

// Create instance
instance = customClass.newInstance();

// Call methods via reflection
method = customClass.getMethod("processData", String.class);
result = method.invoke(instance, "mydata");

Working with Metadata

Access and modify element metadata:

//!BeanShell
import de.espirit.firstspirit.access.*;

element = pageStore.getStoreElement("mypage");
metaData = element.getMetaData();

// Read metadata
createdBy = metaData.getCreatedBy();
createdDate = metaData.getCreatedDate();
changedBy = metaData.getChangedBy();
changedDate = metaData.getChangedDate();

context.logInfo("Created by: " + createdBy.getLogin() + " on " + createdDate);
context.logInfo("Last changed by: " + changedBy.getLogin() + " on " + changedDate);

Scheduled Task Integration

Create scripts for scheduled execution:

//!BeanShell
import de.espirit.firstspirit.access.schedule.*;

// Script executed as scheduled task
context.logInfo("Scheduled task started at: " + new Date());

// Perform maintenance tasks
// - Clean up old content
// - Generate reports
// - Synchronize data
// - Backup operations

// Return status
return ScheduleEntry.State.SUCCESS;  // or State.ERROR, State.INTERRUPTED

GUI Dialogs in SiteArchitect

Show dialogs when executing from SiteArchitect:

//!BeanShell
import de.espirit.firstspirit.client.access.*;
import javax.swing.*;

// Check if running in SiteArchitect
if (context.is(BaseContext.Env.PREVIEW)) {
    // Show confirmation dialog
    result = JOptionPane.showConfirmDialog(
        null,
        "Do you want to proceed?",
        "Confirmation",
        JOptionPane.YES_NO_OPTION
    );

    if (result != JOptionPane.YES_OPTION) {
        context.logInfo("User cancelled operation");
        return false;
    }
}

// Continue with operation

Resources

Official Documentation

  • FirstSpirit Access API JavaDoc
  • BeanShell User Manual: http://www.beanshell.org/manual.html
  • FirstSpirit Developer Portal

Quick Reference

TaskCode Pattern
Get elementstore.getStoreElement("uid")
Save elementelement.save()
Lock elementlockService.setLock(element)
Release elementreleaseAgent.releaseStoreElement(element)
Get form fieldformData.get(language, "fieldname")
Log messagecontext.logInfo("message")
Get agentcontext.requireSpecialist(AgentType.TYPE)

Common Imports

//!BeanShell
// Core API
import de.espirit.firstspirit.access.*;
import de.espirit.firstspirit.access.store.*;
import de.espirit.firstspirit.access.store.pagestore.*;
import de.espirit.firstspirit.access.store.mediastore.*;
import de.espirit.firstspirit.access.store.contentstore.*;
import de.espirit.firstspirit.access.store.templatestore.*;

// Agencies
import de.espirit.firstspirit.agency.*;

// Forms
import de.espirit.firstspirit.forms.*;

// Generation
import de.espirit.firstspirit.generate.*;

// Utilities
import java.util.*;
import java.io.*;
import java.text.*;