    001    // Copyright 2007, 2008, 2010 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.TrackableComponentEventCallback;
    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    @SuppressWarnings("unchecked")
    034    public class AjaxComponentEventRequestHandler implements ComponentEventRequestHandler
    035    {
    036        private final RequestPageCache cache;
    037
    038        private final Request request;
    039
    040        private final PageRenderQueue queue;
    041
    042        private final ComponentEventResultProcessor resultProcessor;
    043
    044        private final PageContentTypeAnalyzer pageContentTypeAnalyzer;
    045
    046        private final Environment environment;
    047
    048        private final AjaxPartialResponseRenderer partialRenderer;
    049
    050        private final PageActivator pageActivator;
    051
    052        public AjaxComponentEventRequestHandler(RequestPageCache cache, Request request, PageRenderQueue queue, @Ajax
    053        ComponentEventResultProcessor resultProcessor, PageActivator pageActivator,
    054                                                PageContentTypeAnalyzer pageContentTypeAnalyzer, Environment environment,
    055                                                AjaxPartialResponseRenderer partialRenderer)
    056        {
    057            this.cache = cache;
    058            this.queue = queue;
    059            this.resultProcessor = resultProcessor;
    060            this.pageActivator = pageActivator;
    061            this.pageContentTypeAnalyzer = pageContentTypeAnalyzer;
    062            this.request = request;
    063            this.environment = environment;
    064            this.partialRenderer = partialRenderer;
    065        }
    066
    067        public void handle(ComponentEventRequestParameters parameters) throws IOException
    068        {
    069            Page activePage = cache.get(parameters.getActivePageName());
    070
    071            final Holder<Boolean> resultProcessorInvoked = Holder.create();
    072            resultProcessorInvoked.put(false);
    073
    074            ComponentEventResultProcessor interceptor = new ComponentEventResultProcessor()
    075            {
    076                public void processResultValue(Object value) throws IOException
    077                {
    078                    resultProcessorInvoked.put(true);
    079
    080                    resultProcessor.processResultValue(value);
    081                }
    082            };
    083
    084            // If we end up doing a partial render, the page render queue service needs to know the
    085            // page that will be rendered (for logging purposes, if nothing else).
    086
    087            queue.setRenderingPage(activePage);
    088
    089            if (pageActivator.activatePage(activePage.getRootElement().getComponentResources(), parameters
    090                    .getPageActivationContext(), interceptor))
    091                return;
    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            TrackableComponentEventCallback callback = new ComponentResultProcessorWrapper(interceptor);
    106
    107            environment.push(ComponentEventResultProcessor.class, interceptor);
    108            environment.push(TrackableComponentEventCallback.class, callback);
    109
    110            boolean handled = element
    111                    .triggerContextEvent(parameters.getEventType(), parameters.getEventContext(), callback);
    112
    113            if (!handled)
    114                throw new TapestryException(ServicesMessages.eventNotHandled(element, parameters.getEventType()), element,
    115                        null);
    116
    117            environment.pop(TrackableComponentEventCallback.class);
    118            environment.pop(ComponentEventResultProcessor.class);
    119
    120
    121            // If the result processor was passed a value, then it will already have rendered. Otherwise it was not passed a value,
    122            // but it's still possible that we still want to do a partial page render ... if filters were added to the render queue.
    123            // In that event, run the partial page render now and return.
    124
    125            boolean wasInvoked = resultProcessorInvoked.get();
    126
    127            if ((!wasInvoked) && queue.isPartialRenderInitialized())
    128            {
    129                partialRenderer.renderPartialPageMarkup();
    130                return;
    131            }
    132
    133            // If the result processor was passed a value, then it will already have rendered, and there is nothing more to do.
    134
    135            if (wasInvoked) { return; }
    136
    137            // Send an empty JSON reply if no value was returned from the component event handler method.
    138            // This is the typical behavior when an Ajax component event handler returns null.
    139
    140            JSONObject reply = new JSONObject();
    141
    142            resultProcessor.processResultValue(reply);
    143        }
    144    }