/*
* 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 .
*
* 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 ,
* 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