001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bstefanescu 016 */ 017package org.nuxeo.ecm.webengine.model.impl; 018 019import java.io.BufferedInputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URL; 025import java.util.Iterator; 026import java.util.Map; 027import java.util.concurrent.ConcurrentHashMap; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.common.xmap.Context; 032import org.nuxeo.common.xmap.XMap; 033import org.nuxeo.ecm.webengine.ResourceBinding; 034import org.nuxeo.ecm.webengine.WebEngine; 035import org.nuxeo.ecm.webengine.WebException; 036import org.nuxeo.runtime.api.Framework; 037 038/** 039 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 040 */ 041public class ModuleManager { 042 043 private static final Log log = LogFactory.getLog(ModuleManager.class); 044 045 protected final Map<String, ModuleConfiguration> modules; 046 047 protected final Map<String, ModuleConfiguration> paths; 048 049 protected final Map<String, ModuleConfiguration> roots; 050 051 protected WebEngine engine; 052 053 public ModuleManager(WebEngine engine) { 054 this.engine = engine; 055 modules = new ConcurrentHashMap<String, ModuleConfiguration>(); 056 paths = new ConcurrentHashMap<String, ModuleConfiguration>(); 057 roots = new ConcurrentHashMap<String, ModuleConfiguration>(); 058 } 059 060 /** 061 * Gets a module given its name. 062 * 063 * @return the module or null if none 064 */ 065 public ModuleConfiguration getModule(String key) { 066 return modules.get(key); 067 } 068 069 public ModuleConfiguration getModuleByPath(String path) { 070 if (!path.startsWith("/")) { 071 path = "/" + path; 072 } 073 return paths.get(path); 074 } 075 076 public ModuleConfiguration getRootModule() { 077 return paths.get("/"); 078 } 079 080 public ModuleConfiguration[] getModules() { 081 return modules.values().toArray(new ModuleConfiguration[modules.size()]); 082 } 083 084 public ModuleConfiguration getModuleByConfigFile(File file) { 085 ModuleConfiguration[] ar = getModules(); 086 for (ModuleConfiguration mc : ar) { 087 if (file.equals(mc.file)) { 088 return mc; 089 } 090 } 091 return null; 092 } 093 094 public synchronized void registerModule(ModuleConfiguration descriptor) { 095 log.info("Registering web module: " + descriptor.name); 096 modules.put(descriptor.name, descriptor); 097 String path = descriptor.path; 098 if (path != null) { 099 // TODO remove this 100 // compat. method now modules should be declared through 101 // WebApplication class 102 if (!path.startsWith("/")) { 103 path = "/" + path; 104 } 105 paths.put(path, descriptor); 106 } 107 if (descriptor.roots != null) { 108 for (Class<?> cl : descriptor.roots) { 109 roots.put(cl.getName(), descriptor); 110 } 111 } 112 } 113 114 // TODO the class path is not updated by this operation ... 115 public synchronized File unregisterModule(String name) { 116 ModuleConfiguration md = modules.remove(name); 117 if (md == null) { 118 return null; 119 } 120 Iterator<ModuleConfiguration> it = paths.values().iterator(); 121 while (it.hasNext()) { // remove all module occurrence in paths map 122 ModuleConfiguration p = it.next(); 123 if (p.name.equals(md.name)) { 124 it.remove(); 125 } 126 } 127 if (md.roots != null) { 128 for (Class<?> cl : md.roots) { 129 roots.remove(cl); 130 } 131 } 132 return md.file; 133 } 134 135 public ModuleConfiguration getModuleByRootClass(Class<?> clazz) { 136 return roots.get(clazz.getName()); 137 } 138 139 public synchronized void bind(String name, String path) { 140 ModuleConfiguration md = modules.get(name); 141 if (md != null) { 142 paths.put(path, md); 143 } 144 } 145 146 public void loadModules(File root) { 147 for (String name : root.list()) { 148 String path = name + "/module.xml"; 149 File file = new File(root, path); 150 if (file.isFile()) { 151 loadModule(file); 152 } 153 } 154 } 155 156 public void loadModule(ModuleConfiguration mc) { 157 // this should be called after the class path is updated ... 158 loadModuleRootResources(mc); 159 mc.setEngine(engine); 160 registerModule(mc); 161 } 162 163 public void loadModule(File file) { 164 ModuleConfiguration md = loadConfiguration(file); 165 // this should be called after the class path is updated ... 166 loadModuleRootResources(md); 167 md.setEngine(engine); 168 registerModule(md); 169 } 170 171 public void loadModuleFromDir(File moduleRoot) { 172 File file = new File(moduleRoot, "module.xml"); 173 if (file.isFile()) { 174 loadModule(file); 175 } 176 } 177 178 public void reloadModule(String name) { 179 log.info("Reloading module: " + name); 180 File cfg = unregisterModule(name); 181 if (cfg != null) { 182 loadModule(cfg); 183 } 184 } 185 186 public void reloadModules() { 187 log.info("Reloading modules"); 188 for (ModuleConfiguration mc : getModules()) { 189 reloadModule(mc.name); 190 } 191 } 192 193 protected ModuleConfiguration loadConfiguration(File file) { 194 if (engine == null) { 195 engine = Framework.getLocalService(WebEngine.class); 196 } 197 try { 198 ModuleConfiguration mc = readConfiguration(engine, file); 199 mc.file = file; 200 if (mc.directory == null) { 201 mc.directory = file.getParentFile().getCanonicalFile(); 202 } 203 return mc; 204 } catch (IOException e) { 205 throw WebException.wrap("Faile to load module configuration: " + file, e); 206 } 207 } 208 209 public static ModuleConfiguration readConfiguration(final WebEngine engine, File file) throws IOException { 210 XMap xmap = new XMap(); 211 xmap.register(ModuleConfiguration.class); 212 InputStream in = new BufferedInputStream(new FileInputStream(file)); 213 ModuleConfiguration mc = (ModuleConfiguration) xmap.load(createXMapContext(engine), in); 214 return mc; 215 } 216 217 public void loadModuleRootResources(ModuleConfiguration mc) { 218 if (mc.resources != null) { 219 for (ResourceBinding rb : mc.resources) { 220 try { 221 rb.resolve(engine); 222 engine.addResourceBinding(rb); 223 } catch (ClassNotFoundException e) { 224 throw WebException.wrap("Faile to load module root resource: " + rb, e); 225 } 226 } 227 } 228 } 229 230 protected static Context createXMapContext(final WebEngine engine) { 231 return new Context() { 232 private static final long serialVersionUID = 1L; 233 234 @Override 235 public Class<?> loadClass(String className) throws ClassNotFoundException { 236 return engine.getWebLoader().loadClass(className); 237 } 238 239 @Override 240 public URL getResource(String name) { 241 return engine.getWebLoader().getResource(name); 242 } 243 }; 244 } 245 246}