Commit 1ead9cac authored by Matt Tucker's avatar Matt Tucker Committed by matt

Plugin code refactoring.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@408 b35dd754-fafc-0310-a699-88a17e54d16e
parent f4160c0f
...@@ -9,9 +9,8 @@ ...@@ -9,9 +9,8 @@
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
*/ */
package org.jivesoftware.messenger.container.spi; package org.jivesoftware.messenger.container;
import org.jivesoftware.messenger.container.*;
import org.jivesoftware.messenger.JiveGlobals; import org.jivesoftware.messenger.JiveGlobals;
import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
...@@ -21,33 +20,39 @@ import org.mortbay.jetty.servlet.WebApplicationContext; ...@@ -21,33 +20,39 @@ import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.log.*; import org.mortbay.log.*;
/** /**
* A simple wrapper that allows Jetty to run inside the Messenger * The admin console plugin. It starts a Jetty instance on the configured
* container. Jetty settings are extracted from the ModuleContext. * port and loads the admin console web application.
* The Jetty module is primarily designed to host the JSP web
* administration interface to the server when running in standalone
* mode without an external servlet container.
* *
* @author Iain Shigeoka * @author Matt Tucker
*/ */
public class JettyModule implements Module { public class AdminConsolePlugin implements Plugin {
private Server jetty = null; private Server jetty = null;
private WebApplicationContext webAppContext = null;
private Container serverContainer = null;
private ServiceRegistration reg = null;
private String port = null; private String port = null;
/** /**
* Create a jetty module. * Create a jetty module.
*/ */
public JettyModule() { public AdminConsolePlugin() {
} }
public String getName() { public String getName() {
return "Admin Console"; return "Admin Console";
} }
public void initialize(Container container) { public String getDescription() {
return "Web-based admin console for Jive Messenger.";
}
public String getAuthor() {
return "Jive Software";
}
public String getVersion() {
return "2.0";
}
public void initialize(PluginManager manager, File pluginDir) {
try { try {
// Configure logging to a file, creating log dir if needed // Configure logging to a file, creating log dir if needed
System.setProperty("org.apache.commons.logging.LogFactory","org.mortbay.log.Factory"); System.setProperty("org.apache.commons.logging.LogFactory","org.mortbay.log.Factory");
...@@ -68,50 +73,30 @@ public class JettyModule implements Module { ...@@ -68,50 +73,30 @@ public class JettyModule implements Module {
// Configure HTTP socket listener // Configure HTTP socket listener
port = JiveGlobals.getProperty("embedded-web.port", "9090"); port = JiveGlobals.getProperty("embedded-web.port", "9090");
jetty.addListener(port); jetty.addListener(port);
this.serverContainer = container;
// Add web-app // Add web-app
// TODO this shouldn't be hardcoded to look for the "admin" plugin. WebApplicationContext webAppContext = jetty.addWebApplication("/",
webAppContext = jetty.addWebApplication("/", JiveGlobals.getMessengerHome() + pluginDir.getAbsoluteFile() + File.separator + "webapp");
File.separator + "plugins" + File.separator + "admin" +
File.separator + "webapp");
webAppContext.setWelcomeFiles(new String[]{"index.jsp"}); webAppContext.setWelcomeFiles(new String[]{"index.jsp"});
}
catch (Exception e) {
Log.error("Trouble initializing Jetty", e);
}
}
public void start() {
try {
jetty.start(); jetty.start();
ServiceItem serverItem = new ServiceItem(null, this, null); Log.info("Started admin console on port: " + port);
reg = serverContainer.getServiceLookup().register(serverItem);
Log.info("Started embedded web server on port: " + port);
} }
catch (Exception e) { catch (Exception e) {
Log.error("Trouble starting Jetty", e); Log.error("Trouble initializing admin console", e);
stop();
} }
} }
public void stop() { public void destroy() {
try { try {
if (jetty != null) { if (jetty != null) {
jetty.stop(); jetty.stop();
jetty = null; jetty = null;
} }
if (reg != null) {
reg.cancel();
reg = null;
}
} }
catch (InterruptedException e) { catch (InterruptedException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
} }
public void destroy() {
}
} }
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.container;
import java.io.File;
/**
* Plugin interface.
*
* @author Matt Tucker
*/
public interface Plugin {
/**
* Returns the name of this plugin.
*
* @return the plugin's name.
*/
public String getName();
/**
* Returns the description of the plugin or <tt>null</tt> if there is no description.
*
* @return this plugin's description.
*/
public String getDescription();
/**
* Returns the author of this plugin.
*
* @return the plugin's author.
*/
public String getAuthor();
/**
* The plugin's version.
*
* @return the version of the plugin.
*/
public String getVersion();
/**
* Initializes the plugin.
*
* @param manager the plugin manager.
* @param pluginDirectory the directory where the plugin is located.
*/
public void initialize(PluginManager manager, File pluginDirectory);
/**
* Destroys the plugin.
*/
public void destroy();
}
\ No newline at end of file
...@@ -9,9 +9,8 @@ ...@@ -9,9 +9,8 @@
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
*/ */
package org.jivesoftware.messenger.container.spi; package org.jivesoftware.messenger.container;
import org.jivesoftware.messenger.container.Module;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.net.MalformedURLException; import java.net.MalformedURLException;
...@@ -22,47 +21,37 @@ import java.util.Iterator; ...@@ -22,47 +21,37 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
/** /**
* <p>This class extends the current classpath with plug-in jars and classes * ClassLoader for plugins. It searches the plugin directory for classes
* and makes it simple to load up modules from that extended classpath.</p> * and JAR files, then constructs a class loader for the resources found.
* <p>This tool will add any jars in the <tt>lib</tt> directory * Resources are loaded as follows:<ul>
* (if it exists), and the <tt>classes</tt> directory (if it exists) to *
* the classpath.</p> * <li>Any JAR files in the <tt>lib</tt> will be added to the classpath.
* <p>JiveModuleLoader also sets the ccontext classloader for loaded * <li>Any files in the classes directory will be added to the classpath.
* modules to a classloader with extended classpath and sets the parent * </ul>
* classloader to the 'best' available according to the following ranked
* choices:</p>
* <ol>
* <li>The current thread's current context class loader</li>
* <li>The class loader for this class</li>
* <li>The system class loader</li>
* </ol>
* *
* @author Iain Shigeoka
* @author Derek DeMoro * @author Derek DeMoro
*/ */
public class JiveModuleLoader { class PluginClassLoader {
/**
* The class loader used by this pseudo loader *
*/
private URLClassLoader classLoader; private URLClassLoader classLoader;
/** /**
* Create a classloader for the given root directory. * Constructs a plugin loader for the given plugin directory.
* We locate all the relevant URLs then create a standard URLClassLoader.
* *
* @param dir The directory to search for other classes * @param pluginDir the plugin directory.
* @throws java.lang.SecurityException If the created class loader violates existing security constraints * @throws java.lang.SecurityException if the created class loader violates
* @throws java.net.MalformedURLException If a located resource name doesn't properly convert to a URL * existing security constraints.
* @throws java.net.MalformedURLException if a located resource name cannot be
* properly converted to a URL.
*/ */
public JiveModuleLoader(String dir) throws MalformedURLException, SecurityException { public PluginClassLoader(File pluginDir) throws MalformedURLException, SecurityException {
final List list = new ArrayList(); final List list = new ArrayList();
File classes = new File(dir + File.separator + "classes" + File.separator); File classesDir = new File(pluginDir, "classes");
if (classes.exists()) { if (classesDir.exists()) {
list.add(classes.toURL()); list.add(classesDir.toURL());
} }
File lib = new File(dir + File.separator + "lib" + File.separator); File libDir = new File(pluginDir, "lib");
File[] jars = lib.listFiles(new FilenameFilter() { File[] jars = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
return name.endsWith(".jar") || name.endsWith(".zip"); return name.endsWith(".jar") || name.endsWith(".zip");
} }
...@@ -84,27 +73,25 @@ public class JiveModuleLoader { ...@@ -84,27 +73,25 @@ public class JiveModuleLoader {
} }
/** /**
* Load a class using this class loader. * Load a class using this plugin class loader.
* *
* @param className The fully qualified name of the class to load * @param name the fully qualified name of the class to load.
* @return The module object loaded * @return The module object loaded
* @throws ClassNotFoundException if the class could not be loaded by this class loader. * @throws ClassNotFoundException if the class could not be loaded by this class loader.
* @throws IllegalAccessException if the class constructor was private or protected. * @throws IllegalAccessException if the class constructor was private or protected.
* @throws InstantiationException if the class could not be instantiated (initialization error). * @throws InstantiationException if the class could not be instantiated (initialization error).
* @throws SecurityException if the custom class loader not allowed. * @throws SecurityException if the custom class loader not allowed.
*/ */
public Module loadModule(String className) throws ClassNotFoundException, IllegalAccessException, public Class loadClass(String name) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, SecurityException InstantiationException, SecurityException
{ {
Class moduleClass = classLoader.loadClass(className); return classLoader.loadClass(name);
Module mod = (Module)moduleClass.newInstance();
return mod;
} }
/** /**
* Locates the best class loader based on context (see class description). * Locates the best parent class loader based on context.
* *
* @return The best parent classloader to use * @return the best parent classloader to use.
*/ */
private ClassLoader findParentClassLoader() { private ClassLoader findParentClassLoader() {
ClassLoader parent = Thread.currentThread().getContextClassLoader(); ClassLoader parent = Thread.currentThread().getContextClassLoader();
......
...@@ -3,26 +3,16 @@ ...@@ -3,26 +3,16 @@
* $Revision$ * $Revision$
* $Date$ * $Date$
* *
* Copyright 2004 Jive Software. * Copyright (C) 2004 Jive Software. All rights reserved.
* *
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * This software is published under the terms of the GNU Public License (GPL),
* you may not use this file except in compliance with the License. * a copy of which is included in this distribution.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jivesoftware.messenger.container; package org.jivesoftware.messenger.container;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.XMLProperties; import org.jivesoftware.util.XMLProperties;
import org.jivesoftware.messenger.container.spi.JiveModuleLoader;
import org.jivesoftware.messenger.JiveGlobals; import org.jivesoftware.messenger.JiveGlobals;
import java.io.*; import java.io.*;
...@@ -40,16 +30,14 @@ import java.util.concurrent.TimeUnit; ...@@ -40,16 +30,14 @@ import java.util.concurrent.TimeUnit;
*/ */
public class PluginManager { public class PluginManager {
private Container container;
private File pluginDirectory; private File pluginDirectory;
private Map<String,Module> plugins; private Map<String,Plugin> plugins;
private boolean setupMode = !(Boolean.valueOf(JiveGlobals.getXMLProperty("setup")).booleanValue()); private boolean setupMode = !(Boolean.valueOf(JiveGlobals.getXMLProperty("setup")).booleanValue());
private ScheduledExecutorService executor = null; private ScheduledExecutorService executor = null;
public PluginManager(File pluginDir, Container container) { public PluginManager(File pluginDir) {
this.pluginDirectory = pluginDir; this.pluginDirectory = pluginDir;
this.container = container; plugins = new HashMap<String,Plugin>();
plugins = new HashMap<String,Module>();
} }
/** /**
...@@ -57,7 +45,7 @@ public class PluginManager { ...@@ -57,7 +45,7 @@ public class PluginManager {
*/ */
public void start() { public void start() {
executor = new ScheduledThreadPoolExecutor(2); executor = new ScheduledThreadPoolExecutor(2);
executor.scheduleWithFixedDelay(new PluginLoader(), 0, 5, TimeUnit.SECONDS); executor.scheduleWithFixedDelay(new PluginMonitor(), 0, 5, TimeUnit.SECONDS);
} }
/** /**
...@@ -69,9 +57,8 @@ public class PluginManager { ...@@ -69,9 +57,8 @@ public class PluginManager {
executor.shutdown(); executor.shutdown();
} }
// Shutdown all installed plugins. // Shutdown all installed plugins.
for (Module module : plugins.values()) { for (Plugin plugin : plugins.values()) {
module.stop(); plugin.destroy();
module.destroy();
} }
} }
...@@ -93,22 +80,20 @@ public class PluginManager { ...@@ -93,22 +80,20 @@ public class PluginManager {
if (setupMode && !(pluginDir.getName().equals("admin"))) { if (setupMode && !(pluginDir.getName().equals("admin"))) {
return; return;
} }
// ClassLoader
Log.debug("Loading plugin " + pluginDir.getName()); Log.debug("Loading plugin " + pluginDir.getName());
Module mod = null; Plugin plugin = null;
try { try {
File moduleConfig = new File(pluginDir, "module.xml"); File pluginConfig = new File(pluginDir, "plugin.xml");
if (moduleConfig.exists()) { if (pluginConfig.exists()) {
XMLProperties moduleProps = new XMLProperties(moduleConfig); XMLProperties pluginProps = new XMLProperties(pluginConfig);
JiveModuleLoader modLoader = new JiveModuleLoader(pluginDir.toString()); PluginClassLoader pluginLoader = new PluginClassLoader(pluginDir);
mod = modLoader.loadModule(moduleProps.getProperty("module")); String className = pluginProps.getProperty("class");
mod.initialize(container); plugin = (Plugin)pluginLoader.loadClass(className).newInstance();
mod.start(); plugin.initialize(this, pluginDir);
plugins.put(pluginDir.getName(), mod); plugins.put(pluginDir.getName(), plugin);
} }
else { else {
Log.warn("Plugin " + pluginDir + Log.warn("Plugin " + pluginDir + " could not be loaded: no plugin.xml file found");
" not loaded: no module.xml configuration file not found");
} }
} }
catch (Exception e) { catch (Exception e) {
...@@ -121,7 +106,7 @@ public class PluginManager { ...@@ -121,7 +106,7 @@ public class PluginManager {
* checks for new plugin JAR files and extracts them if they haven't already * checks for new plugin JAR files and extracts them if they haven't already
* been extracted. Then, any new plugin directories are loaded. * been extracted. Then, any new plugin directories are loaded.
*/ */
private class PluginLoader implements Runnable { private class PluginMonitor implements Runnable {
public void run() { public void run() {
try { try {
...@@ -142,7 +127,7 @@ public class PluginManager { ...@@ -142,7 +127,7 @@ public class PluginManager {
try { try {
ZipFile zipFile = new ZipFile(jarFile); ZipFile zipFile = new ZipFile(jarFile);
// Ensure that this JAR is a plugin. // Ensure that this JAR is a plugin.
if (zipFile.getEntry("module.xml") == null) { if (zipFile.getEntry("plugin.xml") == null) {
continue; continue;
} }
dir.mkdir(); dir.mkdir();
......
...@@ -176,8 +176,8 @@ public abstract class BootstrapContainer implements Container, ServiceLookupProv ...@@ -176,8 +176,8 @@ public abstract class BootstrapContainer implements Container, ServiceLookupProv
} }
startCoreModules(); startCoreModules();
// Load plugins. // Load plugins.
File pluginDir = new File(messengerHome + "/plugins"); File pluginDir = new File(messengerHome, "plugins");
pluginManager = new PluginManager(pluginDir, this); pluginManager = new PluginManager(pluginDir);
pluginManager.start(); pluginManager.start();
} }
catch (Exception e) { catch (Exception e) {
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file stores default properties for the given module..
Property names must be in the format: "prop.name.is.blah=value"
That will be stored as:
<prop>
<name>
<is>
<blah>value</blah>
</is>
</name>
</prop>
All properties must be under the "jive" element.
This file should live in the root of the plug-in's subdirectory.
-->
<!-- root element, all properties must be under this element -->
<jive>
<module>org.jivesoftware.messenger.container.spi.JettyModule</module>
</jive>
<?xml version="1.0" encoding="UTF-8"?>
<!--
Plugin configuration file for the admin console. This file must
live in the root of the plugin's directory.
-->
<plugin>
<class>org.jivesoftware.messenger.container.AdminConsolePlugin</class>
</plugin>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment