1 package org.apache.turbine.services.urlmapper;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.Set;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 import java.util.stream.Collectors;
35 import java.util.stream.Stream;
36
37 import javax.xml.bind.JAXBContext;
38 import javax.xml.bind.JAXBException;
39 import javax.xml.bind.Unmarshaller;
40
41 import org.apache.commons.configuration2.Configuration;
42 import org.apache.fulcrum.parser.ParameterParser;
43 import org.apache.logging.log4j.LogManager;
44 import org.apache.logging.log4j.Logger;
45 import org.apache.turbine.services.InitializationException;
46 import org.apache.turbine.services.TurbineBaseService;
47 import org.apache.turbine.services.TurbineServices;
48 import org.apache.turbine.services.servlet.ServletService;
49 import org.apache.turbine.services.urlmapper.model.URLMapEntry;
50 import org.apache.turbine.services.urlmapper.model.URLMappingContainer;
51 import org.apache.turbine.util.uri.TurbineURI;
52 import org.apache.turbine.util.uri.URIParam;
53
54 import com.fasterxml.jackson.databind.ObjectMapper;
55 import com.fasterxml.jackson.databind.json.JsonMapper;
56 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class TurbineURLMapperService
73 extends TurbineBaseService
74 implements URLMapperService
75 {
76
77
78
79 private static final Logger log = LogManager.getLogger(TurbineURLMapperService.class);
80
81
82
83
84 private static final String DEFAULT_CONFIGURATION_FILE = "/WEB-INF/conf/turbine-url-mapping.xml";
85
86
87
88
89 private static final String CONFIGURATION_FILE_KEY = "configFile";
90
91
92
93
94 private URLMappingContainer container;
95
96
97
98
99 private static final Pattern NAMED_GROUPS_PATTERN = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>.+?\\)");
100
101
102
103
104 private static final Pattern MULTI_SLASH_PATTERN = Pattern.compile("[/]+");
105
106
107
108
109 private static final String CONTEXT_PATH_PARAMETER = "contextPath";
110
111
112
113
114 private static final String WEBAPP_ROOT_PARAMETER = "webAppRoot";
115
116
117
118
119 private static final Set<String> DEFAULT_PARAMETERS = new HashSet<>(Arrays.asList(
120 CONTEXT_PATH_PARAMETER,
121 WEBAPP_ROOT_PARAMETER
122 ));
123
124
125
126
127
128
129
130 @Override
131 public void mapToURL(TurbineURI uri)
132 {
133 if (!uri.hasPathInfo() && !uri.hasQueryData())
134 {
135 return;
136 }
137
138 List<URIParam> pathInfo = uri.getPathInfo();
139 List<URIParam> queryData = uri.getQueryData();
140
141
142
143 Map<String, Object> uriParameterMap =
144 Stream.concat(pathInfo.stream(), queryData.stream())
145 .collect(Collectors.toMap(
146 URIParam::getKey,
147 URIParam::getValue,
148 (e1, e2) -> e1,
149 LinkedHashMap::new));
150
151 for (URLMapEntry urlMap : container.getMapEntries())
152 {
153 Set<String> keys = new HashSet<>(uriParameterMap.keySet());
154 keys.removeAll(urlMap.getIgnoreParameters().keySet());
155
156 Set<String> entryKeys = new HashSet<>(urlMap.getGroupNamesMap().keySet());
157
158 Set<String> implicitKeysFound = urlMap.getImplicitParameters().entrySet().stream()
159 .filter(entry -> Objects.equals(uriParameterMap.get(entry.getKey()), entry.getValue()))
160 .map(Map.Entry::getKey)
161 .collect(Collectors.toSet());
162
163 entryKeys.addAll(implicitKeysFound);
164
165 if (entryKeys.containsAll(keys))
166 {
167 Matcher matcher = NAMED_GROUPS_PATTERN.matcher(urlMap.getUrlPattern().pattern());
168 StringBuffer sb = new StringBuffer();
169
170 while (matcher.find())
171 {
172 String key = matcher.group(1);
173
174 if (CONTEXT_PATH_PARAMETER.equals(key))
175 {
176
177 matcher.appendReplacement(sb, "");
178 } else if (WEBAPP_ROOT_PARAMETER.equals(key))
179 {
180 matcher.appendReplacement(sb, uri.getScriptName());
181 } else
182 {
183 boolean ignore = urlMap.getIgnoreParameters().keySet().stream()
184 .anyMatch( x-> x.equals( key ) );
185 matcher.appendReplacement(sb,
186 Matcher.quoteReplacement(
187 (!ignore)? Objects.toString(uriParameterMap.get(key)):""));
188
189 pathInfo.removeIf(uriParam -> key.equals(uriParam.getKey()));
190 queryData.removeIf(uriParam -> key.equals(uriParam.getKey()));
191 }
192 }
193
194 matcher.appendTail(sb);
195
196 implicitKeysFound.forEach(key -> {
197 pathInfo.removeIf(uriParam -> key.equals(uriParam.getKey()));
198 queryData.removeIf(uriParam -> key.equals(uriParam.getKey()));
199 });
200
201
202 uri.setScriptName(MULTI_SLASH_PATTERN.matcher(sb).replaceAll("/").replaceFirst( "/$", "" ));
203
204 break;
205 }
206 }
207
208 log.debug("mapped to uri: {} ", uri);
209 }
210
211
212
213
214
215
216
217 @Override
218 public void mapFromURL(String url, ParameterParser pp)
219 {
220 for (URLMapEntry urlMap : container.getMapEntries())
221 {
222 url = url.replaceFirst( "/$", "" );
223 Matcher matcher = urlMap.getUrlPattern().matcher(url);
224 if (matcher.matches())
225 {
226
227 urlMap.getGroupNamesMap().entrySet().stream()
228
229 .filter(group -> !DEFAULT_PARAMETERS.contains(group.getKey()))
230 .forEach(group ->
231 pp.setString(group.getKey(), matcher.group(group.getValue().intValue())));
232
233
234 urlMap.getImplicitParameters().entrySet().forEach(e ->
235 pp.add(e.getKey(), e.getValue()));
236
237
238 urlMap.getOverrideParameters().entrySet().forEach(e ->
239 pp.setString(e.getKey(), e.getValue()));
240
241
242 urlMap.getIgnoreParameters().keySet().forEach(k ->
243 pp.remove(k));
244
245 log.debug("mapped {} params from url {} ", pp.getKeys().length, url);
246
247 break;
248 }
249 }
250 }
251
252
253
254
255
256
257 @Override
258 public void init() throws InitializationException
259 {
260 Configuration cfg = getConfiguration();
261
262 String configFile = cfg.getString(CONFIGURATION_FILE_KEY, DEFAULT_CONFIGURATION_FILE);
263
264
265
266 if (!configFile.startsWith("/"))
267 {
268 configFile = "/" + configFile;
269 }
270
271 ServletServicerg/apache/turbine/services/servlet/ServletService.html#ServletService">ServletService servletService = (ServletService) TurbineServices.getInstance().getService(ServletService.SERVICE_NAME);
272
273 try (InputStream reader = servletService.getResourceAsStream(configFile))
274 {
275 if (configFile.endsWith(".xml"))
276 {
277 JAXBContext jaxb = JAXBContext.newInstance(URLMappingContainer.class);
278 Unmarshaller unmarshaller = jaxb.createUnmarshaller();
279 container = (URLMappingContainer) unmarshaller.unmarshal(reader);
280 } else if (configFile.endsWith(".yml"))
281 {
282
283
284 ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
285 container = mapper.readValue(reader, URLMappingContainer.class);
286 } else if (configFile.endsWith(".json"))
287 {
288 ObjectMapper mapper = JsonMapper.builder().build();
289 container = mapper.readValue(reader, URLMappingContainer.class);
290 }
291 }
292 catch (IOException | JAXBException e)
293 {
294 throw new InitializationException("Could not load configuration file " + configFile, e);
295 }
296
297
298 for (URLMapEntry urlMap : container.getMapEntries())
299 {
300 int position = 1;
301 Map<String, Integer> groupNamesMap = new HashMap<>();
302 Matcher matcher = NAMED_GROUPS_PATTERN.matcher(urlMap.getUrlPattern().pattern());
303
304 while (matcher.find())
305 {
306 groupNamesMap.put(matcher.group(1), Integer.valueOf(position++));
307 }
308 urlMap.setGroupNamesMap(groupNamesMap);
309 }
310
311 log.info("Loaded {} url-mappings from {}", Integer.valueOf(container.getMapEntries().size()), configFile);
312
313 setInit(true);
314 }
315
316
317
318
319 @Override
320 public void shutdown()
321 {
322 container.getMapEntries().clear();
323 setInit(false);
324 }
325 }