View Javadoc

1   package net.sourceforge.blogentis.plugins.impl;
2   
3   //-----------------------------------------------------------------------
4   //Blogentis - a blog publishing platform.
5   //Copyright (C) 2004 Tassos Bassoukos <abassouk@gmail.com>
6   //
7   //This library is free software; you can redistribute it and/or
8   //modify it under the terms of the GNU Lesser General Public
9   //License as published by the Free Software Foundation; either
10  //version 2.1 of the License, or (at your option) any later version.
11  //
12  //This library is distributed in the hope that it will be useful,
13  //but WITHOUT ANY WARRANTY; without even the implied warranty of
14  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  //Lesser General Public License for more details.
16  //
17  //You should have received a copy of the GNU Lesser General Public
18  //License along with this library; if not, write to the Free Software
19  //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  //-----------------------------------------------------------------------
21  //
22  //$Id: BlogPluginServiceImpl.java,v 1.2 2004/10/28 10:45:51 tassos Exp $
23  //
24  
25  import java.io.BufferedReader;
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStreamReader;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Collections;
33  import java.util.Enumeration;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  
39  import net.sourceforge.blogentis.om.Blog;
40  import net.sourceforge.blogentis.plugins.BlogPluginService;
41  import net.sourceforge.blogentis.plugins.IBlogExtensionPoint;
42  import net.sourceforge.blogentis.plugins.IExtensionPoint;
43  import net.sourceforge.blogentis.plugins.IPlugin;
44  import net.sourceforge.blogentis.plugins.IPluginService;
45  import net.sourceforge.blogentis.plugins.IPrefs;
46  import net.sourceforge.blogentis.utils.MappedConfiguration;
47  
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.apache.turbine.Turbine;
51  import org.apache.turbine.services.InitializationException;
52  import org.apache.turbine.services.TurbineBaseService;
53  
54  /***
55   * @author abas
56   */
57  public class BlogPluginServiceImpl
58          extends TurbineBaseService
59          implements IPluginService {
60  
61      private static final Log log = LogFactory
62              .getLog(BlogPluginServiceImpl.class);
63  
64      private IPlugin[] plugins = null;
65      private ExtensionPointList globalExtensionPoints = new ExtensionPointList();
66      private Map blogExtensionPoints = new HashMap();
67  
68      private static final String PLUGIN_DESCRIPTOR = "plugins.resourceName";
69      private static final String PLUGIN_DESCRIPTOR_DEFAULT = "/plugins.list";
70      private static final String PLUGIN_CONFIG = "plugins.configFile";
71      private static final String PLUGIN_CONFIG_DEFAULT = "/WEB-INF/conf/plugins.list";
72  
73      /***
74       * Get the URIs of all files named "/plugins.list"
75       * 
76       * @return a List of the URIs of all files names "/plugin/list"
77       * @throws IOException
78       *             an exception propagated from the ClassLoader.
79       */
80      private List getPluginFileLists()
81              throws IOException {
82          Enumeration e = this.getClass().getClassLoader()
83                  .getResources(
84                                getConfiguration()
85                                        .getString(PLUGIN_DESCRIPTOR,
86                                                   PLUGIN_DESCRIPTOR_DEFAULT));
87          ArrayList l = new ArrayList();
88          while (e.hasMoreElements())
89              l.add(e.nextElement());
90          l.add(new File(Turbine.getRealPath(getConfiguration()
91                  .getString(PLUGIN_CONFIG, PLUGIN_CONFIG_DEFAULT))).toURL());
92          return l;
93      }
94  
95      /***
96       * Reads the classpath URIs in the pluginFileList and returns all plugin
97       * classes defined by these URIs
98       * 
99       * The current implementation expects the URIs to point to files that
100      * contain one-per-line class names.
101      * 
102      * @param pluginFileList
103      *            the list of files to search for plugins
104      * @return a list of class names.
105      * @throws IOException
106      */
107     private List getPluginClassList(List pluginFileList) {
108         ArrayList l = new ArrayList();
109         for(Iterator i = pluginFileList.iterator(); i.hasNext();) {
110             URL url = (URL)i.next();
111             log.debug("Trying file " + url.toString());
112             try {
113                 BufferedReader r = new BufferedReader(new InputStreamReader(url
114                         .openStream(), "utf-8"));
115                 String line;
116                 while ((line = r.readLine()) != null) {
117                     line = line.trim();
118                     if (line.length() == 0 || line.charAt(0) == '#')
119                         continue;
120                     l.add(line);
121                 }
122             } catch (Exception e) {
123                 log.warn("Could not load " + url, e);
124             }
125         }
126         return l;
127     }
128 
129     /***
130      * 
131      * @param pluginClassnameList
132      * @return
133      */
134     private List getPluginClasses(List pluginClassnameList) {
135         ArrayList l = new ArrayList();
136         for(Iterator i = pluginClassnameList.iterator(); i.hasNext();) {
137             String className = (String)i.next();
138             log.debug("Trying class " + className);
139             try {
140                 Class c = Class.forName(className);
141                 if (!IPlugin.class.isAssignableFrom(c)) {
142                     log.warn("Not a plugin: " + className);
143                     continue;
144                 }
145                 l.add(c.newInstance());
146             } catch (Exception e1) {
147                 log.warn("Class not found " + className, e1);
148                 continue;
149             } catch (LinkageError e) {
150                 log.error("Class not found " + className, e);
151                 continue;
152             }
153         }
154         return l;
155     }
156 
157     /***
158      * Load the plugin classes. Searches in the classpath for all instances of
159      * /plugin.list, which contains a list of classes, one per line. Empty lines
160      * and lines starting with # will be ignored. Each class so found will be
161      * loaded and checked on the implementation of the IPlugin interface.
162      * 
163      * @throws Exception
164      */
165     private void initPlugins()
166             throws Exception {
167         List tmp;
168         tmp = getPluginFileLists();
169         tmp = getPluginClassList(tmp);
170         tmp = getPluginClasses(tmp);
171         plugins = (IPlugin[])tmp.toArray(new IPlugin[] {});
172     }
173 
174     public void init() {
175         log.debug("Loading plugins...");
176         try {
177             initPlugins();
178         } catch (Exception e1) {
179             log.error("Could not get the list of plugins", e1);
180             plugins = new IPlugin[] {};
181         }
182         setInit(true);
183         for(int i = 0; i < plugins.length; i++) {
184             log.debug("Loading " + plugins[i].getName());
185             try {
186                 plugins[i].startPlugin();
187             } catch (InitializationException e) {
188                 log
189                         .error("Could not initialize plugin "
190                                 + plugins[i].getName(), e);
191             }
192         }
193     }
194 
195     public void shutdown() {
196         for(int i = plugins.length - 1; i >= 0; i--) {
197             plugins[i].stopPlugin();
198         }
199         setInit(false);
200     }
201 
202     /***
203      * List the plugins in the order of their definition.
204      * 
205      * @return an iterator that returns IPlugin
206      */
207     public Iterator getPlugins() {
208         return Arrays.asList(plugins).iterator();
209     }
210 
211     /***
212      * Register a global extension point.
213      * 
214      * @param point
215      *            the global extension point to register.
216      */
217     public synchronized void registerExtensionPoint(IExtensionPoint point) {
218         globalExtensionPoints.registerExtensionPoint(point);
219     }
220 
221     /***
222      * Remove a global extension point.
223      * 
224      * @param point
225      *            the extension point to remove.
226      */
227     public synchronized void deregisterExtensionPoint(IExtensionPoint point) {
228         globalExtensionPoints.deregisterExtensionPoint(point);
229     }
230 
231     /***
232      * Register a blog-specific extension point.
233      * 
234      * @param blog
235      *            the blog that the extension point will apply.
236      * @param point
237      *            the extension point that will be registered.
238      */
239     public synchronized void registerExtensionPoint(Blog blog,
240                                                     IBlogExtensionPoint point) {
241         ExtensionPointList epl = (ExtensionPointList)blogExtensionPoints
242                 .get(blog.getName());
243         if (epl == null) {
244             epl = new ExtensionPointList();
245             blogExtensionPoints.put(blog.getName(), epl);
246         }
247         epl.registerExtensionPoint(point);
248     }
249 
250     /***
251      * Remove a blog-specific extension point
252      * 
253      * @param blog
254      *            the blog that should contain the extension point.
255      * @param point
256      *            the extension point to remove.
257      */
258     public synchronized void deregisterExtensionPoint(Blog blog,
259                                                       IBlogExtensionPoint point) {
260         ExtensionPointList epl = (ExtensionPointList)blogExtensionPoints
261                 .get(blog.getName());
262         if (epl != null)
263             epl.deregisterExtensionPoint(point);
264     }
265 
266     public IExtensionPoint locateExtensionPoint(Class className) {
267         return globalExtensionPoints.locateExtensionPoint(className);
268     }
269 
270     public synchronized IBlogExtensionPoint locateExtensionPoint(Blog blog,
271                                                                  Class className) {
272         ExtensionPointList epl = getBlogExtensionList(blog);
273         return (IBlogExtensionPoint)epl.locateExtensionPoint(className);
274     }
275 
276     public synchronized void reloadExtensionPointsForBlog(Blog blog) {
277         if (log.isDebugEnabled())
278             log.debug("Reloading extension points for " + blog.getName());
279         blogExtensionPoints.remove(blog.getName());
280         getBlogExtensionList(blog);
281     }
282 
283     private List getValidPluginNames(Blog blog) {
284         boolean doSave = false;
285         MappedConfiguration config =blog.getConfiguration();
286         // Blog may be new, and no plugin list yet available.
287         List pluginNames = config.getList(BlogPluginService.PLUGIN_LIST,
288                                           Collections.EMPTY_LIST);
289         // remove all plugins thet we do not know of from the plugin list.
290         List unknown = new ArrayList(pluginNames);
291         for(int j = 0; j < plugins.length; j++)
292             unknown.remove(plugins[j].getClass().getName());
293         pluginNames.removeAll(unknown);
294         // initialize plugin list to all plugins.
295         if (pluginNames.size() == 0) {
296             pluginNames = new ArrayList(plugins.length);
297             for(int i = 0; i < plugins.length; i++)
298                 pluginNames.add(plugins[i].getClass().getName());
299             doSave = true;
300         }
301         if (doSave || unknown.size() > 0) {
302             config.clearProperty(BlogPluginService.PLUGIN_LIST);
303             config.addProperty(BlogPluginService.PLUGIN_LIST, pluginNames);
304             try {
305                 config.save();
306             } catch (Exception e) {
307                 log.error("Could not save configuration for " + blog.getName(),
308                           e);
309             }
310         }
311         return pluginNames;
312     }
313 
314     private synchronized ExtensionPointList getBlogExtensionList(Blog blog) {
315         ExtensionPointList epl = (ExtensionPointList)blogExtensionPoints
316                 .get(blog.getName());
317         if (epl != null)
318             return epl;
319         epl = new ExtensionPointList();
320         blogExtensionPoints.put(blog.getName(), epl);
321         List l = getValidPluginNames(blog);
322         for(Iterator i = l.iterator(); i.hasNext();) {
323             String pluginName = (String)i.next();
324             for(int j = 0; j < plugins.length; j++) {
325                 if (plugins[j].getClass().getName().equals(pluginName)) {
326                     plugins[j].registerInBlog(blog);
327                 }
328             }
329         }
330         return epl;
331     }
332 
333     public Iterator getExtensionPoints(Blog blog) {
334         return ((ExtensionPointList)blogExtensionPoints.get(blog.getName()))
335                 .iterator();
336     }
337 
338     public IPrefs getPreferencesFor(Blog blog, Class extensionClass) {
339         return new PrefsImpl(blog, getClass().getName());
340     }
341 }