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 clear() {
095        children.clear();
096        _children = null;
097    }
098
099    public synchronized void flushWebResources() {
100        resourceEntries.clear();
101        ResourceBundle.clearCache(this);
102    }
103
104    public LocalClassLoader[] getChildren() {
105        LocalClassLoader[] cls = _children;
106        if (cls == null) {
107            synchronized (this) {
108                _children = children.toArray(new LocalClassLoader[children.size()]);
109                cls = _children;
110            }
111        }
112        return cls;
113    }
114
115    /**
116     * Do not synchronize this method at method level to avoid deadlocks.
117     */
118    @Override
119    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
120        try {
121            synchronized (this) {
122                return super.loadClass(name, resolve);
123            }
124        } catch (ClassNotFoundException e) {
125            for (LocalClassLoader cl : getChildren()) {
126                try {
127                    return cl.loadLocalClass(name, resolve);
128                } catch (ClassNotFoundException ee) {
129                    // do nothing
130                }
131            }
132        }
133        throw new ClassNotFoundException(name);
134    }
135
136    @Override
137    public URL getResource(String name) {
138        URL url = super.getResource(name);
139        if (url != null) {
140            return url;
141        }
142        for (LocalClassLoader cl : getChildren()) {
143            url = cl.getLocalResource(name);
144            if (url != null) {
145                return url;
146            }
147        }
148        return null;
149    }
150
151    @Override
152    public InputStream getResourceAsStream(String name) {
153        InputStream is = super.getResourceAsStream(name);
154        if (is != null) {
155            return is;
156        }
157        for (LocalClassLoader cl : getChildren()) {
158            try {
159                is = cl.getLocalResourceAsStream(name);
160            } catch (IOException e) {
161                throw new RuntimeException("Cannot read input from " + name, e);
162            }
163            if (is != null) {
164                return is;
165            }
166        }
167        return null;
168    }
169
170    @Override
171    public Enumeration<URL> getResources(String name) throws IOException {
172        CompoundEnumeration<URL> enums = new CompoundEnumeration<URL>();
173        enums.add(super.getResources(name));
174        for (LocalClassLoader cl : getChildren()) {
175            enums.add(cl.getLocalResources(name));
176        }
177        return enums;
178    }
179
180    @Override
181    public void addURL(URL url) {
182        super.addURL(url);
183    }
184
185    public ClassLoader getParentClassLoader() {
186        return parent;
187    }
188
189    @Override
190    public ClassLoader getClassLoader() {
191        return this;
192    }
193
194    protected static class CompoundEnumeration<E> implements Enumeration<E> {
195
196        private final List<Enumeration<E>> enums = new ArrayList<Enumeration<E>>();
197
198        private int index = 0;
199
200        public CompoundEnumeration() {
201            // nothing to do
202        }
203
204        public CompoundEnumeration(List<Enumeration<E>> enums) {
205            this.enums.addAll(enums);
206        }
207
208        private boolean next() {
209            while (index < enums.size()) {
210                if (enums.get(index) != null && enums.get(index).hasMoreElements()) {
211                    return true;
212                }
213                index++;
214            }
215            return false;
216        }
217
218        @Override
219        public boolean hasMoreElements() {
220            return next();
221        }
222
223        @Override
224        public E nextElement() {
225            if (!next()) {
226                throw new NoSuchElementException();
227            }
228            return enums.get(index).nextElement();
229        }
230
231        public void add(Enumeration<E> e) {
232            if (!e.hasMoreElements()) {
233                return;
234            }
235            enums.add(e);
236        }
237    }
238
239}