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 Defense.notNull(service, "service"); 104 Defense.notNull(parameters, "parameters"); 105 106 String serviceName = service.getName(); 107 108 if (serviceName == null) 109 throw new ApplicationRuntimeException(ImplMessages.serviceNameIsNull()); 110 111 parameters.put(ServiceConstants.SERVICE, serviceName); 112 113 squeezeServiceParameters(parameters); 114 115 IEngine engine = _requestCycle.getEngine(); 116 117 ServiceEncoding serviceEncoding = createServiceEncoding(parameters); 118 119 // Give persistent property strategies a chance to store extra data 120 // into the link. 121 122 if (stateful) 123 _persistenceStrategySource.addParametersForPersistentProperties(serviceEncoding, post); 124 125 String fullServletPath = _contextPath + serviceEncoding.getServletPath(); 126 127 return new EngineServiceLink(_requestCycle, fullServletPath, engine.getOutputEncoding(), 128 _codec, _request, parameters, stateful); 129 } 130 131 public ServiceEncoder[] getServiceEncoders() 132 { 133 return _encoders; 134 } 135 136 /** 137 * Creates a new service encoding, and allows the encoders to modify it before returning. 138 */ 139 140 private ServiceEncoding createServiceEncoding(Map parameters) 141 { 142 ServiceEncodingImpl result = new ServiceEncodingImpl(_servletPath, parameters); 143 144 for (int i = 0; i < _encoders.length; i++) 145 { 146 _encoders[i].encode(result); 147 148 if (result.isModified()) 149 break; 150 } 151 152 return result; 153 } 154 155 protected void squeezeServiceParameters(Map parameters) 156 { 157 Object[] serviceParameters = (Object[]) parameters.get(ServiceConstants.PARAMETER); 158 159 if (serviceParameters == null) 160 return; 161 162 String[] squeezed = squeeze(serviceParameters); 163 164 parameters.put(ServiceConstants.PARAMETER, squeezed); 165 } 166 167 public Object[] extractListenerParameters(IRequestCycle cycle) 168 { 169 String[] squeezed = cycle.getParameters(ServiceConstants.PARAMETER); 170 171 if (Tapestry.size(squeezed) == 0) 172 return EMPTY; 173 174 try 175 { 176 return _dataSqueezer.unsqueeze(squeezed); 177 } 178 catch (Exception ex) 179 { 180 throw new ApplicationRuntimeException(ex); 181 } 182 } 183 184 private String[] squeeze(Object[] input) 185 { 186 try 187 { 188 return _dataSqueezer.squeeze(input); 189 } 190 catch (Exception ex) 191 { 192 throw new ApplicationRuntimeException(ex); 193 } 194 } 195 196 public void setDataSqueezer(DataSqueezer dataSqueezer) 197 { 198 _dataSqueezer = dataSqueezer; 199 } 200 201 public void setContributions(List contributions) 202 { 203 _contributions = contributions; 204 } 205 206 public void setErrorLog(ErrorLog errorLog) 207 { 208 _errorLog = errorLog; 209 } 210 211 public void setServletPath(String servletPath) 212 { 213 _servletPath = servletPath; 214 } 215 216 public void setContextPath(String contextPath) 217 { 218 _contextPath = contextPath; 219 } 220 221 public void setRequest(WebRequest request) 222 { 223 _request = request; 224 } 225 226 /** 227 * This is kind of limiting; it's possible that other things beyond persistence strategies will 228 * want to have a hand at encoding data into URLs. If that comes to pass, we'll need to 229 * implement an event coordinator/listener combo to let implementations know about links being 230 * generated. 231 */ 232 233 public void setPersistenceStrategySource( 234 PropertyPersistenceStrategySource persistenceStrategySource) 235 { 236 _persistenceStrategySource = persistenceStrategySource; 237 } 238 239 public void setRequestCycle(IRequestCycle requestCycle) 240 { 241 _requestCycle = requestCycle; 242 } 243 }