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            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    }