001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php 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 */ 016package org.kuali.rice.kew.plugin; 017 018import java.io.File; 019import java.io.IOException; 020import java.net.MalformedURLException; 021import java.net.URL; 022import java.net.URLClassLoader; 023import java.util.Enumeration; 024 025import org.kuali.rice.core.api.lifecycle.Lifecycle; 026import org.kuali.rice.core.api.util.collect.CollectionUtils; 027 028/** 029 * A simple class loader implementation which looks at itself before delegating to its parent. 030 * 031 * @author Kuali Rice Team (rice.collab@kuali.org) 032 */ 033public class PluginClassLoader extends URLClassLoader implements Lifecycle {//implements Modifiable { 034 static final String CLASSES_DIR = "classes"; 035 static final String LIB_DIR = "lib"; 036 private static final String[] SYSTEM_CLASSES = new String[] { "java.", "javax.servlet.", "javax.xml.", "javax.management.", "org.xml.", "org.w3c." }; 037 038 //private ModificationTracker modTracker = new ModificationTracker(); 039 //this is purposely typed. 040 private PluginConfig config; 041 private boolean started = false; 042 043 public PluginClassLoader() { 044 super(new URL[0]); 045 } 046 047 public PluginClassLoader(ClassLoader parent) { 048 super(new URL[0], parent); 049 } 050 051 public PluginClassLoader(ClassLoader parent, File sharedDirectory, File pluginDirectory) throws MalformedURLException { 052 super(new URL[0], parent); 053 if (sharedDirectory != null) { 054 addClassesDirectory(new File(sharedDirectory, CLASSES_DIR)); 055 addLibDirectory(new File(sharedDirectory, LIB_DIR)); 056 } 057 addClassesDirectory(new File(pluginDirectory, CLASSES_DIR)); 058 addLibDirectory(new File(pluginDirectory, LIB_DIR)); 059 } 060 061 public void addClassesDirectory(File classesDir) throws MalformedURLException { 062 if (classesDir != null && classesDir.isDirectory()) { 063 addURL(classesDir.toURI().toURL()); 064 } 065 } 066 067 public void addLibDirectory(File libDir) throws MalformedURLException { 068 File[] jars = PluginUtils.findJars(libDir); 069 for (int index = 0; index < jars.length; index++) { 070 addURL(jars[index].toURI().toURL()); 071 } 072 } 073 074 public Class loadClass(String className) throws ClassNotFoundException { 075 return loadClass(className, false); 076 } 077 078 public void addURL(URL url) { 079 super.addURL(url); 080 //modTracker.addURL(url); 081 } 082 083 //public boolean isModified() { 084 // return modTracker.isModified(); 085 //} 086 087 public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 088 Class loadedClass = loadExistingClass(name, resolve); 089 if (loadedClass != null) { 090 return loadedClass; 091 } 092 loadedClass = loadSystemClass(name, resolve); 093 if (loadedClass != null) { 094 return loadedClass; 095 } 096 loadedClass = loadLocalClass(name, resolve); 097 if (loadedClass != null) { 098 return loadedClass; 099 } 100 loadedClass = loadParentClass(name, resolve); 101 if (loadedClass != null) { 102 return loadedClass; 103 } 104 throw new ClassNotFoundException(name); 105 } 106 107 public URL getResource(String name) { 108 URL resource = findResource(name); 109 if (resource == null) { 110 resource = getParent().getResource(name); 111 } 112 return resource; 113 } 114 115 public Enumeration<URL> getResources(String name) throws IOException { 116 Enumeration<URL> localResources = findResources(name); 117 Enumeration<URL> parentResources = getParent().getResources(name); 118 return CollectionUtils.concat(localResources, parentResources); 119 } 120 121 122 123 private Class loadExistingClass(String name, boolean resolve) { 124 Class loadedClass = findLoadedClass(name); 125 if (loadedClass != null && resolve) { 126 resolveClass(loadedClass); 127 } 128 return loadedClass; 129 } 130 131 private Class loadSystemClass(String name, boolean resolve) { 132 Class loadedClass = null; 133 if (isSystemClass(name)) { 134 try { 135 loadedClass = getSystemClassLoader().loadClass(name); 136 if (loadedClass != null && resolve) { 137 resolveClass(loadedClass); 138 } 139 } catch (ClassNotFoundException e) { 140 // not found in system class loader 141 } 142 } 143 return loadedClass; 144 } 145 146 private Class loadLocalClass(String name, boolean resolve) { 147 Class loadedClass = null; 148 try { 149 loadedClass = findClass(name); 150 if (loadedClass != null && resolve) { 151 resolveClass(loadedClass); 152 } 153 } catch (ClassNotFoundException e) { 154 // not found locally 155 } 156 return loadedClass; 157 } 158 159 private Class loadParentClass(String name, boolean resolve) { 160 Class loadedClass = null; 161 try { 162 loadedClass = getParent().loadClass(name); 163 if (loadedClass != null && resolve) { 164 resolveClass(loadedClass); 165 } 166 } catch (ClassNotFoundException e) { 167 // not found in parent 168 } 169 return loadedClass; 170 } 171 172 /** 173 * This method modeled on the isSystemPath method in Jetty's ContextLoader. 174 * 175 * When loading classes from the system classloader, we really only want to load certain classes 176 * from there so this will tell us whether or not the class name given is one we want to load 177 * from the system classloader. 178 */ 179 private boolean isSystemClass(String name) { 180 name = name.replace('/','.'); 181 while(name.startsWith(".")) { 182 name=name.substring(1); 183 } 184 for (int index = 0; index < SYSTEM_CLASSES.length; index++) { 185 String systemClass = SYSTEM_CLASSES[index]; 186 if (systemClass.endsWith(".")) { 187 if (name.startsWith(systemClass)) { 188 return true; 189 } 190 } 191 else if (name.equals(systemClass)) { 192 return true; 193 } 194 } 195 return false; 196 } 197 198 public String toString() { 199 StringBuffer sb = new StringBuffer("[PluginClassLoader: urls="); 200 URL[] urls = getURLs(); 201 if (urls == null) { 202 sb.append("null"); 203 } else { 204 for (int i = 0; i < urls.length; i++) { 205 sb.append(urls[i]); 206 sb.append(","); 207 } 208 // remove trailing comma 209 if (urls.length > 1) { 210 sb.setLength(sb.length() - 1); 211 } 212 } 213 sb.append("]"); 214 return sb.toString(); 215 } 216 217 public PluginConfig getConfig() { 218 return config; 219 } 220 221 public void setConfig(PluginConfig config) { 222 this.config = config; 223 } 224 225 public void start() { 226 started = true; 227 } 228 229 public void stop() { 230 config = null; 231 started = false; 232 } 233 234 public boolean isStarted() { 235 return started; 236 } 237}