001 // Copyright 2007, 2008 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.tapestry5.internal.services;
016
017 import org.apache.tapestry5.ContentType;
018 import org.apache.tapestry5.EventConstants;
019 import org.apache.tapestry5.internal.InternalConstants;
020 import org.apache.tapestry5.internal.structure.ComponentPageElement;
021 import org.apache.tapestry5.internal.structure.Page;
022 import org.apache.tapestry5.internal.util.Holder;
023 import org.apache.tapestry5.ioc.internal.util.TapestryException;
024 import org.apache.tapestry5.json.JSONObject;
025 import org.apache.tapestry5.services.*;
026
027 import java.io.IOException;
028
029 /**
030 * Similar to {@link ComponentEventRequestHandlerImpl}, but built around the Ajax request cycle, where the action
031 * request sends back an immediate JSON response containing the new content.
032 */
033 public class AjaxComponentEventRequestHandler implements ComponentEventRequestHandler
034 {
035 private final RequestPageCache cache;
036
037 private final Request request;
038
039 private final PageRenderQueue queue;
040
041 private final ComponentEventResultProcessor resultProcessor;
042
043 private final PageContentTypeAnalyzer pageContentTypeAnalyzer;
044
045 private final Environment environment;
046
047 private final AjaxPartialResponseRenderer partialRenderer;
048
049 public AjaxComponentEventRequestHandler(RequestPageCache cache, Request request, PageRenderQueue queue,
050 @Ajax ComponentEventResultProcessor resultProcessor,
051 PageContentTypeAnalyzer pageContentTypeAnalyzer, Environment environment,
052 AjaxPartialResponseRenderer partialRenderer)
053 {
054 this.cache = cache;
055 this.queue = queue;
056 this.resultProcessor = resultProcessor;
057 this.pageContentTypeAnalyzer = pageContentTypeAnalyzer;
058 this.request = request;
059 this.environment = environment;
060 this.partialRenderer = partialRenderer;
061 }
062
063 public void handle(ComponentEventRequestParameters parameters) throws IOException
064 {
065 Page activePage = cache.get(parameters.getActivePageName());
066
067 final Holder<Boolean> resultProcessorInvoked = Holder.create();
068 resultProcessorInvoked.put(false);
069
070 ComponentEventResultProcessor interceptor = new ComponentEventResultProcessor()
071 {
072 public void processResultValue(Object value) throws IOException
073 {
074 resultProcessorInvoked.put(true);
075
076 resultProcessor.processResultValue(value);
077 }
078 };
079
080 ComponentResultProcessorWrapper callback = new ComponentResultProcessorWrapper(interceptor);
081
082 activePage.getRootElement().triggerContextEvent(EventConstants.ACTIVATE,
083 parameters.getPageActivationContext(), callback);
084
085
086 if (callback.isAborted()) return;
087
088 // If we end up doing a partial render, the page render queue service needs to know the
089 // page that will be rendered (for logging purposes, if nothing else).
090
091 queue.setRenderingPage(activePage);
092
093 ContentType contentType = pageContentTypeAnalyzer.findContentType(activePage);
094
095 request.setAttribute(InternalConstants.CONTENT_TYPE_ATTRIBUTE_NAME, contentType);
096
097 Page containerPage = cache.get(parameters.getContainingPageName());
098
099 ComponentPageElement element = containerPage.getComponentElementByNestedId(parameters.getNestedComponentId());
100
101 // In many cases, the triggered element is a Form that needs to be able to
102 // pass its event handler return values to the correct result processor.
103 // This is certainly the case for forms.
104
105 environment.push(ComponentEventResultProcessor.class, interceptor);
106
107 boolean handled = element.triggerContextEvent(parameters.getEventType(), parameters.getEventContext(),
108 callback);
109
110 if (!handled)
111 throw new TapestryException(ServicesMessages.eventNotHandled(element, parameters.getEventType()), element,
112 null);
113
114 environment.pop(ComponentEventResultProcessor.class);
115
116 if (queue.isPartialRenderInitialized())
117 {
118 partialRenderer.renderPartialPageMarkup();
119 return;
120 }
121
122 // If some other form of return value that's not a partial page render was send through to the
123 // Ajax ComponentEventResultProcessor, then there's nothing more to do.
124
125 if (resultProcessorInvoked.get()) return;
126
127 // Send an empty JSON reply if no value was returned from the component event handler method.
128
129 JSONObject reply = new JSONObject();
130
131 resultProcessor.processResultValue(reply);
132 }
133 }