1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.handler.url.impl;
21
22 import java.util.Set;
23
24 import org.apache.commons.lang3.ObjectUtils;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.sling.api.SlingHttpServletRequest;
27 import org.apache.sling.api.adapter.Adaptable;
28 import org.apache.sling.api.resource.Resource;
29 import org.apache.sling.api.resource.ResourceResolver;
30 import org.apache.sling.models.annotations.Model;
31 import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
32 import org.apache.sling.models.annotations.injectorspecific.OSGiService;
33 import org.apache.sling.models.annotations.injectorspecific.Self;
34 import org.apache.sling.models.annotations.injectorspecific.SlingObject;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import com.day.cq.wcm.api.Page;
39
40 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
41 import io.wcm.handler.url.UrlBuilder;
42 import io.wcm.handler.url.UrlHandler;
43 import io.wcm.handler.url.UrlMode;
44 import io.wcm.handler.url.VanityMode;
45 import io.wcm.handler.url.impl.clientlib.ClientlibProxyRewriter;
46 import io.wcm.handler.url.spi.UrlHandlerConfig;
47 import io.wcm.sling.commons.request.RequestParam;
48 import io.wcm.sling.models.annotations.AemObject;
49 import io.wcm.wcm.commons.instancetype.InstanceTypeService;
50 import io.wcm.wcm.commons.util.Path;
51
52
53
54
55 @Model(adaptables = {
56 SlingHttpServletRequest.class, Resource.class
57 }, adapters = UrlHandler.class)
58 public final class UrlHandlerImpl implements UrlHandler {
59
60 @Self
61 private Adaptable self;
62 @Self
63 private UrlHandlerConfig urlHandlerConfig;
64 @SlingObject
65 private ResourceResolver resolver;
66 @OSGiService
67 private InstanceTypeService instanceTypeService;
68 @OSGiService
69 private ClientlibProxyRewriter clientlibProxyRewriter;
70
71
72 @SlingObject(injectionStrategy = InjectionStrategy.OPTIONAL)
73 private SlingHttpServletRequest request;
74 @AemObject(injectionStrategy = InjectionStrategy.OPTIONAL)
75 private Page currentPage;
76
77 @Override
78 public @NotNull UrlBuilder get(@NotNull String path) {
79 return new UrlBuilderImpl(path, this);
80 }
81
82 @Override
83 public @NotNull UrlBuilder get(@NotNull Resource resource) {
84 return new UrlBuilderImpl(resource, this);
85 }
86
87 @Override
88 public @NotNull UrlBuilder get(@NotNull Page page) {
89 return new UrlBuilderImpl(page, this);
90 }
91
92 @Override
93 @SuppressWarnings({ "null", "unused" })
94 @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
95 public String rewritePathToContext(@NotNull final Resource resource) {
96 if (resource == null) {
97 return null;
98 }
99 if (currentPage != null) {
100 return rewritePathToContext(resource, currentPage.adaptTo(Resource.class));
101 }
102 else {
103 return resource.getPath();
104 }
105 }
106
107 @Override
108 @SuppressWarnings({ "null", "unused" })
109 public String rewritePathToContext(@NotNull final Resource resource, @NotNull final Resource contextResource) {
110 if (resource == null) {
111 return null;
112 }
113 if (contextResource == null) {
114 return resource.getPath();
115 }
116
117
118 String[] contextPathParts = StringUtils.split(Path.getOriginalPath(contextResource.getPath(), resolver), "/");
119 String[] pathParts = StringUtils.split(Path.getOriginalPath(resource.getPath(), resolver), "/");
120
121
122 int siteRootLevelContextPath = urlHandlerConfig.getSiteRootLevel(contextResource);
123 int siteRootLevelPath = urlHandlerConfig.getSiteRootLevel(resource);
124 if ((contextPathParts.length <= siteRootLevelContextPath)
125 || (pathParts.length <= siteRootLevelPath)
126 || !StringUtils.equals(contextPathParts[0], "content")
127 || !StringUtils.equals(pathParts[0], "content")) {
128 return resource.getPath();
129 }
130
131
132 StringBuilder rewrittenPath = new StringBuilder();
133 for (int i = 0; i <= siteRootLevelContextPath; i++) {
134 rewrittenPath.append('/').append(contextPathParts[i]);
135 }
136 for (int i = siteRootLevelPath + 1; i < pathParts.length; i++) {
137 rewrittenPath.append('/').append(pathParts[i]);
138 }
139 return rewrittenPath.toString();
140 }
141
142 @Override
143 public boolean isExternalized(@NotNull String url) {
144 return Externalizer.isExternalized(url);
145 }
146
147 String externalizeLinkUrl(final String url, final Page targetPage, final UrlMode urlMode) {
148
149
150 if (StringUtils.isEmpty(url)) {
151 return null;
152 }
153
154
155 if (Externalizer.isExternalized(url)) {
156 return url;
157 }
158
159 String externalizedUrl;
160 if (urlHandlerConfig.isHostProvidedBySlingMapping()) {
161
162 externalizedUrl = Externalizer.externalizeUrlWithHost(url, resolver, request);
163 }
164 else {
165
166 externalizedUrl = Externalizer.externalizeUrl(url, resolver, request);
167 }
168 if (externalizedUrl != null && !Externalizer.isExternalized(externalizedUrl)) {
169
170 String linkUrlPrefix = getLinkUrlPrefix(urlMode, targetPage);
171 externalizedUrl = StringUtils.defaultString(linkUrlPrefix) + externalizedUrl;
172 }
173 return externalizedUrl;
174 }
175
176 @SuppressWarnings("null")
177 private String getLinkUrlPrefix(UrlMode urlMode, Page targetPage) {
178 UrlMode mode = ObjectUtils.defaultIfNull(urlMode, urlHandlerConfig.getDefaultUrlMode());
179 String configuredUrlPrefix = mode.getLinkUrlPrefix(self, instanceTypeService.getRunModes(), currentPage, targetPage);
180 return UrlPrefix.applyAutoDetection(configuredUrlPrefix, self);
181 }
182
183 String externalizeResourceUrl(final String url, final Resource targetResource, final UrlMode urlMode) {
184
185
186 if (StringUtils.isEmpty(url)) {
187 return null;
188 }
189
190
191
192 if (Externalizer.isExternalized(url) || !Externalizer.isExternalizable(url)) {
193 return url;
194 }
195
196
197 Resource resource = targetResource;
198 if (resource == null && StringUtils.startsWith(url, "/content/")) {
199 resource = resolver.resolve(url);
200 }
201
202
203 String externalizedUrl = clientlibProxyRewriter.rewriteStaticResourcePath(url);
204
205 if (urlHandlerConfig.isHostProvidedBySlingMapping()) {
206
207 externalizedUrl = Externalizer.externalizeUrlWithHost(externalizedUrl, resolver, request);
208 }
209 else {
210
211 externalizedUrl = Externalizer.externalizeUrl(externalizedUrl, resolver, request);
212 }
213 if (externalizedUrl != null && !Externalizer.isExternalized(externalizedUrl)) {
214
215 String resourceUrlPrefix = getResourceUrlPrefix(urlMode, resource);
216 externalizedUrl = StringUtils.defaultString(resourceUrlPrefix) + externalizedUrl;
217 }
218 return externalizedUrl;
219 }
220
221 @SuppressWarnings("null")
222 private String getResourceUrlPrefix(UrlMode urlMode, Resource targetResource) {
223 UrlMode mode = ObjectUtils.defaultIfNull(urlMode, urlHandlerConfig.getDefaultUrlMode());
224 String configuredUrlPrefix = mode.getResourceUrlPrefix(self, instanceTypeService.getRunModes(), currentPage, targetResource);
225 return UrlPrefix.applyAutoDetection(configuredUrlPrefix, self);
226 }
227
228 String buildUrl(String path, String selector, String extension, String suffix, boolean disableSuffixSelector) {
229 if (StringUtils.isBlank(path)) {
230 return null;
231 }
232
233
234 StringBuilder extensionPart = new StringBuilder();
235 if (StringUtils.isNotBlank(extension)) {
236 extensionPart.append('.').append(extension);
237 }
238
239
240 StringBuilder selectorPart = new StringBuilder();
241 if (StringUtils.isNotBlank(selector)) {
242
243 if (!StringUtils.startsWith(selector, ".")) {
244 selectorPart.append('.');
245 }
246 selectorPart.append(selector);
247 }
248
249
250 StringBuilder suffixPart = new StringBuilder();
251 if (StringUtils.isNotBlank(suffix)) {
252
253 if (!StringUtils.startsWith(suffix, "/")) {
254 suffixPart = suffixPart.append("/");
255 }
256 suffixPart.append(suffix);
257
258
259 if (!StringUtils.contains(suffix, ".")) {
260 suffixPart.append(extensionPart);
261 }
262
263
264 if (!disableSuffixSelector) {
265 selectorPart.append('.').append(UrlHandler.SELECTOR_SUFFIX);
266 }
267 }
268
269
270 return path + selectorPart.toString() + extensionPart.toString() + suffixPart.toString();
271 }
272
273 @SuppressWarnings("java:S3776")
274 String appendQueryString(String url, String queryString, Set<String> inheritableParameterNames) {
275 if (StringUtils.isEmpty(url)) {
276 return url;
277 }
278
279
280 StringBuilder urlBuilder = new StringBuilder();
281 StringBuilder queryParams = new StringBuilder();
282 int separatorPos = url.indexOf('?');
283 if (separatorPos >= 0) {
284 queryParams.append(url.substring(separatorPos + 1));
285 urlBuilder.append(url.substring(0, separatorPos));
286 }
287 else {
288 urlBuilder.append(url);
289 }
290
291
292 if (StringUtils.isNotBlank(queryString)) {
293 if (queryParams.length() > 0) {
294 queryParams.append('&');
295 }
296 queryParams.append(queryString);
297 }
298
299
300 if (inheritableParameterNames != null && request != null) {
301 for (String parameterName : inheritableParameterNames) {
302 if (queryParams.indexOf(parameterName + "=") == -1) {
303 String[] values = RequestParam.getMultiple(request, parameterName);
304 if (values != null) {
305 for (String value : values) {
306 if (StringUtils.isNotEmpty(value)) {
307 if (queryParams.length() > 0) {
308 queryParams.append('&');
309 }
310 queryParams.append(parameterName);
311 queryParams.append('=');
312 queryParams.append(value);
313 }
314 }
315 }
316 }
317 }
318 }
319
320
321 if (queryParams.length() > 0) {
322 urlBuilder.append('?');
323 urlBuilder.append(queryParams);
324 }
325 return urlBuilder.toString();
326 }
327
328 String setFragment(String url, String fragment) {
329 if (StringUtils.isEmpty(url)) {
330 return url;
331 }
332
333
334 StringBuilder urlBuilder;
335 int index = url.indexOf('#');
336 if (index >= 0) {
337 urlBuilder = new StringBuilder(url.substring(0, index));
338 }
339 else {
340 urlBuilder = new StringBuilder(url);
341 }
342
343
344 if (StringUtils.isNotBlank(fragment)) {
345 if (!StringUtils.startsWith(fragment, "#")) {
346 urlBuilder.append('#');
347 }
348 urlBuilder.append(fragment);
349 }
350
351 return urlBuilder.toString();
352 }
353
354 @Override
355 public String applySiteUrlAutoDetection(@Nullable String siteUrl) {
356 return UrlPrefix.applyAutoDetection(siteUrl, self);
357 }
358
359 VanityMode getDefaultVanityMode() {
360 return urlHandlerConfig.getDefaultVanityMode();
361 }
362
363 }