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            Defense.notNull(service, "service");
104            Defense.notNull(parameters, "parameters");
105    
106            String serviceName = service.getName();
107    
108            if (serviceName == null)
109                throw new ApplicationRuntimeException(ImplMessages.serviceNameIsNull());
110    
111            parameters.put(ServiceConstants.SERVICE, serviceName);
112    
113            squeezeServiceParameters(parameters);
114    
115            IEngine engine = _requestCycle.getEngine();
116    
117            ServiceEncoding serviceEncoding = createServiceEncoding(parameters);
118    
119            // Give persistent property strategies a chance to store extra data
120            // into the link.
121    
122            if (stateful)
123                _persistenceStrategySource.addParametersForPersistentProperties(serviceEncoding, post);
124    
125            String fullServletPath = _contextPath + serviceEncoding.getServletPath();
126    
127            return new EngineServiceLink(_requestCycle, fullServletPath, engine.getOutputEncoding(),
128                    _codec, _request, parameters, stateful);
129        }
130    
131        public ServiceEncoder[] getServiceEncoders()
132        {
133            return _encoders;
134        }
135    
136        /**
137         * Creates a new service encoding, and allows the encoders to modify it before returning.
138         */
139    
140        private ServiceEncoding createServiceEncoding(Map parameters)
141        {
142            ServiceEncodingImpl result = new ServiceEncodingImpl(_servletPath, parameters);
143    
144            for (int i = 0; i < _encoders.length; i++)
145            {
146                _encoders[i].encode(result);
147    
148                if (result.isModified())
149                    break;
150            }
151    
152            return result;
153        }
154    
155        protected void squeezeServiceParameters(Map parameters)
156        {
157            Object[] serviceParameters = (Object[]) parameters.get(ServiceConstants.PARAMETER);
158    
159            if (serviceParameters == null)
160                return;
161    
162            String[] squeezed = squeeze(serviceParameters);
163    
164            parameters.put(ServiceConstants.PARAMETER, squeezed);
165        }
166    
167        public Object[] extractListenerParameters(IRequestCycle cycle)
168        {
169            String[] squeezed = cycle.getParameters(ServiceConstants.PARAMETER);
170    
171            if (Tapestry.size(squeezed) == 0)
172                return EMPTY;
173    
174            try
175            {
176                return _dataSqueezer.unsqueeze(squeezed);
177            }
178            catch (Exception ex)
179            {
180                throw new ApplicationRuntimeException(ex);
181            }
182        }
183    
184        private String[] squeeze(Object[] input)
185        {
186            try
187            {
188                return _dataSqueezer.squeeze(input);
189            }
190            catch (Exception ex)
191            {
192                throw new ApplicationRuntimeException(ex);
193            }
194        }
195    
196        public void setDataSqueezer(DataSqueezer dataSqueezer)
197        {
198            _dataSqueezer = dataSqueezer;
199        }
200    
201        public void setContributions(List contributions)
202        {
203            _contributions = contributions;
204        }
205    
206        public void setErrorLog(ErrorLog errorLog)
207        {
208            _errorLog = errorLog;
209        }
210    
211        public void setServletPath(String servletPath)
212        {
213            _servletPath = servletPath;
214        }
215    
216        public void setContextPath(String contextPath)
217        {
218            _contextPath = contextPath;
219        }
220    
221        public void setRequest(WebRequest request)
222        {
223            _request = request;
224        }
225    
226        /**
227         * This is kind of limiting; it's possible that other things beyond persistence strategies will
228         * want to have a hand at encoding data into URLs. If that comes to pass, we'll need to
229         * implement an event coordinator/listener combo to let implementations know about links being
230         * generated.
231         */
232    
233        public void setPersistenceStrategySource(
234                PropertyPersistenceStrategySource persistenceStrategySource)
235        {
236            _persistenceStrategySource = persistenceStrategySource;
237        }
238    
239        public void setRequestCycle(IRequestCycle requestCycle)
240        {
241            _requestCycle = requestCycle;
242        }
243    }