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 }