View Javadoc

1   /**
2    * Copyright 2005-2014 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  import java.util.concurrent.Callable;
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 suppressStartupFailure = 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              ContextClassLoaderBinder.doInContextClassLoader(getClassLoader(), new Callable() {
63                  @Override
64                  public Object call() throws Exception {
65                      startupFailure = false;
66                      started = true;
67                      Plugin.super.start();
68                      LOG.info("Starting plugin listeners");
69                      startPluginListeners();
70                      return null;
71                  }
72              });
73              ClassLoader classLoader = getClassLoader();
74              LOG.info(getLogPrefix()+" ...started." + (classLoader != null ? classLoader.toString() : ""));
75          } catch (Throwable t) {
76              LOG.error(getLogPrefix()+" Failure starting plugin.", t);
77              startupFailure = true;
78              started = true;
79              stop();
80              if (!suppressStartupFailure) {
81              	if (t instanceof Error) {
82              		throw (Error)t;
83              	} else if (t instanceof RuntimeException) {
84              		throw (RuntimeException)t;
85              	}
86              	throw new WorkflowRuntimeException("Failed to startup plugin.", t);
87              }
88          }
89      }
90  
91      /**
92       * Stops the plugin.
93       */
94      public synchronized void stop() {
95          if (!started) {
96              LOG.info(getLogPrefix()+" has already been stopped.");
97              return;
98          }
99          LOG.info(getLogPrefix()+" Stopping...");
100         try {
101             ContextClassLoaderBinder.doInContextClassLoader(getClassLoader(), new Callable() {
102                 @Override
103                 public Object call() throws Exception {
104                     started = false;
105                     stopPluginListeners();
106                     // stop resource loaders of super class
107                     Plugin.super.stop();
108                     return null;
109                 }
110             });
111         } catch (Throwable t) {
112         	LOG.error(getLogPrefix()+" Failed when attempting to stop the plugin.", t);
113         }
114         resetPlugin();
115         LOG.info(getLogPrefix()+" ...stopped.");
116     }
117 
118     public boolean isStarted() {
119         return started;
120     }
121 
122     public void addPluginListener(PluginListener pluginListener) {
123     	pluginListeners.add(pluginListener);
124     }
125 
126     public void removePluginListener(PluginListener pluginListener) {
127     	pluginListeners.remove(pluginListener);
128     }
129 
130     protected void startPluginListeners() {
131         for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) {
132             PluginListener listener = (PluginListener) iterator.next();
133             listener.pluginInitialized(this);
134         }
135     }
136 
137     /**
138      * If we fail to stop a plugin listener, try the next one but don't propogate any
139      * exceptions out of this method.  Otherwise the plugin ends up dying and can't be
140      * reloaded from a hot deploy.
141      */
142     protected void stopPluginListeners() {
143         for (Iterator iterator = pluginListeners.iterator(); iterator.hasNext();) {
144             PluginListener listener = (PluginListener) iterator.next();
145             try {
146             	listener.pluginDestroyed(this);
147             } catch (Throwable t) {
148             	LOG.error(getLogPrefix()+" Failed when invoking pluginDestroyed on Plugin Listener '"+listener.getClass().getName()+"'.", t);
149             }
150         }
151     }
152 
153     public boolean isSuppressStartupFailure() {
154 		return suppressStartupFailure;
155 	}
156 
157 	public void setSuppressStartupFailure(boolean suppressStartupFailure) {
158 		this.suppressStartupFailure = suppressStartupFailure;
159 	}
160 
161 	/**
162      * Cleanup plugin resources.
163      */
164     private void resetPlugin() {
165         if (!startupFailure) {
166         	setClassLoader(null);
167         }
168         pluginListeners.clear();
169     }
170 
171     private String getLogPrefix() {
172         return toString();
173     }
174 
175     public Config getConfig() {
176     	return config;
177     }
178 
179     public String toString() {
180         return "[Plugin: " + this.getName() + "]";
181     }
182 
183 }