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 org.apache.hivemind.ApplicationRuntimeException;
018    import org.apache.hivemind.util.Defense;
019    import org.apache.tapestry.*;
020    import org.apache.tapestry.event.BrowserEvent;
021    import org.apache.tapestry.services.LinkFactory;
022    import org.apache.tapestry.services.ResponseRenderer;
023    import org.apache.tapestry.services.ServiceConstants;
024    import org.apache.tapestry.web.WebRequest;
025    import org.apache.tapestry.web.WebSession;
026    
027    import java.io.IOException;
028    import java.util.HashMap;
029    import java.util.Map;
030    
031    /**
032     * Implementation of the direct service, which encodes the page and component id in the service
033     * context, and passes application-defined parameters as well.
034     * 
035     * @author Howard Lewis Ship
036     * @since 1.0.9
037     */
038    
039    public class DirectService implements IEngineService
040    {
041        /** @since 4.0 */
042        protected ResponseRenderer _responseRenderer;
043    
044        /** @since 4.0 */
045        protected LinkFactory _linkFactory;
046    
047        /** @since 4.0 */
048        protected WebRequest _request;
049    
050        /** @since 4.0 */
051        private IRequestCycle _requestCycle;
052    
053        public ILink getLink(boolean post, Object parameter)
054        {
055            Defense.isAssignable(parameter, DirectServiceParameter.class, "parameter");
056    
057            DirectServiceParameter dsp = (DirectServiceParameter) parameter;
058    
059            IComponent component = dsp.getDirect();
060            IDirect direct = 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 = _requestCycle.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.PAGE, activePage.getPageName());
078            parameters.put(ServiceConstants.COMPONENT, component.getIdPath());
079            parameters.put(ServiceConstants.CONTAINER, componentPage == activePage ? null
080                    : componentPage.getPageName());
081            parameters.put(ServiceConstants.SESSION, stateful ? "T" : null);
082            
083            // handle dynamic XHR/JSON parameters
084            if (dsp.isAsync() || dsp.isJson()) {
085                
086                if (dsp.getUpdateParts() != null && dsp.getUpdateParts().length > 0)
087                    parameters.put(ServiceConstants.UPDATE_PARTS, dsp.getUpdateParts());
088                
089                if (dsp.isJson())
090                    parameters.put("json", String.valueOf(dsp.isJson()));
091                
092                // don't need to put isAsync parameter in as that is handled via http headers
093            }
094            
095            parameters.put(ServiceConstants.PARAMETER, dsp.getServiceParameters());
096            
097            return _linkFactory.constructLink(this, post, parameters, direct.isStateful());
098        }
099    
100        public void service(IRequestCycle cycle) throws IOException
101        {
102            String componentId = cycle.getParameter(ServiceConstants.COMPONENT);
103            String componentPageName = cycle.getParameter(ServiceConstants.CONTAINER);
104            String activePageName = cycle.getParameter(ServiceConstants.PAGE);
105            boolean activeSession = cycle.getParameter(ServiceConstants.SESSION) != null;
106            
107            IPage page = cycle.getPage(activePageName);
108    
109            cycle.activate(page);
110    
111            IPage componentPage = componentPageName == null ? page : cycle.getPage(componentPageName);
112    
113            IComponent component = componentPage.getNestedComponent(componentId);
114    
115            IDirect direct = null;
116    
117            try
118            {
119                direct = (IDirect) component;
120            }
121            catch (ClassCastException ex)
122            {
123                throw new ApplicationRuntimeException(EngineMessages.wrongComponentType(
124                        component,
125                        IDirect.class), component, null, ex);
126            }
127    
128            // Check for a StaleSession only when the session was stateful when
129            // the link was created.
130    
131            if (activeSession && direct.isStateful())
132            {
133                WebSession session = _request.getSession(false);
134    
135                if (session == null || session.isNew())
136                    throw new StaleSessionException(EngineMessages.requestStateSession(direct),
137                            componentPage);
138            }
139    
140            Object[] parameters = _linkFactory.extractListenerParameters(cycle);
141    
142            triggerComponent(cycle, direct, parameters);
143    
144            // Render the response. This will be the active page
145            // unless the direct component (or its delegate) changes it.
146    
147            _responseRenderer.renderResponse(cycle);
148        }
149    
150        /** @since 4.0 */
151    
152        protected void triggerComponent(IRequestCycle cycle, IDirect direct, Object[] parameters)
153        {
154            if (BrowserEvent.hasBrowserEvent(cycle))
155            {    
156                BrowserEvent event = new BrowserEvent(cycle);
157                
158                Object[] parms = new Object[parameters.length + 1];
159                System.arraycopy(parameters, 0, parms, 0, parameters.length);
160                parms[parms.length - 1] = event;
161                
162                cycle.setListenerParameters(parms);
163            } else
164                cycle.setListenerParameters(parameters);
165            
166            direct.trigger(cycle);
167        }
168    
169        public String getName()
170        {
171            return Tapestry.DIRECT_SERVICE;
172        }
173    
174        /** @since 4.0 */
175        public void setResponseRenderer(ResponseRenderer responseRenderer)
176        {
177            _responseRenderer = responseRenderer;
178        }
179    
180        /** @since 4.0 */
181        public void setLinkFactory(LinkFactory linkFactory)
182        {
183            _linkFactory = linkFactory;
184        }
185    
186        /** @since 4.0 */
187        public void setRequest(WebRequest request)
188        {
189            _request = request;
190        }
191    
192        /** @since 4.0 */
193        public void setRequestCycle(IRequestCycle requestCycle)
194        {
195            _requestCycle = requestCycle;
196        }
197    }