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.services.impl;
016
017 import org.apache.commons.codec.net.URLCodec;
018 import org.apache.hivemind.ApplicationRuntimeException;
019 import org.apache.hivemind.ErrorLog;
020 import org.apache.hivemind.order.Orderer;
021 import org.apache.hivemind.util.Defense;
022 import org.apache.tapestry.IEngine;
023 import org.apache.tapestry.IRequestCycle;
024 import org.apache.tapestry.Tapestry;
025 import org.apache.tapestry.engine.*;
026 import org.apache.tapestry.record.PropertyPersistenceStrategySource;
027 import org.apache.tapestry.services.DataSqueezer;
028 import org.apache.tapestry.services.LinkFactory;
029 import org.apache.tapestry.services.ServiceConstants;
030 import org.apache.tapestry.util.QueryParameterMap;
031 import org.apache.tapestry.web.WebRequest;
032
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.Map;
036
037 /**
038 * @author Howard M. Lewis Ship
039 * @since 4.0
040 */
041 public class LinkFactoryImpl implements LinkFactory
042 {
043
044 protected URLCodec _codec = new URLCodec();
045
046 protected PropertyPersistenceStrategySource _persistenceStrategySource;
047
048 protected IRequestCycle _requestCycle;
049
050 protected WebRequest _request;
051
052 private DataSqueezer _dataSqueezer;
053
054 private ErrorLog _errorLog;
055
056 /**
057 * List of {@link org.apache.tapestry.services.impl.ServiceEncoderContribution}.
058 */
059
060 private List _contributions;
061
062 private ServiceEncoder[] _encoders;
063
064 private String _servletPath;
065
066 private final Object[] _empty = new Object[0];
067
068 public void initializeService()
069 {
070 Orderer orderer = new Orderer(_errorLog, "encoder");
071
072 Iterator i = _contributions.iterator();
073
074 while (i.hasNext())
075 {
076 ServiceEncoderContribution c = (ServiceEncoderContribution) i.next();
077
078 orderer.add(c, c.getId(), c.getAfter(), c.getBefore());
079 }
080
081 List ordered = orderer.getOrderedObjects();
082 int count = ordered.size();
083
084 _encoders = new ServiceEncoder[count];
085
086 for (int j = 0; j < count; j++)
087 {
088 ServiceEncoderContribution c = (ServiceEncoderContribution) ordered.get(j);
089
090 _encoders[j] = c.getEncoder();
091 }
092
093 }
094
095 public ILink constructLink(IEngineService service, boolean post, Map parameters, boolean stateful)
096 {
097 finalizeParameters(service, parameters);
098
099 IEngine engine = _requestCycle.getEngine();
100
101 QueryParameterMap qmap = new QueryParameterMap(parameters);
102
103 ServiceEncoding serviceEncoding = createServiceEncoding(qmap);
104
105 // Give persistent property strategies a chance to store extra data
106 // into the link.
107
108 if (stateful)
109 _persistenceStrategySource.addParametersForPersistentProperties(serviceEncoding, post);
110
111 String fullServletPath = _request.getContextPath() + serviceEncoding.getServletPath();
112
113 return new EngineServiceLink(_requestCycle, fullServletPath, engine.getOutputEncoding(),
114 _codec, _request, qmap, stateful);
115 }
116
117 protected void finalizeParameters(IEngineService service, Map parameters)
118 {
119 Defense.notNull(service, "service");
120 Defense.notNull(parameters, "parameters");
121
122 String serviceName = service.getName();
123
124 if (serviceName == null)
125 throw new ApplicationRuntimeException(ImplMessages.serviceNameIsNull());
126
127 parameters.put(ServiceConstants.SERVICE, serviceName);
128
129 squeezeServiceParameters(parameters);
130 }
131
132 public ServiceEncoder[] getServiceEncoders()
133 {
134 return _encoders;
135 }
136
137 /**
138 * Creates a new service encoding, and allows the encoders to modify it before returning.
139 */
140
141 protected ServiceEncoding createServiceEncoding(QueryParameterMap parameters)
142 {
143 ServiceEncodingImpl result = new ServiceEncodingImpl(_servletPath, parameters);
144
145 for (int i = 0; i < _encoders.length; i++)
146 {
147 _encoders[i].encode(result);
148
149 if (result.isModified())
150 break;
151 }
152
153 return result;
154 }
155
156 protected void squeezeServiceParameters(Map parameters)
157 {
158 Object[] serviceParameters = (Object[]) parameters.get(ServiceConstants.PARAMETER);
159
160 if (serviceParameters == null)
161 return;
162
163 parameters.put(ServiceConstants.PARAMETER, squeeze(serviceParameters));
164 }
165
166 public Object[] extractListenerParameters(IRequestCycle cycle)
167 {
168 String[] squeezed = cycle.getParameters(ServiceConstants.PARAMETER);
169
170 if (Tapestry.size(squeezed) == 0)
171 return _empty;
172
173 try
174 {
175 return _dataSqueezer.unsqueeze(squeezed);
176 }
177 catch (Exception ex)
178 {
179 throw new ApplicationRuntimeException(ex);
180 }
181 }
182
183 private String[] squeeze(Object[] input)
184 {
185 try
186 {
187 return _dataSqueezer.squeeze(input);
188 }
189 catch (Exception ex)
190 {
191 throw new ApplicationRuntimeException(ex);
192 }
193 }
194
195 public void setDataSqueezer(DataSqueezer dataSqueezer)
196 {
197 _dataSqueezer = dataSqueezer;
198 }
199
200 public void setContributions(List contributions)
201 {
202 _contributions = contributions;
203 }
204
205 public void setErrorLog(ErrorLog errorLog)
206 {
207 _errorLog = errorLog;
208 }
209
210 public void setServletPath(String servletPath)
211 {
212 _servletPath = servletPath;
213 }
214
215 public void setRequest(WebRequest request)
216 {
217 _request = request;
218 }
219
220 /**
221 * This is kind of limiting; it's possible that other things beyond persistence strategies will
222 * want to have a hand at encoding data into URLs. If that comes to pass, we'll need to
223 * implement an event coordinator/listener combo to let implementations know about links being
224 * generated.
225 *
226 * @param persistenceStrategySource
227 * The persistent strategy to use.
228 */
229
230 public void setPersistenceStrategySource(PropertyPersistenceStrategySource persistenceStrategySource)
231 {
232 _persistenceStrategySource = persistenceStrategySource;
233 }
234
235 public void setRequestCycle(IRequestCycle requestCycle)
236 {
237 _requestCycle = requestCycle;
238 }
239 }