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.MutableClassLoader;
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 MutableClassLoader,
041        WebResourcesCacheFlusher {
042
043    public LocalClassLoader createLocalClassLoader(URL... urls) {
044        LocalClassLoader cl = new LocalURLClassLoader(urls, this);
045        addChildren(cl);
046        return cl;
047    }
048
049    protected DevFrameworkBootstrap bootstrap;
050
051    protected List<LocalClassLoader> children;
052
053    protected volatile LocalClassLoader[] _children;
054
055    public NuxeoDevWebappClassLoader() {
056        super();
057        this.children = new ArrayList<LocalClassLoader>();
058    }
059
060    public NuxeoDevWebappClassLoader(ClassLoader parent) {
061        super(parent);
062        this.children = new ArrayList<LocalClassLoader>();
063    }
064
065    public void setBootstrap(DevFrameworkBootstrap bootstrap) {
066        this.bootstrap = bootstrap;
067    }
068
069    public DevFrameworkBootstrap getBootstrap() {
070        return bootstrap;
071    }
072
073    public synchronized void addChildren(LocalClassLoader loader) {
074        children.add(loader);
075        _children = null;
076    }
077
078    public synchronized void removeChildren(ClassLoader loader) {
079        children.remove(loader);
080        _children = null;
081    }
082
083    public synchronized void clear() {
084        children.clear();
085        _children = null;
086    }
087
088    public synchronized void flushWebResources() {
089        resourceEntries.clear();
090        ResourceBundle.clearCache(this);
091    }
092
093    public LocalClassLoader[] getChildren() {
094        LocalClassLoader[] cls = _children;
095        if (cls == null) {
096            synchronized (this) {
097                _children = children.toArray(new LocalClassLoader[children.size()]);
098                cls = _children;
099            }
100        }
101        return cls;
102    }
103
104    /**
105     * Do not synchronize this method at method level to avoid deadlocks.
106     */
107    @Override
108    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
109        try {
110            synchronized (this) {
111                return super.loadClass(name, resolve);
112            }
113        } catch (ClassNotFoundException e) {
114            for (LocalClassLoader cl : getChildren()) {
115                try {
116                    return cl.loadLocalClass(name, resolve);
117                } catch (ClassNotFoundException ee) {
118                    // do nothing
119                }
120            }
121        }
122        throw new ClassNotFoundException(name);
123    }
124
125    @Override
126    public URL getResource(String name) {
127        URL url = super.getResource(name);
128        if (url != null) {
129            return url;
130        }
131        for (LocalClassLoader cl : getChildren()) {
132            url = cl.getLocalResource(name);
133            if (url != null) {
134                return url;
135            }
136        }
137        return null;
138    }
139
140    @Override
141    public InputStream getResourceAsStream(String name) {
142        InputStream is = super.getResourceAsStream(name);
143        if (is != null) {
144            return is;
145        }
146        for (LocalClassLoader cl : getChildren()) {
147            try {
148                is = cl.getLocalResourceAsStream(name);
149            } catch (IOException e) {
150                throw new RuntimeException("Cannot read input from " + name, e);
151            }
152            if (is != null) {
153                return is;
154            }
155        }
156        return null;
157    }
158
159    @Override
160    public Enumeration<URL> getResources(String name) throws IOException {
161        CompoundEnumeration<URL> enums = new CompoundEnumeration<URL>();
162        enums.add(super.getResources(name));
163        for (LocalClassLoader cl : getChildren()) {
164            enums.add(cl.getLocalResources(name));
165        }
166        return enums;
167    }
168
169    @Override
170    public void addURL(URL url) {
171        super.addURL(url);
172    }
173
174    @Override
175    public void setParentClassLoader(ClassLoader pcl) {
176        super.setParentClassLoader(pcl);
177    }
178
179    public ClassLoader getParentClassLoader() {
180        return parent;
181    }
182
183    @Override
184    public ClassLoader getClassLoader() {
185        return this;
186    }
187
188    protected static class CompoundEnumeration<E> implements Enumeration<E> {
189
190        private final List<Enumeration<E>> enums = new ArrayList<Enumeration<E>>();
191
192        private int index = 0;
193
194        public CompoundEnumeration() {
195            // nothing to do
196        }
197
198        public CompoundEnumeration(List<Enumeration<E>> enums) {
199            this.enums.addAll(enums);
200        }
201
202        private boolean next() {
203            while (index < enums.size()) {
204                if (enums.get(index) != null && enums.get(index).hasMoreElements()) {
205                    return true;
206                }
207                index++;
208            }
209            return false;
210        }
211
212        @Override
213        public boolean hasMoreElements() {
214            return next();
215        }
216
217        @Override
218        public E nextElement() {
219            if (!next()) {
220                throw new NoSuchElementException();
221            }
222            return enums.get(index).nextElement();
223        }
224
225        public void add(Enumeration<E> e) {
226            if (!e.hasMoreElements()) {
227                return;
228            }
229            enums.add(e);
230        }
231    }
232
233}