All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dishevelled.thumbnail.swing.LastModifiedCache Maven / Gradle / Ivy

/*

    dsh-thumbnail  Implementation of the freedesktop.org thumbnail specification.
    Copyright (c) 2013-2019 held jointly by the individual authors.

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
    License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library;  if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.

    > http://www.fsf.org/licensing/licenses/lgpl.html
    > http://www.opensource.org/licenses/lgpl-license.php

*/
package org.dishevelled.thumbnail.swing;

import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.IOException;

import java.net.URI;

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import com.google.common.collect.Maps;

/**
 * Last modified cache.
 *
 * @author  Michael Heuer
 */
final class LastModifiedCache
{
    /** Executor service. */
    private final ExecutorService executorService;

    /** File system watcher. */
    private final WatchService watcher;

    /** Watch keys. */
    private final ConcurrentMap watchKeys = Maps.newConcurrentMap();

    /** Maximum cache size. */
    private static final int MAXIMUM_SIZE = 100000;

    /** Cache of last modified timestamps keyed by URI. */
    private final LoadingCache cache = CacheBuilder.newBuilder()
        .maximumSize(MAXIMUM_SIZE)
        .build(new CacheLoader()
               {
                   @Override
                   public Long load(final Path path) throws IOException
                   {
                       Path parent = path.getParent();
                       if (parent != null)
                       {
                           watchKeys.putIfAbsent(parent.register(watcher, ENTRY_DELETE, ENTRY_MODIFY), parent);
                       }
                       return (path.toFile().lastModified());
                   }
               });

    /**
     * Create a new last modified cache.
     *
     * @param executorService executor service, must not be null
     */
    LastModifiedCache(final ExecutorService executorService)
    {
        if (executorService == null)
        {
            throw new IllegalArgumentException("executorService must not be null");
        }
        this.executorService = executorService;

        try
        {
            watcher = FileSystems.getDefault().newWatchService();
        }
        catch (Exception e)
        {
            throw new RuntimeException("could not create last modified cache", e);
        }
        executorService.submit(new Runnable()
            {
                @Override
                public void run()
                {
                    processEvents();
                }
            });
    }


    /**
     * Process file system watcher events.
     */
    void processEvents()
    {
        for (;;)
        {
            WatchKey key = null;
            try
            {
                key = watcher.take();
            }
            catch (InterruptedException e)
            {
                return;
            }
            Path dir = watchKeys.get(key);
            for (WatchEvent event : key.pollEvents())
            {
                WatchEvent.Kind kind = event.kind();
                if (kind == OVERFLOW)
                {
                    continue;
                }

                WatchEvent pathEvent = cast(event);
                Path name = pathEvent.context();
                Path path = dir.resolve(name);
                if (kind == ENTRY_DELETE)
                {
                    deleted(path);
                }
                else if (kind == ENTRY_MODIFY)
                {
                    modified(path);
                }
            }
            if (!key.reset())
            {
                watchKeys.remove(key);
            }
        }
    }

    /**
     * Notify this last modified cache the specified path has been deleted.
     *
     * @param path deleted path
     */
    void deleted(final Path path)
    {
        cache.invalidate(path);
    }

    /**
     * Notify this last modified cache the specified path has been modified.
     *
     * @param path modified path
     */
    void modified(final Path path)
    {
        cache.invalidate(path);
    }

    /**
     * Return the last modified timestamp for the specified URI.
     *
     * @param uri URI
     * @return the last modified timestamp for the specified URI
     */
    long get(final URI uri)
    {
        return get(Paths.get(uri));
    }

    /**
     * Return the last modified timestamp for the specified path.
     *
     * @param path path
     * @return the last modified timestamp for the specified path
     */
    long get(final Path path)
    {
        return cache.getUnchecked(path);
    }

    /**
     * Cast the specified watch event to WatchEvent<T>.
     *
     * @param  type
     * @param event watch event to cast
     * @return the specified watch event cast to WatchEvent<T>
     */
    @SuppressWarnings("unchecked")
    private static  WatchEvent cast(final WatchEvent event)
    {
        return (WatchEvent) event;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy