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