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}