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