View Javadoc

1   /**
2    * Copyright 2005-2013 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  import java.util.concurrent.Callable;
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         final PluginClassLoader classLoader = createPluginClassLoader();
104         LOG.info("Created plugin ClassLoader: " + classLoader);
105         return ContextClassLoaderBinder.doInContextClassLoader(classLoader, new Callable<Plugin>() {
106             public Plugin call() throws IOException {
107                 return loadWithinContextClassLoader(classLoader);
108             }
109         });
110     }
111 
112     public boolean isRemoved() {
113     	return false;
114     }
115 
116     /**
117      * Executes loading of the plugin within the current context classloader set to the Plugin's classloader.
118      */
119     protected Plugin loadWithinContextClassLoader(PluginClassLoader classLoader) throws PluginException, IOException {
120     	URL url = getPluginConfigURL();
121         PluginConfig pluginConfig = loadPluginConfig(url);
122         QName qPluginName = getPluginName(pluginConfig);
123         classLoader.setConfig(pluginConfig);
124         ConfigContext.init(classLoader, pluginConfig);
125         configureExtraClasspath(classLoader, pluginConfig);
126         this.logPrefix = PluginUtils.getLogPrefix(qPluginName).toString();
127         LOG.info("Constructing plugin '" + simplePluginName + "' with classloader: " + classLoader);
128         Plugin plugin = new Plugin(qPluginName, pluginConfig, classLoader);
129         installResourceLoader(plugin);
130         installPluginListeners(plugin);
131         return plugin;
132     }
133 
134     protected void installResourceLoader(Plugin plugin) {
135     	PluginUtils.installResourceLoader(plugin);
136     }
137 
138     protected void installPluginListeners(Plugin plugin) {
139     	PluginUtils.installPluginListeners(plugin);
140     }
141 
142     protected void configureExtraClasspath(PluginClassLoader classLoader, PluginConfig config) throws MalformedURLException {
143 		String extraClassesDirs = config.getProperty(Config.EXTRA_CLASSES_DIR);
144 		if (!org.apache.commons.lang.StringUtils.isEmpty(extraClassesDirs)) {
145 			String[] extraClasses = extraClassesDirs.split(",");
146 			for (int index = 0; index < extraClasses.length; index++) {
147 				File extraClassesDir = new File(extraClasses[index]);
148 				if (extraClassesDir.exists()) {
149 					classLoader.addClassesDirectory(extraClassesDir);
150 				}
151 			}
152 		}
153 		String extraLibDirs = config.getProperty(Config.EXTRA_LIB_DIR);
154 		if (!org.apache.commons.lang.StringUtils.isEmpty(extraLibDirs)) {
155 			String[] extraLibs = extraLibDirs.split(",");
156 			for (int index = 0; index < extraLibs.length; index++) {
157 				File extraLibDir = new File(extraLibs[index]);
158 				if (extraLibDir.exists()) {
159 					classLoader.addLibDirectory(extraLibDir);
160 				}
161 			}
162 		}
163 	}
164 
165 
166     protected QName getPluginName(PluginConfig pluginConfig) {
167     	String applicationId = pluginConfig.getProperty(CoreConstants.Config.APPLICATION_ID);
168     	QName qPluginName = null;
169         if (StringUtils.isBlank(applicationId)) {
170         	qPluginName = new QName(CoreConfigHelper.getApplicationId(), simplePluginName);
171         } else {
172         	qPluginName = new QName(applicationId, simplePluginName);
173         }
174     	return qPluginName;
175     }
176 
177     protected PluginConfig loadPluginConfig(URL url) {
178         PluginConfigParser parser = new PluginConfigParser();
179         try {
180             PluginConfig pluginConfig  = parser.parse(url, parentConfig);
181             pluginConfig.parseConfig();
182             return pluginConfig;
183         } catch (FileNotFoundException e) {
184             throw new PluginException(getLogPrefix() + " Could not locate the plugin config file at path " + url, e);
185         } catch (IOException ioe) {
186             throw new PluginException(getLogPrefix() + " Could not read the plugin config file", ioe);
187         } catch (XmlException ixe) {
188             throw new PluginException(getLogPrefix() + " Could not parse the plugin config file", ixe);
189         }
190     }
191 }