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    }