001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.core.request.mapper; 018 019import java.util.List; 020 021import org.apache.wicket.Application; 022import org.apache.wicket.request.Request; 023import org.apache.wicket.request.Url; 024import org.apache.wicket.request.component.IRequestablePage; 025import org.apache.wicket.request.mapper.info.PageComponentInfo; 026import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; 027import org.apache.wicket.request.mapper.parameter.PageParameters; 028import org.apache.wicket.request.mapper.parameter.PageParametersEncoder; 029import org.apache.wicket.util.string.Strings; 030 031/** 032 * Decodes and encodes the following URLs: 033 * 034 * <pre> 035 * Page Class - Render (BookmarkablePageRequestHandler) 036 * /wicket/bookmarkable/org.apache.wicket.MyPage 037 * (will redirect to hybrid alternative if page is not stateless) 038 * 039 * Page Instance - Render Hybrid (RenderPageRequestHandler for pages that were created using bookmarkable URLs) 040 * /wicket/bookmarkable/org.apache.wicket.MyPage?2 041 * 042 * Page Instance - Bookmarkable Listener (BookmarkableListenerRequestHandler) 043 * /wicket/bookmarkable/org.apache.wicket.MyPage?2-click-foo-bar-baz 044 * /wicket/bookmarkable/org.apache.wicket.MyPage?2-click.1-foo-bar-baz (1 is behavior index) 045 * (these will redirect to hybrid if page is not stateless) 046 * </pre> 047 * 048 * @author Matej Knopp 049 */ 050public class BookmarkableMapper extends AbstractBookmarkableMapper 051{ 052 /** 053 * Construct. 054 */ 055 public BookmarkableMapper() 056 { 057 this(new PageParametersEncoder()); 058 } 059 060 /** 061 * Construct. 062 * 063 * @param pageParametersEncoder 064 */ 065 public BookmarkableMapper(IPageParametersEncoder pageParametersEncoder) 066 { 067 super("notUsed", pageParametersEncoder); 068 } 069 070 @Override 071 protected Url buildUrl(UrlInfo info) 072 { 073 Url url = new Url(); 074 url.getSegments().add(getContext().getNamespace()); 075 url.getSegments().add(getContext().getBookmarkableIdentifier()); 076 url.getSegments().add(info.getPageClass().getName()); 077 078 encodePageComponentInfo(url, info.getPageComponentInfo()); 079 080 return encodePageParameters(url, info.getPageParameters(), pageParametersEncoder); 081 } 082 083 @Override 084 protected UrlInfo parseRequest(Request request) 085 { 086 if (Application.exists()) 087 { 088 if (Application.get().getSecuritySettings().getEnforceMounts()) 089 { 090 return null; 091 } 092 } 093 094 if (matches(request)) 095 { 096 Url url = request.getUrl(); 097 098 // try to extract page and component information from URL 099 PageComponentInfo info = getPageComponentInfo(url); 100 101 List<String> segments = url.getSegments(); 102 103 // load the page class 104 String className; 105 if (segments.size() >= 3) 106 { 107 className = segments.get(2); 108 } 109 else 110 { 111 className = segments.get(1); 112 } 113 114 if (Strings.isEmpty(className)) 115 { 116 return null; 117 } 118 119 Class<? extends IRequestablePage> pageClass = getPageClass(className); 120 121 if (pageClass != null && IRequestablePage.class.isAssignableFrom(pageClass)) 122 { 123 // extract the PageParameters from URL if there are any 124 PageParameters pageParameters = extractPageParameters(request, 3, 125 pageParametersEncoder); 126 if (pageParameters != null) 127 { 128 pageParameters.setLocale(resolveLocale()); 129 } 130 131 return new UrlInfo(info, pageClass, pageParameters); 132 } 133 } 134 return null; 135 } 136 137 @Override 138 protected boolean pageMustHaveBeenCreatedBookmarkable() 139 { 140 return true; 141 } 142 143 @Override 144 public int getCompatibilityScore(Request request) 145 { 146 int score = 0; 147 if (matches(request)) 148 { 149 score = Integer.MAX_VALUE; 150 } 151 return score; 152 } 153 154 private boolean matches(final Request request) 155 { 156 boolean matches = false; 157 Url url = request.getUrl(); 158 Url baseUrl = request.getClientUrl(); 159 String namespace = getContext().getNamespace(); 160 String bookmarkableIdentifier = getContext().getBookmarkableIdentifier(); 161 String pageIdentifier = getContext().getPageIdentifier(); 162 163 List<String> segments = url.getSegments(); 164 int segmentsSize = segments.size(); 165 166 if (segmentsSize >= 3 && urlStartsWithAndHasPageClass(url, namespace, bookmarkableIdentifier)) 167 { 168 matches = true; 169 } 170 // baseUrl = 'wicket/bookmarkable/com.example.SomePage[?...]', requestUrl = 'bookmarkable/com.example.SomePage' 171 else if (baseUrl.getSegments().size() == 3 && urlStartsWith(baseUrl, namespace, bookmarkableIdentifier) 172 && segmentsSize >= 2 && urlStartsWithAndHasPageClass(url, bookmarkableIdentifier)) 173 { 174 matches = true; 175 } 176 // baseUrl = 'bookmarkable/com.example.SomePage', requestUrl = 'bookmarkable/com.example.SomePage' 177 else if (baseUrl.getSegments().size() == 2 && urlStartsWith(baseUrl, bookmarkableIdentifier) 178 && segmentsSize == 2 && urlStartsWithAndHasPageClass(url, bookmarkableIdentifier)) 179 { 180 matches = true; 181 } 182 // baseUrl = 'wicket/page[?...]', requestUrl = 'bookmarkable/com.example.SomePage' 183 else if (baseUrl.getSegments().size() == 2 && urlStartsWith(baseUrl, namespace, pageIdentifier) 184 && segmentsSize >= 2 && urlStartsWithAndHasPageClass(url, bookmarkableIdentifier)) 185 { 186 matches = true; 187 } 188 189 return matches; 190 } 191 192 /** 193 * Checks whether the url starts with the given segments and additionally 194 * checks whether the following segment is non-empty 195 * 196 * @param url 197 * The url to be checked 198 * @param segments 199 * The expected leading segments 200 * @return {@code true} if the url starts with the given segments and there is non-empty segment after them 201 */ 202 protected boolean urlStartsWithAndHasPageClass(Url url, String... segments) 203 { 204 boolean result = urlStartsWith(url, segments); 205 206 if (result) 207 { 208 List<String> urlSegments = url.getSegments(); 209 if (urlSegments.size() == segments.length) 210 { 211 result = false; 212 } 213 else 214 { 215 String pageClassSegment = urlSegments.get(segments.length); 216 if (Strings.isEmpty(pageClassSegment)) 217 { 218 result = false; 219 } 220 } 221 } 222 223 return result; 224 } 225}