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 015package org.apache.tapestry.services.impl; 016 017import java.util.Iterator; 018import java.util.List; 019import java.util.Map; 020 021import org.apache.commons.codec.net.URLCodec; 022import org.apache.hivemind.ApplicationRuntimeException; 023import org.apache.hivemind.ErrorLog; 024import org.apache.hivemind.order.Orderer; 025import org.apache.hivemind.util.Defense; 026import org.apache.tapestry.IEngine; 027import org.apache.tapestry.IRequestCycle; 028import org.apache.tapestry.Tapestry; 029import org.apache.tapestry.engine.EngineServiceLink; 030import org.apache.tapestry.engine.IEngineService; 031import org.apache.tapestry.engine.ILink; 032import org.apache.tapestry.engine.ServiceEncoder; 033import org.apache.tapestry.engine.ServiceEncoding; 034import org.apache.tapestry.engine.ServiceEncodingImpl; 035import org.apache.tapestry.record.PropertyPersistenceStrategySource; 036import org.apache.tapestry.services.DataSqueezer; 037import org.apache.tapestry.services.LinkFactory; 038import org.apache.tapestry.services.ServiceConstants; 039import org.apache.tapestry.web.WebRequest; 040 041/** 042 * @author Howard M. Lewis Ship 043 * @since 4.0 044 */ 045public 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}