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
015package org.apache.tapestry.services.impl;
016
017import java.util.Iterator;
018import java.util.List;
019import java.util.Map;
020
021import org.apache.commons.codec.net.URLCodec;
022import org.apache.hivemind.ApplicationRuntimeException;
023import org.apache.hivemind.ErrorLog;
024import org.apache.hivemind.order.Orderer;
025import org.apache.hivemind.util.Defense;
026import org.apache.tapestry.IEngine;
027import org.apache.tapestry.IRequestCycle;
028import org.apache.tapestry.Tapestry;
029import org.apache.tapestry.engine.EngineServiceLink;
030import org.apache.tapestry.engine.IEngineService;
031import org.apache.tapestry.engine.ILink;
032import org.apache.tapestry.engine.ServiceEncoder;
033import org.apache.tapestry.engine.ServiceEncoding;
034import org.apache.tapestry.engine.ServiceEncodingImpl;
035import org.apache.tapestry.record.PropertyPersistenceStrategySource;
036import org.apache.tapestry.services.DataSqueezer;
037import org.apache.tapestry.services.LinkFactory;
038import org.apache.tapestry.services.ServiceConstants;
039import org.apache.tapestry.web.WebRequest;
040
041/**
042 * @author Howard M. Lewis Ship
043 * @since 4.0
044 */
045public 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}