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