org.ow2.chameleon.fuchsia.discovery.filebased.monitor.DirectoryMonitor.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.chameleon.fuchsia.discovery.filebased.monitor.DirectoryMonitor.java

Source

package org.ow2.chameleon.fuchsia.discovery.filebased.monitor;

/*
 * #%L
 * OW2 Chameleon - Fuchsia Discovery FileBased
 * %%
 * Copyright (C) 2009 - 2014 OW2 Chameleon
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class DirectoryMonitor implements BundleActivator, ServiceTrackerCustomizer {

    /**
     * logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(DirectoryMonitor.class);

    /**
     * List of deployers.
     */
    private final List<Deployer> deployers = new ArrayList<Deployer>();
    /**
     * The directory.
     */
    private final File directory;
    /**
     * Polling period.
     * -1 to disable polling.
     */
    private final long polling;
    /**
     * A monitor listening file changes.
     */
    private final FileAlterationMonitor monitor;
    /**
     * The lock avoiding concurrent modifications of the deployers map.
     */
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private final String trackedClassName;
    /**
     * Service tracking to retrieve deployers.
     */
    private ServiceTracker tracker;
    private BundleContext context;

    public DirectoryMonitor(String directorypath, long polling, String classname) {

        this.directory = new File(directorypath);
        this.trackedClassName = classname;
        this.polling = polling;

        if (!directory.isDirectory()) {
            LOG.info("Monitored directory {} not existing - creating directory", directory.getAbsolutePath());
            if (!this.directory.mkdirs()) {
                throw new IllegalStateException("Monitored directory doesn't exist and cannot be created.");
            }
        }

        // We observes all files.
        FileAlterationObserver observer = new FileAlterationObserver(directory, TrueFileFilter.INSTANCE);
        observer.checkAndNotify();
        observer.addListener(new FileMonitor());
        monitor = new FileAlterationMonitor(polling, observer);

    }

    /**
     * Acquires the write lock only and only if the write lock is not already held by the current thread.
     *
     * @return {@literal true} if the lock was acquired within the method, {@literal false} otherwise.
     */
    private boolean acquireWriteLockIfNotHeld() {
        if (!lock.isWriteLockedByCurrentThread()) {
            lock.writeLock().lock();
            return true;
        }
        return false;
    }

    /**
     * Releases the write lock only and only if the write lock is held by the current thread.
     *
     * @return {@literal true} if the lock has no more holders, {@literal false} otherwise.
     */
    private boolean releaseWriteLockIfHeld() {
        if (lock.isWriteLockedByCurrentThread()) {
            lock.writeLock().unlock();
        }
        return lock.getWriteHoldCount() == 0;
    }

    /**
     * Acquires the read lock only and only if no read lock is already held by the current thread.
     *
     * @return {@literal true} if the lock was acquired within the method, {@literal false} otherwise.
     */
    private boolean acquireReadLockIfNotHeld() {
        if (lock.getReadHoldCount() == 0) {
            lock.readLock().lock();
            return true;
        }
        return false;
    }

    /**
     * Releases the read lock only and only if the read lock is held by the current thread.
     *
     * @return {@literal true} if the lock has no more holders, {@literal false} otherwise.
     */
    private boolean releaseReadLockIfHeld() {
        if (lock.getReadHoldCount() != 0) {
            lock.readLock().unlock();
        }
        return lock.getReadHoldCount() == 0;
    }

    public void start(final BundleContext context) throws DirectoryMonitoringException {
        this.context = context;
        LOG.info("Starting installing bundles from {}", directory.getAbsolutePath());
        this.tracker = new ServiceTracker(context, this.trackedClassName, this);

        // To avoid concurrency, we take the write lock here.
        try {
            acquireWriteLockIfNotHeld();

            // Arrives will be blocked until we release teh write lock
            this.tracker.open();

            // Register file monitor
            startFileMonitoring();

        } finally {
            releaseWriteLockIfHeld();
        }

        // Initialization does not need the write lock, read is enough.
        try {
            acquireReadLockIfNotHeld();
            // Per extension, open deployer.
            Collection<File> files = FileUtils.listFiles(directory, null, true);
            for (File file : files) {
                for (Deployer deployer : deployers) {
                    if (deployer.accept(file)) {
                        deployer.open(files);
                    }
                }
            }
        } finally {
            releaseReadLockIfHeld();
        }
    }

    private void startFileMonitoring() throws DirectoryMonitoringException {
        if (polling == -1L) {
            LOG.debug("No file monitoring for {}", directory.getAbsolutePath());
            return;
        }

        LOG.info("Starting file monitoring for {} - polling : {} ms", directory.getName(), polling);
        try {
            monitor.start();
        } catch (Exception e) {
            throw new DirectoryMonitoringException("Exception while starting the FileAlterationMonitor.", e);
        }
    }

    public void stop(BundleContext context) throws DirectoryMonitoringException {
        // To avoid concurrency, we take the write lock here.
        try {
            acquireWriteLockIfNotHeld();
            this.tracker.close();
            if (monitor != null) {
                LOG.debug("Stopping file monitoring of {}", directory.getAbsolutePath());
                // Wait 5 milliseconds.
                monitor.stop(5);
            }
        } catch (Exception e) {
            throw new DirectoryMonitoringException("Exception while stopping the FileAlterationMonitor.", e);
        } finally {
            releaseWriteLockIfHeld();
        }

        // No concurrency involved from here.
        for (Deployer deployer : deployers) {
            deployer.close();
        }
    }

    public Object addingService(ServiceReference reference) {

        Deployer deployer = (Deployer) context.getService(reference);

        try {
            acquireWriteLockIfNotHeld();
            deployers.add(deployer);
            Collection<File> files = FileUtils.listFiles(directory, null, true);
            List<File> accepted = new ArrayList<File>();
            for (File file : files) {
                if (deployer.accept(file)) {
                    accepted.add(file);
                }
            }
            deployer.open(accepted);
        } finally {
            releaseWriteLockIfHeld();
        }

        return deployer;

    }

    public void modifiedService(ServiceReference reference, Object o) {
        // Cannot happen, deployers do not have properties.
    }

    public void removedService(ServiceReference reference, Object o) {
        Deployer deployer = (Deployer) o;
        try {
            acquireWriteLockIfNotHeld();
            deployers.remove(deployer);
        } finally {
            releaseWriteLockIfHeld();
        }
    }

    private class FileMonitor extends FileAlterationListenerAdaptor {

        /**
         * A jar file was created.
         *
         * @param file the file
         */
        @Override
        public void onFileCreate(File file) {
            LOG.info("File " + file + " created in " + directory);

            // Callback called outside the protected region.
            for (Deployer deployer : getDeployers(file)) {
                try {
                    deployer.onFileCreate(file);
                } catch (Exception e) {
                    LOG.error("Error during the management of " + file.getAbsolutePath() + " (created) by "
                            + deployer, e);
                }
            }
        }

        @Override
        public void onFileChange(File file) {
            LOG.info("File " + file + " from " + directory + " changed");

            for (Deployer deployer : getDeployers(file)) {
                try {
                    deployer.onFileChange(file);
                } catch (Exception e) {
                    LOG.error(
                            "Error during the management of " + file.getAbsolutePath() + " (change) by " + deployer,
                            e);
                }
            }
        }

        @Override
        public void onFileDelete(File file) {
            LOG.info("File " + file + " deleted from " + directory);

            for (Deployer deployer : getDeployers(file)) {
                try {
                    deployer.onFileDelete(file);
                } catch (Exception e) {
                    LOG.error(
                            "Error during the management of " + file.getAbsolutePath() + " (delete) by " + deployer,
                            e);
                }
            }
        }
    }

    private Set<Deployer> getDeployers(File file) {
        Set<Deployer> depl = new HashSet<Deployer>();
        try {
            acquireReadLockIfNotHeld();
            for (Deployer deployer : deployers) {
                if (deployer.accept(file)) {
                    depl.add(deployer);
                }
            }
        } finally {
            releaseReadLockIfHeld();
        }
        return depl;
    }
}