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}