0
# GString Templates
1
2
The GStringTemplateEngine provides template processing using GString closures stored as writable templates. It's designed for streaming scenarios and offers efficient template storage and execution through closure-based architecture.
3
4
## Capabilities
5
6
### GString Template Engine Class
7
8
Creates templates using GString closures with efficient storage and streaming support.
9
10
```java { .api }
11
/**
12
* Template engine using GString and JSP-style syntax, storing templates as writable closures.
13
* Optimized for streaming scenarios and efficient template reuse.
14
*/
15
public class GStringTemplateEngine extends TemplateEngine {
16
/**
17
* Creates a new GStringTemplateEngine with default class loader
18
*/
19
public GStringTemplateEngine();
20
21
/**
22
* Creates a new GStringTemplateEngine with custom class loader
23
* @param parentLoader ClassLoader used for template compilation
24
*/
25
public GStringTemplateEngine(ClassLoader parentLoader);
26
}
27
```
28
29
**Usage Examples:**
30
31
```java
32
import groovy.text.GStringTemplateEngine;
33
import groovy.text.Template;
34
import java.util.Map;
35
import java.util.HashMap;
36
37
// Create GString template engine
38
GStringTemplateEngine engine = new GStringTemplateEngine();
39
40
// Template with mixed JSP and GString syntax
41
String templateText = """
42
Dear <%= firstname %> $lastname,
43
44
We <% if (accepted) print 'are pleased' else print 'regret' %>
45
to inform you that your paper entitled
46
'$title' was ${ accepted ? 'accepted' : 'rejected' }.
47
48
The conference committee.
49
""";
50
51
Template template = engine.createTemplate(templateText);
52
53
Map<String, Object> binding = new HashMap<>();
54
binding.put("firstname", "Grace");
55
binding.put("lastname", "Hopper");
56
binding.put("accepted", true);
57
binding.put("title", "Groovy for COBOL programmers");
58
59
String result = template.make(binding).toString();
60
```
61
62
### Template Syntax Support
63
64
GStringTemplateEngine supports the same template syntax as SimpleTemplateEngine and StreamingTemplateEngine:
65
66
#### JSP-Style Scriptlets
67
Use `<% %>` blocks for executing Groovy code:
68
69
```java
70
String template = """
71
<%
72
def items = ['apple', 'banana', 'orange']
73
for (item in items) {
74
print "- " + item + "\\n"
75
}
76
%>
77
""";
78
```
79
80
#### JSP-Style Expressions
81
Use `<%= %>` blocks for outputting expression results:
82
83
```java
84
String template = """
85
Today is <%= new Date().format('yyyy-MM-dd') %>
86
Temperature: <%= temperature %>°C
87
""";
88
```
89
90
#### GString Expressions
91
Use `${expression}` or `$variable` for variable substitution:
92
93
```java
94
String template = """
95
Welcome ${user.name}!
96
You have ${messages.size()} new messages.
97
Your account balance is $balance.
98
""";
99
```
100
101
### Closure-Based Architecture
102
103
GStringTemplateEngine internally converts templates to closure-based representations:
104
105
```java
106
// The engine converts template text like this:
107
String template = "Hello ${name}! Today is <%= new Date() %>";
108
109
// Into closure-based code similar to:
110
// { out -> out << "Hello ${name}! Today is "; out << new Date(); }
111
```
112
113
This architecture provides several benefits:
114
- **Efficient reuse**: Templates are compiled once and can be applied multiple times
115
- **Memory efficiency**: Closure-based storage reduces memory overhead
116
- **Thread safety**: Each template execution gets its own closure instance
117
118
### Streaming Support
119
120
GStringTemplateEngine is particularly well-suited for streaming scenarios:
121
122
```java
123
import groovy.text.GStringTemplateEngine;
124
import groovy.text.Template;
125
import groovy.lang.Writable;
126
import java.io.FileWriter;
127
import java.io.Writer;
128
129
GStringTemplateEngine engine = new GStringTemplateEngine();
130
131
String reportTemplate = """
132
# User Report
133
134
<% users.each { user -> %>
135
## User: ${user.name}
136
- Email: ${user.email}
137
- Status: ${user.active ? 'Active' : 'Inactive'}
138
- Last Login: <%= user.lastLogin?.format('yyyy-MM-dd') ?: 'Never' %>
139
140
<% } %>
141
142
Total users: ${users.size()}
143
""";
144
145
Template template = engine.createTemplate(reportTemplate);
146
147
// Create large dataset
148
List<Map<String, Object>> users = new ArrayList<>();
149
for (int i = 1; i <= 10000; i++) {
150
Map<String, Object> user = new HashMap<>();
151
user.put("name", "User " + i);
152
user.put("email", "user" + i + "@example.com");
153
user.put("active", i % 3 == 0);
154
users.add(user);
155
}
156
157
Map<String, Object> binding = new HashMap<>();
158
binding.put("users", users);
159
160
// Stream directly to file
161
Writable result = template.make(binding);
162
try (Writer writer = new FileWriter("report.txt")) {
163
result.writeTo(writer);
164
}
165
```
166
167
### Template Reuse and Performance
168
169
GStringTemplateEngine excels at template reuse scenarios:
170
171
```java
172
GStringTemplateEngine engine = new GStringTemplateEngine();
173
174
// Compile template once
175
String emailTemplate = """
176
Subject: Welcome ${name}!
177
178
Dear ${name},
179
180
Thank you for joining ${company}. Your account has been created with username: ${username}
181
182
<% if (isPremium) { %>
183
As a premium member, you have access to:
184
- Priority support
185
- Advanced features
186
- Extended storage
187
<% } %>
188
189
Welcome aboard!
190
The ${company} Team
191
""";
192
193
Template template = engine.createTemplate(emailTemplate);
194
195
// Reuse template for multiple users
196
List<Map<String, Object>> newUsers = getNewUsers(); // Assume this method exists
197
198
for (Map<String, Object> user : newUsers) {
199
Map<String, Object> binding = new HashMap<>();
200
binding.put("name", user.get("name"));
201
binding.put("username", user.get("username"));
202
binding.put("company", "Acme Corp");
203
binding.put("isPremium", user.get("accountType").equals("premium"));
204
205
String email = template.make(binding).toString();
206
sendEmail(user.get("email"), email); // Assume this method exists
207
}
208
```
209
210
### Web Integration
211
212
GStringTemplateEngine can be used with servlet containers:
213
214
```xml
215
<servlet>
216
<servlet-name>GStringTemplate</servlet-name>
217
<servlet-class>groovy.servlet.TemplateServlet</servlet-class>
218
<init-param>
219
<param-name>template.engine</param-name>
220
<param-value>groovy.text.GStringTemplateEngine</param-value>
221
</init-param>
222
</servlet>
223
```
224
225
### Binding Resolution Strategy
226
227
GStringTemplateEngine uses `DELEGATE_FIRST` resolution strategy to ensure proper variable binding:
228
229
```java
230
// Template variables are resolved in this order:
231
// 1. Template binding (Map passed to make())
232
// 2. Template closure delegate
233
// 3. Template closure owner
234
235
String template = """
236
Value from binding: $bindingVar
237
<%
238
// This variable will be stored in the binding, not the script
239
localVar = "This goes to binding"
240
%>
241
Local variable: $localVar
242
""";
243
```
244
245
### Error Handling
246
247
GStringTemplateEngine provides standard Groovy error handling:
248
249
```java
250
try {
251
GStringTemplateEngine engine = new GStringTemplateEngine();
252
Template template = engine.createTemplate(templateText);
253
String result = template.make(binding).toString();
254
} catch (CompilationFailedException e) {
255
// Template compilation failed - syntax error in template
256
System.err.println("Compilation error: " + e.getMessage());
257
} catch (IOException e) {
258
// I/O error when reading template source
259
System.err.println("I/O error: " + e.getMessage());
260
} catch (ClassNotFoundException e) {
261
// Missing class dependencies
262
System.err.println("Class not found: " + e.getMessage());
263
} catch (Exception e) {
264
// Runtime errors during template execution
265
System.err.println("Runtime error: " + e.getMessage());
266
}
267
```
268
269
### Best Practices
270
271
1. **Template Reuse**: Compile templates once and reuse with different bindings
272
2. **Large Data**: Use streaming with `writeTo()` for large outputs
273
3. **Variable Scope**: Be aware that variables assigned in scriptlets go to the binding
274
4. **Thread Safety**: Create separate template instances for concurrent use
275
5. **Memory Management**: Let templates be garbage collected when no longer needed