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 private static final int DEFAULT_HTTPS_PORT = 443;
043
044 private final IRequestCycle _cycle;
045
046 private final String _servletPath;
047
048 private final URLCodec _codec;
049
050 private String _encoding;
051
052 private boolean _stateful;
053
054 /** @since 4.0 */
055 private final QueryParameterMap _parameters;
056
057 /** @since 4.0 */
058
059 private final WebRequest _request;
060
061 /**
062 * Creates a new EngineServiceLink.
063 *
064 * @param cycle
065 * The {@link IRequestCycle} the EngineServiceLink is to be created for.
066 * @param servletPath
067 * The path used to invoke the Tapestry servlet.
068 * @param codec
069 * A codec for converting strings into URL-safe formats.
070 * @param encoding
071 * The output encoding for the request.
072 * @param parameters
073 * The query parameters to be encoded into the url. Keys are strings, values are
074 * null, string or array of string. The map is retained, not copied.
075 * @param stateful
076 * if true, the service which generated the EngineServiceLink is stateful and expects
077 * that the final URL will be passed through {@link IRequestCycle#encodeURL(String)}.
078 */
079
080 public EngineServiceLink(IRequestCycle cycle, String servletPath, String encoding,
081 URLCodec codec, WebRequest request, Map parameters, boolean stateful)
082 {
083 Defense.notNull(cycle, "cycle");
084 Defense.notNull(servletPath, "servletPath");
085 Defense.notNull(encoding, "encoding");
086 Defense.notNull(codec, "codec");
087 Defense.notNull(request, "request");
088 Defense.notNull(parameters, "parameters");
089
090 _cycle = cycle;
091 _servletPath = servletPath;
092 _encoding = encoding;
093 _codec = codec;
094 _request = request;
095 _parameters = new QueryParameterMap(parameters);
096 _stateful = stateful;
097 }
098
099 public String getURL()
100 {
101 return getURL(null, true);
102 }
103
104 public String getURL(String anchor, boolean includeParameters)
105 {
106 return constructURL(new StringBuffer(), anchor, includeParameters);
107 }
108
109 public String getAbsoluteURL()
110 {
111 return getAbsoluteURL(null, null, 0, null, true);
112 }
113
114 public String getURL(String scheme, String server, int port, String anchor,
115 boolean includeParameters)
116 {
117 boolean useAbsolute = EngineUtils.needAbsoluteURL(scheme, server, port, _request);
118
119 return useAbsolute ? getAbsoluteURL(scheme, server, port, anchor, includeParameters)
120 : getURL(anchor, includeParameters);
121 }
122
123 public String getAbsoluteURL(String scheme, String server, int port, String anchor,
124 boolean includeParameters)
125 {
126 StringBuffer buffer = new StringBuffer();
127
128 if (scheme == null)
129 scheme = _request.getScheme();
130
131 buffer.append(scheme);
132 buffer.append("://");
133
134 if (server == null)
135 server = _request.getServerName();
136
137 buffer.append(server);
138
139 if (port == 0)
140 port = _request.getServerPort();
141
142 if (!(scheme.equals("http") && port == DEFAULT_HTTP_PORT))
143 {
144 buffer.append(':');
145 buffer.append(port);
146 }
147
148 // Add the servlet path and the rest of the URL & query parameters.
149 // The servlet path starts with a leading slash.
150
151 return constructURL(buffer, anchor, includeParameters);
152 }
153
154 private String constructURL(StringBuffer buffer, String anchor, boolean includeParameters)
155 {
156 buffer.append(_servletPath);
157
158 if (includeParameters)
159 addParameters(buffer);
160
161 if (anchor != null)
162 {
163 buffer.append('#');
164 buffer.append(anchor);
165 }
166
167 String result = buffer.toString();
168
169 result = _cycle.encodeURL(result);
170
171 return result;
172 }
173
174 private void addParameters(StringBuffer buffer)
175 {
176 String[] names = getParameterNames();
177
178 String sep = "?";
179
180 for (int i = 0; i < names.length; i++)
181 {
182 String name = names[i];
183 String[] values = getParameterValues(name);
184
185 if (values == null)
186 continue;
187
188 for (int j = 0; j < values.length; j++)
189 {
190 buffer.append(sep);
191 buffer.append(name);
192 buffer.append("=");
193 buffer.append(encode(values[j]));
194
195 sep = "&";
196 }
197
198 }
199 }
200
201 private String encode(String value)
202 {
203 try
204 {
205 return _codec.encode(value, _encoding);
206 }
207 catch (UnsupportedEncodingException ex)
208 {
209 throw new ApplicationRuntimeException(Tapestry.format("illegal-encoding", _encoding),
210 ex);
211 }
212 }
213
214 public String[] getParameterNames()
215 {
216 return _parameters.getParameterNames();
217 }
218
219 public String[] getParameterValues(String name)
220 {
221 return _parameters.getParameterValues(name);
222 }
223 }