DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
Description
If you have application which require encoding of locale in pages URLs, for instance:
www.domain.com/uk/home
www.domain.com/nl/home
(For other use cases like www.domain.com/username see e.g. http://blog.jteam.nl/2010/02/24/wicket-root-mounts/.)
RequestDecorator
Simple decorator class for a Request object, used in LocaleUrlCodingStrategyDecorator to strip the locale from the original
url
import org.apache.wicket.Request;
import org.apache.wicket.Page;
import java.util.Locale;
import java.util.Map;
public class RequestDecorator extends Request {
/**
* Decorated request.
*/
private final Request request;
/**
* Constructor.
*
* @param request to decorate.
*/
public RequestDecorator(final Request request) {
if (request == null) {
throw new IllegalArgumentException("Decorated Request cannot be NULL!");
}
this.request = request;
}
/**
* {@inheritDoc}
*/
@Override
public Locale getLocale() {
return request.getLocale();
}
/**
* {@inheritDoc}
*/
@Override
public String getParameter(final String key) {
return request.getParameter(key);
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, String[]> getParameterMap() {
return request.getParameterMap();
}
/**
* {@inheritDoc}
*/
@Override
public String[] getParameters(final String key) {
return request.getParameters(key);
}
/**
* {@inheritDoc}
*/
@Override
public String getPath() {
return request.getPath();
}
/**
* {@inheritDoc}
*/
@Override
public String getQueryString() {
return request.getQueryString();
}
/**
* {@inheritDoc}
*/
@Override
public String getRelativePathPrefixToContextRoot() {
return request.getRelativePathPrefixToContextRoot();
}
/**
* {@inheritDoc}
*/
@Override
public String getRelativePathPrefixToWicketHandler() {
return request.getRelativePathPrefixToWicketHandler();
}
/**
* {@inheritDoc}
*/
@Override
public String getURL() {
return request.getURL();
}
@Override
public String decodeURL(String url) {
return request.decodeURL(url);
}
@Override
public Page getPage() {
return request.getPage();
}
@Override
public void setPage(Page page) {
super.setPage(page);
}
@Override
public String getRelativeURL() {
return request.getRelativeURL();
}
@Override
public boolean mergeVersion() {
return request.mergeVersion();
}
@Override
public String toString() {
return request.toString();
}
}
RequestCodingStrategyDecorator
To do this, you'll need to create your own implementation of IRequestCodingStrategy.
We need to have a decorator of IRequestCodingStrategy, below is the implementation:
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.Request;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.request.IRequestCodingStrategy;
import org.apache.wicket.request.RequestParameters;
import org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy;
/**
* Decorator of {@link IRequestCodingStrategy} interface.
*
* @author Alex Objelean
*/
public class RequestCodingStrategyDecorator
implements IRequestCodingStrategy {
/**
* Decorated strategy.
*/
private final IRequestCodingStrategy strategy;
/**
* Constructor.
* @param strategy to decorate.
*/
public RequestCodingStrategyDecorator(final IRequestCodingStrategy strategy) {
if (strategy == null) {
throw new IllegalArgumentException("Strategy cannot be null!");
}
this.strategy = strategy;
}
/**
* {@inheritDoc}
*/
public void addIgnoreMountPath(final String path) {
this.strategy.addIgnoreMountPath(path);
}
/**
* {@inheritDoc}
*/
public RequestParameters decode(final Request request) {
return this.strategy.decode(request);
}
/**
* {@inheritDoc}
*/
public CharSequence encode(final RequestCycle requestCycle, final IRequestTarget requestTarget) {
return this.strategy.encode(requestCycle, requestTarget);
}
/**
* @return decorated {@link IRequestCodingStrategy}.
*/
public final IRequestCodingStrategy getDecoratedStrategy() {
return this.strategy;
}
/**
* {@inheritDoc}
*/
public void mount(final IRequestTargetUrlCodingStrategy urlCodingStrategy) {
this.strategy.mount(urlCodingStrategy);
}
/**
* {@inheritDoc}
*/
public CharSequence pathForTarget(final IRequestTarget requestTarget) {
return this.strategy.pathForTarget(requestTarget);
}
/**
* {@inheritDoc}
*/
public String rewriteStaticRelativeUrl(final String string) {
return this.strategy.rewriteStaticRelativeUrl(string);
}
/**
* {@inheritDoc}
*/
public IRequestTarget targetForRequest(final RequestParameters requestParameters) {
return this.strategy.targetForRequest(requestParameters);
}
/**
* {@inheritDoc}
*/
public void unmount(final String path) {
this.strategy.unmount(path);
}
/**
* {@inheritDoc}
*/
public IRequestTargetUrlCodingStrategy urlCodingStrategyForPath(final String path) {
return this.strategy.urlCodingStrategyForPath(path);
}
}
LocaleUrlCodingStrategyDecorator
Next step is to create the coding strategy responsible for adding locale support to your urls.
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.apache.commons.lang.LocaleUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.RedirectToUrlException;
import org.apache.wicket.Request;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Session;
import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
import org.apache.wicket.request.IRequestCodingStrategy;
import org.apache.wicket.request.RequestParameters;
import org.apache.wicket.request.target.coding.AbstractRequestTargetUrlCodingStrategy;
import org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy;
import org.apache.wicket.request.target.component.IBookmarkablePageRequestTarget;
import org.apache.wicket.request.target.component.IPageRequestTarget;
/**
* Coding strategy that prepends locale to url, useful for caching localizable applications.
* <p>
* Changes the locale, depending on found locale in request path. Delegates the encoding/decoding call to original
* decorated coding strategy, but with processed request path (strip locale).
*
* @author Alex Objelean
*
*/
public class LocaleUrlCodingStrategyDecorator
extends RequestCodingStrategyDecorator {
/**
* Simple implementation of {@link IRequestTargetUrlCodingStrategy}, used only to make wicket filter treat original
* request as wicket related.
*/
private static class PassThroughUrlCodingStrategy
extends AbstractRequestTargetUrlCodingStrategy {
/**
* Construct.
*
* @param path
*/
public PassThroughUrlCodingStrategy(final String path) {
super(path);
}
/**
* {@inheritDoc}
*/
public IRequestTarget decode(final RequestParameters requestParameters) {
return null;
}
/**
* {@inheritDoc}
*/
public CharSequence encode(final IRequestTarget requestTarget) {
return null;
}
/**
* {@inheritDoc}
*/
public boolean matches(final IRequestTarget requestTarget) {
return false;
}
}
/** log. */
private static org.apache.log4j.Logger log = Logger.getLogger(LocaleUrlCodingStrategyDecorator.class);
/**
* Set of supported locales.
*/
private final Set<String> locales = new HashSet<String>();
/**
* Construct.
*
* @param defaultStrategy The default strategy most requests are forwarded to
*/
public LocaleUrlCodingStrategyDecorator(final IRequestCodingStrategy defaultStrategy) {
super(defaultStrategy);
locales.add("en");
locales.add("ro");
locales.add("pt");
locales.add("es");
locales.add("ru");
locales.add("fr");
}
/**
* Decode the querystring of the URL. one.
*
* @see org.apache.wicket.request.IRequestCodingStrategy#decode(org.apache.wicket.Request)
*/
@Override
public RequestParameters decode(final Request request) {
// log.debug("<decode>");
// log.debug("\trequestUrl: " + request.getURL());
try {
final String requestPathLocale = getRequestPathLocale(request);
final Locale locale = LocaleUtils.toLocale(requestPathLocale);
if (requestPathLocale != null) {
if (!Session.get().getLocale().equals(locale)) {
log.debug("Changing locale to: " + locale);
Session.get().setLocale(locale);
}
final String url = request.decodeURL(request.getURL());
// remove locale from request
final String urlWithoutLocale = url.replace(getLocaleString(request.getLocale()), "");
// log.debug("DecodedUrl: " + urlWithoutLocale);
// use decorator for decoding
return getDecoratedStrategy().decode(new RequestDecorator(request) {
@Override
public String getURL() {
return urlWithoutLocale;
}
});
} else if (!request.getPath().startsWith(WebRequestCodingStrategy.RESOURCES_PATH_PREFIX)){
//redirect to locale aware url for all request which are not resource related
//ISSUE: Redirect any url's without i18n to the default /en/ versions of those pages
throw new RedirectToUrlException("en/" + request.getPath() + "?" + request.getQueryString());
}
return getDecoratedStrategy().decode(request);
} finally {
// log.debug("</decode>");
}
}
/**
* Encode the querystring of the URL
*/
@Override
public CharSequence encode(final RequestCycle requestCycle, final IRequestTarget requestTarget) {
// log.debug("<encode>");
String url = getDecoratedStrategy().encode(requestCycle, requestTarget).toString();
try {
// log.debug("\turl: " + url);
//rewrite only requests for pages & links (ignore others, like resources)
if (requestTarget instanceof IBookmarkablePageRequestTarget || requestTarget instanceof IPageRequestTarget) {
final String localeString = getLocaleString(Session.get().getLocale());
if (url.startsWith("../")) {
//TODO move this logic to utility method?
//last index is incremented by 3, because ../ has 3 characters
final int lastIndex = url.lastIndexOf("../") + 3;
final String remainingUrl = url.substring(lastIndex);
if (StringUtils.isEmpty(remainingUrl)) {
return url;
}
url = url.substring(0, lastIndex) + localeString + remainingUrl;
return url;
}
// if starts with . -> skip
if (url.startsWith(".")) {
return url;
}
url = localeString + url;
}
return url;
} finally {
// log.debug("\tEncoding: " + url);
// log.debug("</encode>");
}
}
/**
* @param locale
* @return
*/
private String getLocaleString(final Locale locale) {
return locale.getLanguage() + "/";
}
/**
* Returns encoded locale in the request path. If none is found, null is returned.
*
* @param request
* @return
*/
private String getRequestPathLocale(final Request request) {
final String path = request.getPath();
for (final String locale : locales) {
if (path.startsWith(locale + "/")) {
return locale;
}
}
return null;
}
/**
* Remove locale from path.
*
* @param path to strip.
* @return path without locale.
*/
private String stripLocaleFropPath(final String path) {
for (final String locale : locales) {
final String toStrip = locale + "/";
if (path.startsWith(toStrip)) {
return path.replaceFirst(toStrip, "");
}
}
return path;
}
/**
* {@inheritDoc}
*/
@Override
public IRequestTarget targetForRequest(final RequestParameters requestParameters) {
//delegate call to decorated codingStrategy, but with locale removed
final String newPath = stripLocaleFropPath(requestParameters.getPath());
requestParameters.setPath(newPath);
return super.targetForRequest(requestParameters);
}
/**
* {@inheritDoc}
*/
@Override
public IRequestTargetUrlCodingStrategy urlCodingStrategyForPath(final String path) {
final String newPath = stripLocaleFropPath(path);
if (StringUtils.isEmpty(newPath)) {
// treat this kind of situation by returing some not null codingStrategy,
// to let this kind of request treated by wicket
return new PassThroughUrlCodingStrategy(newPath);
}
return super.urlCodingStrategyForPath(newPath);
}
}
Usage
In MyApplication.java, override newRequestCycleProcessor method & add the newly created LocaleUrlCodingStrategyDecorator.
/**
* {@inheritDoc}
*/
@Override
protected IRequestCycleProcessor newRequestCycleProcessor() {
return new WebRequestCycleProcessor() {
@Override
protected IRequestCodingStrategy newRequestCodingStrategy() {
return new LocaleUrlCodingStrategyDecorator(super.newRequestCodingStrategy());
}
}
}
Conclusion
TODO.