001: /*
002: * $Id: WebRequestCodingStrategy.java,v 1.24 2006/02/15 02:00:30 jonathanlocke
003: * Exp $ $Revision: 509689 $ $Date: 2007-02-20 18:57:33 +0100 (Tue, 20 Feb 2007) $
004: *
005: * ==============================================================================
006: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
007: * use this file except in compliance with the License. You may obtain a copy of
008: * the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
014: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
015: * License for the specific language governing permissions and limitations under
016: * the License.
017: */
018: package wicket.protocol.http.request;
019:
020: import java.io.UnsupportedEncodingException;
021: import java.net.URLEncoder;
022: import java.util.Comparator;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.SortedMap;
026: import java.util.TreeMap;
027: import java.util.Map.Entry;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031:
032: import wicket.Application;
033: import wicket.Component;
034: import wicket.IRedirectListener;
035: import wicket.IRequestTarget;
036: import wicket.Page;
037: import wicket.PageMap;
038: import wicket.PageParameters;
039: import wicket.Request;
040: import wicket.RequestCycle;
041: import wicket.RequestListenerInterface;
042: import wicket.Session;
043: import wicket.WicketRuntimeException;
044: import wicket.protocol.http.WebRequest;
045: import wicket.protocol.http.WebRequestCycle;
046: import wicket.request.IRequestCodingStrategy;
047: import wicket.request.IRequestTargetMountsInfo;
048: import wicket.request.RequestParameters;
049: import wicket.request.target.coding.IRequestTargetUrlCodingStrategy;
050: import wicket.request.target.component.IBookmarkablePageRequestTarget;
051: import wicket.request.target.component.IPageRequestTarget;
052: import wicket.request.target.component.listener.IListenerInterfaceRequestTarget;
053: import wicket.request.target.resource.ISharedResourceRequestTarget;
054: import wicket.util.string.AppendingStringBuffer;
055: import wicket.util.string.Strings;
056:
057: /**
058: * Request parameters factory implementation that uses http request parameters
059: * and path info to construct the request parameters object.
060: *
061: * @author Eelco Hillenius
062: * @author Jonathan Locke
063: */
064: public class WebRequestCodingStrategy implements
065: IRequestCodingStrategy, IRequestTargetMountsInfo {
066: /** Name of interface target query parameter */
067: public static final String NAME_SPACE = "wicket:";
068:
069: /** Name of interface target query parameter */
070: public static final String INTERFACE_PARAMETER_NAME = NAME_SPACE
071: + "interface";
072:
073: /** AJAX query parameter name */
074: public static final String BEHAVIOR_ID_PARAMETER_NAME = NAME_SPACE
075: + "behaviorId";
076:
077: /** Parameter name used all over the place */
078: public static final String BOOKMARKABLE_PAGE_PARAMETER_NAME = NAME_SPACE
079: + "bookmarkablePage";
080:
081: /** Pagemap parameter constant */
082: public static final String PAGEMAP = NAME_SPACE + "pageMapName";
083:
084: /**
085: * Parameter name that tells decode to ignore this request if the
086: * page+version encoded in the url is not on top of the stack. The value of
087: * this parameter is not important, it simply has to be present to enable
088: * the behavior
089: */
090: public static final String IGNORE_IF_NOT_ACTIVE_PARAMETER_NAME = NAME_SPACE
091: + "ignoreIfNotActive";
092:
093: /** Comparator implementation that sorts longest strings first */
094: private static final Comparator lengthComparator = new Comparator() {
095: public int compare(Object o1, Object o2) {
096: // longer first
097: if (o1 == o2) {
098: return 0;
099: } else if (o1 == null) {
100: return 1;
101: } else if (o2 == null) {
102: return -1;
103: } else {
104: String lhs = (String) o1;
105: String rhs = (String) o2;
106: return 0 - lhs.compareTo(rhs);
107: }
108: }
109: };
110:
111: /** log. */
112: private static final Log log = LogFactory
113: .getLog(WebRequestCodingStrategy.class);
114:
115: /**
116: * map of path mounts for mount encoders on paths.
117: * <p>
118: * mountsOnPath is sorted by longest paths first to improve resolution of
119: * possible path conflicts. <br />
120: * For example: <br/> we mount Page1 on /page and Page2 on /page/test <br />
121: * Page1 uses a parameters encoder that only encodes parameter values <br />
122: * now suppose we want to access Page1 with a single paramter param="test".
123: * we have a url collision since both pages can be access with /page/test
124: * <br />
125: * the sorting by longest path first guarantees that the iterator will
126: * return the mount /page/test before it returns mount /page therefore
127: * giving deterministic behavior to path resolution by always trying to
128: * match the longest possible path first.
129: * </p>
130: */
131: private final SortedMap/* <String,IRequestTargetUrlCodingStrategy> */mountsOnPath = new TreeMap(
132: lengthComparator);
133:
134: /** cached url prefix. */
135: private CharSequence urlPrefix;
136:
137: /**
138: * Construct.
139: */
140: public WebRequestCodingStrategy() {
141: }
142:
143: /**
144: * @see wicket.request.IRequestCodingStrategy#decode(wicket.Request)
145: */
146: public final RequestParameters decode(final Request request) {
147: final RequestParameters parameters = new RequestParameters();
148: final String pathInfo = getRequestPath(request);
149: parameters.setPath(pathInfo);
150: parameters.setPageMapName(request.getParameter(PAGEMAP));
151: addInterfaceParameters(request, parameters);
152: addBookmarkablePageParameters(request, parameters);
153: addResourceParameters(request, parameters);
154: parameters.setBehaviorId(request
155: .getParameter(BEHAVIOR_ID_PARAMETER_NAME));
156: if (request.getParameter(IGNORE_IF_NOT_ACTIVE_PARAMETER_NAME) != null) {
157: parameters.setOnlyProcessIfPathActive(true);
158: }
159:
160: Map map = request.getParameterMap();
161: Iterator iterator = map.keySet().iterator();
162: while (iterator.hasNext()) {
163: String key = (String) iterator.next();
164: if (key.startsWith(NAME_SPACE)) {
165: iterator.remove();
166: }
167: }
168: parameters.setParameters(map);
169: return parameters;
170: }
171:
172: /**
173: * Encode the given request target. If a mount is found, that mounted url
174: * will be returned. Otherwise, one of the delegation methods will be
175: * called. In case you are using custom targets that are not part of the
176: * default target hierarchy, you need to override
177: * {@link #doEncode(RequestCycle, IRequestTarget)}, which will be called
178: * after the defaults have been tried. When that doesn't provide a url
179: * either, and exception will be thrown saying that encoding could not be
180: * done.
181: *
182: * @see wicket.request.IRequestCodingStrategy#encode(wicket.RequestCycle,
183: * wicket.IRequestTarget)
184: */
185: public final CharSequence encode(final RequestCycle requestCycle,
186: final IRequestTarget requestTarget) {
187: // first check whether the target was mounted
188: CharSequence path = pathForTarget(requestTarget);
189: if (path != null) {
190: CharSequence prefix = urlPrefix(requestCycle);
191: // special check if the prefix ends on '/' because a mount always
192: // starts with '/'
193: if (prefix.charAt(prefix.length() - 1) == '/')
194: prefix = prefix.subSequence(0, prefix.length() - 1);
195: final AppendingStringBuffer buffer = new AppendingStringBuffer(
196: prefix.length() + path.length());
197: buffer.append(prefix);
198: buffer.append(path);
199: return requestCycle.getOriginalResponse().encodeURL(buffer);
200: }
201:
202: // no mount found; go on with default processing
203: if (requestTarget instanceof IBookmarkablePageRequestTarget) {
204: return encode(requestCycle,
205: (IBookmarkablePageRequestTarget) requestTarget);
206: } else if (requestTarget instanceof ISharedResourceRequestTarget) {
207: return encode(requestCycle,
208: (ISharedResourceRequestTarget) requestTarget);
209: } else if (requestTarget instanceof IListenerInterfaceRequestTarget) {
210: return encode(requestCycle,
211: (IListenerInterfaceRequestTarget) requestTarget);
212: } else if (requestTarget instanceof IPageRequestTarget) {
213: return encode(requestCycle,
214: (IPageRequestTarget) requestTarget);
215: }
216:
217: // fallthough for non-default request targets
218: String url = doEncode(requestCycle, requestTarget);
219: if (url != null) {
220: return url;
221: }
222: // Just return null intead of throwing an exception. So that it can be
223: // handled better
224: return null;
225: }
226:
227: /**
228: * @see wicket.request.IRequestTargetMountsInfo#listMounts()
229: */
230: public IRequestTargetUrlCodingStrategy[] listMounts() {
231: return (IRequestTargetUrlCodingStrategy[]) mountsOnPath
232: .values()
233: .toArray(
234: new IRequestTargetUrlCodingStrategy[mountsOnPath
235: .size()]);
236: }
237:
238: /**
239: * @see wicket.request.IRequestTargetMounter#urlCodingStrategyForPath(java.lang.String)
240: */
241: public final IRequestTargetUrlCodingStrategy urlCodingStrategyForPath(
242: final String path) {
243: if (path == null) {
244: return (IRequestTargetUrlCodingStrategy) mountsOnPath
245: .get(null);
246: } else if (!path.equals("/")) // ignore root paths.. is this the right
247: // path?
248: {
249: for (final Iterator it = mountsOnPath.entrySet().iterator(); it
250: .hasNext();) {
251: final Map.Entry entry = (Entry) it.next();
252: final String key = (String) entry.getKey();
253: if (path.startsWith(key)) {
254: return (IRequestTargetUrlCodingStrategy) entry
255: .getValue();
256: }
257: }
258: }
259: return null;
260: }
261:
262: /**
263: * @see wicket.request.IRequestTargetMounter#mount(java.lang.String,
264: * wicket.request.target.coding.IRequestTargetUrlCodingStrategy)
265: */
266: public final void mount(String path,
267: IRequestTargetUrlCodingStrategy encoder) {
268: if (path == null) {
269: throw new IllegalArgumentException(
270: "Argument path must be not-null");
271: }
272: if (path.equals("/")) {
273: throw new IllegalArgumentException(
274: "The mount path '/' is reserved for the application home page");
275: }
276: if (encoder == null) {
277: throw new IllegalArgumentException(
278: "Argument encoder must be not-null");
279: }
280:
281: // sanity check
282: if (!path.startsWith("/")) {
283: path = "/" + path;
284: }
285:
286: if (mountsOnPath.containsKey(path)) {
287: throw new WicketRuntimeException(path
288: + " is already mounted for "
289: + mountsOnPath.get(path));
290: }
291: mountsOnPath.put(path, encoder);
292: }
293:
294: /**
295: * @see wicket.request.IRequestCodingStrategy#pathForTarget(wicket.IRequestTarget)
296: */
297: public final CharSequence pathForTarget(IRequestTarget requestTarget) {
298: // first check whether the target was mounted
299: IRequestTargetUrlCodingStrategy encoder = getMountEncoder(requestTarget);
300: if (encoder != null) {
301: return encoder.encode(requestTarget);
302: }
303: return null;
304: }
305:
306: /**
307: * @see wicket.request.IRequestCodingStrategy#targetForRequest(wicket.request.RequestParameters)
308: */
309: public final IRequestTarget targetForRequest(
310: RequestParameters requestParameters) {
311: IRequestTargetUrlCodingStrategy encoder = urlCodingStrategyForPath(requestParameters
312: .getPath());
313: return (encoder != null) ? encoder.decode(requestParameters)
314: : null;
315: }
316:
317: /**
318: * @see wicket.request.IRequestCodingStrategy#unmount(java.lang.String)
319: */
320: public final void unmount(String path) {
321: if (path == null) {
322: throw new IllegalArgumentException(
323: "Argument path must be not-null");
324: }
325:
326: // sanity check
327: if (!path.startsWith("/")) {
328: path = "/" + path;
329: }
330:
331: mountsOnPath.remove(path);
332: }
333:
334: /**
335: * Adds bookmarkable page related parameters (page alias and optionally page
336: * parameters). Any bookmarkable page alias mount will override this method;
337: * hence if a mount is found, this method will not be called.
338: *
339: * If you override this method to behave different then also
340: * {@link #encode(RequestCycle, IBookmarkablePageRequestTarget)} should be
341: * overridden to by in sync with that behaviour.
342: *
343: * @param request
344: * the incoming request
345: * @param parameters
346: * the parameters object to set the found values on
347: */
348: protected void addBookmarkablePageParameters(final Request request,
349: final RequestParameters parameters) {
350: final String requestString = request
351: .getParameter(WebRequestCodingStrategy.BOOKMARKABLE_PAGE_PARAMETER_NAME);
352: if (requestString != null) {
353: final String[] components = Strings.split(requestString,
354: Component.PATH_SEPARATOR);
355: if (components.length != 2) {
356: throw new WicketRuntimeException(
357: "Invalid bookmarkablePage parameter: "
358: + requestString
359: + ", expected: 'pageMapName:pageClassName'");
360: }
361:
362: // Extract any pagemap name
363: final String pageMapName = components[0];
364: parameters
365: .setPageMapName(pageMapName.length() == 0 ? PageMap.DEFAULT_NAME
366: : pageMapName);
367:
368: // Extract bookmarkable page class name
369: final String pageClassName = components[1];
370: parameters.setBookmarkablePageClass(pageClassName);
371: }
372: }
373:
374: /**
375: * Adds page related parameters (path and pagemap and optionally version and
376: * interface).
377: *
378: * If you override this method to behave different then also
379: * {@link #encode(RequestCycle, IListenerInterfaceRequestTarget)} should be
380: * overridden to by in sync with that behaviour.
381: *
382: * @param request
383: * the incoming request
384: * @param parameters
385: * the parameters object to set the found values on
386: */
387: protected void addInterfaceParameters(final Request request,
388: final RequestParameters parameters) {
389: // Format of interface target parameter is
390: // <page-map-name>:<path>:<version>:<interface>
391: final String requestString = request
392: .getParameter(INTERFACE_PARAMETER_NAME);
393: if (requestString != null) {
394: // Split into array of strings
395: String[] pathComponents = Strings.split(requestString,
396: Component.PATH_SEPARATOR);
397:
398: // There must be at least 4 components
399: if (pathComponents.length < 4) {
400: throw new WicketRuntimeException(
401: "Internal error parsing "
402: + INTERFACE_PARAMETER_NAME + " = "
403: + requestString);
404: }
405:
406: // Set pagemap name
407: final String pageMapName = pathComponents[0];
408: parameters
409: .setPageMapName(pageMapName.length() == 0 ? PageMap.DEFAULT_NAME
410: : pageMapName);
411:
412: // Extract interface name after last colon
413: final String interfaceName = pathComponents[pathComponents.length - 1];
414: parameters
415: .setInterfaceName(interfaceName.length() != 0 ? interfaceName
416: : IRedirectListener.INTERFACE.getName());
417:
418: // Extract version
419: final String versionNumberString = pathComponents[pathComponents.length - 2];
420: final int versionNumber = Strings
421: .isEmpty(versionNumberString) ? 0 : Integer
422: .parseInt(versionNumberString);
423: parameters.setVersionNumber(versionNumber);
424:
425: // Component path is everything after pageMapName and before version
426: final int start = pageMapName.length() + 1;
427: final int end = requestString.length()
428: - interfaceName.length()
429: - versionNumberString.length() - 2;
430: final String componentPath = requestString.substring(start,
431: end);
432: parameters.setComponentPath(componentPath);
433: }
434: }
435:
436: /**
437: * Adds (shared) resource related parameters (resource key). Any shared
438: * resource key mount will override this method; hence if a mount is found,
439: * this method will not be called.
440: *
441: * If you override this method to behave different then also
442: * {@link #encode(RequestCycle, ISharedResourceRequestTarget)} should be
443: * overridden to by in sync with that behaviour.
444: *
445: * @param request
446: * the incomming request
447: * @param parameters
448: * the parameters object to set the found values on
449: */
450: protected void addResourceParameters(Request request,
451: RequestParameters parameters) {
452: String pathInfo = request.getPath();
453: if (pathInfo != null && pathInfo.startsWith("/resources/")) {
454: int ix = "/resources/".length();
455: if (pathInfo.length() > ix) {
456: StringBuffer path = new StringBuffer(pathInfo
457: .substring(ix));
458: int ixSemiColon = path.indexOf(";");
459: // strip off any jsession id
460: if (ixSemiColon != -1) {
461: int ixEnd = path.indexOf("?");
462: if (ixEnd == -1) {
463: ixEnd = path.length();
464: }
465: path.delete(ixSemiColon, ixEnd);
466: }
467: parameters.setResourceKey(path.toString());
468: }
469: }
470: }
471:
472: /**
473: * In case you are using custom targets that are not part of the default
474: * target hierarchy, you need to override this method, which will be called
475: * after the defaults have been tried. When this doesn't provide a url
476: * either (returns null), an exception will be thrown by the encode method
477: * saying that encoding could not be done.
478: *
479: * @param requestCycle
480: * the current request cycle (for efficient access)
481: *
482: * @param requestTarget
483: * the request target
484: * @return the url to the provided target
485: */
486: protected String doEncode(RequestCycle requestCycle,
487: IRequestTarget requestTarget) {
488: return null;
489: }
490:
491: /**
492: * Encode a page class target.
493: *
494: * If you override this method to behave different then also
495: * {@link #addBookmarkablePageParameters(Request, RequestParameters)} should
496: * be overridden to by in sync with that behaviour.
497: *
498: * @param requestCycle
499: * the current request cycle
500: * @param requestTarget
501: * the target to encode
502: * @return the encoded url
503: */
504: protected CharSequence encode(RequestCycle requestCycle,
505: IBookmarkablePageRequestTarget requestTarget) {
506: // Begin encoding URL
507: final AppendingStringBuffer url = new AppendingStringBuffer(64);
508: url.append(urlPrefix(requestCycle));
509:
510: // Get page Class
511: final Class pageClass = requestTarget.getPageClass();
512: final Application application = Application.get();
513:
514: // Find pagemap name
515: String pageMapName = requestTarget.getPageMapName();
516: if (pageMapName == null) {
517: IRequestTarget currentTarget = requestCycle
518: .getRequestTarget();
519: if (currentTarget instanceof IPageRequestTarget) {
520: Page currentPage = ((IPageRequestTarget) currentTarget)
521: .getPage();
522: final PageMap pageMap = currentPage.getPageMap();
523: if (pageMap.isDefault()) {
524: pageMapName = "";
525: } else {
526: pageMapName = pageMap.getName();
527: }
528: } else {
529: pageMapName = "";
530: }
531: }
532:
533: boolean firstParameter = true;
534: if (!application.getHomePage().equals(pageClass)
535: || !"".equals(pageMapName)) {
536: firstParameter = false;
537: url.append('?');
538: url
539: .append(WebRequestCodingStrategy.BOOKMARKABLE_PAGE_PARAMETER_NAME);
540: url.append('=');
541:
542: // Add <page-map-name>:<bookmarkable-page-class>
543: String pageClassName = pageClass.getName();
544: /*
545: * Encode the url so it is correct even for class names containing
546: * non ASCII characters, like ä, æ, ø, å etc.
547: *
548: * The reason for this is that when redirecting to these
549: * bookmarkable pages, we need to have the url encoded correctly
550: * because we can't rely on the browser to interpret the unencoded
551: * url correctly.
552: */
553: try {
554: pageClassName = URLEncoder.encode(pageClassName,
555: "UTF-8");
556: } catch (UnsupportedEncodingException e) {
557: throw new RuntimeException(e);
558: }
559: url.append(pageMapName + Component.PATH_SEPARATOR
560: + pageClassName);
561: }
562:
563: // Get page parameters
564: final PageParameters parameters = requestTarget
565: .getPageParameters();
566: if (parameters != null) {
567: for (final Iterator iterator = parameters.keySet()
568: .iterator(); iterator.hasNext();) {
569: final String key = (String) iterator.next();
570: final String value = parameters.getString(key);
571: if (value != null) {
572: String escapedValue = value;
573: try {
574: escapedValue = URLEncoder.encode(escapedValue,
575: application.getRequestCycleSettings()
576: .getResponseRequestEncoding());
577: } catch (UnsupportedEncodingException ex) {
578: log.error(ex.getMessage(), ex);
579: }
580: if (!firstParameter) {
581: url.append('&');
582: } else {
583: firstParameter = false;
584: url.append('?');
585: }
586: url.append(key);
587: url.append('=');
588: url.append(escapedValue);
589: }
590: }
591: }
592: return requestCycle.getOriginalResponse().encodeURL(url);
593: }
594:
595: /**
596: * Encode a shared resource target.
597: *
598: * If you override this method to behave different then also
599: * {@link #addResourceParameters(Request, RequestParameters)} should be
600: * overridden to by in sync with that behaviour.
601: *
602: * @param requestCycle
603: * the current request cycle
604: * @param requestTarget
605: * the target to encode
606: * @return the encoded url
607: */
608: protected CharSequence encode(RequestCycle requestCycle,
609: ISharedResourceRequestTarget requestTarget) {
610: final CharSequence prefix = urlPrefix(requestCycle);
611: final String sharedResourceKey = requestTarget.getResourceKey();
612: if ((sharedResourceKey == null)
613: || (sharedResourceKey.trim().length() == 0)) {
614: return prefix;
615: } else {
616: final AppendingStringBuffer buffer = new AppendingStringBuffer(
617: sharedResourceKey.length() + prefix.length() + 11);
618: buffer.append(prefix);
619: if ((buffer.length() > 0)
620: && buffer.charAt(buffer.length() - 1) == '/') {
621: buffer.append("resources/");
622: } else {
623: buffer.append("/resources/");
624: }
625: buffer.append(sharedResourceKey);
626: Map map = requestTarget.getRequestParameters()
627: .getParameters();
628: if (map != null && map.size() > 0) {
629: buffer.append('?');
630: Iterator it = map.entrySet().iterator();
631: while (it.hasNext()) {
632: Map.Entry entry = (Entry) it.next();
633: buffer.append(entry.getKey());
634: buffer.append('=');
635: buffer.append(entry.getValue());
636: buffer.append('&');
637: }
638: buffer.setLength(buffer.length() - 1);
639: }
640: return requestCycle.getOriginalResponse().encodeURL(buffer);
641: }
642: }
643:
644: /**
645: * Encode a listener interface target.
646: *
647: * If you override this method to behave different then also
648: * {@link #addInterfaceParameters(Request, RequestParameters)} should be
649: * overridden to by in sync with that behaviour.
650: *
651: * @param requestCycle
652: * the current request cycle
653: * @param requestTarget
654: * the target to encode
655: * @return the encoded url
656: */
657: protected CharSequence encode(RequestCycle requestCycle,
658: IListenerInterfaceRequestTarget requestTarget) {
659: final RequestListenerInterface rli = requestTarget
660: .getRequestListenerInterface();
661:
662: // Start string buffer for url
663: final AppendingStringBuffer url = new AppendingStringBuffer(64);
664: url.append(urlPrefix(requestCycle));
665: url.append('?');
666: url.append(INTERFACE_PARAMETER_NAME);
667: url.append('=');
668:
669: // Get component and page for request target
670: final Component component = requestTarget.getTarget();
671: final Page page = component.getPage();
672:
673: // Add pagemap
674: final PageMap pageMap = page.getPageMap();
675: if (!pageMap.isDefault()) {
676: url.append(pageMap.getName());
677: }
678: url.append(Component.PATH_SEPARATOR);
679:
680: // Add path to component
681: url.append(component.getPath());
682: url.append(Component.PATH_SEPARATOR);
683:
684: // Add version
685: final int versionNumber = component.getPage()
686: .getCurrentVersionNumber();
687: if (!rli.getRecordsPageVersion()) {
688: url.append(Page.LATEST_VERSION);
689: } else if (versionNumber > 0) {
690: url.append(versionNumber);
691: }
692: url.append(Component.PATH_SEPARATOR);
693:
694: // Add listener interface
695: final String listenerName = rli.getName();
696: if (!IRedirectListener.INTERFACE.getName().equals(listenerName)) {
697: url.append(listenerName);
698: }
699:
700: return requestCycle.getOriginalResponse().encodeURL(url);
701: }
702:
703: /**
704: * Encode a page target.
705: *
706: * @param requestCycle
707: * the current request cycle
708: * @param requestTarget
709: * the target to encode
710: * @return the encoded url
711: */
712: protected CharSequence encode(RequestCycle requestCycle,
713: IPageRequestTarget requestTarget) {
714: // Get the page we want a url from:
715: Page page = requestTarget.getPage();
716:
717: // A url to a page is the IRedirectListener interface:
718: CharSequence urlRedirect = page
719: .urlFor(IRedirectListener.INTERFACE);
720:
721: // Touch the page once because it could be that it did go from stateless
722: // to statefull or it was a internally made page where just a url must
723: // be made for (frames)
724: Session.get().touch(page);
725: return urlRedirect;
726: }
727:
728: /**
729: * Gets the mount encoder for the given request target if any.
730: *
731: * @param requestTarget
732: * the request target to match
733: * @return the mount encoder if any
734: */
735: protected IRequestTargetUrlCodingStrategy getMountEncoder(
736: IRequestTarget requestTarget) {
737: // TODO Post 1.2: Performance: Optimize algorithm if possible and/ or
738: // cache lookup results
739: for (Iterator i = mountsOnPath.values().iterator(); i.hasNext();) {
740: IRequestTargetUrlCodingStrategy encoder = (IRequestTargetUrlCodingStrategy) i
741: .next();
742: if (encoder.matches(requestTarget)) {
743: return encoder;
744: }
745: }
746:
747: return null;
748: }
749:
750: /**
751: * Gets the request info path. This is an overridable method in order to
752: * provide users with a means to implement e.g. a path encryption scheme.
753: * This method by default returns {@link Request#getPath()}.
754: *
755: * @param request
756: * the request
757: * @return the path info object, possibly processed
758: */
759: protected String getRequestPath(Request request) {
760: return request.getPath();
761: }
762:
763: /**
764: * Gets prefix.
765: *
766: * @param requestCycle
767: * the request cycle
768: *
769: * @return prefix
770: */
771: protected final CharSequence urlPrefix(
772: final RequestCycle requestCycle) {
773: if (urlPrefix == null) {
774: final AppendingStringBuffer buffer = new AppendingStringBuffer();
775: final WebRequest request = ((WebRequestCycle) requestCycle)
776: .getWebRequest();
777: if (request != null) {
778: String contextPath = Application.get()
779: .getApplicationSettings().getContextPath();
780: if (contextPath == null) {
781: contextPath = ((WebRequestCycle) RequestCycle.get())
782: .getWebRequest().getContextPath();
783: if (contextPath == null) {
784: contextPath = "";
785: }
786: }
787: if (!contextPath.equals("/")) {
788: buffer.append(contextPath);
789: }
790: String path = request.getServletPath();
791: if (path != null && !path.equals("")) {
792: if (!buffer.endsWith("/") && !path.startsWith("/"))
793: buffer.append("/");
794: buffer.append(path);
795: }
796: }
797: // special check, if everything is empty then we need to define '/'
798: // as urlPrefix
799: // else all urls get relative this is bad for mounts.
800: // Except for mounts who have to do a special check else mounts get:
801: // //mount
802: // see encode(RequestCycle,IRequestTarget) when a mount is found.
803: if (buffer.length() == 0) {
804: urlPrefix = "/";
805: } else {
806: /*
807: * make the urls consistent. if the mapping is /app/* wicket
808: * will switch off between /app/foo and /app?wicket:interface
809: * which messes with the path, appending / always to the end
810: * makes sure the base path is always /context/servletmapping/
811: */
812: if (!buffer.endsWith("/")) {
813: buffer.append("/");
814: }
815: urlPrefix = buffer;
816: }
817: }
818: return urlPrefix;
819: }
820: }
|