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