001/* 002 * (C) Copyright 2006-2010 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.runtime.tomcat.dev; 018 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.lang.reflect.Method; 023import java.net.URL; 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Timer; 029import java.util.TimerTask; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.osgi.application.FrameworkBootstrap; 034import org.nuxeo.osgi.application.MutableClassLoader; 035 036/** 037 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 038 */ 039public class DevFrameworkBootstrap extends FrameworkBootstrap implements DevBundlesManager { 040 041 protected final Log log = LogFactory.getLog(DevFrameworkBootstrap.class); 042 043 protected DevBundle[] devBundles; 044 045 protected Timer bundlesCheck; 046 047 protected long lastModified = 0; 048 049 protected ReloadServiceInvoker reloadServiceInvoker; 050 051 protected File devBundlesFile; 052 053 protected final File seamdev; 054 055 protected final File webclasses; 056 057 public DevFrameworkBootstrap(MutableClassLoader cl, File home) throws IOException { 058 super(cl, home); 059 devBundlesFile = new File(home, "dev.bundles"); 060 seamdev = new File(home, "nuxeo.war/WEB-INF/dev"); 061 webclasses = new File(home, "nuxeo.war/WEB-INF/classes"); 062 } 063 064 @Override 065 public void start() throws ReflectiveOperationException, IOException { 066 // check if we have dev. bundles or libs to deploy and add them to the 067 // classpath 068 preloadDevBundles(); 069 // start the framework 070 super.start(); 071 reloadServiceInvoker = new ReloadServiceInvoker((ClassLoader) loader); 072 writeComponentIndex(); 073 postloadDevBundles(); // start dev bundles if any 074 String installReloadTimerOption = (String) env.get(INSTALL_RELOAD_TIMER); 075 if (installReloadTimerOption != null && Boolean.parseBoolean(installReloadTimerOption) == Boolean.TRUE) { 076 toggleTimer(); 077 } 078 } 079 080 @Override 081 public void toggleTimer() { 082 // start reload timer 083 if (bundlesCheck != null) { 084 bundlesCheck.cancel(); 085 bundlesCheck = null; 086 } else { 087 bundlesCheck = new Timer("Dev Bundles Loader"); 088 bundlesCheck.scheduleAtFixedRate(new TimerTask() { 089 @Override 090 public void run() { 091 loadDevBundles(); 092 } 093 }, 2000, 2000); 094 } 095 } 096 097 @Override 098 public boolean isTimerRunning() { 099 return bundlesCheck != null; 100 } 101 102 @Override 103 public void stop() throws ReflectiveOperationException { 104 if (bundlesCheck != null) { 105 bundlesCheck.cancel(); 106 bundlesCheck = null; 107 } 108 super.stop(); 109 } 110 111 @Override 112 public String getDevBundlesLocation() { 113 return devBundlesFile.getAbsolutePath(); 114 } 115 116 /** 117 * Load the development bundles and libs if any in the classpath before starting the framework. 118 */ 119 protected void preloadDevBundles() throws IOException { 120 if (!devBundlesFile.isFile()) { 121 return; 122 } 123 lastModified = devBundlesFile.lastModified(); 124 devBundles = DevBundle.parseDevBundleLines(new FileInputStream(devBundlesFile)); 125 if (devBundles.length == 0) { 126 devBundles = null; 127 return; 128 } 129 installNewClassLoader(devBundles); 130 } 131 132 protected void postloadDevBundles() throws ReflectiveOperationException { 133 if (devBundles != null) { 134 reloadServiceInvoker.hotDeployBundles(devBundles); 135 } 136 } 137 138 @Override 139 public void loadDevBundles() { 140 long tm = devBundlesFile.lastModified(); 141 if (lastModified >= tm) { 142 return; 143 } 144 lastModified = tm; 145 try { 146 reloadDevBundles(DevBundle.parseDevBundleLines(new FileInputStream(devBundlesFile))); 147 } catch (ReflectiveOperationException | IOException e) { 148 log.error("Failed to deploy dev bundles", e); 149 } 150 } 151 152 @Override 153 public void resetDevBundles(String path) { 154 devBundlesFile = new File(path); 155 lastModified = 0; 156 loadDevBundles(); 157 } 158 159 @Override 160 public DevBundle[] getDevBundles() { 161 return devBundles; 162 } 163 164 protected synchronized void reloadDevBundles(DevBundle[] bundles) throws ReflectiveOperationException { 165 166 if (devBundles != null) { // clear last context 167 try { 168 reloadServiceInvoker.hotUndeployBundles(devBundles); 169 clearClassLoader(); 170 } finally { 171 devBundles = null; 172 } 173 } 174 175 if (bundles != null) { // create new context 176 try { 177 installNewClassLoader(bundles); 178 reloadServiceInvoker.hotDeployBundles(bundles); 179 } finally { 180 devBundles = bundles; 181 } 182 } 183 } 184 185 protected void clearClassLoader() { 186 NuxeoDevWebappClassLoader devLoader = (NuxeoDevWebappClassLoader) loader; 187 devLoader.clear(); 188 System.gc(); 189 } 190 191 protected void installNewClassLoader(DevBundle[] bundles) { 192 List<URL> jarUrls = new ArrayList<URL>(); 193 List<File> seamDirs = new ArrayList<File>(); 194 List<File> resourceBundleFragments = new ArrayList<File>(); 195 // filter dev bundles types 196 for (DevBundle bundle : bundles) { 197 if (bundle.devBundleType.isJar) { 198 try { 199 jarUrls.add(bundle.url()); 200 } catch (IOException e) { 201 log.error("Cannot install " + bundle); 202 } 203 } else if (bundle.devBundleType == DevBundleType.Seam) { 204 seamDirs.add(bundle.file()); 205 } else if (bundle.devBundleType == DevBundleType.ResourceBundleFragment) { 206 resourceBundleFragments.add(bundle.file()); 207 } 208 } 209 210 // install class loader 211 NuxeoDevWebappClassLoader devLoader = (NuxeoDevWebappClassLoader) loader; 212 devLoader.createLocalClassLoader(jarUrls.toArray(new URL[jarUrls.size()])); 213 214 // install seam classes in hot sync folder 215 try { 216 installSeamClasses(seamDirs.toArray(new File[seamDirs.size()])); 217 } catch (IOException e) { 218 log.error("Cannot install seam classes in hotsync folder", e); 219 } 220 221 // install l10n resources 222 try { 223 installResourceBundleFragments(resourceBundleFragments); 224 } catch (IOException e) { 225 log.error("Cannot install l10n resources", e); 226 } 227 } 228 229 public void writeComponentIndex() { 230 File file = new File(home.getParentFile(), "sdk"); 231 file.mkdirs(); 232 file = new File(file, "components.index"); 233 // if (file.isFile()) { 234 // return; 235 // } 236 try { 237 Method m = getClassLoader().loadClass("org.nuxeo.runtime.model.impl.ComponentRegistrySerializer").getMethod( 238 "writeIndex", File.class); 239 m.invoke(null, file); 240 } catch (ReflectiveOperationException t) { 241 // ignore 242 } 243 } 244 245 public void installSeamClasses(File[] dirs) throws IOException { 246 if (seamdev.exists()) { 247 IOUtils.deleteTree(seamdev); 248 } 249 seamdev.mkdirs(); 250 for (File dir : dirs) { 251 IOUtils.copyTree(dir, seamdev); 252 } 253 } 254 255 public void installResourceBundleFragments(List<File> files) throws IOException { 256 Map<String, List<File>> fragments = new HashMap<String, List<File>>(); 257 258 for (File file : files) { 259 String name = resourceBundleName(file); 260 if (!fragments.containsKey(name)) { 261 fragments.put(name, new ArrayList<File>()); 262 } 263 fragments.get(name).add(file); 264 } 265 for (String name : fragments.keySet()) { 266 IOUtils.appendResourceBundleFragments(name, fragments.get(name), webclasses); 267 } 268 } 269 270 protected static String resourceBundleName(File file) { 271 String name = file.getName(); 272 return name.substring(name.lastIndexOf('-') + 1); 273 } 274 275}