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}