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.log4j.Logger;
19  import org.kuali.rice.core.api.config.property.Config;
20  import org.kuali.rice.core.api.util.ContextClassLoaderBinder;
21  import org.kuali.rice.core.impl.resourceloader.BaseWrappingResourceLoader;
22  import org.kuali.rice.kew.api.WorkflowRuntimeException;
23  
24  import javax.xml.namespace.QName;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  
30  /**
31   * A KEW Plugin.  A Plugin represents a distinct classloading space living below (as a child) of the core
32   * KEW classloader.  It allows for loading of plugin resources from core components of the system.
33   * Essentially a Plugin is a specialized ResourceLoader with a custom classloader and attached configuration.
34   *
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   */
37  public class Plugin extends BaseWrappingResourceLoader {
38  
39  	private static final Logger LOG = Logger.getLogger(Plugin.class);
40      private Config config;
41      private List<PluginListener> pluginListeners = new ArrayList<PluginListener>();
42  
43      private boolean supressStartupFailure = true;
44      private boolean started = false;
45      private boolean startupFailure = false;
46  
47      public Plugin(QName name, Config config, ClassLoader classLoader) {
48      	super(name, classLoader);
49      	this.config = config;
50      }
51  
52      /**
53       * Starts the plugin.
54       */
55      public synchronized void start() {
56          if (started) {
57              LOG.info(getLogPrefix()+" has already been started.");
58              return;
59          }
60          LOG.info(getLogPrefix()+" Starting...");
61          try {
62              bindThread();
63              try {
64                  startupFailure = false;
65                  started = true;
66                  super.start();
67                  LOG.info("Starting plugin listeners");
68                  startPluginListeners();
69              } finally {
70                  unbindThread();
71              }
72              ClassLoader classLoader = getClassLoader();
73              LOG.info(getLogPrefix()+" ...started." + (classLoader != null ? classLoader.toString() : ""));
74          } catch (Throwable t) {
75              LOG.error(getLogPrefix()+" Failure starting plugin.", t);
76              startupFailure = true;
77              started = true;
78              stop();
79              if (!supressStartupFailure) {
80              	if (t instanceof Error) {
81              		throw (Error)t;
82              	} else if (t instanceof RuntimeException) {
83              		throw (RuntimeException)t;
84              	}
85              	throw new WorkflowRuntimeException("Failed to startup plugin.", t);
86              }
87          }
88      }
89  
90      /**
91       * Stops the plugin.
92       */
93      public synchronized void stop() {
94          if (!started) {
95              LOG.info(getLogPrefix()+" has already been stopped.");
96              return;
97          }
98          LOG.info(getLogPrefix()+" Stopping...");
99          bindThread();
100         try {
101             started = false;
102             stopPluginListeners();
103             // stop resource loaders of super class
104             super.stop();
105         } catch (Throwable t) {
106         	LOG.error(getLogPrefix()+" Failed when attempting to stop the plugin.", t);
107         } finally {
108             unbindThread();
109         }
110         resetPlugin();
111         LOG.info(getLogPrefix()+" ...stopped.");
112     }
113 
114     public boolean isStarted() {
115         return started;
116     }
117 
118     public void addPluginListener(PluginListener pluginListener) {
119     	pluginListeners.add(pluginListener);
120     }
121 
122     public void removePluginListener(PluginListener pluginListener) {
123     	pluginListeners.remove(pluginListener);
124     }
125 
126     protected void startPluginListeners() {
127         for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) {
128             PluginListener listener = (PluginListener) iterator.next();
129             listener.pluginInitialized(this);
130         }
131     }
132 
133     /**
134      * If we fail to stop a plugin listener, try the next one but don't propogate any
135      * exceptions out of this method.  Otherwise the plugin ends up dying and can't be
136      * reloaded from a hot deploy.
137      */
138     protected void stopPluginListeners() {
139         for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) {
140             PluginListener listener = (PluginListener) iterator.next();
141             try {
142             	listener.pluginDestroyed(this);
143             } catch (Throwable t) {
144             	LOG.error(getLogPrefix()+" Failed when invoking pluginDestroyed on Plugin Listener '"+listener.getClass().getName()+"'.", t);
145             }
146         }
147     }
148 
149     public boolean isSupressStartupFailure() {
150 		return supressStartupFailure;
151 	}
152 
153 	public void setSupressStartupFailure(boolean supressStartupFailure) {
154 		this.supressStartupFailure = supressStartupFailure;
155 	}
156 
157 	public void bindThread() {
158 		ContextClassLoaderBinder.bind(getClassLoader());
159     }
160 
161     public void unbindThread() {
162         ContextClassLoaderBinder.unbind();
163     }
164 
165 	/**
166      * Cleanup plugin resources.
167      */
168     private void resetPlugin() {
169         if (!startupFailure) {
170         	setClassLoader(null);
171         }
172         pluginListeners.clear();
173     }
174 
175     private String getLogPrefix() {
176         return toString();
177     }
178 
179     public Config getConfig() {
180     	return config;
181     }
182 
183     public String toString() {
184         return "[Plugin: " + this.getName() + "]";
185     }
186 
187 }