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        /** @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    }