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