001/*
002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 *
017 * $Id$
018 */
019
020package org.nuxeo.runtime.tomcat.dev;
021
022import java.io.IOException;
023import java.io.InputStream;
024import java.net.URL;
025import java.util.ArrayList;
026import java.util.Enumeration;
027import java.util.List;
028import java.util.NoSuchElementException;
029import java.util.ResourceBundle;
030
031import org.nuxeo.osgi.application.MutableClassLoader;
032import org.nuxeo.runtime.tomcat.NuxeoWebappClassLoader;
033
034/**
035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
036 */
037
038public class NuxeoDevWebappClassLoader extends NuxeoWebappClassLoader implements MutableClassLoader,
039        WebResourcesCacheFlusher {
040
041    public LocalClassLoader createLocalClassLoader(URL... urls) {
042        LocalClassLoader cl = new LocalURLClassLoader(urls, this);
043        addChildren(cl);
044        return cl;
045    }
046
047    protected DevFrameworkBootstrap bootstrap;
048
049    protected List<LocalClassLoader> children;
050
051    protected volatile LocalClassLoader[] _children;
052
053    public NuxeoDevWebappClassLoader() {
054        super();
055        this.children = new ArrayList<LocalClassLoader>();
056    }
057
058    public NuxeoDevWebappClassLoader(ClassLoader parent) {
059        super(parent);
060        this.children = new ArrayList<LocalClassLoader>();
061    }
062
063    public void setBootstrap(DevFrameworkBootstrap bootstrap) {
064        this.bootstrap = bootstrap;
065    }
066
067    public DevFrameworkBootstrap getBootstrap() {
068        return bootstrap;
069    }
070
071    public synchronized void addChildren(LocalClassLoader loader) {
072        children.add(loader);
073        _children = null;
074    }
075
076    public synchronized void removeChildren(ClassLoader loader) {
077        children.remove(loader);
078        _children = null;
079    }
080
081    public synchronized void clear() {
082        children.clear();
083        _children = null;
084    }
085
086    public synchronized void flushWebResources() {
087        resourceEntries.clear();
088        ResourceBundle.clearCache(this);
089    }
090
091    public LocalClassLoader[] getChildren() {
092        LocalClassLoader[] cls = _children;
093        if (cls == null) {
094            synchronized (this) {
095                _children = children.toArray(new LocalClassLoader[children.size()]);
096                cls = _children;
097            }
098        }
099        return cls;
100    }
101
102    /**
103     * Do not synchronize this method at method level to avoid deadlocks.
104     */
105    @Override
106    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
107        try {
108            synchronized (this) {
109                return super.loadClass(name, resolve);
110            }
111        } catch (ClassNotFoundException e) {
112            for (LocalClassLoader cl : getChildren()) {
113                try {
114                    return cl.loadLocalClass(name, resolve);
115                } catch (ClassNotFoundException ee) {
116                    // do nothing
117                }
118            }
119        }
120        throw new ClassNotFoundException(name);
121    }
122
123    @Override
124    public URL getResource(String name) {
125        URL url = super.getResource(name);
126        if (url != null) {
127            return url;
128        }
129        for (LocalClassLoader cl : getChildren()) {
130            url = cl.getLocalResource(name);
131            if (url != null) {
132                return url;
133            }
134        }
135        return null;
136    }
137
138    @Override
139    public InputStream getResourceAsStream(String name) {
140        InputStream is = super.getResourceAsStream(name);
141        if (is != null) {
142            return is;
143        }
144        for (LocalClassLoader cl : getChildren()) {
145            try {
146                is = cl.getLocalResourceAsStream(name);
147            } catch (IOException e) {
148                throw new RuntimeException("Cannot read input from " + name, e);
149            }
150            if (is != null) {
151                return is;
152            }
153        }
154        return null;
155    }
156
157    @Override
158    public Enumeration<URL> getResources(String name) throws IOException {
159        CompoundEnumeration<URL> enums = new CompoundEnumeration<URL>();
160        enums.add(super.getResources(name));
161        for (LocalClassLoader cl : getChildren()) {
162            enums.add(cl.getLocalResources(name));
163        }
164        return enums;
165    }
166
167    @Override
168    public void addURL(URL url) {
169        super.addURL(url);
170    }
171
172    @Override
173    public void setParentClassLoader(ClassLoader pcl) {
174        super.setParentClassLoader(pcl);
175    }
176
177    public ClassLoader getParentClassLoader() {
178        return parent;
179    }
180
181    @Override
182    public ClassLoader getClassLoader() {
183        return this;
184    }
185
186    protected static class CompoundEnumeration<E> implements Enumeration<E> {
187
188        private final List<Enumeration<E>> enums = new ArrayList<Enumeration<E>>();
189
190        private int index = 0;
191
192        public CompoundEnumeration() {
193            // nothing to do
194        }
195
196        public CompoundEnumeration(List<Enumeration<E>> enums) {
197            this.enums.addAll(enums);
198        }
199
200        private boolean next() {
201            while (index < enums.size()) {
202                if (enums.get(index) != null && enums.get(index).hasMoreElements()) {
203                    return true;
204                }
205                index++;
206            }
207            return false;
208        }
209
210        @Override
211        public boolean hasMoreElements() {
212            return next();
213        }
214
215        @Override
216        public E nextElement() {
217            if (!next()) {
218                throw new NoSuchElementException();
219            }
220            return enums.get(index).nextElement();
221        }
222
223        public void add(Enumeration<E> e) {
224            if (!e.hasMoreElements()) {
225                return;
226            }
227            enums.add(e);
228        }
229    }
230
231}