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 }