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