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 */ 017package org.nuxeo.ecm.webengine.loader.store; 018 019import java.io.IOException; 020import java.net.URL; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.Enumeration; 024import java.util.LinkedHashSet; 025import java.util.List; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029 030/** 031 * The class loader allows modifying the stores (adding/removing). Mutable operations are thread safe. 032 * 033 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 034 */ 035public class ResourceStoreClassLoader extends ClassLoader implements Cloneable { 036 037 private final Log log = LogFactory.getLog(ResourceStoreClassLoader.class); 038 039 private volatile ResourceStore[] stores; 040 041 private final LinkedHashSet<ResourceStore> cp; // class path 042 043 public ResourceStoreClassLoader(final ClassLoader pParent) { 044 this(pParent, new LinkedHashSet<ResourceStore>()); 045 } 046 047 protected ResourceStoreClassLoader(final ClassLoader pParent, LinkedHashSet<ResourceStore> cp) { 048 super(pParent); 049 this.cp = cp; 050 if (!cp.isEmpty()) { 051 stores = cp.toArray(new ResourceStore[cp.size()]); 052 } 053 } 054 055 public synchronized boolean addStore(ResourceStore store) { 056 if (cp.add(store)) { 057 stores = cp.toArray(new ResourceStore[cp.size()]); 058 return true; 059 } 060 return false; 061 } 062 063 public synchronized boolean removeStore(ResourceStore store) { 064 if (cp.remove(store)) { 065 stores = cp.toArray(new ResourceStore[cp.size()]); 066 return true; 067 } 068 return false; 069 } 070 071 @Override 072 public synchronized ResourceStoreClassLoader clone() { 073 return new ResourceStoreClassLoader(getParent(), new LinkedHashSet<ResourceStore>(cp)); 074 } 075 076 public ResourceStore[] getStores() { 077 return stores; 078 } 079 080 protected Class<?> fastFindClass(final String name) { 081 ResourceStore[] _stores = stores; // use a local variable 082 if (_stores != null) { 083 for (final ResourceStore store : _stores) { 084 final byte[] clazzBytes = store.getBytes(convertClassToResourcePath(name)); 085 if (clazzBytes != null) { 086 if (log.isTraceEnabled()) { 087 log.trace(getId() + " found class: " + name + " (" + clazzBytes.length + " bytes)"); 088 } 089 doDefinePackage(name); 090 return defineClass(name, clazzBytes, 0, clazzBytes.length); 091 } 092 } 093 } 094 return null; 095 } 096 097 /** 098 * Without this method getPackage() returns null 099 * 100 * @param name 101 */ 102 protected void doDefinePackage(String name) { 103 int i = name.lastIndexOf('.'); 104 if (i > -1) { 105 String pkgname = name.substring(0, i); 106 Package pkg = getPackage(pkgname); 107 if (pkg == null) { 108 definePackage(pkgname, null, null, null, null, null, null, null); 109 } 110 } 111 } 112 113 @Override 114 protected URL findResource(String name) { 115 ResourceStore[] _stores = stores; // use a local variable 116 if (_stores != null) { 117 for (final ResourceStore store : _stores) { 118 final URL url = store.getURL(name); 119 if (url != null) { 120 if (log.isTraceEnabled()) { 121 log.trace(getId() + " found resource: " + name); 122 } 123 return url; 124 } 125 } 126 } 127 return null; 128 } 129 130 @Override 131 protected Enumeration<URL> findResources(String name) throws IOException { 132 ResourceStore[] _stores = stores; // use a local variable 133 if (_stores != null) { 134 List<URL> result = new ArrayList<URL>(); 135 for (final ResourceStore store : _stores) { 136 final URL url = store.getURL(name); 137 if (url != null) { 138 if (log.isTraceEnabled()) { 139 log.trace(getId() + " found resource: " + name); 140 } 141 result.add(url); 142 } 143 } 144 return Collections.enumeration(result); 145 } 146 return null; 147 } 148 149 @Override 150 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 151 // log.debug(getId() + " looking for: " + name); 152 Class<?> clazz = findLoadedClass(name); 153 154 if (clazz == null) { 155 clazz = fastFindClass(name); 156 157 if (clazz == null) { 158 159 final ClassLoader parent = getParent(); 160 if (parent != null) { 161 clazz = parent.loadClass(name); 162 // log.debug(getId() + " delegating loading to parent: " + name); 163 } else { 164 throw new ClassNotFoundException(name); 165 } 166 167 } else { 168 if (log.isDebugEnabled()) { 169 log.debug(getId() + " loaded from store: " + name); 170 } 171 } 172 } 173 174 if (resolve) { 175 resolveClass(clazz); 176 } 177 178 return clazz; 179 } 180 181 @Override 182 protected Class<?> findClass(final String name) throws ClassNotFoundException { 183 final Class<?> clazz = fastFindClass(name); 184 if (clazz == null) { 185 throw new ClassNotFoundException(name); 186 } 187 return clazz; 188 } 189 190 @Override 191 public Enumeration<URL> getResources(String name) throws IOException { 192 Enumeration<URL> urls = findResources(name); 193 if (urls == null) { 194 final ClassLoader parent = getParent(); 195 if (parent != null) { 196 urls = parent.getResources(name); 197 } 198 } 199 return urls; 200 } 201 202 @Override 203 public URL getResource(String name) { 204 URL url = findResource(name); 205 if (url == null) { 206 final ClassLoader parent = getParent(); 207 if (parent != null) { 208 url = parent.getResource(name); 209 } 210 } 211 return url; 212 } 213 214 // TODO implement this method if you want packages to be supported by this loader 215 @Override 216 protected Package getPackage(String name) { 217 return super.getPackage(name); 218 } 219 220 @Override 221 protected Package[] getPackages() { 222 return super.getPackages(); 223 } 224 225 protected String getId() { 226 return "" + this + "[" + this.getClass().getClassLoader() + "]"; 227 } 228 229 /** 230 * org.my.Class -> org/my/Class.class 231 */ 232 public static String convertClassToResourcePath(final String pName) { 233 return pName.replace('.', '/') + ".class"; 234 } 235 236}