Skip to content

Commit e021d66

Browse files
committed
Merge branch 'develop' into grails7
# Conflicts: # CHANGELOG.md
2 parents 8396cd6 + 66510d2 commit e021d66

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

CHANGELOG.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,29 @@ Spring Boot 3.5, Spring 6.2, Groovy 4.0, Gradle 8.14, Tomcat 10.1, and Java 21.
1010
release grails is officially part of the Apache Foundation. The main required changes to
1111
applications are the following:
1212

13-
* Change to logging config to accommodate the latest version of Logback, and its removal of the groovy DSL.
14-
In order to allow hoist apps to continue to seamlessly provide configuration via groovy, we have replicated
15-
the functionality of the logback DSL in methods on LogConfig.groovy. Any custom logback.groovy scripts
16-
should be moved to an override of `LogbackConfig` class. See `LogbackConfig.` for more details.
17-
Misc. updates and simplification to `build.gradle` and `gradle.properties` to adapt to Gradle 8.
18-
See toolbox for an example of these changes.
19-
* Changes of various core imports from `javax` to `jakarta`.
20-
* Java 17 and Java 21 are both now supported JDKs.
13+
* Change to logging config to accommodate the latest version of Logback, and its removal of the groovy DSL.
14+
In order to allow hoist apps to continue to seamlessly provide configuration via groovy, we have replicated
15+
the functionality of the logback DSL in methods on LogConfig.groovy. Any custom logback.groovy scripts
16+
should be moved to an override of `LogbackConfig` class. See `LogbackConfig.` for more details.
17+
Misc. updates and simplification to `build.gradle` and `gradle.properties` to adapt to Gradle 8.
18+
See toolbox for an example of these changes.
19+
* Changes of various core imports from `javax` to `jakarta`.
20+
* Java 17 and Java 21 are both now supported JDKs.
2121

2222
See the grails documentation at https://docs.grails.org/7.0.0/guide/upgrading.html#upgrading60x
2323
for more information.
2424

25+
26+
## 33.1.0 - 2025-10-24
27+
28+
### 🎁 New Features
29+
30+
* `EmailService.sendEmail` now supports `bcc` and `markImportant` properties.
31+
* New `CollectionUtils` with java utilities for efficient collection creation.
32+
2533
### 🐞 Bug Fixes
26-
* Restore display of Timers in Service AdminStats.
2734

35+
* Restore display of Timers in Service AdminStats.
2836

2937
## 33.0.0 - 2025-09-26
3038

grails-app/services/io/xh/hoist/email/EmailService.groovy

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ class EmailService extends BaseService {
3232
* @param args
3333
* to {String or List} - required, email address(es) of recipient(s)
3434
* from {String} - optional, sender address, defaults to config
35-
* cc {String or List} - optional, cc recipients;
35+
* cc {String or List} - optional, cc recipients
36+
* bcc {String or List} - optional, bcc recipients
3637
* subject {String} - optional
38+
* markImportant {boolean} - optional, to mark email as important via standard headers, default false
3739
* async {Boolean} - option to send email asynchronously, defaults to false
3840
* doLog {Boolean} - option to log email information on send, defaults to true
3941
* logIdentifier {String} - optional, string to append to log message, defaults to subject
@@ -60,10 +62,12 @@ class EmailService extends BaseService {
6062
def override = parseMailConfig('xhEmailOverride'),
6163
filter = parseMailConfig('xhEmailFilter'),
6264
toSpec = filterAddresses(formatAddresses(args.to), filter),
63-
ccSpec = filterAddresses(formatAddresses(args.cc), filter)
65+
ccSpec = filterAddresses(formatAddresses(args.cc), filter),
66+
bccSpec = filterAddresses(formatAddresses(args.bcc), filter)
6467

6568
List<String> toUse = override ? override : toSpec
6669
List<String> ccUse = override ? [] : ccSpec
70+
List<String> bccUse = override ? [] : bccSpec
6771
String fromUse = args.from ? formatAddresses(args.from)[0] : parseMailConfig('xhEmailDefaultSender')[0]
6872
String subjectUse = args.subject ?: ''
6973
List<Map> attachments = parseAttachments(args.attachments)
@@ -92,7 +96,7 @@ class EmailService extends BaseService {
9296
devContext << Utils.appEnvironment.displayName.toUpperCase();
9397
}
9498
if (override) {
95-
devContext << (toSpec.size() > 1 ? "for ${toSpec.size()} receipients" : "for ${toSpec.first()}")
99+
devContext << (toSpec.size() > 1 ? "for ${toSpec.size()} recipients" : "for ${toSpec.first()}")
96100
}
97101
if (devContext) {
98102
subjectUse += " [${devContext.join(', ')}]"
@@ -107,6 +111,17 @@ class EmailService extends BaseService {
107111
if (ccUse) {
108112
cc ccUse.toArray()
109113
}
114+
if (bccUse) {
115+
bcc bccUse.toArray()
116+
}
117+
if (args.markImportant) {
118+
headers (
119+
'Importance': 'High',
120+
'X-MSMail-Priority': 'High',
121+
'X-Priority': 1
122+
)
123+
}
124+
110125
subject subjectUse.take(255)
111126

112127
if (args.containsKey('html')) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.xh.hoist.util;
2+
3+
import java.util.*;
4+
import java.util.function.Function;
5+
6+
/**
7+
* Hoist tools for creating/manipulating collections in Java using groovy
8+
* like syntax.Useful for applications with large datasets and high performance
9+
* requirements.
10+
*/
11+
public class CollectionUtils {
12+
13+
/**
14+
* Create an HashMap for a pre-determined number of items.
15+
* Will size appropriately to avoid any rehashing/re-allocation.
16+
*/
17+
public static <K, V> HashMap<K, V> sizedHashMap(int size) {
18+
return new HashMap<K, V>((int) ((size / 0.75d) + 1d));
19+
}
20+
21+
/**
22+
* Create a declarative hashmap from an alternating collection of key, value pairs
23+
*/
24+
public static <K, V> HashMap<K, V> quickMap(Object... args) {
25+
HashMap<K, V> ret = sizedHashMap(args.length);
26+
for (int i = 0; i < args.length; i += 2) {
27+
ret.put((K) args[i], (V) args[i + 1]);
28+
}
29+
return ret;
30+
}
31+
32+
/**
33+
* Java version of Groovy collectEntries
34+
*/
35+
public static <S, K, V> Map<K, V> collectEntries(Collection<S> col, Function<S, Map.Entry<K, V>> mapper) {
36+
HashMap<K, V> ret = sizedHashMap(col.size());
37+
for (S val : col) {
38+
Map.Entry<K, V> e = mapper.apply(val);
39+
ret.put(e.getKey(), e.getValue());
40+
}
41+
return ret;
42+
}
43+
44+
/**
45+
* Java version of Groovy collect
46+
*/
47+
public static <S, T> List<T> collect(Collection<S> col, Function<S, T> mapper) {
48+
List<T> ret = new ArrayList<>(col.size());
49+
for (S val : col) {
50+
ret.add(mapper.apply(val));
51+
}
52+
return ret;
53+
}
54+
}

0 commit comments

Comments
 (0)