    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}