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