001// Copyright 2011, 2012 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.assets;
016
017import org.apache.tapestry5.commons.Resource;
018import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
019import org.apache.tapestry5.internal.event.InvalidationEventHubImpl;
020import org.apache.tapestry5.ioc.annotations.PostInjection;
021import org.apache.tapestry5.ioc.annotations.Symbol;
022import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
023import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
024import org.apache.tapestry5.ioc.services.UpdateListener;
025import org.apache.tapestry5.ioc.services.UpdateListenerHub;
026
027public class ResourceChangeTrackerImpl extends InvalidationEventHubImpl implements ResourceChangeTracker,
028        UpdateListener
029{
030    private final URLChangeTracker tracker;
031
032    /**
033     * Used in production mode as the last modified time of any resource exposed to the client. Remember that
034     * all exposed assets include a URL with a version number, and each new deployment of the application should change
035     * that version number.
036     */
037    private final long fixedLastModifiedTime = Math.round(System.currentTimeMillis() / 1000d) * 1000L;
038
039    public ResourceChangeTrackerImpl(ClasspathURLConverter classpathURLConverter,
040                                     @Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE)
041                                     boolean productionMode)
042    {
043        super(productionMode);
044
045        // Use granularity of seconds (not milliseconds) since that works properly
046        // with response headers for identifying last modified. Don't track
047        // folder changes, just changes to actual files.
048        tracker = productionMode ? null : new URLChangeTracker(classpathURLConverter, true, false);
049    }
050
051    @PostInjection
052    public void registerWithUpdateListenerHub(UpdateListenerHub hub)
053    {
054        hub.addUpdateListener(this);
055    }
056
057    public long trackResource(Resource resource)
058    {
059        if (tracker == null)
060        {
061            return fixedLastModifiedTime;
062        }
063
064        return tracker.add(resource.toURL());
065    }
066
067    public void addDependency(Resource dependency)
068    {
069        trackResource(dependency);
070    }
071
072    public void forceInvalidationEvent()
073    {
074        fireInvalidationEvent();
075
076        if (tracker != null)
077        {
078            tracker.clear();
079        }
080    }
081
082    public void checkForUpdates()
083    {
084        if (tracker.containsChanges())
085        {
086            forceInvalidationEvent();
087        }
088    }
089
090}