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    }