001 /* 002 * Copyright 2005-2008 The Kuali Foundation 003 * 004 * 005 * Licensed under the Educational Community License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.opensource.org/licenses/ecl2.php 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.kuali.rice.kew.plugin; 018 019 import java.util.ArrayList; 020 import java.util.Iterator; 021 import java.util.List; 022 023 import javax.xml.namespace.QName; 024 025 import org.apache.log4j.Logger; 026 import org.kuali.rice.core.config.Config; 027 import org.kuali.rice.core.resourceloader.BaseWrappingResourceLoader; 028 import org.kuali.rice.core.resourceloader.ContextClassLoaderBinder; 029 import org.kuali.rice.kew.exception.WorkflowRuntimeException; 030 031 032 /** 033 * A KEW Plugin. A Plugin represents a distinct classloading space living below (as a child) of the core 034 * KEW classloader. It allows for loading of plugin resources from core components of the system. 035 * Essentially a Plugin is a specialized ResourceLoader with a custom classloader and attached configuration. 036 * 037 * @author Kuali Rice Team (rice.collab@kuali.org) 038 */ 039 public class Plugin extends BaseWrappingResourceLoader { 040 041 private static final Logger LOG = Logger.getLogger(Plugin.class); 042 private Config config; 043 private List<PluginListener> pluginListeners = new ArrayList<PluginListener>(); 044 045 private boolean supressStartupFailure = true; 046 private boolean started = false; 047 private boolean startupFailure = false; 048 049 public Plugin(QName name, Config config, ClassLoader classLoader) { 050 super(name, classLoader); 051 this.config = config; 052 } 053 054 /** 055 * Starts the plugin. 056 */ 057 public synchronized void start() { 058 if (started) { 059 LOG.info(getLogPrefix()+" has already been started."); 060 return; 061 } 062 LOG.info(getLogPrefix()+" Starting..."); 063 try { 064 bindThread(); 065 try { 066 startupFailure = false; 067 started = true; 068 super.start(); 069 LOG.info("Starting plugin listeners"); 070 startPluginListeners(); 071 } finally { 072 unbindThread(); 073 } 074 ClassLoader classLoader = getClassLoader(); 075 LOG.info(getLogPrefix()+" ...started." + (classLoader != null ? classLoader.toString() : "")); 076 } catch (Throwable t) { 077 LOG.error(getLogPrefix()+" Failure starting plugin.", t); 078 startupFailure = true; 079 started = true; 080 stop(); 081 if (!supressStartupFailure) { 082 if (t instanceof Error) { 083 throw (Error)t; 084 } else if (t instanceof RuntimeException) { 085 throw (RuntimeException)t; 086 } 087 throw new WorkflowRuntimeException("Failed to startup plugin.", t); 088 } 089 } 090 } 091 092 /** 093 * Stops the plugin. 094 */ 095 public synchronized void stop() { 096 if (!started) { 097 LOG.info(getLogPrefix()+" has already been stopped."); 098 return; 099 } 100 LOG.info(getLogPrefix()+" Stopping..."); 101 bindThread(); 102 try { 103 started = false; 104 stopPluginListeners(); 105 // stop resource loaders of super class 106 super.stop(); 107 } catch (Throwable t) { 108 LOG.error(getLogPrefix()+" Failed when attempting to stop the plugin.", t); 109 } finally { 110 unbindThread(); 111 } 112 resetPlugin(); 113 LOG.info(getLogPrefix()+" ...stopped."); 114 } 115 116 public boolean isStarted() { 117 return started; 118 } 119 120 public void addPluginListener(PluginListener pluginListener) { 121 pluginListeners.add(pluginListener); 122 } 123 124 public void removePluginListener(PluginListener pluginListener) { 125 pluginListeners.remove(pluginListener); 126 } 127 128 protected void startPluginListeners() { 129 for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) { 130 PluginListener listener = (PluginListener) iterator.next(); 131 listener.pluginInitialized(this); 132 } 133 } 134 135 /** 136 * If we fail to stop a plugin listener, try the next one but don't propogate any 137 * exceptions out of this method. Otherwise the plugin ends up dying and can't be 138 * reloaded from a hot deploy. 139 */ 140 protected void stopPluginListeners() { 141 for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) { 142 PluginListener listener = (PluginListener) iterator.next(); 143 try { 144 listener.pluginDestroyed(this); 145 } catch (Throwable t) { 146 LOG.error(getLogPrefix()+" Failed when invoking pluginDestroyed on Plugin Listener '"+listener.getClass().getName()+"'.", t); 147 } 148 } 149 } 150 151 public boolean isSupressStartupFailure() { 152 return supressStartupFailure; 153 } 154 155 public void setSupressStartupFailure(boolean supressStartupFailure) { 156 this.supressStartupFailure = supressStartupFailure; 157 } 158 159 public void bindThread() { 160 ContextClassLoaderBinder.bind(getClassLoader()); 161 } 162 163 public void unbindThread() { 164 ContextClassLoaderBinder.unbind(); 165 } 166 167 /** 168 * Cleanup plugin resources. 169 */ 170 private void resetPlugin() { 171 if (!startupFailure) { 172 setClassLoader(null); 173 } 174 pluginListeners.clear(); 175 } 176 177 private String getLogPrefix() { 178 return toString(); 179 } 180 181 public Config getConfig() { 182 return config; 183 } 184 185 public String toString() { 186 return "[Plugin: " + this.getName() + "]"; 187 } 188 189 }