View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.plugin;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.net.URLClassLoader;
23  import java.util.Enumeration;
24  
25  import org.kuali.rice.core.api.lifecycle.Lifecycle;
26  import org.kuali.rice.core.api.util.collect.CollectionUtils;
27  
28  /**
29   * A simple class loader implementation which looks at itself before delegating to its parent.
30   *
31   * @author Kuali Rice Team (rice.collab@kuali.org)
32   */
33  public class PluginClassLoader extends URLClassLoader implements Lifecycle {//implements Modifiable {
34      static final String CLASSES_DIR = "classes";
35      static final String LIB_DIR = "lib";
36      private static final String[] SYSTEM_CLASSES = new String[] { "java.", "javax.servlet.", "javax.xml.", "javax.management.", "org.xml.", "org.w3c." };
37  
38      //private ModificationTracker modTracker = new ModificationTracker();
39      //this is purposely typed.
40      private PluginConfig config;
41      private boolean started = false;
42  
43      public PluginClassLoader() {
44          super(new URL[0]);
45      }
46  
47      public PluginClassLoader(ClassLoader parent) {
48          super(new URL[0], parent);
49      }
50  
51      public PluginClassLoader(ClassLoader parent, File sharedDirectory, File pluginDirectory) throws MalformedURLException {
52          super(new URL[0], parent);
53          if (sharedDirectory != null) {
54              addClassesDirectory(new File(sharedDirectory, CLASSES_DIR));
55              addLibDirectory(new File(sharedDirectory, LIB_DIR));
56          }
57          addClassesDirectory(new File(pluginDirectory, CLASSES_DIR));
58          addLibDirectory(new File(pluginDirectory, LIB_DIR));
59      }
60  
61      public void addClassesDirectory(File classesDir) throws MalformedURLException {
62          if (classesDir != null && classesDir.isDirectory()) {
63              addURL(classesDir.toURI().toURL());
64          }
65      }
66  
67      public void addLibDirectory(File libDir) throws MalformedURLException {
68          File[] jars = PluginUtils.findJars(libDir);
69          for (int index = 0; index < jars.length; index++) {
70              addURL(jars[index].toURI().toURL());
71          }
72      }
73  
74      public Class loadClass(String className) throws ClassNotFoundException {
75          return loadClass(className, false);
76      }
77  
78      public void addURL(URL url) {
79          super.addURL(url);
80          //modTracker.addURL(url);
81      }
82  
83      //public boolean isModified() {
84      //    return modTracker.isModified();
85      //}
86  
87      public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
88          Class loadedClass = loadExistingClass(name, resolve);
89          if (loadedClass != null) {
90              return loadedClass;
91          }
92          loadedClass = loadSystemClass(name, resolve);
93          if (loadedClass != null) {
94              return loadedClass;
95          }
96          loadedClass = loadLocalClass(name, resolve);
97          if (loadedClass != null) {
98              return loadedClass;
99          }
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 }