001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.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/ecl1.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 edu.iu.uis.eden.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.config.Config;
027: import org.kuali.rice.resourceloader.BaseWrappingResourceLoader;
028: import org.kuali.rice.resourceloader.ContextClassLoaderBinder;
029:
030: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
031: import edu.iu.uis.eden.plugin.client.PluginListener;
032:
033: /**
034: * A KEW Plugin. A Plugin represents a distinct classloading space living below (as a child) of the core
035: * KEW classloader. It allows for loading of plugin resources from core components of the system.
036: * Essentially a Plugin is a specialized ResourceLoader with a custom classloader and attached configuration.
037: *
038: * @author ewestfal
039: */
040: public class Plugin extends BaseWrappingResourceLoader {
041:
042: private static final Logger LOG = Logger.getLogger(Plugin.class);
043: private Config config;
044: private List<PluginListener> pluginListeners = new ArrayList<PluginListener>();
045:
046: private boolean supressStartupFailure = true;
047: private boolean started = false;
048: private boolean startupFailure = false;
049:
050: public Plugin(QName name, Config config, ClassLoader classLoader) {
051: super (name, classLoader);
052: this .config = config;
053: }
054:
055: /**
056: * Starts the plugin.
057: */
058: public synchronized void start() {
059: if (started) {
060: LOG.info(getLogPrefix() + " has already been started.");
061: return;
062: }
063: LOG.info(getLogPrefix() + " Starting...");
064: try {
065: bindThread();
066: try {
067: startupFailure = false;
068: started = true;
069: super .start();
070: LOG.info("Starting plugin listeners");
071: startPluginListeners();
072: } finally {
073: unbindThread();
074: }
075: ClassLoader classLoader = getClassLoader();
076: LOG.info(getLogPrefix()
077: + " ...started."
078: + (classLoader != null ? classLoader.toString()
079: : ""));
080: } catch (Throwable t) {
081: LOG.error(getLogPrefix() + " Failure starting plugin.", t);
082: startupFailure = true;
083: started = true;
084: stop();
085: if (!supressStartupFailure) {
086: if (t instanceof Error) {
087: throw (Error) t;
088: } else if (t instanceof RuntimeException) {
089: throw (RuntimeException) t;
090: }
091: throw new WorkflowRuntimeException(
092: "Failed to startup plugin.", t);
093: }
094: }
095: }
096:
097: /**
098: * Stops the plugin.
099: */
100: public synchronized void stop() {
101: if (!started) {
102: LOG.info(getLogPrefix() + " has already been stopped.");
103: return;
104: }
105: LOG.info(getLogPrefix() + " Stopping...");
106: bindThread();
107: try {
108: started = false;
109: stopPluginListeners();
110: // stop resource loaders of super class
111: super .stop();
112: } catch (Throwable t) {
113: LOG.error(getLogPrefix()
114: + " Failed when attempting to stop the plugin.", t);
115: } finally {
116: unbindThread();
117: }
118: resetPlugin();
119: LOG.info(getLogPrefix() + " ...stopped.");
120: }
121:
122: public boolean isStarted() {
123: return started;
124: }
125:
126: public void addPluginListener(PluginListener pluginListener) {
127: pluginListeners.add(pluginListener);
128: }
129:
130: public void removePluginListener(PluginListener pluginListener) {
131: pluginListeners.remove(pluginListener);
132: }
133:
134: protected void startPluginListeners() {
135: for (Iterator iterator = pluginListeners.iterator(); iterator
136: .hasNext();) {
137: PluginListener listener = (PluginListener) iterator.next();
138: listener.pluginInitialized(this );
139: }
140: }
141:
142: /**
143: * If we fail to stop a plugin listener, try the next one but don't propogate any
144: * exceptions out of this method. Otherwise the plugin ends up dying and can't be
145: * reloaded from a hot deploy.
146: */
147: protected void stopPluginListeners() {
148: for (Iterator iterator = pluginListeners.iterator(); iterator
149: .hasNext();) {
150: PluginListener listener = (PluginListener) iterator.next();
151: try {
152: listener.pluginDestroyed(this );
153: } catch (Throwable t) {
154: LOG
155: .error(
156: getLogPrefix()
157: + " Failed when invoking pluginDestroyed on Plugin Listener '"
158: + listener.getClass().getName()
159: + "'.", t);
160: }
161: }
162: }
163:
164: public boolean isSupressStartupFailure() {
165: return supressStartupFailure;
166: }
167:
168: public void setSupressStartupFailure(boolean supressStartupFailure) {
169: this .supressStartupFailure = supressStartupFailure;
170: }
171:
172: public void bindThread() {
173: ContextClassLoaderBinder.bind(getClassLoader());
174: }
175:
176: public void unbindThread() {
177: ContextClassLoaderBinder.unbind();
178: }
179:
180: /**
181: * Cleanup plugin resources.
182: */
183: private void resetPlugin() {
184: if (!startupFailure) {
185: setClassLoader(null);
186: }
187: pluginListeners.clear();
188: }
189:
190: private String getLogPrefix() {
191: return toString();
192: }
193:
194: public Config getConfig() {
195: return config;
196: }
197:
198: public String toString() {
199: return "[Plugin: " + this .getName() + "]";
200: }
201:
202: }
|