/**
* Copyright 2014 55 Minutes (http://www.55minutes.com)
*
* Licensed 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 fiftyfive.wicket.resource;
import java.util.List;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.IRequestMapper;
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.AbstractMapper;
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.ResourceReference;
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.util.IProvider;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.time.Time;
/**
* Maps a single, static URL to a list of resources. When that URL is requested, respond by
* merging all the resources together by delegating to {@link MergedResourceRequestHandler}.
*
* @since 3.0
*/
public class MergedResourceMapper extends AbstractMapper implements IRequestMapper
{
protected final String[] mountSegments;
protected final List<ResourceReference> resources;
protected final IPageParametersEncoder parametersEncoder;
protected final IProvider<? extends IResourceCachingStrategy> cachingStrategy;
public MergedResourceMapper(String path,
List<ResourceReference> resources,
IPageParametersEncoder parametersEncoder,
IProvider<? extends IResourceCachingStrategy> cachingStrategy)
{
// TODO: validate arguments
this.resources = resources;
this.mountSegments = getMountSegments(path);
this.parametersEncoder = parametersEncoder;
this.cachingStrategy = cachingStrategy;
}
public int getCompatibilityScore(Request request)
{
return 0;
}
public IRequestHandler mapRequest(Request request)
{
PageParameters parameters = null;
List<String> requestSegments = request.getUrl().getSegments();
if(requestSegments.size() < this.mountSegments.length)
{
return null;
}
for(int i=0; i<this.mountSegments.length; i++)
{
String segment = requestSegments.get(i);
if(i+1 == this.mountSegments.length)
{
parameters = extractPageParameters(
request,
this.mountSegments.length,
this.parametersEncoder);
ResourceUrl resourceUrl = new ResourceUrl(segment, parameters);
this.cachingStrategy.get().undecorateUrl(resourceUrl);
segment = resourceUrl.getFileName();
}
if(!segment.equals(this.mountSegments[i]))
{
return null;
}
}
return new MergedResourceRequestHandler(
this.resources,
parameters,
getLastModifiedTime(getLastModifiedReference()));
}
public Url mapHandler(IRequestHandler requestHandler)
{
if(!(requestHandler instanceof ResourceReferenceRequestHandler)) return null;
boolean matched = false;
ResourceReferenceRequestHandler handler = (ResourceReferenceRequestHandler) requestHandler;
for(ResourceReference ref : this.resources)
{
if(ref.equals(handler.getResourceReference()))
{
matched = true;
break;
}
}
if(!matched) return null;
Url url = new Url();
PageParameters parameters = new PageParameters(handler.getPageParameters());
for(int i=0; i<this.mountSegments.length; i++)
{
String segment = mountSegments[i];
if(i+1 == this.mountSegments.length)
{
segment = applyCachingStrategy(segment, parameters);
}
url.getSegments().add(segment);
}
return encodePageParameters(url, parameters, this.parametersEncoder);
}
protected String applyCachingStrategy(String fileName, PageParameters parameters)
{
ResourceUrl resourceUrl = new ResourceUrl(fileName, parameters);
ResourceReference lastMod = getLastModifiedReference();
IResource res = lastMod.getResource();
if(res instanceof IStaticCacheableResource)
{
this.cachingStrategy.get().decorateUrl(resourceUrl, (IStaticCacheableResource) res);
}
return resourceUrl.getFileName();
}
protected ResourceReference getLastModifiedReference()
{
ResourceReference lastModifiedRef = null;
long lastMillis = -1;
for(ResourceReference ref : this.resources)
{
Time refModified = getLastModifiedTime(ref);
if(refModified != null && refModified.getMilliseconds() > lastMillis)
{
lastMillis = refModified.getMilliseconds();
lastModifiedRef = ref;
}
}
return lastModifiedRef != null ? lastModifiedRef : this.resources.get(0);
}
/**
* De-reference the resource and open its stream to determine the last modified time.
*/
protected Time getLastModifiedTime(ResourceReference ref)
{
Time modified = null;
IResource res = ref.getResource();
if(res instanceof IStaticCacheableResource)
{
IResourceStream stream = ((IStaticCacheableResource) res).getCacheableResourceStream();
modified = stream.lastModifiedTime();
}
return modified;
}
}