0
# Theme Resolution
1
2
Chain-based theme resolution system that evaluates multiple resolution strategies in priority order to determine the appropriate theme for each request.
3
4
## Capabilities
5
6
### ChainingThemeResolver
7
8
Theme resolver that chains multiple resolvers and returns the first non-default result.
9
10
```java { .api }
11
/**
12
* Theme resolver that chains multiple resolvers using chain of responsibility pattern
13
*/
14
public class ChainingThemeResolver extends AbstractThemeResolver {
15
16
/**
17
* Default constructor creating empty resolver chain
18
*/
19
public ChainingThemeResolver();
20
21
/**
22
* Adds resolver to the chain for evaluation
23
* @param r Theme resolver to add to the chain
24
* @return This ChainingThemeResolver for method chaining
25
*/
26
@CanIgnoreReturnValue
27
public ChainingThemeResolver addResolver(ThemeResolver r);
28
29
/**
30
* Resolves theme name by trying each resolver in chain order
31
* @param httpServletRequest HTTP request to resolve theme for
32
* @return Theme name from first resolver returning non-default result, or default theme
33
*/
34
@Override
35
public String resolveThemeName(@Nonnull HttpServletRequest httpServletRequest);
36
37
/**
38
* No-op implementation for theme setting - theme is resolved, not set
39
* @param httpServletRequest HTTP request
40
* @param httpServletResponse HTTP response
41
* @param s Theme name
42
*/
43
@Override
44
public void setThemeName(@Nonnull HttpServletRequest httpServletRequest,
45
HttpServletResponse httpServletResponse, String s);
46
}
47
```
48
49
**Usage Examples:**
50
51
```java
52
import org.apereo.cas.services.web.ChainingThemeResolver;
53
import org.springframework.web.servlet.theme.CookieThemeResolver;
54
import org.springframework.web.servlet.theme.SessionThemeResolver;
55
import org.springframework.web.servlet.theme.FixedThemeResolver;
56
57
// Create chaining resolver
58
ChainingThemeResolver chainResolver = new ChainingThemeResolver();
59
chainResolver.setDefaultThemeName("default");
60
61
// Add resolvers in priority order
62
CookieThemeResolver cookieResolver = new CookieThemeResolver();
63
cookieResolver.setDefaultThemeName("default");
64
chainResolver.addResolver(cookieResolver);
65
66
SessionThemeResolver sessionResolver = new SessionThemeResolver();
67
sessionResolver.setDefaultThemeName("default");
68
chainResolver.addResolver(sessionResolver);
69
70
FixedThemeResolver fixedResolver = new FixedThemeResolver();
71
fixedResolver.setDefaultThemeName("default");
72
chainResolver.addResolver(fixedResolver);
73
74
// Resolve theme for request
75
String themeName = chainResolver.resolveThemeName(request);
76
```
77
78
## Resolution Chain Strategy
79
80
The ChainingThemeResolver evaluates resolvers in the order they were added:
81
82
1. **First Match Wins**: Returns theme from first resolver that returns non-default theme
83
2. **Skip Default**: Continues chain if resolver returns the configured default theme name
84
3. **Final Fallback**: Returns default theme if no resolver provides a specific theme
85
86
```java
87
// Example evaluation flow:
88
// 1. CookieThemeResolver returns "default" -> continue
89
// 2. SessionThemeResolver returns "dark" -> return "dark"
90
// 3. Remaining resolvers are not evaluated
91
```
92
93
## Standard Resolution Chain
94
95
A typical CAS theme resolution chain includes:
96
97
1. **Cookie Theme Resolver** - Highest priority, user preference persistence
98
2. **Session Theme Resolver** - Session-scoped theme selection
99
3. **Request Header Theme Resolver** - API-driven theme selection
100
4. **Registered Service Theme Resolver** - Service-specific themes
101
5. **Fixed Theme Resolver** - Default fallback theme
102
103
```java
104
// Standard chain configuration (as done by auto-configuration)
105
chainResolver
106
.addResolver(cookieThemeResolver) // Priority 1
107
.addResolver(sessionThemeResolver) // Priority 2
108
.addResolver(headerThemeResolver) // Priority 3
109
.addResolver(serviceThemeResolver) // Priority 4
110
.addResolver(fixedThemeResolver); // Priority 5 (fallback)
111
```
112
113
## Theme Resolution Behavior
114
115
### Default Theme Handling
116
117
- Each resolver has a configured `defaultThemeName`
118
- Chain continues evaluation when resolver returns default theme
119
- Final fallback is the chain's own default theme
120
121
### Theme Name Validation
122
123
- No built-in theme name validation
124
- Theme names are passed through as-is
125
- Theme existence validation happens at theme source level
126
127
### Request Context
128
129
Theme resolution occurs within the context of:
130
- HTTP servlet request
131
- Spring Web MVC request cycle
132
- CAS authentication flow
133
- Service-specific processing
134
135
## Error Handling
136
137
The chaining resolver provides robust error handling:
138
139
- **Resolver Exceptions**: Logged and chain continues with next resolver
140
- **Null Results**: Treated as default theme, chain continues
141
- **Empty Strings**: Treated as default theme, chain continues
142
- **Chain Exhaustion**: Returns configured default theme
143
144
## Performance Considerations
145
146
- **Lazy Evaluation**: Resolvers evaluated only until first match
147
- **Ordered Execution**: Place fastest/most-likely resolvers first
148
- **Caching**: Individual resolvers may implement caching
149
- **Request Scoped**: Resolution happens per-request, not cached across requests
150
151
## Custom Resolver Integration
152
153
```java
154
// Example custom resolver
155
public class DatabaseThemeResolver extends AbstractThemeResolver {
156
@Override
157
public String resolveThemeName(HttpServletRequest request) {
158
String userId = getUserIdFromRequest(request);
159
return userThemeService.getThemeForUser(userId);
160
}
161
162
@Override
163
public void setThemeName(HttpServletRequest request,
164
HttpServletResponse response, String themeName) {
165
// Implement theme persistence if needed
166
}
167
}
168
169
// Add to chain
170
DatabaseThemeResolver dbResolver = new DatabaseThemeResolver();
171
dbResolver.setDefaultThemeName("default");
172
chainResolver.addResolver(dbResolver);
173
```