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