Package org.drools.io.impl

Source Code of org.drools.io.impl.ResourceChangeScannerImpl$ProcessChangeSet

/*
* Copyright 2010 JBoss Inc
*
* 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.
*/

package org.drools.io.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Future;

import org.drools.io.internal.InternalResource;
import org.kie.ChangeSet;
import org.kie.SystemEventListener;
import org.kie.concurrent.ExecutorProviderFactory;
import org.kie.io.Resource;
import org.kie.io.ResourceChangeNotifier;
import org.kie.io.ResourceChangeScanner;
import org.kie.io.ResourceChangeScannerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResourceChangeScannerImpl
    implements
    ResourceChangeScanner {
   
    private final Logger logger = LoggerFactory.getLogger( ResourceChangeScanner.class );

    private final Map<Resource, Set<ResourceChangeNotifier>> resources;
    private final Set<Resource>                              directories;
    private int                                              interval;

    private Future<Boolean>                                  scannerSchedulerExecutor;
    private ProcessChangeSet                                 scannerScheduler;

    public ResourceChangeScannerImpl() {
        this.resources = new HashMap<Resource, Set<ResourceChangeNotifier>>();
        this.directories = new HashSet<Resource>();
        this.setInterval( 60 );
        logger.info( "ResourceChangeScanner created with default interval=60" );
    }

    public void configure(ResourceChangeScannerConfiguration configuration) {
        this.setInterval( ((ResourceChangeScannerConfigurationImpl) configuration).getInterval() );
        logger.info( "ResourceChangeScanner reconfigured with interval=" + getInterval() );

        // restart it if it's already running.
        if ( this.scannerScheduler != null && this.scannerScheduler.isRunning() ) {
            stop();
            start();
        }
    }

    public ResourceChangeScannerConfiguration newResourceChangeScannerConfiguration() {
        return new ResourceChangeScannerConfigurationImpl();
    }

    public ResourceChangeScannerConfiguration newResourceChangeScannerConfiguration(Properties properties) {
        return new ResourceChangeScannerConfigurationImpl( properties );
    }

    public void subscribeNotifier(ResourceChangeNotifier notifier,
                                  Resource resource) {
        synchronized ( this.resources ) {
            if ( ((InternalResource) resource).isDirectory() ) {
                this.directories.add( resource );
            }
            Set<ResourceChangeNotifier> notifiers = this.resources.get( resource );
            if ( notifiers == null ) {
                notifiers = new HashSet<ResourceChangeNotifier>();
                this.resources.put( resource,
                                    notifiers );
            }
            logger.debug( "ResourceChangeScanner subcribing notifier=" + notifier + " to resource=" + resource );
            notifiers.add( notifier );
        }
    }

    public void unsubscribeNotifier(ResourceChangeNotifier notifier,
                                    Resource resource) {
        synchronized ( this.resources ) {
            Set<ResourceChangeNotifier> notifiers = this.resources.get( resource );
            if ( notifiers == null ) {
                return;
            }
            logger.debug( "ResourceChangeScanner unsubcribing notifier=" + notifier + " to resource=" + resource );
            notifiers.remove( notifier );
            if ( notifiers.isEmpty() ) {
                logger.debug( "ResourceChangeScanner resource=" + resource + " now has no subscribers" );
                this.resources.remove( resource );
                this.directories.remove( resource ); // don't bother with
                // isDirectory check, as
                // doing a remove is
                // harmless if it doesn't
                // exist
            }
        }
    }

    public Map<Resource, Set<ResourceChangeNotifier>> getResources() {
        return resources;
    }

    public void scan() {
        logger.debug( "ResourceChangeScanner attempt to scan " + this.resources.size() + " resources" );

        synchronized ( this.resources ) {
            Map<ResourceChangeNotifier, ChangeSet> notifications = new HashMap<ResourceChangeNotifier, ChangeSet>();

            List<Resource> removed = new ArrayList<Resource>();

            // detect modified and added
            for ( Resource resource : this.directories ) {
                logger.debug( "ResourceChangeScanner scanning directory=" + resource );
                for ( Resource child : ((InternalResource) resource).listResources() ) {
                    if ( ((InternalResource) child).isDirectory() ) {
                        continue; // ignore sub directories
                    }
                    if ( !this.resources.containsKey( child ) ) {

                        logger.debug( "ResourceChangeScanner new resource=" + child );
                        // child is new
                        ((InternalResource) child).setResourceType( ((InternalResource) resource).getResourceType() );
                        Set<ResourceChangeNotifier> notifiers = this.resources.get( resource ); // get notifiers for this
                        // directory
                        for ( ResourceChangeNotifier notifier : notifiers ) {
                            ChangeSetImpl changeSet = (ChangeSetImpl) notifications.get( notifier );
                            if ( changeSet == null ) {
                                // lazy initialise changeSet
                                changeSet = new ChangeSetImpl();
                                notifications.put( notifier,
                                                   changeSet );
                            }
                            if ( changeSet.getResourcesAdded().isEmpty() ) {
                                changeSet.setResourcesAdded( new ArrayList<Resource>() );
                            }
                            changeSet.getResourcesAdded().add( child );
                            notifier.subscribeChildResource( resource,
                                                             child );
                        }
                    }
                }
            }

            for ( Entry<Resource, Set<ResourceChangeNotifier>> entry : this.resources.entrySet() ) {
                Resource resource = entry.getKey();
                Set<ResourceChangeNotifier> notifiers = entry.getValue();

                if ( !((InternalResource) resource).isDirectory() ) {
                    // detect if Resource has been removed
                    long lastModified = ((InternalResource) resource).getLastModified();
                    long lastRead = ((InternalResource) resource).getLastRead();
                   
                    if ( lastModified == 0 ) {
                        logger.debug( "ResourceChangeScanner removed resource=" + resource );
                        removed.add( resource );
                        // resource is no longer present
                        // iterate notifiers for this resource and add to each
                        // removed
                        for ( ResourceChangeNotifier notifier : notifiers ) {
                            ChangeSetImpl changeSet = (ChangeSetImpl) notifications.get( notifier );
                            if ( changeSet == null ) {
                                // lazy initialise changeSet
                                changeSet = new ChangeSetImpl();
                                notifications.put( notifier,
                                                   changeSet );
                            }
                            if ( changeSet.getResourcesRemoved().isEmpty() ) {
                                changeSet.setResourcesRemoved( new ArrayList<Resource>() );
                            }
                            changeSet.getResourcesRemoved().add( resource );
                        }
                    } else if ( lastRead < lastModified && lastRead >= 0 ) {
                        logger.debug( "ResourceChangeScanner modified resource=" + resource + " : " + lastRead + " : " + lastModified );
                        // it's modified
                        // iterate notifiers for this resource and add to each
                        // modified
                        for ( ResourceChangeNotifier notifier : notifiers ) {
                            ChangeSetImpl changeSet = (ChangeSetImpl) notifications.get( notifier );
                            if ( changeSet == null ) {
                                // lazy initialise changeSet
                                changeSet = new ChangeSetImpl();
                                notifications.put( notifier,
                                                   changeSet );
                            }
                            if ( changeSet.getResourcesModified().isEmpty() ) {
                                changeSet.setResourcesModified( new ArrayList<Resource>() );
                            }
                            changeSet.getResourcesModified().add( resource );
                        }
                    }
                }
            }

            // now iterate and removed the removed resources, we do this so as
            // not to mutate the foreach loop while iterating
            for ( Resource resource : removed ) {
                this.resources.remove( resource );
            }

            for ( Entry<ResourceChangeNotifier, ChangeSet> entry : notifications.entrySet() ) {
                ResourceChangeNotifier notifier = entry.getKey();
                ChangeSet changeSet = entry.getValue();
                notifier.publishChangeSet( changeSet );
            }
        }
    }

    public void setInterval(int interval) {
        if ( interval <= 0 ) {
            throw new IllegalArgumentException( "Invalid interval time: " + interval + ". It should be a positive number bigger than 0" );
        }

        this.interval = interval;
        logger.info( "ResourceChangeScanner reconfigured with interval=" + getInterval() );

        if ( this.scannerScheduler != null && this.scannerScheduler.isRunning() ) {
            stop();
            start();
        }
    }

    public int getInterval() {
        return this.interval;
    }

    public void start() {
        this.scannerScheduler = new ProcessChangeSet( this.resources,
                                                      this,
                                                      this.interval );
        this.scannerSchedulerExecutor =
                ExecutorProviderFactory.getExecutorProvider().<Boolean>getCompletionService()
                        .submit(this.scannerScheduler, true);
    }

    public void stop() {
        if ( this.scannerScheduler != null && this.scannerScheduler.isRunning() ) {
            this.scannerScheduler.stop();
            this.scannerSchedulerExecutor.cancel(true);
            this.scannerScheduler = null;
        }
    }

    public void reset() {
        this.resources.clear();
        this.directories.clear();
    }

    public static class ProcessChangeSet
        implements
        Runnable {
       
        private final Logger logger = LoggerFactory.getLogger( ProcessChangeSet.class );
       
        private volatile boolean                                 scan;
        private final ResourceChangeScannerImpl                  scanner;
        private final long                                       interval;
        private final Map<Resource, Set<ResourceChangeNotifier>> resources;

        ProcessChangeSet(Map<Resource, Set<ResourceChangeNotifier>> resources,
                         ResourceChangeScannerImpl scanner,
                         int interval) {
            this.resources = resources;
            this.scanner = scanner;
            this.interval = interval;
            this.scan = true;
        }

        public int getInterval() {
            return (int) this.interval;
        }

        public void stop() {
            this.scan = false;
        }

        public boolean isRunning() {
            return this.scan;
        }

        public void run() {
            synchronized ( this ) {
                if ( this.scan ) {
                    this.logger.info( "ResourceChangeNotification scanner has started" );
                }
                while ( this.scan ) {
                    try {
                        // logger.trace( "BEFORE : sync this.resources" );
                        synchronized ( this.resources ) {
                            // logger.trace( "DURING : sync this.resources" );
                            // lock the resources, as we don't want this modified
                            // while processing
                            this.scanner.scan();
                        }
                        // logger.trace( "AFTER : SCAN" );
                    } catch (RuntimeException e) {
                        this.logger.error( e.getMessage(), e );
                    } catch (Error e) {
                        this.logger.error( e.getMessage(), e );
                    }
                    try {
                        this.logger.debug( "ResourceChangeScanner thread is waiting for " + this.interval + " seconds." );
                        wait( this.interval * 1000 );
                    } catch ( InterruptedException e ) {
                        if ( this.scan ) {
                            this.logger.error( e.getMessage(), new RuntimeException( "ResourceChangeNotification ChangeSet scanning thread was interrupted, but shutdown was not requested",
                                                                           e ) );
                        }
                    }

                }
                this.logger.info( "ResourceChangeNotification scanner has stopped" );
            }
        }
    }

    public void setSystemEventListener(SystemEventListener listener) {
        // TODO Auto-generated method stub
       
    }
}
TOP

Related Classes of org.drools.io.impl.ResourceChangeScannerImpl$ProcessChangeSet

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.