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 }