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 java.io.IOException; 018 import java.util.HashMap; 019 import java.util.Map; 020 021 import org.apache.hivemind.ApplicationRuntimeException; 022 import org.apache.hivemind.util.Defense; 023 import org.apache.tapestry.IAction; 024 import org.apache.tapestry.IComponent; 025 import org.apache.tapestry.IPage; 026 import org.apache.tapestry.IRequestCycle; 027 import org.apache.tapestry.StaleSessionException; 028 import org.apache.tapestry.Tapestry; 029 import org.apache.tapestry.services.LinkFactory; 030 import org.apache.tapestry.services.ResponseRenderer; 031 import org.apache.tapestry.services.ServiceConstants; 032 import org.apache.tapestry.web.WebRequest; 033 import org.apache.tapestry.web.WebSession; 034 035 /** 036 * A context-sensitive service related to {@link org.apache.tapestry.form.Form}and 037 * {@link org.apache.tapestry.link.ActionLink}. Encodes the page, component and an action id in the 038 * service context. 039 * 040 * @author Howard Lewis Ship 041 * @since 1.0.9 042 */ 043 044 public class ActionService implements IEngineService 045 { 046 /** @since 4.0 */ 047 private ResponseRenderer _responseRenderer; 048 049 /** @since 4.0 */ 050 private LinkFactory _linkFactory; 051 052 /** @since 4.0 */ 053 private static final String ACTION = "action"; 054 055 /** @since 4.0 */ 056 private WebRequest _request; 057 058 public ILink getLink(IRequestCycle cycle, boolean post, Object parameter) 059 { 060 Defense.isAssignable(parameter, ActionServiceParameter.class, "parameter"); 061 062 ActionServiceParameter asp = (ActionServiceParameter) parameter; 063 064 IComponent component = asp.getComponent(); 065 IPage activePage = cycle.getPage(); 066 IPage componentPage = component.getPage(); 067 068 Map parameters = new HashMap(); 069 070 boolean stateful = _request.getSession(false) != null; 071 072 parameters.put(ServiceConstants.SERVICE, getName()); 073 parameters.put(ServiceConstants.COMPONENT, component.getIdPath()); 074 parameters.put(ServiceConstants.PAGE, activePage.getPageName()); 075 parameters.put(ServiceConstants.CONTAINER, activePage == componentPage ? null 076 : componentPage.getPageName()); 077 parameters.put(ACTION, asp.getActionId()); 078 parameters.put(ServiceConstants.SESSION, stateful ? "T" : null); 079 080 return _linkFactory.constructLink(cycle, post, parameters, true); 081 } 082 083 public void service(IRequestCycle cycle) throws IOException 084 { 085 String componentId = cycle.getParameter(ServiceConstants.COMPONENT); 086 String componentPageName = cycle.getParameter(ServiceConstants.CONTAINER); 087 String activePageName = cycle.getParameter(ServiceConstants.PAGE); 088 String actionId = cycle.getParameter(ACTION); 089 boolean activeSession = cycle.getParameter(ServiceConstants.SESSION) != null; 090 091 IPage page = cycle.getPage(activePageName); 092 093 // Setup the page for the rewind, then do the rewind. 094 095 cycle.activate(page); 096 097 IPage componentPage = componentPageName == null ? page : cycle.getPage(componentPageName); 098 099 IComponent component = componentPage.getNestedComponent(componentId); 100 101 IAction action = null; 102 103 try 104 { 105 action = (IAction) component; 106 } 107 catch (ClassCastException ex) 108 { 109 throw new ApplicationRuntimeException(EngineMessages.wrongComponentType( 110 component, 111 IAction.class), component, null, ex); 112 } 113 114 // Only perform the stateful check if the application was stateful 115 // when the URL was rendered. 116 117 if (activeSession && action.getRequiresSession()) 118 { 119 WebSession session = _request.getSession(false); 120 121 if (session == null || session.isNew()) 122 throw new StaleSessionException(EngineMessages.requestStateSession(component), 123 componentPage); 124 125 } 126 127 cycle.rewindPage(actionId, action); 128 129 // During the rewind, a component may change the page. This will take 130 // effect during the second render, which renders the HTML response. 131 132 // Render that response. 133 134 _responseRenderer.renderResponse(cycle); 135 } 136 137 public String getName() 138 { 139 return Tapestry.ACTION_SERVICE; 140 } 141 142 /** @since 4.0 */ 143 public void setResponseRenderer(ResponseRenderer responseRenderer) 144 { 145 _responseRenderer = responseRenderer; 146 } 147 148 /** @since 4.0 */ 149 public void setLinkFactory(LinkFactory linkFactory) 150 { 151 _linkFactory = linkFactory; 152 } 153 154 /** @since 4.0 */ 155 public void setRequest(WebRequest request) 156 { 157 _request = request; 158 } 159 }