1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.config;
22
23 import java.io.Serializable;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Properties;
28
29 import org.apache.commons.beanutils.BeanUtils;
30 import org.apache.struts.action.ActionForward;
31 import org.apache.struts.util.WildcardHelper;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36
37
38
39
40
41
42
43
44 public class ActionConfigMatcher implements Serializable {
45 private static final long serialVersionUID = -7803926870173575845L;
46
47
48
49
50 private transient final Logger log =
51 LoggerFactory.getLogger(ActionConfigMatcher.class);
52
53
54
55
56 private static final WildcardHelper wildcard = new WildcardHelper();
57
58
59
60
61 private ArrayList<Mapping> compiledPaths;
62
63
64
65
66
67
68
69
70
71 public ActionConfigMatcher(ActionConfig[] configs) {
72 compiledPaths = new ArrayList<>();
73
74 int[] pattern;
75 String path;
76
77 for (int x = 0; x < configs.length; x++) {
78 path = configs[x].getPath();
79
80 if ((path != null) && (path.indexOf('*') > -1)) {
81 if ((path.length() > 0) && (path.charAt(0) == '/')) {
82 path = path.substring(1);
83 }
84
85 log.debug("Compiling action config path '{}'", path);
86
87 pattern = wildcard.compilePattern(path);
88 compiledPaths.add(new Mapping(pattern, configs[x]));
89 }
90 }
91 }
92
93
94
95
96
97
98
99 public ActionConfig match(String path) {
100 ActionConfig config = null;
101
102 if (compiledPaths.size() > 0) {
103 log.debug("Attempting to match '{}' to a wildcard pattern",
104 path);
105
106 if ((path.length() > 0) && (path.charAt(0) == '/')) {
107 path = path.substring(1);
108 }
109
110 HashMap<String, String> vars = new HashMap<>();
111
112 for (Mapping m : compiledPaths) {
113 if (wildcard.match(vars, path, m.getPattern())) {
114 log.debug("Path matches pattern '{}'",
115 m.getActionConfig().getPath());
116
117 try {
118 config =
119 convertActionConfig(path, m.getActionConfig(), vars);
120 } catch (IllegalStateException e) {
121 log.warn("Path matches pattern '{}' but is "
122 + "incompatible with the matching config due "
123 + "to recursive substitution: {}",
124 m.getActionConfig().getPath(), path);
125 config = null;
126 }
127 }
128 }
129 }
130
131 return config;
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146 protected ActionConfig convertActionConfig(String path, ActionConfig orig,
147 Map<String, String> vars) {
148 ActionConfig config = null;
149
150 try {
151 config = (ActionConfig) BeanUtils.cloneBean(orig);
152 } catch (Exception ex) {
153 log.warn("Unable to clone action config, recommend not using "
154 + "wildcards", ex);
155
156 return null;
157 }
158
159 config.setName(convertParam(orig.getName(), vars));
160
161 if ((path.length() == 0) || (path.charAt(0) != '/')) {
162 path = "/" + path;
163 }
164
165 config.setPath(path);
166 config.setType(convertParam(orig.getType(), vars));
167 config.setRoles(convertParam(orig.getRoles(), vars));
168 config.setParameter(convertParam(orig.getParameter(), vars));
169 config.setAttribute(convertParam(orig.getAttribute(), vars));
170 config.setForward(convertParam(orig.getForward(), vars));
171 config.setInclude(convertParam(orig.getInclude(), vars));
172 config.setInput(convertParam(orig.getInput(), vars));
173 config.setCatalog(convertParam(orig.getCatalog(), vars));
174 config.setCommand(convertParam(orig.getCommand(), vars));
175 config.setMultipartClass(convertParam(orig.getMultipartClass(), vars));
176 config.setPrefix(convertParam(orig.getPrefix(), vars));
177 config.setSuffix(convertParam(orig.getSuffix(), vars));
178
179 ForwardConfig[] fConfigs = orig.findForwardConfigs();
180 ForwardConfig cfg;
181
182 for (int x = 0; x < fConfigs.length; x++) {
183 try {
184 cfg = (ActionForward) BeanUtils.cloneBean(fConfigs[x]);
185 } catch (Exception ex) {
186 log.warn("Unable to clone action config, recommend not using "
187 + "wildcards", ex);
188 return null;
189 }
190 cfg.setName(fConfigs[x].getName());
191 cfg.setPath(convertParam(fConfigs[x].getPath(), vars));
192 cfg.setRedirect(fConfigs[x].getRedirect());
193 cfg.setCommand(convertParam(fConfigs[x].getCommand(), vars));
194 cfg.setCatalog(convertParam(fConfigs[x].getCatalog(), vars));
195 cfg.setModule(convertParam(fConfigs[x].getModule(), vars));
196
197 replaceProperties(fConfigs[x].getProperties(), cfg.getProperties(),
198 vars);
199
200 config.removeForwardConfig(fConfigs[x]);
201 config.addForwardConfig(cfg);
202 }
203
204 replaceProperties(orig.getProperties(), config.getProperties(), vars);
205
206 ExceptionConfig[] exConfigs = orig.findExceptionConfigs();
207
208 for (int x = 0; x < exConfigs.length; x++) {
209 config.addExceptionConfig(exConfigs[x]);
210 }
211
212 config.freeze();
213
214 return config;
215 }
216
217
218
219
220
221
222
223
224
225
226
227 protected void replaceProperties(Properties orig, Properties props, Map<String, String> vars) {
228 for (Map.Entry<Object, Object> entry : orig.entrySet()) {
229 props.setProperty((String) entry.getKey(),
230 convertParam((String) entry.getValue(), vars));
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243
244 protected String convertParam(String val, Map<String, String> vars) {
245 if (val == null) {
246 return null;
247 } else if (val.indexOf("{") == -1) {
248 return val;
249 }
250
251 StringBuilder key = new StringBuilder("{0}");
252 StringBuilder ret = new StringBuilder(val);
253 String keyStr;
254 int x;
255
256 for (Map.Entry<String, String> entry : vars.entrySet()) {
257 key.setCharAt(1, entry.getKey().charAt(0));
258 keyStr = key.toString();
259
260
261
262
263 if (entry.getValue().contains(keyStr)) {
264 throw new IllegalStateException();
265 }
266
267
268 while ((x = ret.toString().indexOf(keyStr)) > -1) {
269 ret.replace(x, x + 3, entry.getValue());
270 }
271 }
272
273 return ret.toString();
274 }
275
276
277
278
279
280 private class Mapping implements Serializable {
281 private static final long serialVersionUID = -4524356639556048603L;
282
283
284
285
286 private int[] pattern;
287
288
289
290
291 private ActionConfig config;
292
293
294
295
296
297
298
299 public Mapping(int[] pattern, ActionConfig config) {
300 this.pattern = pattern;
301 this.config = config;
302 }
303
304
305
306
307
308
309 public int[] getPattern() {
310 return this.pattern;
311 }
312
313
314
315
316
317
318 public ActionConfig getActionConfig() {
319 return this.config;
320 }
321 }
322 }