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.
Overview
BeanShell is a lightweight Java scripting language that provides FirstSpirit developers with powerful capabilities for automation, content manipulation, and workflow customization.
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:
- ServerManager → Tools → Execute Script
- Executable Components in project configuration
- Workflow Scripts in workflow definitions
- Templates using
$CMS_SET()$or format templates
Script Header
Every BeanShell script should start with the BeanShell comment marker:
//!BeanShell//!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:
| Context | Available Objects | Use Cases |
|---|---|---|
| Executable | context, project, user | Administrative tasks, content manipulation |
| Workflow | context, workflowable, transition | Workflow automation, validation |
| Template | context, #global, #item | Dynamic content generation |
| Scheduled | context, project | Periodic 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
| Method | Description | Example |
|---|---|---|
getProject() | Get current project | context.getProject() |
getUser() | Get executing user | context.getUser() |
logInfo(msg) | Log info message | context.logInfo("Done") |
logError(msg) | Log error message | context.logError("Failed") |
is(Env) | Check environment | context.is(Env.WEBEDIT) |
requireSpecialist() | Get agent/service | context.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 transitionExample 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
| Error | Cause | Solution |
|---|---|---|
NullPointerException | Element not found or field is null | Add null checks before accessing objects |
AccessDeniedException | User lacks permissions | Check user rights or execute as admin |
ElementDeletedException | Element was deleted | Refresh references or check element state |
LockException | Element is locked by another user | Check locks with LockService |
ClassCastException | Wrong type assumption | Use 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
Common execution issues:
- Script doesn't start: Check for syntax errors, verify
//!BeanShellheader - 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 unclearFirstSpirit 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.INTERRUPTEDGUI 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 operationResources
Official Documentation
- FirstSpirit Access API JavaDoc
- BeanShell User Manual:
http://www.beanshell.org/manual.html - FirstSpirit Developer Portal
Quick Reference
| Task | Code Pattern |
|---|---|
| Get element | store.getStoreElement("uid") |
| Save element | element.save() |
| Lock element | lockService.setLock(element) |
| Release element | releaseAgent.releaseStoreElement(element) |
| Get form field | formData.get(language, "fieldname") |
| Log message | context.logInfo("message") |
| Get agent | context.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.*;