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
015 package org.apache.tapestry5.internal.services;
016
017 import org.apache.tapestry5.SymbolConstants;
018 import org.apache.tapestry5.internal.util.Holder;
019 import org.apache.tapestry5.ioc.Invokable;
020 import org.apache.tapestry5.ioc.annotations.IntermediateType;
021 import org.apache.tapestry5.ioc.annotations.Symbol;
022 import org.apache.tapestry5.ioc.internal.util.ConcurrentBarrier;
023 import org.apache.tapestry5.ioc.util.TimeInterval;
024 import org.apache.tapestry5.services.*;
025
026 import java.io.IOException;
027 import 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 */
034 public 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 }