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