Introduction to GroupTemplate
The GroupTemplate class serves as the central orchestrator within the Beetl template engine framework. It acts as the primary entry point for template operations, managing resources, configurations, and execution contexts. When applications need to dynamically evaluate expressions or generate content based on varying business rules, GroupTemplate provides the necessary infrastructure to separate dynamic logic from static code.
A practical use case involves computational scenarios where formulas are determined at runtime. Consider a financial system where profit-sharing calculations may follow different rules:
- Profit Share = Interest × 0.2
- Profit Share = (Interest + Penalty) × 0.1
Rather than hardcoding these formulas, Beetl allows dynamic evaluation:
// Template representations
<%print(interest * 0.2);%>
<%print((interest + penalty) * 0.2);%>
The application can execute these formulas generically:
public void computeFormula(String formulaExpression, Map<String, Object> context) {
Configuration config = Configuration.defaultConfiguration();
GroupTemplate templateGroup = new GroupTemplate(
new StringTemplateResourceLoader(),
config
);
Template template = templateGroup.getTemplate(formulaExpression);
template.binding(context);
String result = template.render();
// Process result...
}
Core Components Analysis
Essential Fields
The GroupTemplate maintains several critical components:
public class GroupTemplate {
// Runtime class loading context
private ClassLoader templateClassLoader = resolveContextClassLoader();
// Attribute access strategy factory
private AABuilder accessStrategyBuilder = new AABuilder();
// Resource management
private ResourceLoader resourceManager;
// Engine configuration
private Configuration engineConfig;
// Template processing engine
private TemplateEngine processingEngine;
// Compiled template cache
private Cache compiledCache = ProgramCacheFactory.defaultCache();
// Event handling
private List<Listener> eventListeners = new ArrayList<>();
// Function registry
private Map<String, Function> registeredFunctions = new HashMap<>();
// Output formatters
private Map<String, Format> namedFormatters = new HashMap<>();
private Map<Class<?>, Format> typeFormatters = new HashMap<>(0);
// Virtual attribute handling
private List<VirtualAttributeEval> virtualHandlers = new ArrayList<>();
private Map<Class<?>, VirtualClassAttribute> virtualClassMap = new HashMap<>();
// Tag processing
private Map<String, TagFactory> tagProcessors = new HashMap<>();
// Security and search utilities
private ClassSearch classFinder;
private NativeSecurityManager securityManager;
private ErrorHandler errorProcessor;
// Shared context
private Map<String, Object> globalContext;
// Buffer management
private ContextLocalBuffers contextBuffers;
// HTML attribute conversion
private AttributeNameConvert htmlAttributeConverter;
}
Field Functionality
- templateClassLoader: Manages class loading during template execution, defaulting to thread context loader
- accessStrategyBuilder: Creates attribute access strategies for different data types (MapAA for Maps, ListAA for Collections)
- resourceManager: Handles template resource retrieval from various sources
- engineConfig: Central configuration hub for all engine settings
- processingEngine: Core engine with createProgram() method for template compilation
- compiledCache: L1 cache using ConcurrentHashMap for compiled templates
Initialization Process
Constructor Workflow
The constructor follows a three-phase initialization:
public GroupTemplate() {
try {
// Phase 1: Load default configuration
this.engineConfig = Configuration.defaultConfiguration();
// Phase 2: Initialize components
initializeComponents();
// Phase 3: Setup resource loading
setupResourceManagement();
} catch (Exception e) {
throw new BeetlException("Template initialization failed", e);
}
}
Component Initialization
The initializeComponents() method orchestrates several subsystems:
private void initializeComponents() {
// Build configuration tree
engineConfig.build();
// Initialize processing engine
processingEngine = TemplateEngineFactory.getEngine(engineConfig.getEngine());
// Setup various subsystems
registerCustomFunctions();
initializeFormatters();
configureTags();
setupVirtualAttributes();
initializeBuffers();
// Initialize utilities
classFinder = new ClassSearch(engineConfig.getPkgList(), this);
securityManager = createSecurityManager();
errorProcessor = createErrorHandler();
htmlAttributeConverter = createAttributeConverter();
}
Architecture Insights
The GroupTemplate design demonstrates several key architectural patterns:
- Centralized Configuration: All settings flow through the Configuration object, enabling consistent behavior across components
- Extensible Registry Pattern: Functions, formatters, and tags are registered in maps, allowing runtime extension
- Cache Abstraction: The compiledCache encapsulates ConcurrentHashMap with a minimal interface (get/put/remove/clear)
- Context Management: Shared variables and local buffers provide isolation between template executions
The caching implementation particularly shows effective encapsulation - rather than exposing ConcurrentHashMap directly, it provides a focused API that prevents misuse while maintaining performance. This pattern is valuable for building custom cache managers that balance flexibility with control.