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 }