View Javadoc

1   /*
2    * Copyright 2006-2011 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  
17  package org.kuali.rice.kew.plugin;
18  
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  
25  import javax.xml.namespace.QName;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.log4j.Logger;
29  import org.kuali.rice.core.api.CoreConstants;
30  import org.kuali.rice.core.api.config.CoreConfigHelper;
31  import org.kuali.rice.core.api.config.property.Config;
32  import org.kuali.rice.core.api.config.property.ConfigContext;
33  import org.kuali.rice.core.util.ClassLoaderUtils;
34  import org.kuali.rice.core.util.ContextClassLoaderBinder;
35  import org.kuali.rice.core.util.xml.XmlException;
36  
37  
38  /**
39   * Abstract base PluginLoader implementation.
40   * Delegates to template methods to obtain plugin ClassLoader and plugin config file URL,
41   * then load the config under the plugin ClassLoader, and constructs a Plugin object.
42   *
43   * @author Kuali Rice Team (rice.collab@kuali.org)
44   */
45  public abstract class BasePluginLoader implements PluginLoader {
46      private static final Logger LOG = Logger.getLogger(BasePluginLoader.class);
47  
48      private static final String META_INF_PATH = "META-INF";
49      private static final String PLUGIN_CONFIG_PATH = META_INF_PATH + "/workflow.xml";
50  
51      protected final String simplePluginName;
52      protected String logPrefix;
53  
54      protected final ClassLoader parentClassLoader;
55      protected final Config parentConfig;
56      protected final File sharedPluginDirectory;
57      protected String pluginConfigPath = PLUGIN_CONFIG_PATH;
58  
59      public BasePluginLoader(String simplePluginName, File sharedPluginDirectory, ClassLoader parentClassLoader, Config parentConfig) {
60          this.sharedPluginDirectory = sharedPluginDirectory;
61          if (parentClassLoader == null) {
62              parentClassLoader = ClassLoaderUtils.getDefaultClassLoader();
63          }
64          this.parentClassLoader = parentClassLoader;
65          this.parentConfig = parentConfig;
66          this.simplePluginName = simplePluginName;
67          this.logPrefix = simplePluginName;
68      }
69  
70      protected String getLogPrefix() {
71          return logPrefix;
72      }
73      
74      public String getPluginName() {
75          return simplePluginName;
76      }
77  
78      public void setPluginConfigPath(String pluginConfigPath) {
79          this.pluginConfigPath = pluginConfigPath;
80      }
81  
82      protected String getSimplePluginName() {
83      	return simplePluginName;
84      }
85  
86      /**
87       * Template method that subclasses should implement to supply an appropriate
88       * plugin ClassLoader
89       * @return an appropriate PluginClassLoader
90       * @throws IOException if anything goes awry
91       */
92      protected abstract PluginClassLoader createPluginClassLoader() throws IOException;
93      /**
94       * Template method that subclasses should implement to supply an appropriate
95       * URL to the plugin's configuration
96       * @return an appropriate URL to the plugin's configuration
97       * @throws IOException if anything goes awry
98       */
99      protected abstract URL getPluginConfigURL() throws PluginException, IOException;
100 
101     /**
102      * Loads and creates the Plugin.
103      */
104     public Plugin load() throws Exception {
105         PluginClassLoader classLoader = createPluginClassLoader();
106         LOG.info("Created plugin ClassLoader: " + classLoader);
107         ContextClassLoaderBinder.bind(classLoader);
108         try {
109             return loadWithinContextClassLoader(classLoader);
110         } finally {
111             ContextClassLoaderBinder.unbind();
112         }
113     }
114 
115     public boolean isRemoved() {
116     	return false;
117     }
118 
119     /**
120      * Executes loading of the plugin within the current context classloader set to the Plugin's classloader.
121      */
122     protected Plugin loadWithinContextClassLoader(PluginClassLoader classLoader) throws PluginException, IOException {
123     	URL url = getPluginConfigURL();
124         PluginConfig pluginConfig = loadPluginConfig(url);
125         QName qPluginName = getPluginName(pluginConfig);
126         classLoader.setConfig(pluginConfig);
127         ConfigContext.init(classLoader, pluginConfig);
128         configureExtraClasspath(classLoader, pluginConfig);
129         this.logPrefix = PluginUtils.getLogPrefix(qPluginName).toString();
130         LOG.info("Constructing plugin '" + simplePluginName + "' with classloader: " + classLoader);
131         Plugin plugin = new Plugin(qPluginName, pluginConfig, classLoader);
132         installResourceLoader(plugin);
133         installPluginListeners(plugin);
134         return plugin;
135     }
136 
137     protected void installResourceLoader(Plugin plugin) {
138     	PluginUtils.installResourceLoader(plugin);
139     }
140 
141     protected void installPluginListeners(Plugin plugin) {
142     	PluginUtils.installPluginListeners(plugin);
143     }
144 
145     protected void configureExtraClasspath(PluginClassLoader classLoader, PluginConfig config) throws MalformedURLException {
146 		String extraClassesDirs = config.getProperty(Config.EXTRA_CLASSES_DIR);
147 		if (!org.apache.commons.lang.StringUtils.isEmpty(extraClassesDirs)) {
148 			String[] extraClasses = extraClassesDirs.split(",");
149 			for (int index = 0; index < extraClasses.length; index++) {
150 				File extraClassesDir = new File(extraClasses[index]);
151 				if (extraClassesDir.exists()) {
152 					classLoader.addClassesDirectory(extraClassesDir);
153 				}
154 			}
155 		}
156 		String extraLibDirs = config.getProperty(Config.EXTRA_LIB_DIR);
157 		if (!org.apache.commons.lang.StringUtils.isEmpty(extraLibDirs)) {
158 			String[] extraLibs = extraLibDirs.split(",");
159 			for (int index = 0; index < extraLibs.length; index++) {
160 				File extraLibDir = new File(extraLibs[index]);
161 				if (extraLibDir.exists()) {
162 					classLoader.addLibDirectory(extraLibDir);
163 				}
164 			}
165 		}
166 	}
167 
168 
169     protected QName getPluginName(PluginConfig pluginConfig) {
170     	String applicationId = pluginConfig.getProperty(CoreConstants.Config.APPLICATION_ID);
171     	QName qPluginName = null;
172         if (StringUtils.isBlank(applicationId)) {
173         	qPluginName = new QName(CoreConfigHelper.getApplicationId(), simplePluginName);
174         } else {
175         	qPluginName = new QName(applicationId, simplePluginName);
176         }
177     	return qPluginName;
178     }
179 
180     protected PluginConfig loadPluginConfig(URL url) {
181         PluginConfigParser parser = new PluginConfigParser();
182         try {
183             PluginConfig pluginConfig  = parser.parse(url, parentConfig);
184             pluginConfig.parseConfig();
185             return pluginConfig;
186         } catch (FileNotFoundException e) {
187             throw new PluginException(getLogPrefix() + " Could not locate the plugin config file at path " + url, e);
188         } catch (IOException ioe) {
189             throw new PluginException(getLogPrefix() + " Could not read the plugin config file", ioe);
190         } catch (XmlException ixe) {
191             throw new PluginException(getLogPrefix() + " Could not parse the plugin config file", ixe);
192         }
193     }
194 }