Package org.apache.lenya.ac.file

Source Code of org.apache.lenya.ac.file.FileItemManager

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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.
*
*/

/* $Id: FileItemManager.java 485769 2006-12-11 17:41:23Z andreas $  */

package org.apache.lenya.ac.file;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.lenya.ac.AccessControlException;
import org.apache.lenya.ac.AccreditableManager;
import org.apache.lenya.ac.Group;
import org.apache.lenya.ac.Groupable;
import org.apache.lenya.ac.Item;
import org.apache.lenya.ac.ItemManager;
import org.apache.lenya.ac.ItemManagerListener;
import org.apache.lenya.ac.impl.ItemConfiguration;

/**
* Abstract superclass for classes that manage items loaded from configuration
* files.
*/
public abstract class FileItemManager extends AbstractLogEnabled implements ItemManager {

    private Map items = new HashMap();
    private File configurationDirectory;
    private DirectoryChangeNotifier notifier;

    private AccreditableManager accreditableManager;

    /**
     * Create a new ItemManager.
     * @param accreditableManager The {@link AccreditableManager}.
     */
    protected FileItemManager(AccreditableManager accreditableManager) {
        this.accreditableManager = accreditableManager;
    }

    /**
     * Configures the item manager.
     * @param _configurationDirectory where the items are fetched from
     * @throws AccessControlException if the item manager cannot be instantiated
     */
    public void configure(File _configurationDirectory) throws AccessControlException {
        assert _configurationDirectory != null;

        if (!_configurationDirectory.exists() || !_configurationDirectory.isDirectory()) {
            throw new AccessControlException("The directory ["
                    + _configurationDirectory.getAbsolutePath() + "] does not exist!");
        }

        this.configurationDirectory = _configurationDirectory;
        this.notifier = new DirectoryChangeNotifier(_configurationDirectory, getFileFilter());
        this.notifier.enableLogging(getLogger());
        loadItems();
    }

    /**
     * Reloads the items if an item was changed / added / removed.
     * @throws AccessControlException when something went wrong.
     */
    protected void loadItems() throws AccessControlException {

        boolean changed;
        try {
            changed = this.notifier.hasChanged();
        } catch (IOException e) {
            throw new AccessControlException(e);
        }

        if (changed) {

            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Item configuration has changed - reloading.");
            }

            File[] addedFiles = this.notifier.getAddedFiles();

            for (int i = 0; i < addedFiles.length; i++) {
                Item item = loadItem(addedFiles[i]);
                add(item);
            }

            File[] removedFiles = this.notifier.getRemovedFiles();
            for (int i = 0; i < removedFiles.length; i++) {
                String fileName = removedFiles[i].getName();
                String id = fileName.substring(0, fileName.length() - getSuffix().length());

                Item item = (Item) this.items.get(id);

                if (item != null) {

                    if (item instanceof Groupable) {
                        ((Groupable) item).removeFromAllGroups();
                    }
                    if (item instanceof Group) {
                        ((Group) item).removeAllMembers();
                    }

                    remove(item);
                }
            }

            File[] changedFiles = this.notifier.getChangedFiles();
            for (int i = 0; i < changedFiles.length; i++) {
                Item item = loadItem(changedFiles[i]);
                update(item);
            }

        }

    }

    /**
     * Loads an item from a file.
     * @param file The file.
     * @return An item.
     * @throws AccessControlException when something went wrong.
     */
    protected Item loadItem(File file) throws AccessControlException {
        Configuration config = getItemConfiguration(file);

        String fileName = file.getName();
        String id = fileName.substring(0, fileName.length() - getSuffix().length());
        Item item = (Item) this.items.get(id);

        String klass = ItemConfiguration.getItemClass(config);
        if (item == null) {
            try {
                Class[] paramTypes = { ItemManager.class, Logger.class };
                Constructor ctor = Class.forName(klass).getConstructor(paramTypes);
                Object[] params = { this, getLogger() };
                item = (Item) ctor.newInstance(params);
            } catch (Exception e) {
                String errorMsg = "Exception when trying to instanciate: " + klass
                        + " with exception: " + e.fillInStackTrace();

                // an exception occured when trying to instanciate
                // a user.
                getLogger().error(errorMsg);
                throw new AccessControlException(errorMsg, e);
            }
        }

        try {
            item.configure(config);
        } catch (ConfigurationException e) {
            String errorMsg = "Exception when trying to configure: " + klass;
            throw new AccessControlException(errorMsg, e);
        }
        return item;
    }

    /**
     * Loads teh configuration of an item from a file.
     * @param file The file.
     * @return A configuration.
     * @throws AccessControlException when something went wrong.
     */
    protected Configuration getItemConfiguration(File file) throws AccessControlException {
        DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
        Configuration config = null;

        try {
            assert file.exists();
            config = builder.buildFromFile(file);
        } catch (Exception e) {
            String errorMsg = "Exception when reading the configuration from file: "
                    + file.getName();

            // an exception occured when trying to read the configuration
            // from the identity file.
            getLogger().error(errorMsg);
            throw new AccessControlException(errorMsg, e);
        }
        return config;
    }

    protected void removeItem(File file) {
        // do nothing
    }

    /**
     * Returns an item for a given ID.
     * @param id The id.
     * @return An item.
     */
    public Item getItem(String id) {
        try {
            loadItems();
        } catch (AccessControlException e) {
            throw new IllegalStateException(e.getMessage());
        }
        return (Item) this.items.get(id);
    }

    /**
     * get all items
     * @return an array of items
     */
    public Item[] getItems() {
        try {
            loadItems();
        } catch (AccessControlException e) {
            throw new IllegalStateException(e.getMessage());
        }
        return (Item[]) this.items.values().toArray(new Item[this.items.values().size()]);
    }

    /**
     * Add an Item to this manager
     * @param item to be added
     * @throws AccessControlException when the notification threw this
     *         exception.
     */
    public void add(Item item) throws AccessControlException {
        assert item != null;
        this.items.put(item.getId(), item);
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Item [" + item + "] added.");
        }
        notifyAdded(item);
    }

    /**
     * Remove an item from this manager
     * @param item to be removed
     * @throws AccessControlException when the notification threw this
     *         exception.
     */
    public void remove(Item item) throws AccessControlException {
        this.items.remove(item.getId());
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Item [" + item + "] removed.");
        }
        notifyRemoved(item);
    }

    /**
     * Update an item.
     * @param newItem The new version of the item.
     * @throws AccessControlException when the notification threw this
     *         exception.
     */
    public void update(Item newItem) throws AccessControlException {
        this.items.remove(newItem.getId());
        this.items.put(newItem.getId(), newItem);
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Item [" + newItem + "] updated.");
        }
    }

    /**
     * Returns if the ItemManager contains an object.
     * @param item The object.
     * @return A boolean value.
     */
    public boolean contains(Item item) {
        try {
            loadItems();
        } catch (AccessControlException e) {
            throw new IllegalStateException(e.getMessage());
        }
        return this.items.containsValue(item);
    }

    /**
     * Get the directory where the items are located.
     * @return a <code>File</code>
     */
    public File getConfigurationDirectory() {
        return this.configurationDirectory;
    }

    /**
     * Get a file filter which filters for files containing items.
     * @return a <code>FileFilter</code>
     */
    protected FileFilter getFileFilter() {
        FileFilter filter = new FileFilter() {
            public boolean accept(File pathname) {
                return (pathname.getName().endsWith(getSuffix()));
            }
        };

        return filter;
    }

    /**
     * Returns the file extension to be used.
     * @return A string.
     */
    protected abstract String getSuffix();

    private List itemManagerListeners = new ArrayList();

    /**
     * Attaches an item manager listener to this item manager.
     * @param listener An item manager listener.
     */
    public void addItemManagerListener(ItemManagerListener listener) {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Adding listener: [" + listener + "]");
        }
        if (!this.itemManagerListeners.contains(listener)) {
            this.itemManagerListeners.add(listener);
        }
    }

    /**
     * Removes an item manager listener from this item manager.
     * @param listener An item manager listener.
     */
    public void removeItemManagerListener(ItemManagerListener listener) {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Removing listener: [" + listener + "]");
        }
        this.itemManagerListeners.remove(listener);
    }

    /**
     * Notifies the listeners that an item was added.
     * @param item The item that was added.
     * @throws AccessControlException if an error occurs.
     */
    protected void notifyAdded(Item item) throws AccessControlException {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Item was added: [" + item + "]");
        }
        List clone = new ArrayList(this.itemManagerListeners);
        for (Iterator i = clone.iterator(); i.hasNext();) {
            ItemManagerListener listener = (ItemManagerListener) i.next();
            listener.itemAdded(item);
        }
    }

    /**
     * Notifies the listeners that an item was removed.
     * @param item The item that was removed.
     * @throws AccessControlException if an error occurs.
     */
    protected void notifyRemoved(Item item) throws AccessControlException {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Item was removed: [" + item + "]");
        }
        List clone = new ArrayList(this.itemManagerListeners);
        for (Iterator i = clone.iterator(); i.hasNext();) {
            ItemManagerListener listener = (ItemManagerListener) i.next();
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Notifying listener: [" + listener + "]");
            }
            listener.itemRemoved(item);
        }
    }

    /**
     * Helper class to observe a directory for changes.
     */
    public static class DirectoryChangeNotifier extends AbstractLogEnabled {

        /**
         * Ctor.
         * @param _directory The directory to observe.
         * @param _filter A filter to specify the file type to observe.
         */
        public DirectoryChangeNotifier(File _directory, FileFilter _filter) {
            this.directory = _directory;
            this.filter = _filter;
        }

        private File directory;
        private FileFilter filter;
        private Map canonicalPath2LastModified = new HashMap();

        private Set addedFiles = new HashSet();
        private Set removedFiles = new HashSet();
        private Set changedFiles = new HashSet();

        /**
         * Checks if the directory has changed (a new file was added, a file was
         * removed, a file has changed).
         * @return A boolean value.
         * @throws IOException when something went wrong.
         */
        public boolean hasChanged() throws IOException {

            this.addedFiles.clear();
            this.removedFiles.clear();
            this.changedFiles.clear();

            File[] files = this.directory.listFiles(this.filter);

            Set newPathSet = new HashSet();

            for (int i = 0; i < files.length; i++) {
                String canonicalPath = files[i].getCanonicalPath();
                newPathSet.add(canonicalPath);

                if (!this.canonicalPath2LastModified.containsKey(canonicalPath)) {
                    this.addedFiles.add(new File(canonicalPath));

                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("New file: [" + canonicalPath + "]");
                    }

                } else {
                    Long lastModifiedObject = (Long) this.canonicalPath2LastModified
                            .get(canonicalPath);
                    long lastModified = lastModifiedObject.longValue();
                    if (lastModified < files[i].lastModified()) {
                        this.changedFiles.add(files[i]);
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("File has changed: [" + canonicalPath + "]");
                        }
                    }
                }
                Long lastModified = new Long(files[i].lastModified());
                this.canonicalPath2LastModified.put(canonicalPath, lastModified);
            }

            Set oldPathSet = this.canonicalPath2LastModified.keySet();
            String[] oldPaths = (String[]) oldPathSet.toArray(new String[oldPathSet.size()]);
            for (int i = 0; i < oldPaths.length; i++) {
                if (!newPathSet.contains(oldPaths[i])) {
                    this.removedFiles.add(new File(oldPaths[i]));
                    this.canonicalPath2LastModified.remove(oldPaths[i]);
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("File removed: [" + oldPaths[i] + "]");
                    }
                }
            }

            return !this.addedFiles.isEmpty() || !this.removedFiles.isEmpty()
                    || !this.changedFiles.isEmpty();
        }

        /**
         * Returns the added files.
         * @return An array of files.
         */
        public File[] getAddedFiles() {
            return (File[]) this.addedFiles.toArray(new File[this.addedFiles.size()]);
        }

        /**
         * Returns the removed files.
         * @return An array of files.
         */
        public File[] getRemovedFiles() {
            return (File[]) this.removedFiles.toArray(new File[this.removedFiles.size()]);
        }

        /**
         * Returns the changed files.
         * @return An array of files.
         */
        public File[] getChangedFiles() {
            return (File[]) this.changedFiles.toArray(new File[this.changedFiles.size()]);
        }

    }

    public AccreditableManager getAccreditableManager() {
        return this.accreditableManager;
    }

}
TOP

Related Classes of org.apache.lenya.ac.file.FileItemManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.