org.apache.wicket.request.mapper.MountedMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.ops4j.pax.wicket.service Show documentation
Show all versions of org.ops4j.pax.wicket.service Show documentation
Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and
unloading of Wicket components and pageSources.
/*
* 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.request.mapper;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.Application;
import org.apache.wicket.RequestListenerInterface;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.handler.ListenerInterfaceRequestHandler;
import org.apache.wicket.request.mapper.info.ComponentInfo;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.info.PageInfo;
import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
import org.apache.wicket.util.ClassProvider;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.string.Strings;
/**
* Encoder for mounted URL. The mount path can contain parameter placeholders, i.e.
* /mount/${foo}/path
. In that case the appropriate segment from the URL will be
* accessible as named parameter "foo" in the {@link PageParameters}. Similarly when the URL is
* constructed, the second segment will contain the value of the "foo" named page parameter.
* Optional parameters are denoted by using a # instead of $: /mount/#{foo}/path/${bar}
* has an optional {@code foo} parameter, a fixed {@code /path/} part and a required {@code bar}
* parameter. When in doubt, parameters are matched from left to right, where required parameters
* are matched before optional parameters, and optional parameters eager (from left to right).
*
* Decodes and encodes the following URLs:
*
*
* Page Class - Render (BookmarkablePageRequestHandler for mounted pages)
* /mount/point
* (these will redirect to hybrid alternative if page is not stateless)
*
* IPage Instance - Render Hybrid (RenderPageRequestHandler for mounted pages)
* /mount/point?2
*
* IPage Instance - Bookmarkable Listener (BookmarkableListenerInterfaceRequestHandler for mounted pages)
* /mount/point?2-click-foo-bar-baz
* /mount/point?2-5.click.1-foo-bar-baz (1 is behavior index, 5 is render count)
* (these will redirect to hybrid if page is not stateless)
*
*
* @author Matej Knopp
*/
public class MountedMapper extends AbstractBookmarkableMapper
{
private final IPageParametersEncoder pageParametersEncoder;
private static class MountPathSegment
{
private int segmentIndex;
private String fixedPart;
private int minParameters;
private int optionalParameters;
public MountPathSegment(int segmentIndex)
{
this.segmentIndex = segmentIndex;
}
public void setFixedPart(String fixedPart)
{
this.fixedPart = fixedPart;
}
public void addRequiredParameter()
{
minParameters++;
}
public void addOptionalParameter()
{
optionalParameters++;
}
public int getSegmentIndex()
{
return segmentIndex;
}
public String getFixedPart()
{
return fixedPart;
}
public int getMinParameters()
{
return minParameters;
}
public int getOptionalParameters()
{
return optionalParameters;
}
public int getMaxParameters()
{
return getOptionalParameters() + getMinParameters();
}
public int getFixedPartSize()
{
return getFixedPart() == null ? 0 : 1;
}
@Override
public String toString()
{
return "(" + getSegmentIndex() + ") " + getMinParameters() + "-" + getMaxParameters() +
" " + (getFixedPart() == null ? "(end)" : getFixedPart());
}
}
private final List pathSegments;
private final String[] mountSegments;
/** bookmarkable page class. */
private final ClassProvider extends IRequestablePage> pageClassProvider;
/**
* Construct.
*
* @param mountPath
* @param pageClass
*/
public MountedMapper(String mountPath, Class extends IRequestablePage> pageClass)
{
this(mountPath, pageClass, new PageParametersEncoder());
}
/**
* Construct.
*
* @param mountPath
* @param pageClassProvider
*/
public MountedMapper(String mountPath,
ClassProvider extends IRequestablePage> pageClassProvider)
{
this(mountPath, pageClassProvider, new PageParametersEncoder());
}
/**
* Construct.
*
* @param mountPath
* @param pageClass
* @param pageParametersEncoder
*/
public MountedMapper(String mountPath, Class extends IRequestablePage> pageClass,
IPageParametersEncoder pageParametersEncoder)
{
this(mountPath, ClassProvider.of(pageClass), pageParametersEncoder);
}
/**
* Construct.
*
* @param mountPath
* @param pageClassProvider
* @param pageParametersEncoder
*/
public MountedMapper(String mountPath,
ClassProvider extends IRequestablePage> pageClassProvider,
IPageParametersEncoder pageParametersEncoder)
{
Args.notEmpty(mountPath, "mountPath");
Args.notNull(pageClassProvider, "pageClassProvider");
Args.notNull(pageParametersEncoder, "pageParametersEncoder");
this.pageParametersEncoder = pageParametersEncoder;
this.pageClassProvider = pageClassProvider;
mountSegments = getMountSegments(mountPath);
pathSegments = getPathSegments(mountSegments);
}
private List getPathSegments(String[] segments)
{
List ret = new ArrayList();
int segmentIndex = 0;
MountPathSegment curPathSegment = new MountPathSegment(segmentIndex);
ret.add(curPathSegment);
for (String curSegment : segments)
{
if (isFixedSegment(curSegment))
{
curPathSegment.setFixedPart(curSegment);
curPathSegment = new MountPathSegment(segmentIndex + 1);
ret.add(curPathSegment);
}
else if (getPlaceholder(curSegment) != null)
{
curPathSegment.addRequiredParameter();
}
else
{
curPathSegment.addOptionalParameter();
}
segmentIndex++;
}
return ret;
}
private boolean isFixedSegment(String segment)
{
return getOptionalPlaceholder(segment) == null && getPlaceholder(segment) == null;
}
/**
* @see org.apache.wicket.request.mapper.AbstractBookmarkableMapper#parseRequest(org.apache.wicket.request.Request)
*/
@Override
protected UrlInfo parseRequest(Request request)
{
Url url = request.getUrl();
// when redirect to buffer/render is active and redirectFromHomePage returns true
// check mounted class against the home page class. if it matches let wicket redirect
// to the mounted URL
if (redirectFromHomePage() && checkHomePage(url))
{
return new UrlInfo(null, getContext().getHomePageClass(), newPageParameters());
}
// check if the URL starts with the proper segments
else if (urlStartsWith(url, mountSegments))
{
// try to extract page and component information from URL
PageComponentInfo info = getPageComponentInfo(url);
Class extends IRequestablePage> pageClass = getPageClass();
PageParameters pageParameters = extractPageParameters(request, url);
return new UrlInfo(info, pageClass, pageParameters);
}
else
{
return null;
}
}
/*
* extract the PageParameters from URL if there are any
*/
private PageParameters extractPageParameters(Request request, Url url)
{
int[] matchedParameters = getMatchedSegmentSizes(url);
int total = 0;
for (int curMatchSize : matchedParameters)
total += curMatchSize;
PageParameters pageParameters = extractPageParameters(request, total, pageParametersEncoder);
int skippedParameters = 0;
for (int pathSegmentIndex = 0; pathSegmentIndex < pathSegments.size(); pathSegmentIndex++)
{
MountPathSegment curPathSegment = pathSegments.get(pathSegmentIndex);
int matchSize = matchedParameters[pathSegmentIndex] - curPathSegment.getFixedPartSize();
int optionalParameterMatch = matchSize - curPathSegment.getMinParameters();
for (int matchSegment = 0; matchSegment < matchSize; matchSegment++)
{
if (pageParameters == null)
{
pageParameters = new PageParameters();
}
int curSegmentIndex = matchSegment + curPathSegment.getSegmentIndex();
String curSegment = mountSegments[curSegmentIndex];
String placeholder = getPlaceholder(curSegment);
String optionalPlaceholder = getOptionalPlaceholder(curSegment);
// extract the parameter from URL
if (placeholder != null)
{
pageParameters.add(placeholder,
url.getSegments().get(curSegmentIndex - skippedParameters));
}
else if (optionalPlaceholder != null && optionalParameterMatch > 0)
{
pageParameters.add(optionalPlaceholder,
url.getSegments().get(curSegmentIndex - skippedParameters));
optionalParameterMatch--;
}
}
skippedParameters += curPathSegment.getMaxParameters() - matchSize;
}
return pageParameters;
}
@Override
protected boolean urlStartsWith(Url url, String... segments)
{
if (url == null)
{
return false;
}
else
{
return getMatchedSegmentSizes(url) != null;
}
}
private int[] getMatchedSegmentSizes(Url url)
{
int[] ret = new int[pathSegments.size()];
int segmentIndex = 0;
int pathSegmentIndex = 0;
for (MountPathSegment curPathSegment : pathSegments.subList(0, pathSegments.size() - 1))
{
boolean foundFixedPart = false;
segmentIndex += curPathSegment.getMinParameters();
int max = Math.min(curPathSegment.getOptionalParameters() + 1,
url.getSegments().size() - segmentIndex);
for (int count = max - 1; count >= 0; count--)
{
if (url.getSegments()
.get(segmentIndex + count)
.equals(curPathSegment.getFixedPart()))
{
foundFixedPart = true;
segmentIndex += count + 1;
ret[pathSegmentIndex] = count + curPathSegment.getMinParameters() + 1;
break;
}
}
if (!foundFixedPart)
return null;
pathSegmentIndex++;
}
MountPathSegment lastSegment = pathSegments.get(pathSegments.size() - 1);
segmentIndex += lastSegment.getMinParameters();
if (segmentIndex > url.getSegments().size())
return null;
ret[pathSegmentIndex] = Math.min(lastSegment.getMaxParameters(), url.getSegments().size() -
segmentIndex + lastSegment.getMinParameters());
return ret;
}
protected PageParameters newPageParameters()
{
return new PageParameters();
}
@Override
public Url mapHandler(IRequestHandler requestHandler)
{
Url url = super.mapHandler(requestHandler);
if (url == null && requestHandler instanceof ListenerInterfaceRequestHandler &&
getRecreateMountedPagesAfterExpiry())
{
ListenerInterfaceRequestHandler handler = (ListenerInterfaceRequestHandler)requestHandler;
IRequestablePage page = handler.getPage();
if (checkPageInstance(page))
{
String componentPath = handler.getComponentPath();
RequestListenerInterface listenerInterface = handler.getListenerInterface();
Integer renderCount = null;
if (listenerInterface.isIncludeRenderCount())
{
renderCount = page.getRenderCount();
}
PageInfo pageInfo = new PageInfo(page.getPageId());
ComponentInfo componentInfo = new ComponentInfo(renderCount,
requestListenerInterfaceToString(listenerInterface), componentPath,
handler.getBehaviorIndex());
PageComponentInfo pageComponentInfo = new PageComponentInfo(pageInfo, componentInfo);
PageParameters parameters = new PageParameters(page.getPageParameters());
UrlInfo urlInfo = new UrlInfo(pageComponentInfo, page.getClass(),
parameters.mergeWith(handler.getPageParameters()));
url = buildUrl(urlInfo);
}
}
return url;
}
boolean getRecreateMountedPagesAfterExpiry()
{
return Application.get().getPageSettings().getRecreateMountedPagesAfterExpiry();
}
/**
* @see org.apache.wicket.request.mapper.AbstractBookmarkableMapper#buildUrl(org.apache.wicket.request.mapper.AbstractBookmarkableMapper.UrlInfo)
*/
@Override
protected Url buildUrl(UrlInfo info)
{
Url url = new Url();
for (String s : mountSegments)
{
url.getSegments().add(s);
}
encodePageComponentInfo(url, info.getPageComponentInfo());
PageParameters copy = new PageParameters(info.getPageParameters());
int dropped = 0;
for (int i = 0; i < mountSegments.length; ++i)
{
String placeholder = getPlaceholder(mountSegments[i]);
String optionalPlaceholder = getOptionalPlaceholder(mountSegments[i]);
if (placeholder != null)
{
url.getSegments().set(i - dropped, copy.get(placeholder).toString(""));
copy.remove(placeholder);
}
else if (optionalPlaceholder != null)
{
if (copy.getNamedKeys().contains(optionalPlaceholder))
{
url.getSegments().set(i - dropped, copy.get(optionalPlaceholder).toString(""));
copy.remove(optionalPlaceholder);
}
else
{
url.getSegments().remove(i - dropped);
dropped++;
}
}
}
return encodePageParameters(url, copy, pageParametersEncoder);
}
/**
* Check if the URL is for home page and the home page class match mounted class. If so,
* redirect to mounted URL.
*
* @param url
* @return request handler or null
*/
private boolean checkHomePage(Url url)
{
if (url.getSegments().isEmpty() && url.getQueryParameters().isEmpty())
{
// this is home page
if (getPageClass().equals(getContext().getHomePageClass()) && redirectFromHomePage())
{
return true;
}
}
return false;
}
/**
* If this method returns true
and application home page class is same as the class
* mounted with this encoder, request to home page will result in a redirect to the mounted
* path.
*
* @return whether this encode should respond to home page request when home page class is same
* as mounted class.
*/
protected boolean redirectFromHomePage()
{
return true;
}
/**
* @see org.apache.wicket.request.mapper.AbstractBookmarkableMapper#pageMustHaveBeenCreatedBookmarkable()
*/
@Override
protected boolean pageMustHaveBeenCreatedBookmarkable()
{
return false;
}
/**
* @see org.apache.wicket.request.mapper.AbstractBookmarkableMapper#getCompatibilityScore(org.apache.wicket.request.Request)
*/
@Override
public int getCompatibilityScore(Request request)
{
if (urlStartsWith(request.getUrl(), mountSegments))
{
return mountSegments.length;
}
else
{
return 0;
}
}
/**
* @see org.apache.wicket.request.mapper.AbstractBookmarkableMapper#checkPageClass(java.lang.Class)
*/
@Override
protected boolean checkPageClass(Class extends IRequestablePage> pageClass)
{
return pageClass.equals(this.getPageClass());
}
private Class extends IRequestablePage> getPageClass()
{
return pageClassProvider.get();
}
@Override
public String toString()
{
return "MountedMapper [mountSegments=" + Strings.join("/", mountSegments) + "]";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy