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 @@
* 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.util.LocaleUtils;
import org.jivesoftware.util.Log;
......@@ -21,33 +20,39 @@ import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.log.*;
/**
* A simple wrapper that allows Jetty to run inside the Messenger
* container. Jetty settings are extracted from the ModuleContext.
* 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.
* The admin console plugin. It starts a Jetty instance on the configured
* port and loads the admin console web application.
*
* @author Iain Shigeoka
* @author Matt Tucker
*/
public class JettyModule implements Module {
public class AdminConsolePlugin implements Plugin {
private Server jetty = null;
private WebApplicationContext webAppContext = null;
private Container serverContainer = null;
private ServiceRegistration reg = null;
private String port = null;
/**
* Create a jetty module.
*/
public JettyModule() {
public AdminConsolePlugin() {
}
public String getName() {
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 {
// Configure logging to a file, creating log dir if needed
System.setProperty("org.apache.commons.logging.LogFactory","org.mortbay.log.Factory");
......@@ -68,50 +73,30 @@ public class JettyModule implements Module {
// Configure HTTP socket listener
port = JiveGlobals.getProperty("embedded-web.port", "9090");
jetty.addListener(port);
this.serverContainer = container;
// Add web-app
// TODO this shouldn't be hardcoded to look for the "admin" plugin.
webAppContext = jetty.addWebApplication("/", JiveGlobals.getMessengerHome() +
File.separator + "plugins" + File.separator + "admin" +
File.separator + "webapp");
WebApplicationContext webAppContext = jetty.addWebApplication("/",
pluginDir.getAbsoluteFile() + File.separator + "webapp");
webAppContext.setWelcomeFiles(new String[]{"index.jsp"});
}
catch (Exception e) {
Log.error("Trouble initializing Jetty", e);
}
}
public void start() {
try {
jetty.start();
ServiceItem serverItem = new ServiceItem(null, this, null);
reg = serverContainer.getServiceLookup().register(serverItem);
Log.info("Started embedded web server on port: " + port);
Log.info("Started admin console on port: " + port);
}
catch (Exception e) {
Log.error("Trouble starting Jetty", e);
stop();
Log.error("Trouble initializing admin console", e);
}
}
public void stop() {
public void destroy() {
try {
if (jetty != null) {
jetty.stop();
jetty = null;
}
if (reg != null) {
reg.cancel();
reg = null;
}
}
catch (InterruptedException 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 @@
* 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.FilenameFilter;
import java.net.MalformedURLException;
......@@ -22,47 +21,37 @@ import java.util.Iterator;
import java.util.List;
/**
* <p>This class extends the current classpath with plug-in jars and classes
* and makes it simple to load up modules from that extended classpath.</p>
* <p>This tool will add any jars in the <tt>lib</tt> directory
* (if it exists), and the <tt>classes</tt> directory (if it exists) to
* the classpath.</p>
* <p>JiveModuleLoader also sets the ccontext classloader for loaded
* modules to a classloader with extended classpath and sets the parent
* 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>
* ClassLoader for plugins. It searches the plugin directory for classes
* and JAR files, then constructs a class loader for the resources found.
* Resources are loaded as follows:<ul>
*
* <li>Any JAR files in the <tt>lib</tt> will be added to the classpath.
* <li>Any files in the classes directory will be added to the classpath.
* </ul>
*
* @author Iain Shigeoka
* @author Derek DeMoro
*/
public class JiveModuleLoader {
class PluginClassLoader {
/**
* The class loader used by this pseudo loader *
*/
private URLClassLoader classLoader;
/**
* Create a classloader for the given root directory.
* We locate all the relevant URLs then create a standard URLClassLoader.
* Constructs a plugin loader for the given plugin directory.
*
* @param dir The directory to search for other classes
* @throws java.lang.SecurityException If the created class loader violates existing security constraints
* @throws java.net.MalformedURLException If a located resource name doesn't properly convert to a URL
* @param pluginDir the plugin directory.
* @throws java.lang.SecurityException if the created class loader violates
* 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();
File classes = new File(dir + File.separator + "classes" + File.separator);
if (classes.exists()) {
list.add(classes.toURL());
File classesDir = new File(pluginDir, "classes");
if (classesDir.exists()) {
list.add(classesDir.toURL());
}
File lib = new File(dir + File.separator + "lib" + File.separator);
File[] jars = lib.listFiles(new FilenameFilter() {
File libDir = new File(pluginDir, "lib");
File[] jars = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar") || name.endsWith(".zip");
}
......@@ -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
* @throws ClassNotFoundException if the class could not be loaded by this class loader.
* @throws IllegalAccessException if the class constructor was private or protected.
* @throws InstantiationException if the class could not be instantiated (initialization error).
* @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
{
Class moduleClass = classLoader.loadClass(className);
Module mod = (Module)moduleClass.newInstance();
return mod;
return classLoader.loadClass(name);
}
/**
* 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() {
ClassLoader parent = Thread.currentThread().getContextClassLoader();
......
......@@ -3,26 +3,16 @@
* $Revision$
* $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");
* you may not use this file except in compliance with the License.
* 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.
* 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 org.jivesoftware.util.Log;
import org.jivesoftware.util.XMLProperties;
import org.jivesoftware.messenger.container.spi.JiveModuleLoader;
import org.jivesoftware.messenger.JiveGlobals;
import java.io.*;
......@@ -40,16 +30,14 @@ import java.util.concurrent.TimeUnit;
*/
public class PluginManager {
private Container container;
private File pluginDirectory;
private Map<String,Module> plugins;
private Map<String,Plugin> plugins;
private boolean setupMode = !(Boolean.valueOf(JiveGlobals.getXMLProperty("setup")).booleanValue());
private ScheduledExecutorService executor = null;
public PluginManager(File pluginDir, Container container) {
public PluginManager(File pluginDir) {
this.pluginDirectory = pluginDir;
this.container = container;
plugins = new HashMap<String,Module>();
plugins = new HashMap<String,Plugin>();
}
/**
......@@ -57,7 +45,7 @@ public class PluginManager {
*/
public void start() {
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 {
executor.shutdown();
}
// Shutdown all installed plugins.
for (Module module : plugins.values()) {
module.stop();
module.destroy();
for (Plugin plugin : plugins.values()) {
plugin.destroy();
}
}
......@@ -93,22 +80,20 @@ public class PluginManager {
if (setupMode && !(pluginDir.getName().equals("admin"))) {
return;
}
// ClassLoader
Log.debug("Loading plugin " + pluginDir.getName());
Module mod = null;
Plugin plugin = null;
try {
File moduleConfig = new File(pluginDir, "module.xml");
if (moduleConfig.exists()) {
XMLProperties moduleProps = new XMLProperties(moduleConfig);
JiveModuleLoader modLoader = new JiveModuleLoader(pluginDir.toString());
mod = modLoader.loadModule(moduleProps.getProperty("module"));
mod.initialize(container);
mod.start();
plugins.put(pluginDir.getName(), mod);
File pluginConfig = new File(pluginDir, "plugin.xml");
if (pluginConfig.exists()) {
XMLProperties pluginProps = new XMLProperties(pluginConfig);
PluginClassLoader pluginLoader = new PluginClassLoader(pluginDir);
String className = pluginProps.getProperty("class");
plugin = (Plugin)pluginLoader.loadClass(className).newInstance();
plugin.initialize(this, pluginDir);
plugins.put(pluginDir.getName(), plugin);
}
else {
Log.warn("Plugin " + pluginDir +
" not loaded: no module.xml configuration file not found");
Log.warn("Plugin " + pluginDir + " could not be loaded: no plugin.xml file found");
}
}
catch (Exception e) {
......@@ -121,7 +106,7 @@ public class PluginManager {
* checks for new plugin JAR files and extracts them if they haven't already
* been extracted. Then, any new plugin directories are loaded.
*/
private class PluginLoader implements Runnable {
private class PluginMonitor implements Runnable {
public void run() {
try {
......@@ -142,7 +127,7 @@ public class PluginManager {
try {
ZipFile zipFile = new ZipFile(jarFile);
// Ensure that this JAR is a plugin.
if (zipFile.getEntry("module.xml") == null) {
if (zipFile.getEntry("plugin.xml") == null) {
continue;
}
dir.mkdir();
......
......@@ -176,8 +176,8 @@ public abstract class BootstrapContainer implements Container, ServiceLookupProv
}
startCoreModules();
// Load plugins.
File pluginDir = new File(messengerHome + "/plugins");
pluginManager = new PluginManager(pluginDir, this);
File pluginDir = new File(messengerHome, "plugins");
pluginManager = new PluginManager(pluginDir);
pluginManager.start();
}
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