View Javadoc

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