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.engine;
016
017import java.io.IOException;
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.util.Defense;
023import org.apache.tapestry.IComponent;
024import org.apache.tapestry.IDirect;
025import org.apache.tapestry.IPage;
026import org.apache.tapestry.IRequestCycle;
027import org.apache.tapestry.StaleSessionException;
028import org.apache.tapestry.Tapestry;
029import org.apache.tapestry.services.LinkFactory;
030import org.apache.tapestry.services.ResponseRenderer;
031import org.apache.tapestry.services.ServiceConstants;
032import org.apache.tapestry.web.WebRequest;
033import org.apache.tapestry.web.WebSession;
034
035/**
036 * Implementation of the direct service, which encodes the page and component id in the service
037 * context, and passes application-defined parameters as well.
038 * 
039 * @author Howard Lewis Ship
040 * @since 1.0.9
041 */
042
043public class DirectService implements IEngineService
044{
045    /** @since 4.0 */
046    protected ResponseRenderer _responseRenderer;
047
048    /** @since 4.0 */
049    protected LinkFactory _linkFactory;
050
051    /** @since 4.0 */
052    protected WebRequest _request;
053
054    /** @since 4.0 */
055    private IRequestCycle _requestCycle;
056
057    public ILink getLink(boolean post, Object parameter)
058    {
059        Defense.isAssignable(parameter, DirectServiceParameter.class, "parameter");
060
061        DirectServiceParameter dsp = (DirectServiceParameter) parameter;
062
063        IComponent component = dsp.getDirect();
064
065        // New since 1.0.1, we use the component to determine
066        // the page, not the cycle. Through the use of tricky
067        // things such as Block/InsertBlock, it is possible
068        // that a component from a page different than
069        // the response page will render.
070        // In 1.0.6, we start to record *both* the render page
071        // and the component page (if different).
072
073        IPage activePage = _requestCycle.getPage();
074        IPage componentPage = component.getPage();
075
076        Map parameters = new HashMap();
077
078        boolean stateful = _request.getSession(false) != null;
079
080        parameters.put(ServiceConstants.PAGE, activePage.getPageName());
081        parameters.put(ServiceConstants.COMPONENT, component.getIdPath());
082        parameters.put(ServiceConstants.CONTAINER, componentPage == activePage ? null
083                : componentPage.getPageName());
084        parameters.put(ServiceConstants.SESSION, stateful ? "T" : null);
085        parameters.put(ServiceConstants.PARAMETER, dsp.getServiceParameters());
086
087        return _linkFactory.constructLink(this, post, parameters, true);
088    }
089
090    public void service(IRequestCycle cycle) throws IOException
091    {
092        String componentId = cycle.getParameter(ServiceConstants.COMPONENT);
093        String componentPageName = cycle.getParameter(ServiceConstants.CONTAINER);
094        String activePageName = cycle.getParameter(ServiceConstants.PAGE);
095        boolean activeSession = cycle.getParameter(ServiceConstants.SESSION) != null;
096
097        IPage page = cycle.getPage(activePageName);
098
099        cycle.activate(page);
100
101        IPage componentPage = componentPageName == null ? page : cycle.getPage(componentPageName);
102
103        IComponent component = componentPage.getNestedComponent(componentId);
104
105        IDirect direct = null;
106
107        try
108        {
109            direct = (IDirect) component;
110        }
111        catch (ClassCastException ex)
112        {
113            throw new ApplicationRuntimeException(EngineMessages.wrongComponentType(
114                    component,
115                    IDirect.class), component, null, ex);
116        }
117
118        // Check for a StaleSession only when the session was stateful when
119        // the link was created.
120
121        if (activeSession && direct.isStateful())
122        {
123            WebSession session = _request.getSession(false);
124
125            if (session == null || session.isNew())
126                throw new StaleSessionException(EngineMessages.requestStateSession(direct),
127                        componentPage);
128        }
129
130        Object[] parameters = _linkFactory.extractListenerParameters(cycle);
131
132        triggerComponent(cycle, direct, parameters);
133
134        // Render the response. This will be the active page
135        // unless the direct component (or its delegate) changes it.
136
137        _responseRenderer.renderResponse(cycle);
138    }
139
140    /** @since 4.0 */
141
142    protected void triggerComponent(IRequestCycle cycle, IDirect direct, Object[] parameters)
143    {
144        cycle.setListenerParameters(parameters);
145
146        direct.trigger(cycle);
147    }
148
149    public String getName()
150    {
151        return Tapestry.DIRECT_SERVICE;
152    }
153
154    /** @since 4.0 */
155    public void setResponseRenderer(ResponseRenderer responseRenderer)
156    {
157        _responseRenderer = responseRenderer;
158    }
159
160    /** @since 4.0 */
161    public void setLinkFactory(LinkFactory linkFactory)
162    {
163        _linkFactory = linkFactory;
164    }
165
166    /** @since 4.0 */
167    public void setRequest(WebRequest request)
168    {
169        _request = request;
170    }
171
172    /** @since 4.0 */
173    public void setRequestCycle(IRequestCycle requestCycle)
174    {
175        _requestCycle = requestCycle;
176    }
177}