001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.engine; 016 017 import java.io.UnsupportedEncodingException; 018 import java.util.Map; 019 020 import org.apache.commons.codec.net.URLCodec; 021 import org.apache.hivemind.ApplicationRuntimeException; 022 import org.apache.hivemind.util.Defense; 023 import org.apache.tapestry.IRequestCycle; 024 import org.apache.tapestry.Tapestry; 025 import org.apache.tapestry.util.QueryParameterMap; 026 import org.apache.tapestry.web.WebRequest; 027 028 /** 029 * A EngineServiceLink represents a possible action within the client web browser; either clicking a 030 * link or submitting a form, which is constructed primarily from the servlet path, with some 031 * additional query parameters. A full URL for the EngineServiceLink can be generated, or the query 032 * parameters for the EngineServiceLink can be extracted (separately from the servlet path). The 033 * latter case is used when submitting constructing {@link org.apache.tapestry.form.Form forms}. 034 * 035 * @author Howard Lewis Ship 036 * @since 3.0 037 */ 038 039 public class EngineServiceLink implements ILink 040 { 041 private static final int DEFAULT_HTTP_PORT = 80; 042 043 private final IRequestCycle _cycle; 044 045 private final String _servletPath; 046 047 private final URLCodec _codec; 048 049 private String _encoding; 050 051 private boolean _stateful; 052 053 /** @since 4.0 */ 054 private final QueryParameterMap _parameters; 055 056 /** @since 4.0 */ 057 058 private final WebRequest _request; 059 060 /** 061 * Creates a new EngineServiceLink. 062 * 063 * @param cycle 064 * The {@link IRequestCycle} the EngineServiceLink is to be created for. 065 * @param servletPath 066 * The path used to invoke the Tapestry servlet. 067 * @param codec 068 * A codec for converting strings into URL-safe formats. 069 * @param encoding 070 * The output encoding for the request. 071 * @param parameters 072 * The query parameters to be encoded into the url. Keys are strings, values are 073 * null, string or array of string. The map is retained, not copied. 074 * @param stateful 075 * if true, the service which generated the EngineServiceLink is stateful and expects 076 * that the final URL will be passed through {@link IRequestCycle#encodeURL(String)}. 077 */ 078 079 public EngineServiceLink(IRequestCycle cycle, String servletPath, String encoding, 080 URLCodec codec, WebRequest request, Map parameters, boolean stateful) 081 { 082 Defense.notNull(cycle, "cycle"); 083 Defense.notNull(servletPath, "servletPath"); 084 Defense.notNull(encoding, "encoding"); 085 Defense.notNull(codec, "codec"); 086 Defense.notNull(request, "request"); 087 Defense.notNull(parameters, "parameters"); 088 089 _cycle = cycle; 090 _servletPath = servletPath; 091 _encoding = encoding; 092 _codec = codec; 093 _request = request; 094 _parameters = new QueryParameterMap(parameters); 095 _stateful = stateful; 096 } 097 098 public String getURL() 099 { 100 return getURL(null, true); 101 } 102 103 public String getURL(String anchor, boolean includeParameters) 104 { 105 return constructURL(new StringBuffer(), anchor, includeParameters); 106 } 107 108 public String getAbsoluteURL() 109 { 110 return getAbsoluteURL(null, null, 0, null, true); 111 } 112 113 public String getAbsoluteURL(String scheme, String server, int port, String anchor, 114 boolean includeParameters) 115 { 116 StringBuffer buffer = new StringBuffer(); 117 118 if (scheme == null) 119 scheme = _request.getScheme(); 120 121 buffer.append(scheme); 122 buffer.append("://"); 123 124 if (server == null) 125 server = _request.getServerName(); 126 127 buffer.append(server); 128 129 if (port == 0) 130 port = _request.getServerPort(); 131 132 if (!(scheme.equals("http") && port == DEFAULT_HTTP_PORT)) 133 { 134 buffer.append(':'); 135 buffer.append(port); 136 } 137 138 // Add the servlet path and the rest of the URL & query parameters. 139 // The servlet path starts with a leading slash. 140 141 return constructURL(buffer, anchor, includeParameters); 142 } 143 144 private String constructURL(StringBuffer buffer, String anchor, boolean includeParameters) 145 { 146 buffer.append(_servletPath); 147 148 if (includeParameters) 149 addParameters(buffer); 150 151 if (anchor != null) 152 { 153 buffer.append('#'); 154 buffer.append(anchor); 155 } 156 157 String result = buffer.toString(); 158 159 if (_stateful) 160 result = _cycle.encodeURL(result); 161 162 return result; 163 } 164 165 private void addParameters(StringBuffer buffer) 166 { 167 String[] names = getParameterNames(); 168 169 String sep = "?"; 170 171 for (int i = 0; i < names.length; i++) 172 { 173 String name = names[i]; 174 String[] values = getParameterValues(name); 175 176 if (values == null) 177 continue; 178 179 for (int j = 0; j < values.length; j++) 180 { 181 buffer.append(sep); 182 buffer.append(name); 183 buffer.append("="); 184 buffer.append(encode(values[j])); 185 186 sep = "&"; 187 } 188 189 } 190 } 191 192 private String encode(String value) 193 { 194 try 195 { 196 return _codec.encode(value, _encoding); 197 } 198 catch (UnsupportedEncodingException ex) 199 { 200 throw new ApplicationRuntimeException(Tapestry.format("illegal-encoding", _encoding), 201 ex); 202 } 203 } 204 205 public String[] getParameterNames() 206 { 207 return _parameters.getParameterNames(); 208 } 209 210 public String[] getParameterValues(String name) 211 { 212 return _parameters.getParameterValues(name); 213 } 214 }