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    }