427 lines
14 KiB
Java
427 lines
14 KiB
Java
/*
|
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
|
* (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.mozilla.org/MPL/>.
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
|
|
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Venice Web Communities System.
|
|
*
|
|
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
|
|
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
|
|
* Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
package com.silverwrist.dynamo.module;
|
|
|
|
import java.io.*;
|
|
import java.net.MalformedURLException;
|
|
import java.util.*;
|
|
import java.util.jar.*;
|
|
import org.apache.log4j.Logger;
|
|
import org.w3c.dom.*;
|
|
import com.silverwrist.util.xml.*;
|
|
import com.silverwrist.dynamo.Namespaces;
|
|
import com.silverwrist.dynamo.app.ApplicationContainer;
|
|
import com.silverwrist.dynamo.db.NamespaceCache;
|
|
import com.silverwrist.dynamo.except.*;
|
|
import com.silverwrist.dynamo.iface.*;
|
|
import com.silverwrist.dynamo.util.*;
|
|
|
|
public class ModuleManager implements NamedObject, ComponentInitialize, ComponentShutdown, ModuleOperations
|
|
{
|
|
/*--------------------------------------------------------------------------------
|
|
* Static data members
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static Logger logger = Logger.getLogger(ModuleManager.class);
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Attributes
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private String m_name;
|
|
private ApplicationContainer m_appcon;
|
|
private NamespaceCache m_nscache;
|
|
private ModuleDBOps m_ops;
|
|
private File m_mod_dir;
|
|
private HashMap m_jar_to_module = new HashMap();
|
|
private HashMap m_qname_to_module = new HashMap();
|
|
private InstallServiceManager m_instservice;
|
|
private ServiceProvider m_install_services;
|
|
private ComponentShutdown m_shut1;
|
|
private ComponentShutdown m_shut2;
|
|
private ComponentShutdown m_shut3;
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Constructor
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public ModuleManager()
|
|
{ // do nothing
|
|
} // end constructor
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static final void pushFileList(LinkedList list, File file)
|
|
{
|
|
File[] subfiles = file.listFiles();
|
|
if (subfiles==null)
|
|
return;
|
|
Arrays.sort(subfiles);
|
|
for (int i=subfiles.length-1; i>=0; i--)
|
|
list.addFirst(subfiles[i]);
|
|
|
|
} // end pushFileList
|
|
|
|
private static final boolean isDynamoModule(File f)
|
|
{
|
|
JarFile jf = null;
|
|
try
|
|
{ // open this file as a JAR file
|
|
jf = new JarFile(f);
|
|
Manifest mft = jf.getManifest();
|
|
if (mft==null)
|
|
return false;
|
|
Attributes attrs = mft.getMainAttributes();
|
|
if (attrs==null)
|
|
return false;
|
|
if (attrs.getValue("X-Dynamo-Module-Class")!=null)
|
|
return true;
|
|
|
|
} // end try
|
|
catch (IOException e)
|
|
{ // if there's an exception, we just bail
|
|
return false;
|
|
|
|
} // end catch
|
|
finally
|
|
{ // clean up before we go
|
|
try
|
|
{ // close the file
|
|
if (jf!=null)
|
|
jf.close();
|
|
|
|
} // end try
|
|
catch (IOException e)
|
|
{ // ignore exceptions
|
|
} // end catch
|
|
|
|
} // end finally
|
|
|
|
return false;
|
|
|
|
} // end isDynamoModule
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface NamedObject
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public String getName()
|
|
{
|
|
return m_name;
|
|
|
|
} // end getName
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface ComponentInitialize
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Initialize the component.
|
|
*
|
|
* @param config_root Pointer to the section of the Dynamo XML configuration file that configures this
|
|
* particular component. This is to be considered "read-only" by the component.
|
|
* @param services An implementation of {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider}
|
|
* which provides initialization services to the component. This will include an implementation
|
|
* of {@link com.silverwrist.dynamo.iface.ObjectProvider ObjectProvider} which may be used to
|
|
* get information about other objects previously initialized by the application.
|
|
* @exception com.silverwrist.dynamo.except.ConfigException If an error is encountered in the component
|
|
* configuration.
|
|
*/
|
|
public void initialize(Element config_root, ServiceProvider services) throws ConfigException
|
|
{
|
|
XMLLoader loader = XMLLoader.get();
|
|
String mod_dir = null, conn_name = null, nscache_name = null, install_conn_name = null;
|
|
ConfigData cfg_data = null;
|
|
try
|
|
{ // verify the right node name
|
|
loader.verifyNodeName(config_root,"object");
|
|
|
|
// get the object's name
|
|
m_name = loader.getAttribute(config_root,"name");
|
|
|
|
// get the raw template directory
|
|
mod_dir = loader.getSubElementText(config_root,"module-directory");
|
|
|
|
// get the database configuration connection
|
|
DOMElementHelper config_root_h = new DOMElementHelper(config_root);
|
|
Element elt = loader.getSubElement(config_root_h,"database");
|
|
conn_name = loader.getAttribute(elt,"connection");
|
|
nscache_name = loader.getAttribute(elt,"namespaces");
|
|
|
|
// get the name of the connection to use for database installation
|
|
elt = loader.getSubElement(config_root_h,"install");
|
|
install_conn_name = loader.getAttribute(elt,"connection");
|
|
|
|
// get the module configuration data
|
|
elt = loader.getSubElement(config_root_h,"config-db");
|
|
cfg_data = new ConfigData(elt);
|
|
|
|
} // end try
|
|
catch (XMLLoadException e)
|
|
{ // error loading XML config data
|
|
throw new ConfigException(e);
|
|
|
|
} // end catch
|
|
|
|
// Get the database connection pool and namespace cache.
|
|
DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,conn_name);
|
|
m_nscache =
|
|
(NamespaceCache)(GetObjectUtils.getDynamoComponent(services,NamespaceCache.class,nscache_name));
|
|
|
|
// Get the database operations object.
|
|
m_ops = ModuleDBOps.get(pool);
|
|
|
|
// Get the installation connection pool.
|
|
pool = GetObjectUtils.getDatabaseConnection(services,install_conn_name);
|
|
|
|
// Get the application container.
|
|
ObjectProvider op = (ObjectProvider)(services.queryService(ObjectProvider.class));
|
|
m_appcon = (ApplicationContainer)(op.getObject(Namespaces.DYNAMO_APPLICATION_NAMESPACE,"__container__"));
|
|
|
|
// Create the module directory object.
|
|
m_mod_dir = new File(m_appcon.translateSubstratePathName(mod_dir));
|
|
if (!(m_mod_dir.isDirectory()))
|
|
{ // module directory does not exist - this is an error
|
|
ConfigException ce = new ConfigException(ModuleManager.class,"ModuleMessages","moddir.notexist");
|
|
ce.setParameter(0,m_mod_dir.getAbsolutePath());
|
|
throw ce;
|
|
|
|
} // end if
|
|
|
|
// Get the install service manager and set up the install service provider.
|
|
m_instservice = new InstallServiceManager();
|
|
m_instservice.addService(ModuleConfigurationData.class,(ModuleConfigurationData)cfg_data);
|
|
m_instservice.addService(DatabaseInstaller.class,new InstallerImpl(pool));
|
|
m_install_services = m_instservice.getServiceProvider(m_appcon.getInitServices());
|
|
|
|
// Hook this into the service providers.
|
|
SingletonServiceProvider ssp = new SingletonServiceProvider("ModuleManager",ModuleOperations.class,this);
|
|
HookServiceProviders hooker = (HookServiceProviders)(services.queryService(HookServiceProviders.class));
|
|
m_shut1 = hooker.hookInitServiceProvider(ssp);
|
|
m_shut2 = hooker.hookRuntimeServiceProvider(ssp);
|
|
m_shut3 = hooker.hookOutputServiceProvider(ssp);
|
|
|
|
} // end initialize
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface ComponentShutdown
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public void shutdown()
|
|
{
|
|
// Get the list of all modules and clear the maps.
|
|
LinkedList tmp = new LinkedList(m_jar_to_module.values());
|
|
m_jar_to_module.clear();
|
|
m_qname_to_module.clear();
|
|
|
|
// Shut down all the modules.
|
|
while (!(tmp.isEmpty()))
|
|
{ // remove modules one at a time and shut them down
|
|
ModuleLoader ml = (ModuleLoader)(tmp.removeFirst());
|
|
if (ml.isInitialized())
|
|
ml.shutdown();
|
|
ml.dispose();
|
|
|
|
} // end while
|
|
|
|
// Shut down everything else.
|
|
m_shut3.shutdown();
|
|
m_shut3 = null;
|
|
m_shut2.shutdown();
|
|
m_shut2 = null;
|
|
m_shut1.shutdown();
|
|
m_shut1 = null;
|
|
m_install_services = null;
|
|
m_instservice.dispose();
|
|
m_instservice = null;
|
|
m_ops.dispose();
|
|
m_ops = null;
|
|
m_nscache = null;
|
|
m_appcon = null;
|
|
|
|
} // end shutdown
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface ModuleOperations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public Module findModule(String namespace, String name)
|
|
{
|
|
QualifiedNameKey qname = new QualifiedNameKey(namespace,name);
|
|
synchronized (this)
|
|
{ // look up module in hash table
|
|
return (Module)(m_qname_to_module.get(qname));
|
|
|
|
} // end synchronized block
|
|
|
|
} // end findModule
|
|
|
|
public synchronized Module findModuleByFilename(String filename)
|
|
{
|
|
// look for the right module already in the map
|
|
return (Module)(m_jar_to_module.get(filename));
|
|
|
|
} // end findModuleByFilename
|
|
|
|
public Module loadModule(String name, boolean initialize) throws ModuleException
|
|
{
|
|
ModuleLoader rc = null;
|
|
synchronized (this)
|
|
{ // look for the right module already in the map
|
|
rc = (ModuleLoader)(m_jar_to_module.get(name));
|
|
if (rc!=null)
|
|
return (Module)rc; // found it already
|
|
|
|
// create the JAR file reference
|
|
File mod_file = new File(m_mod_dir,name);
|
|
if (!(mod_file.canRead()))
|
|
{ // unable to read the module file
|
|
ModuleException me = new ModuleException(ModuleLoader.class,"ModuleMessages","jar.notFound");
|
|
me.setParameter(0,mod_file.getAbsolutePath());
|
|
throw me;
|
|
|
|
} // end if
|
|
|
|
try
|
|
{ // create a new ModuleLoader
|
|
rc = new ModuleLoader(mod_file,name,m_ops,m_nscache,m_install_services);
|
|
|
|
} // end try
|
|
catch (MalformedURLException e)
|
|
{ // the module URL is bad
|
|
ModuleException me = new ModuleException(ModuleLoader.class,"ModuleMessages","jar.badURL",e);
|
|
me.setParameter(0,mod_file.getAbsolutePath());
|
|
throw me;
|
|
|
|
} // end catch
|
|
|
|
// enter this module into the maps
|
|
QualifiedNameKey key = rc.getModuleID();
|
|
m_qname_to_module.put(key,rc);
|
|
m_jar_to_module.put(name,rc);
|
|
|
|
} // end synchronized block
|
|
|
|
// initialize the module if we need to do that
|
|
if (initialize)
|
|
rc.initialize(m_appcon.getInitServices());
|
|
|
|
return (Module)rc;
|
|
|
|
} // end loadModule
|
|
|
|
public void freeUnusedModules()
|
|
{
|
|
HashMap tmp = new HashMap();
|
|
synchronized (this)
|
|
{ // build the map of modules to free
|
|
Iterator it = m_jar_to_module.entrySet().iterator();
|
|
while (it.hasNext())
|
|
{ // test it and build the map to remove
|
|
Map.Entry ntry = (Map.Entry)(it.next());
|
|
ModuleLoader ml = (ModuleLoader)(ntry.getValue());
|
|
if (!(ml.inUse()))
|
|
tmp.put(ntry.getKey(),ml.getModuleID());
|
|
|
|
} // end while
|
|
|
|
it = tmp.entrySet().iterator();
|
|
while (it.hasNext())
|
|
{ // loop and remove the modules, shutting them down as we go
|
|
Map.Entry ntry = (Map.Entry)(it.next());
|
|
ModuleLoader ml = (ModuleLoader)(m_qname_to_module.get(ntry.getValue()));
|
|
m_jar_to_module.remove(ntry.getKey());
|
|
m_qname_to_module.remove(ntry.getValue());
|
|
if (ml.isInitialized())
|
|
ml.shutdown();
|
|
ml.dispose();
|
|
|
|
} // end while
|
|
|
|
} // end synchronized block
|
|
|
|
} // end freeUnusedModules
|
|
|
|
public List listAllModuleNames()
|
|
{
|
|
ArrayList rc = new ArrayList();
|
|
int len = m_mod_dir.getAbsolutePath().length();
|
|
LinkedList process = new LinkedList();
|
|
pushFileList(process,m_mod_dir);
|
|
while (process.size()>0)
|
|
{ // examine all files
|
|
File f = (File)(process.removeFirst());
|
|
if (f.isDirectory())
|
|
pushFileList(process,f); // list files in the subdirectory
|
|
else if (isDynamoModule(f))
|
|
{ // strip off the base path and return the name
|
|
String name = f.getAbsolutePath().substring(len);
|
|
if (name.startsWith(File.pathSeparator))
|
|
name = name.substring(File.pathSeparator.length());
|
|
rc.add(name);
|
|
|
|
} // end else if
|
|
|
|
} // end while
|
|
|
|
if (rc.isEmpty())
|
|
return Collections.EMPTY_LIST;
|
|
rc.trimToSize();
|
|
return Collections.unmodifiableList(rc);
|
|
|
|
} // end listAllModuleNames
|
|
|
|
public Set listInstalledModuleNames() throws DatabaseException
|
|
{
|
|
return m_ops.getInstalledModules();
|
|
|
|
} // end listInstalledModuleNames
|
|
|
|
public void loadInstalledModules() throws DatabaseException, ModuleException
|
|
{
|
|
Set set = m_ops.getInstalledModules();
|
|
logger.info("loadInstalledModules(): " + set.size() + " module(s) to load");
|
|
Iterator it = set.iterator();
|
|
while (it.hasNext())
|
|
{ // load each of the installed modules in turn
|
|
String mod_filename = (String)(it.next());
|
|
logger.info("loadInstalledModules(): loading " + mod_filename);
|
|
this.loadModule(mod_filename,true);
|
|
|
|
} // end while
|
|
|
|
} // end loadInstalledModules
|
|
|
|
public ComponentShutdown hookInstallServices(ServiceProvider sp)
|
|
{
|
|
return m_instservice.hook(sp);
|
|
|
|
} // end hookInstallServices
|
|
|
|
} // end class ModuleManager
|