/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.wicket.core.request.mapper; import java.util.List; import java.util.StringTokenizer; import java.util.function.Supplier; import org.apache.wicket.core.util.lang.WicketObjects; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.Request; import org.apache.wicket.request.Url; import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.IResource; import org.apache.wicket.request.resource.MetaInfStaticResourceReference; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.request.resource.ResourceReferenceRegistry; import org.apache.wicket.request.resource.caching.IResourceCachingStrategy; import org.apache.wicket.request.resource.caching.IStaticCacheableResource; import org.apache.wicket.request.resource.caching.ResourceUrl; import org.apache.wicket.resource.ResourceUtil; import org.apache.wicket.resource.bundles.ResourceBundleReference; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.lang.Checks; import org.apache.wicket.util.string.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Generic {@link ResourceReference} encoder that encodes and decodes non-mounted * {@link ResourceReference}s. * <p> * Decodes and encodes the following URLs: * * <pre> * /wicket/resource/org.apache.wicket.ResourceScope/name * /wicket/resource/org.apache.wicket.ResourceScope/name?en * /wicket/resource/org.apache.wicket.ResourceScope/name?-style * /wicket/resource/org.apache.wicket.ResourceScope/resource/name.xyz?en_EN-style * </pre> * * @author Matej Knopp * @author igor.vaynberg * @author Peter Ertl */ public class BasicResourceReferenceMapper extends AbstractResourceReferenceMapper { private static final Logger log = LoggerFactory.getLogger(BasicResourceReferenceMapper.class); protected final IPageParametersEncoder pageParametersEncoder; /** resource caching strategy */ protected final Supplier<? extends IResourceCachingStrategy> cachingStrategy; /** * Construct. * * @param pageParametersEncoder * @param cachingStrategy */ public BasicResourceReferenceMapper(IPageParametersEncoder pageParametersEncoder, Supplier<? extends IResourceCachingStrategy> cachingStrategy) { this.pageParametersEncoder = Args.notNull(pageParametersEncoder, "pageParametersEncoder"); this.cachingStrategy = cachingStrategy; } @Override public IRequestHandler mapRequest(Request request) { Url url = request.getUrl(); if (canBeHandled(url)) { final int segmentsSize = url.getSegments().size(); // extract the PageParameters from URL if there are any PageParameters pageParameters = extractPageParameters(request, segmentsSize, pageParametersEncoder); String className = url.getSegments().get(2); StringBuilder name = new StringBuilder(segmentsSize * 2); for (int i = 3; i < segmentsSize; ++i) { String segment = url.getSegments().get(i); // ignore invalid segments if (segment.indexOf('/') > -1) { return null; } // remove caching information if (i + 1 == segmentsSize && Strings.isEmpty(segment) == false) { // The filename + parameters eventually contain caching // related information which needs to be removed ResourceUrl resourceUrl = new ResourceUrl(segment, pageParameters); getCachingStrategy().undecorateUrl(resourceUrl); segment = resourceUrl.getFileName(); Checks.notEmpty(segment, "Caching strategy returned empty name for '%s'", resourceUrl); } if (name.length() > 0) { name.append('/'); } name.append(segment); } ResourceReference.UrlAttributes attributes = ResourceUtil.decodeResourceReferenceAttributes(url); Class<?> scope = resolveClass(className); if (scope != null && scope.getPackage() != null) { ResourceReference res = getContext().getResourceReferenceRegistry() .getResourceReference(scope, name.toString(), attributes.getLocale(), attributes.getStyle(), attributes.getVariation(), true, true); if (res != null) { return new ResourceReferenceRequestHandler(res, pageParameters); } } } return null; } protected final IResourceCachingStrategy getCachingStrategy() { return cachingStrategy.get(); } protected Class<?> resolveClass(String name) { return WicketObjects.resolveClass(name); } protected String getClassName(Class<?> scope) { return scope.getName(); } @Override public Url mapHandler(IRequestHandler requestHandler) { if (requestHandler instanceof ResourceReferenceRequestHandler) { ResourceReferenceRequestHandler referenceRequestHandler = (ResourceReferenceRequestHandler)requestHandler; ResourceReference reference = referenceRequestHandler.getResourceReference(); Url url; while (reference instanceof ResourceBundleReference) { // unwrap the bundle to render the url for the actual reference reference = ((ResourceBundleReference)reference).getBundleReference(); } if (reference instanceof MetaInfStaticResourceReference) { url = ((MetaInfStaticResourceReference)reference).mapHandler(referenceRequestHandler); // if running on Servlet 3.0 engine url is not null if (url != null) { return url; } // otherwise it has to be served by the standard wicket way } if (reference.canBeRegistered()) { ResourceReferenceRegistry resourceReferenceRegistry = getContext().getResourceReferenceRegistry(); resourceReferenceRegistry.registerResourceReference(reference); } url = new Url(); List<String> segments = url.getSegments(); segments.add(getContext().getNamespace()); segments.add(getContext().getResourceIdentifier()); segments.add(getClassName(reference.getScope())); // setup resource parameters PageParameters parameters = referenceRequestHandler.getPageParameters(); if (parameters == null) { parameters = new PageParameters(); } else { parameters = new PageParameters(parameters); // need to remove indexed parameters otherwise the URL won't be able to decode parameters.clearIndexed(); } ResourceUtil.encodeResourceReferenceAttributes(url, reference); StringTokenizer tokens = new StringTokenizer(reference.getName(), "/"); while (tokens.hasMoreTokens()) { String token = tokens.nextToken(); // on the last component of the resource path if (tokens.hasMoreTokens() == false && Strings.isEmpty(token) == false) { final IResource resource = reference.getResource(); // is resource supposed to be cached? if (resource instanceof IStaticCacheableResource) { final IStaticCacheableResource cacheable = (IStaticCacheableResource)resource; // is caching enabled? if(cacheable.isCachingEnabled()) { // apply caching scheme to resource url final ResourceUrl resourceUrl = new ResourceUrl(token, parameters); getCachingStrategy().decorateUrl(resourceUrl, cacheable); token = resourceUrl.getFileName(); Checks.notEmpty(token, "Caching strategy returned empty name for '%s'", resource); } } } segments.add(token); } if (parameters.isEmpty() == false) { url = encodePageParameters(url, parameters, pageParametersEncoder); } return url; } return null; } @Override public int getCompatibilityScore(Request request) { Url url = request.getUrl(); int score = -1; if (canBeHandled(url)) { score = 1; } return score; } /** * Checks whether the passed Url can be handled by this mapper * * @param url * the Url to check * @return {@code true} - if the Url can be handled, {@code false} - otherwise */ protected boolean canBeHandled(final Url url) { List<String> segments = url.getSegments(); return (segments.size() >= 4 && urlStartsWith(url, getContext().getNamespace(), getContext().getResourceIdentifier()) && Strings.isEmpty(segments.get(3)) == false ); } }