    001// Copyright 2010-2013 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
    015package org.apache.tapestry5.internal.services;
    016
    017import java.util.Collections;
    018import java.util.List;
    019import java.util.Map;
    020
    021import org.apache.tapestry5.commons.services.InvalidationEventHub;
    022import org.apache.tapestry5.commons.util.CollectionFactory;
    023import org.apache.tapestry5.commons.util.ExceptionUtils;
    024import org.apache.tapestry5.http.services.RequestGlobals;
    025import org.apache.tapestry5.internal.InternalConstants;
    026import org.apache.tapestry5.internal.structure.Page;
    027import org.apache.tapestry5.ioc.ScopeConstants;
    028import org.apache.tapestry5.ioc.annotations.ComponentClasses;
    029import org.apache.tapestry5.ioc.annotations.PostInjection;
    030import org.apache.tapestry5.ioc.annotations.Scope;
    031import org.apache.tapestry5.ioc.services.PerthreadManager;
    032import org.apache.tapestry5.services.ComponentClassResolver;
    033import org.slf4j.Logger;
    034
    035/**
    036 * In Tapestry 5.1, the implementation of this worked with the page pool (a pool of page instances, reserved
    037 * to individual requests/threads). Page pooling was deprecated in 5.2 and removed in 5.3.
    038 *
    039 * @since 5.2
    040 */
    041@Scope(ScopeConstants.PERTHREAD)
    042public class RequestPageCacheImpl implements RequestPageCache, Runnable
    043
    044/// This should have a listener too!
    045{
    046    private final Logger logger;
    047
    048    private final ComponentClassResolver resolver;
    049
    050    private final PageSource pageSource;
    051
    052    private final RequestGlobals requestGlobals;
    053
    054    private final Map<String, Page> cache = CollectionFactory.newMap();
    055
    056    public RequestPageCacheImpl(Logger logger, ComponentClassResolver resolver,
    057            PageSource pageSource, RequestGlobals requestGlobals)
    058    {
    059        this.logger = logger;
    060        this.resolver = resolver;
    061        this.pageSource = pageSource;
    062        this.requestGlobals = requestGlobals;
    063    }
    064
    065    @PostInjection
    066    public void listenForThreadCleanup(PerthreadManager perthreadManager,
    067            @ComponentClasses InvalidationEventHub classesHub)
    068    {
    069        perthreadManager.addThreadCleanupCallback(this);
    070        classesHub.addInvalidationCallback(this::listen);
    071    }
    072
    073    public void run()
    074    {
    075        for (Page page : cache.values())
    076        {
    077            try
    078            {
    079                page.detached();
    080            } catch (Throwable t)
    081            {
    082                logger.error("Error detaching page {}: {}", page, ExceptionUtils.toMessage(t), t);
    083            }
    084        }
    085    }
    086
    087    public Page get(String pageName)
    088    {
    089        String canonical = resolver.canonicalizePageName(pageName);
    090
    091        Page page = cache.get(canonical);
    092
    093        if (page == null)
    094        {
    095            page = pageSource.getPage(canonical);
    096
    097            try
    098            {
    099                page.attached();
    100            } catch (Throwable t)
    101            {
    102                throw new RuntimeException(String.format("Unable to attach page %s: %s", canonical,
    103                        ExceptionUtils.toMessage(t)), t);
    104            }
    105
    106            cache.put(canonical, page);
    107        }
    108
    109        // A bit of a hack but whatever.
    110        if (canonical.equals(requestGlobals.getActivePageName()))
    111        {
    112            requestGlobals.getRequest().setAttribute(InternalConstants.ACTIVE_PAGE_LOADED, true);
    113        }
    114
    115        return page;
    116    }
    117
    118    private List<String> listen(List<String> resources)
    119    {
    120        // TODO: we probably don't need this anymore
    121        for (String resource : resources)
    122        {
    123            if (resolver.isPage(resource))
    124            {
    125                final String canonicalName = resolver.canonicalizePageName(
    126                        resolver.getLogicalName(resource));
    127                cache.remove(canonicalName);
    128            }
    129        }
    130        return Collections.emptyList();
    131    }
    132
    133}