001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.jci.listeners;
019    
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.util.Collection;
023    import java.util.HashSet;
024    import java.util.Set;
025    
026    import org.apache.commons.io.IOUtils;
027    import org.apache.commons.jci.ReloadingClassLoader;
028    import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
029    import org.apache.commons.jci.stores.MemoryResourceStore;
030    import org.apache.commons.jci.stores.ResourceStore;
031    import org.apache.commons.jci.stores.Transactional;
032    import org.apache.commons.jci.utils.ConversionUtils;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    
036    /**
037     * This Listener waits for FAM events to trigger a reload of classes
038     * or resources.
039     * 
040     * @author tcurdt
041     */
042    public class ReloadingListener extends AbstractFilesystemAlterationListener {
043    
044        private final Log log = LogFactory.getLog(ReloadingListener.class);
045        
046        private final Set<ReloadNotificationListener> notificationListeners = new HashSet<ReloadNotificationListener>();
047        private final ResourceStore store;
048        
049        public ReloadingListener() {
050            this(new MemoryResourceStore());
051        }
052    
053        public ReloadingListener( final ResourceStore pStore ) {
054            store = pStore;
055        }
056    
057        public ResourceStore getStore() {
058            return store;
059        }
060    
061        public void addReloadNotificationListener( final ReloadNotificationListener pNotificationListener ) {
062            notificationListeners.add(pNotificationListener);
063    
064            if (pNotificationListener instanceof ReloadingClassLoader) {
065                ((ReloadingClassLoader)pNotificationListener).addResourceStore(store);
066            }
067    
068        }
069        
070        public boolean isReloadRequired( final FilesystemAlterationObserver pObserver ) {
071            boolean reload = false;
072    
073            final Collection<File> created = getCreatedFiles();
074            final Collection<File> changed = getChangedFiles();
075            final Collection<File> deleted = getDeletedFiles();
076            
077            log.debug("created:" + created.size() + " changed:" + changed.size() + " deleted:" + deleted.size() + " resources");
078    
079            if (deleted.size() > 0) {
080                for (File file : deleted) {
081                    final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), file));
082                    store.remove(resourceName);
083                }
084                reload = true;
085            }
086    
087            if (created.size() > 0) {
088                for (File file : created) {
089                    FileInputStream is = null;
090                    try {
091                        is = new FileInputStream(file);
092                        final byte[] bytes = IOUtils.toByteArray(is);
093                        final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), file));
094                        store.write(resourceName, bytes);
095                    } catch(final Exception e) {
096                        log.error("could not load " + file, e);
097                    } finally {
098                        IOUtils.closeQuietly(is);
099                    }
100                }
101            }
102    
103            if (changed.size() > 0) {
104                for (File file : changed) {
105                    FileInputStream is = null;
106                    try {
107                        is = new FileInputStream(file);
108                        final byte[] bytes = IOUtils.toByteArray(is);
109                        final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), file));
110                        store.write(resourceName, bytes);
111                    } catch(final Exception e) {
112                        log.error("could not load " + file, e);
113                    } finally {
114                        IOUtils.closeQuietly(is);
115                    }
116                }
117                reload = true;
118            }
119    
120            return reload;
121        }
122        
123        @Override
124        public void onStop( final FilesystemAlterationObserver pObserver ) {
125            
126            
127            if (store instanceof Transactional) {
128                ((Transactional)store).onStart();
129            }
130    
131            final boolean reload = isReloadRequired(pObserver);
132    
133            if (store instanceof Transactional) {
134                ((Transactional)store).onStop();
135            }
136            
137            if (reload) {
138                notifyReloadNotificationListeners();
139            }
140            
141            super.onStop(pObserver);
142        }
143    
144        void notifyReloadNotificationListeners() {
145            for (ReloadNotificationListener listener : notificationListeners) {
146                log.debug("notifying listener " + listener);
147    
148                listener.handleNotification();
149            }
150        }
151        
152        @Override
153        public void onDirectoryCreate( final File pDir ) {                
154        }
155        @Override
156        public void onDirectoryChange( final File pDir ) {                
157        }
158        @Override
159        public void onDirectoryDelete( final File pDir ) {
160        }
161    }