001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.runtime.tomcat.dev;
023
024import java.io.IOException;
025import java.io.InputStream;
026import java.net.URL;
027import java.util.ArrayList;
028import java.util.Enumeration;
029import java.util.List;
030import java.util.NoSuchElementException;
031import java.util.ResourceBundle;
032
033import org.nuxeo.osgi.application.DevMutableClassLoader;
034import org.nuxeo.runtime.tomcat.NuxeoWebappClassLoader;
035
036/**
037 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
038 */
039
040public class NuxeoDevWebappClassLoader extends NuxeoWebappClassLoader implements DevMutableClassLoader,
041        WebResourcesCacheFlusher {
042
043    /**
044     * @since 9.3
045     */
046    @Override
047    public void clearPreviousClassLoader() {
048        clear();
049    }
050
051    /**
052     * @since 9.3
053     */
054    @Override
055    public void addClassLoader(URL... urls) {
056        createLocalClassLoader(urls);
057    }
058
059    public LocalClassLoader createLocalClassLoader(URL... urls) {
060        LocalClassLoader cl = new LocalURLClassLoader(urls, this);
061        addChildren(cl);
062        return cl;
063    }
064
065    protected DevFrameworkBootstrap bootstrap;
066
067    protected List<LocalClassLoader> children;
068
069    protected volatile LocalClassLoader[] _children;
070
071    public NuxeoDevWebappClassLoader() {
072        super();
073        this.children = new ArrayList<LocalClassLoader>();
074    }
075
076    public NuxeoDevWebappClassLoader(ClassLoader parent) {
077        super(parent);
078        this.children = new ArrayList<LocalClassLoader>();
079    }
080
081    public void setBootstrap(DevFrameworkBootstrap bootstrap) {
082        this.bootstrap = bootstrap;
083    }
084
085    public DevFrameworkBootstrap getBootstrap() {
086        return bootstrap;
087    }
088
089    public synchronized void addChildren(LocalClassLoader loader) {
090        children.add(loader);
091        _children = null;
092    }
093
094    public synchronized void removeChildren(ClassLoader loader) {
095        children.remove(loader);
096        _children = null;
097    }
098
099    public synchronized void clear() {
100        children.clear();
101        _children = null;
102    }
103
104    public synchronized void flushWebResources() {
105        resourceEntries.clear();
106        ResourceBundle.clearCache(this);
107    }
108
109    public LocalClassLoader[] getChildren() {
110        LocalClassLoader[] cls = _children;
111        if (cls == null) {
112            synchronized (this) {
113                _children = children.toArray(new LocalClassLoader[children.size()]);
114                cls = _children;
115            }
116        }
117        return cls;
118    }
119
120    /**
121     * Do not synchronize this method at method level to avoid deadlocks.
122     */
123    @Override
124    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
125        try {
126            synchronized (this) {
127                return super.loadClass(name, resolve);
128            }
129        } catch (ClassNotFoundException e) {
130            for (LocalClassLoader cl : getChildren()) {
131                try {
132                    return cl.loadLocalClass(name, resolve);
133                } catch (ClassNotFoundException ee) {
134                    // do nothing
135                }
136            }
137        }
138        throw new ClassNotFoundException(name);
139    }
140
141    @Override
142    public URL getResource(String name) {
143        URL url = super.getResource(name);
144        if (url != null) {
145            return url;
146        }
147        for (LocalClassLoader cl : getChildren()) {
148            url = cl.getLocalResource(name);
149            if (url != null) {
150                return url;
151            }
152        }
153        return null;
154    }
155
156    @Override
157    public InputStream getResourceAsStream(String name) {
158        InputStream is = super.getResourceAsStream(name);
159        if (is != null) {
160            return is;
161        }
162        for (LocalClassLoader cl : getChildren()) {
163            try {
164                is = cl.getLocalResourceAsStream(name);
165            } catch (IOException e) {
166                throw new RuntimeException("Cannot read input from " + name, e);
167            }
168            if (is != null) {
169                return is;
170            }
171        }
172        return null;
173    }
174
175    @Override
176    public Enumeration<URL> getResources(String name) throws IOException {
177        CompoundEnumeration<URL> enums = new CompoundEnumeration<URL>();
178        enums.add(super.getResources(name));
179        for (LocalClassLoader cl : getChildren()) {
180            enums.add(cl.getLocalResources(name));
181        }
182        return enums;
183    }
184
185    @Override
186    public void addURL(URL url) {
187        super.addURL(url);
188    }
189
190    public ClassLoader getParentClassLoader() {
191        return parent;
192    }
193
194    @Override
195    public ClassLoader getClassLoader() {
196        return this;
197    }
198
199    protected static class CompoundEnumeration<E> implements Enumeration<E> {
200
201        private final List<Enumeration<E>> enums = new ArrayList<Enumeration<E>>();
202
203        private int index = 0;
204
205        public CompoundEnumeration() {
206            // nothing to do
207        }
208
209        public CompoundEnumeration(List<Enumeration<E>> enums) {
210            this.enums.addAll(enums);
211        }
212
213        private boolean next() {
214            while (index < enums.size()) {
215                if (enums.get(index) != null && enums.get(index).hasMoreElements()) {
216                    return true;
217                }
218                index++;
219            }
220            return false;
221        }
222
223        @Override
224        public boolean hasMoreElements() {
225            return next();
226        }
227
228        @Override
229        public E nextElement() {
230            if (!next()) {
231                throw new NoSuchElementException();
232            }
233            return enums.get(index).nextElement();
234        }
235
236        public void add(Enumeration<E> e) {
237            if (!e.hasMoreElements()) {
238                return;
239            }
240            enums.add(e);
241        }
242    }
243
244}