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 event service, which encodes the page and component id in the service
033     * context, and passes application-defined parameters as well.
034     * 
035     * @author jkuhnert
036     * @since 4.1
037     */
038    
039    public class DirectEventService implements IEngineService
040    {   
041        /** @since 4.0 */
042        private ResponseRenderer _responseRenderer;
043    
044        /** @since 4.0 */
045        private LinkFactory _linkFactory;
046    
047        /** @since 4.0 */
048        private 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, DirectEventServiceParameter.class, "parameter");
056    
057            DirectEventServiceParameter dsp = (DirectEventServiceParameter) parameter;
058    
059            IComponent component = dsp.getDirect();
060    
061            // New since 1.0.1, we use the component to determine
062            // the page, not the cycle. Through the use of tricky
063            // things such as Block/InsertBlock, it is possible
064            // that a component from a page different than
065            // the response page will render.
066            // In 1.0.6, we start to record *both* the render page
067            // and the component page (if different).
068    
069            IPage activePage = _requestCycle.getPage();
070            IPage componentPage = component.getPage();
071            
072            Map parameters = new HashMap();
073            
074            boolean stateful = _request.getSession(false) != null;
075            
076            parameters.put(ServiceConstants.PAGE, activePage.getPageName());
077            parameters.put(ServiceConstants.COMPONENT, component.getIdPath());
078            parameters.put(ServiceConstants.CONTAINER, componentPage == activePage ? null : componentPage.getPageName());
079            parameters.put(ServiceConstants.SESSION, stateful ? "T" : null);
080    
081            if (dsp.getUpdateParts() != null && dsp.getUpdateParts().length > 0)
082            {
083                parameters.put(ServiceConstants.UPDATE_PARTS, dsp.getUpdateParts());
084            }
085    
086            if (dsp.isJSON())
087            {
088                parameters.put("json", String.valueOf(dsp.isJSON()));
089            }
090            
091            parameters.put(ServiceConstants.PARAMETER, dsp.getServiceParameters());
092            
093            return _linkFactory.constructLink(this, post, parameters, true);
094        }
095    
096        public void service(IRequestCycle cycle) throws IOException
097        {
098            String componentId = cycle.getParameter(ServiceConstants.COMPONENT);
099            String componentPageName = cycle.getParameter(ServiceConstants.CONTAINER);
100            String activePageName = cycle.getParameter(ServiceConstants.PAGE);
101            boolean activeSession = cycle.getParameter(ServiceConstants.SESSION) != null;
102            
103            IPage page = cycle.getPage(activePageName);
104    
105            cycle.activate(page);
106    
107            IPage componentPage = componentPageName == null ? page : cycle.getPage(componentPageName);
108    
109            IComponent component = componentPage.getNestedComponent(componentId);
110    
111            IDirectEvent direct = null;
112    
113            try
114            {
115                direct = (IDirectEvent) component;
116            }
117            catch (ClassCastException ex)
118            {
119                throw new ApplicationRuntimeException(EngineMessages.wrongComponentType(
120                        component,
121                        IDirectEvent.class), component, null, ex);
122            }
123    
124            // Check for a StaleSession only when the session was stateful when
125            // the link was created.
126    
127            if (activeSession && direct.isStateful())
128            {
129                WebSession session = _request.getSession(false);
130    
131                if (session == null || session.isNew())
132                    throw new StaleSessionException(EngineMessages.requestStateSession(direct), componentPage);
133            }
134            
135            Object[] parameters = _linkFactory.extractListenerParameters(cycle);
136            
137            triggerComponent(cycle, direct, parameters);
138            
139            // Render the response. This will be the active page
140            // unless the direct component (or its delegate) changes it.
141            
142            _responseRenderer.renderResponse(cycle);
143        }
144    
145        /** @since 4.0 */
146    
147        protected void triggerComponent(IRequestCycle cycle, IDirectEvent direct, Object[] parameters)
148        {
149            if (!BrowserEvent.hasBrowserEvent(cycle))
150                throw new ApplicationRuntimeException(EngineMessages.noBrowserEvent());
151            
152            BrowserEvent event = new BrowserEvent(cycle);
153            
154            Object[] parms = new Object[parameters.length + 1];
155            System.arraycopy(parameters, 0, parms, 0, parameters.length);
156            parms[parms.length - 1] = event;
157            
158            cycle.setListenerParameters(parms);
159            
160            direct.triggerEvent(cycle, event);
161        }
162    
163        public String getName()
164        {
165            return Tapestry.DIRECT_EVENT_SERVICE;
166        }
167    
168        /** @since 4.0 */
169        public void setResponseRenderer(ResponseRenderer responseRenderer)
170        {
171            _responseRenderer = responseRenderer;
172        }
173    
174        /** @since 4.0 */
175        public void setLinkFactory(LinkFactory linkFactory)
176        {
177            _linkFactory = linkFactory;
178        }
179    
180        /** @since 4.0 */
181        public void setRequest(WebRequest request)
182        {
183            _request = request;
184        }
185    
186        /** @since 4.0 */
187        public void setRequestCycle(IRequestCycle requestCycle)
188        {
189            _requestCycle = requestCycle;
190        }
191    }