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;
019    
020    import java.io.InputStream;
021    import java.net.URL;
022    
023    import org.apache.commons.jci.listeners.ReloadNotificationListener;
024    import org.apache.commons.jci.stores.ResourceStore;
025    import org.apache.commons.jci.stores.ResourceStoreClassLoader;
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    /**
030     * The ReloadingClassLoader uses a delegation mechansim to allow
031     * classes to be reloaded. That means that loadClass calls may
032     * return different results if the class was changed in the underlying
033     * ResourceStore.
034     * 
035     * @author tcurdt
036     */
037    public class ReloadingClassLoader extends ClassLoader implements ReloadNotificationListener {
038        
039        private final Log log = LogFactory.getLog(ReloadingClassLoader.class);
040        
041        private final ClassLoader parent;
042        private ResourceStore[] stores = new ResourceStore[0];
043        private ClassLoader delegate;
044    
045        public ReloadingClassLoader( final ClassLoader pParent ) {        
046            super(pParent);
047            parent = pParent;        
048    
049            delegate = new ResourceStoreClassLoader(parent, stores);
050        }
051    
052        public boolean addResourceStore( final ResourceStore pStore ) {
053            try {        
054                final int n = stores.length;
055                final ResourceStore[] newStores = new ResourceStore[n + 1];
056                System.arraycopy(stores, 0, newStores, 1, n);
057                newStores[0] = pStore;
058                stores = newStores;
059                delegate = new ResourceStoreClassLoader(parent, stores);            
060                return true;
061            } catch ( final RuntimeException e ) {
062                log.error("could not add resource store " + pStore);
063            }
064            return false;
065        }
066    
067        public boolean removeResourceStore( final ResourceStore pStore ) {
068    
069            final int n = stores.length;
070            int i = 0;
071               
072            // FIXME: this should be improved with a Map
073            // find the pStore and index position with var i
074            while ( ( i < n )  && ( stores[i] != pStore ) ) {
075                i++;
076            }
077                        
078            // pStore was not found
079            if ( i == n ) {
080                return false;
081            }
082            
083            // if stores length > 1 then array copy old values, else create new empty store 
084            final ResourceStore[] newStores = new ResourceStore[n - 1];
085            if (i > 0) {
086                System.arraycopy(stores, 0, newStores, 0, i);
087            }
088            if (i < n - 1) {
089                System.arraycopy(stores, i + 1, newStores, i, (n - i - 1));
090            }
091                
092            stores = newStores;
093            delegate = new ResourceStoreClassLoader(parent, stores);
094            return true;
095        }
096        
097        public void handleNotification() {
098            log.debug("reloading");
099            delegate = new ResourceStoreClassLoader(parent, stores);
100        }
101        
102        @Override
103        public void clearAssertionStatus() {
104            delegate.clearAssertionStatus();
105        }
106        @Override
107        public URL getResource(String name) {
108            return delegate.getResource(name);
109        }
110        @Override
111        public InputStream getResourceAsStream(String name) {
112            return delegate.getResourceAsStream(name);
113        }
114        @Override
115        public Class<?> loadClass(String name) throws ClassNotFoundException {
116            return delegate.loadClass(name);
117        }
118        @Override
119        public void setClassAssertionStatus(String className, boolean enabled) {
120            delegate.setClassAssertionStatus(className, enabled);
121        }
122        @Override
123        public void setDefaultAssertionStatus(boolean enabled) {
124            delegate.setDefaultAssertionStatus(enabled);
125        }
126        @Override
127        public void setPackageAssertionStatus(String packageName, boolean enabled) {
128            delegate.setPackageAssertionStatus(packageName, enabled);
129        }
130    }