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: */
017: package org.apache.wicket.request.target.coding;
018:
019: import java.util.Iterator;
020: import java.util.Map;
021: import java.util.TreeMap;
022: import java.util.Map.Entry;
023:
024: import org.apache.wicket.IRequestTarget;
025: import org.apache.wicket.PageMap;
026: import org.apache.wicket.PageParameters;
027: import org.apache.wicket.protocol.http.UnitTestSettings;
028: import org.apache.wicket.request.RequestParameters;
029: import org.apache.wicket.request.target.component.BookmarkableListenerInterfaceRequestTarget;
030: import org.apache.wicket.request.target.component.BookmarkablePageRequestTarget;
031: import org.apache.wicket.util.string.AppendingStringBuffer;
032: import org.apache.wicket.util.value.ValueMap;
033:
034: /**
035: * Encodes and decodes mounts for a single bookmarkable page class, but with the
036: * parameters appended in a URL query string rather than integrated into a URL
037: * hierarchical path.
038: * <p>
039: * For example, whereas
040: * {@link org.apache.wicket.request.target.coding.BookmarkablePageRequestTargetUrlCodingStrategy BookmarkablePageRequestTargetUrlCodingStrategy}
041: * might encode a request target as
042: * "mywebapp/myservlet/admin/productmanagement/action/edit/product/4995",
043: * <code>QueryStringRequestTargetUrlCodingStrategy</code> would encode the
044: * same target as
045: * "mywebapp/myservlet/admin/productmanagement?action=edit&product=4995".
046: * <p>
047: * URLs encoded in this way can be bookmarked just as easily as those produced
048: * by <code>BookmarkablePageRequestTargetUrlCodingStrategy</code>. For
049: * example, Google searches produce bookmarkable links with query strings.
050: * <p>
051: * Whether <code>BookmarkablePageRequestTargetUrlCodingStrategy</code> or
052: * <code>QueryStringRequestTargetUrlCodingStrategy</code> is appropriate for a
053: * given mount depends on:
054: * <ul>
055: * <li>Aesthetic criteria
056: * <li>Interpretations of <a
057: * href="http://www.gbiv.com/protocols/uri/rfc/rfc3986.html">RFC 3986</a>. This
058: * defines the URI standard, including query strings, and states that whereas
059: * the "path component contains data, usually organized in hierarchical form
060: * [divided by slashes]", the "query component [after the question mark]
061: * contains non-hierarchical data".
062: * <li>Findability. Public search engines prefer URLs with parameters stored
063: * hierarchically or in a shorter query string. Google's <a
064: * href="http://www.google.com/support/webmasters/bin/answer.py?answer=35770">Design
065: * and Content Guidelines</a> (as of May 6 2006) state: "Make a site with a
066: * clear hierarchy and text links. Every page should be reachable from at least
067: * one static text link. … If you decide to use dynamic pages (i.e., the
068: * URL contains a '?' character), be aware that not every search engine spider
069: * crawls dynamic pages as well as static pages. It helps to keep the parameters
070: * short and the number of them few."
071: * <li>The complexity of the parameters being passed. More complex parameters
072: * may make more sense expressed as a series of "key=value(s)" pairs in a query
073: * string than shoehorned into a hierarchical structure.
074: * </ul>
075: * <p>
076: * Regardless of which coding strategy is chosen for the mount,
077: * {@link org.apache.wicket.markup.html.link.BookmarkablePageLink BookmarkablePageLink}
078: * can be used to insert a bookmarkable link to the request target.
079: * <p>
080: * This example demonstrates how to mount a path with
081: * <code>QueryStringRequestTargetUrlCodingStrategy</code> within the
082: * <code>init</code> method of a class implementing
083: * {@link org.apache.wicket.protocol.http.WebApplication WebApplication}:
084: * <p>
085: * <code>mount(new QueryStringRequestTargetUrlCodingStrategy("/admin/productmanagement",
086: * admin.ProductManagement.class));</code>
087: * <p>
088: * Note that, as with the main BookmarkablePageRequestTargetUrlCodingStrategy,
089: * if the output of this coding strategy is passed through
090: * {@link javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String) HttpServletResponse.encodeURL}
091: * and the client has cookies turned off, the client's session ID will be stored
092: * in a path parameter, like so:
093: * "/mywebapp/myservlet/admin/productmanagement;jsessionid=730EC527564AF1C73F8C2FB19B604F55?action=edit&product=4995".
094: *
095: * @author Benjamin Hawkes-Lewis
096: */
097: public class QueryStringUrlCodingStrategy extends
098: BookmarkablePageRequestTargetUrlCodingStrategy {
099:
100: /**
101: * Sole constructor.
102: *
103: * @param mountPath
104: * the relative reference URL on which the page is mounted
105: * @param bookmarkablePageClass
106: * the class of the mounted page
107: */
108: public QueryStringUrlCodingStrategy(final String mountPath,
109: final Class bookmarkablePageClass) {
110: super (mountPath, bookmarkablePageClass, PageMap.DEFAULT_NAME);
111: }
112:
113: /**
114: * Append the parameters to the end of the URL.
115: *
116: * @param url
117: * the relative reference URL
118: * @param parameters
119: * parameter names mapped to parameter values
120: */
121: protected void appendParameters(AppendingStringBuffer url,
122: Map parameters) {
123: if (!url.endsWith("/")) {
124: url.append("/");
125: }
126: if (parameters != null && parameters.size() > 0) {
127: final Iterator entries;
128: if (UnitTestSettings.getSortUrlParameters()) {
129: entries = new TreeMap(parameters).entrySet().iterator();
130: } else {
131: entries = parameters.entrySet().iterator();
132: }
133: WebRequestEncoder encoder = new WebRequestEncoder(url);
134: while (entries.hasNext()) {
135: Map.Entry entry = (Entry) entries.next();
136:
137: if (entry.getValue() != null) {
138: encoder.addValue(entry.getKey().toString(), entry
139: .getValue());
140: }
141: }
142: }
143: }
144:
145: public IRequestTarget decode(RequestParameters requestParameters) {
146: String pageMapName = requestParameters.getPageMapName();
147: final PageParameters parameters = new PageParameters(
148: requestParameters.getParameters());
149:
150: // This might be a request to a stateless page, so check for an
151: // interface name.
152: if (requestParameters.getInterfaceName() != null) {
153: return new BookmarkableListenerInterfaceRequestTarget(
154: pageMapName,
155: (Class) bookmarkablePageClassRef.get(), parameters,
156: requestParameters.getComponentPath(),
157: requestParameters.getInterfaceName());
158: } else {
159: return new BookmarkablePageRequestTarget(pageMapName,
160: (Class) bookmarkablePageClassRef.get(), parameters);
161: }
162: }
163:
164: /**
165: * Decodes parameters object from the provided query string
166: *
167: * @param fragment
168: * contains the query string
169: * @param passedParameters
170: * paremeters decoded by wicket before this method - usually off
171: * the query string
172: *
173: * @return Parameters
174: */
175: protected ValueMap decodeParameters(String fragment,
176: Map passedParameters) {
177: ValueMap parameters = new ValueMap();
178:
179: if (passedParameters != null) {
180: parameters.putAll(passedParameters);
181: }
182:
183: return parameters;
184:
185: }
186:
187: }
|