001// Copyright 2006, 2007, 2008, 2009, 2011 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 org.apache.tapestry5.SymbolConstants; 018import org.apache.tapestry5.beanmodel.services.*; 019import org.apache.tapestry5.commons.util.TimeInterval; 020import org.apache.tapestry5.http.services.Request; 021import org.apache.tapestry5.http.services.RequestFilter; 022import org.apache.tapestry5.http.services.RequestHandler; 023import org.apache.tapestry5.http.services.Response; 024import org.apache.tapestry5.internal.util.Holder; 025import org.apache.tapestry5.ioc.Invokable; 026import org.apache.tapestry5.ioc.annotations.IntermediateType; 027import org.apache.tapestry5.ioc.annotations.Symbol; 028import org.apache.tapestry5.ioc.internal.util.ConcurrentBarrier; 029import org.apache.tapestry5.ioc.services.UpdateListenerHub; 030 031import java.io.IOException; 032import java.util.concurrent.TimeUnit; 033 034/** 035 * Implements a barrier that periodically asks the {@link org.apache.tapestry5.ioc.services.UpdateListenerHub} to check for 036 * updates to files. The UpdateListenerHub is invoked from a write method, meaning that when it is called, all other 037 * threads will be blocked. 038 */ 039public class CheckForUpdatesFilter implements RequestFilter 040{ 041 private final long checkInterval; 042 043 private final long updateTimeout; 044 045 private final UpdateListenerHub updateListenerHub; 046 047 private final ConcurrentBarrier barrier = new ConcurrentBarrier(); 048 049 private final Runnable checker = new Runnable() 050 { 051 public void run() 052 { 053 // On a race condition, multiple threads may hit this method briefly. If we've 054 // already done a check, don't run it again. 055 056 if (System.currentTimeMillis() - lastCheck >= checkInterval) 057 { 058 059 // Fire the update event which will force a number of checks and then 060 // corresponding invalidation events. 061 062 updateListenerHub.fireCheckForUpdates(); 063 064 lastCheck = System.currentTimeMillis(); 065 } 066 } 067 }; 068 069 private long lastCheck = 0; 070 071 /** 072 * @param updateListenerHub 073 * invoked, at intervals, to spur the process of detecting changes 074 * @param checkInterval 075 * interval, in milliseconds, between checks 076 * @param updateTimeout 077 * time, in milliseconds, to wait to obtain update lock. 078 */ 079 public CheckForUpdatesFilter(UpdateListenerHub updateListenerHub, 080 081 @Symbol(SymbolConstants.FILE_CHECK_INTERVAL) 082 @IntermediateType(TimeInterval.class) 083 long checkInterval, 084 085 @Symbol(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT) 086 @IntermediateType(TimeInterval.class) 087 long updateTimeout) 088 { 089 this.updateListenerHub = updateListenerHub; 090 this.checkInterval = checkInterval; 091 this.updateTimeout = updateTimeout; 092 } 093 094 public boolean service(final Request request, final Response response, final RequestHandler handler) 095 throws IOException 096 { 097 final Holder<IOException> exceptionHolder = new Holder<IOException>(); 098 099 Invokable<Boolean> invokable = new Invokable<Boolean>() 100 { 101 public Boolean invoke() 102 { 103 if (System.currentTimeMillis() - lastCheck >= checkInterval) 104 barrier.tryWithWrite(checker, updateTimeout, TimeUnit.MILLISECONDS); 105 106 // And, now, back to code within the read lock. 107 108 try 109 { 110 return handler.service(request, response); 111 } 112 catch (IOException ex) 113 { 114 exceptionHolder.put(ex); 115 return false; 116 } 117 } 118 }; 119 120 // Obtain a read lock while handling the request. This will not impair parallel operations, except when a file 121 // check 122 // is needed (the exclusive write lock will block threads attempting to get a read lock). 123 124 boolean result = barrier.withRead(invokable); 125 126 if (exceptionHolder.hasValue()) 127 throw exceptionHolder.get(); 128 129 return result; 130 } 131}