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 }