001/* 002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu, jcarsique 018 */ 019package org.nuxeo.osgi.application; 020 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.OutputStream; 027import java.lang.reflect.Method; 028import java.util.ArrayList; 029import java.util.Enumeration; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import java.util.Properties; 034import java.util.jar.JarEntry; 035import java.util.jar.JarFile; 036import java.util.zip.ZipEntry; 037 038import javax.management.JMException; 039 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042 043/** 044 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 045 */ 046public class FrameworkBootstrap implements LoaderConstants { 047 048 protected static final String DEFAULT_BUNDLES_CP = "bundles/*:plugins/*"; 049 050 protected static final String DEFAULT_LIBS_CP = "lib/*:.:config"; 051 052 private static final Log log = LogFactory.getLog(FrameworkBootstrap.class); 053 054 protected File home; 055 056 protected MutableClassLoader loader; 057 058 protected Map<String, Object> env; 059 060 protected Class<?> frameworkLoaderClass; 061 062 protected long startTime; 063 064 protected boolean scanForNestedJars = true; 065 066 protected boolean flushCache = false; 067 068 public FrameworkBootstrap(ClassLoader cl, File home) throws IOException { 069 this(new MutableClassLoaderDelegate(cl), home); 070 } 071 072 public FrameworkBootstrap(MutableClassLoader loader, File home) throws IOException { 073 this.home = home.getCanonicalFile(); 074 this.loader = loader; 075 initializeEnvironment(); 076 } 077 078 public void setHostName(String value) { 079 env.put(HOST_NAME, value); 080 } 081 082 public void setHostVersion(String value) { 083 env.put(HOST_VERSION, value); 084 } 085 086 public void setDoPreprocessing(boolean doPreprocessing) { 087 env.put(PREPROCESSING, Boolean.toString(doPreprocessing)); 088 } 089 090 public void setDevMode(String devMode) { 091 env.put(DEVMODE, devMode); 092 } 093 094 public void setFlushCache(boolean flushCache) { 095 this.flushCache = flushCache; 096 } 097 098 public void setScanForNestedJars(boolean scanForNestedJars) { 099 this.scanForNestedJars = scanForNestedJars; 100 } 101 102 public Map<String, Object> env() { 103 return env; 104 } 105 106 public MutableClassLoader getLoader() { 107 return loader; 108 } 109 110 public ClassLoader getClassLoader() { 111 return loader.getClassLoader(); 112 } 113 114 public File getHome() { 115 return home; 116 } 117 118 public void initialize() throws ReflectiveOperationException, IOException { 119 startTime = System.currentTimeMillis(); 120 List<File> bundleFiles = buildClassPath(); 121 frameworkLoaderClass = getClassLoader().loadClass("org.nuxeo.osgi.application.loader.FrameworkLoader"); 122 Method init = frameworkLoaderClass.getMethod("initialize", ClassLoader.class, File.class, List.class, Map.class); 123 init.invoke(null, loader.getClassLoader(), home, bundleFiles, env); 124 } 125 126 public void start(MutableClassLoader cl) throws ReflectiveOperationException, IOException, JMException { 127 128 } 129 130 public void stop(MutableClassLoader cl) throws ReflectiveOperationException, JMException { 131 132 } 133 134 public String installBundle(File f) throws ReflectiveOperationException { 135 if (frameworkLoaderClass == null) { 136 throw new IllegalStateException("Framework Loader was not initialized. Call initialize() method first"); 137 } 138 Method install = frameworkLoaderClass.getMethod("install", File.class); 139 return (String) install.invoke(null, f); 140 } 141 142 public void uninstallBundle(String name) throws ReflectiveOperationException { 143 if (frameworkLoaderClass == null) { 144 throw new IllegalStateException("Framework Loader was not initialized. Call initialize() method first"); 145 } 146 Method uninstall = frameworkLoaderClass.getMethod("uninstall", String.class); 147 uninstall.invoke(null, name); 148 } 149 150 @SuppressWarnings({ "unchecked", "rawtypes" }) 151 protected void initializeEnvironment() throws IOException { 152 System.setProperty(HOME_DIR, home.getAbsolutePath()); 153 env = new HashMap<String, Object>(); 154 // initialize with default values 155 env.put(BUNDLES, DEFAULT_BUNDLES_CP); 156 env.put(LIBS, DEFAULT_LIBS_CP); 157 // load launcher.properties file if exists to overwrite default values 158 File file = new File(home, "launcher.properties"); 159 if (!file.isFile()) { 160 return; 161 } 162 Properties p = new Properties(); 163 try (FileInputStream in = new FileInputStream(file)) { 164 p.load(in); 165 env.putAll((Map) p); 166 String v = (String) env.get(SCAN_FOR_NESTED_JARS); 167 if (v != null) { 168 scanForNestedJars = Boolean.parseBoolean(v); 169 } 170 v = (String) env.get(FLUSH_CACHE); 171 if (v != null) { 172 flushCache = Boolean.parseBoolean(v); 173 } 174 } 175 } 176 177 protected void printStartedMessage() { 178 log.info("Framework started in " + ((System.currentTimeMillis() - startTime) / 1000) + " sec."); 179 } 180 181 protected File newFile(String path) throws IOException { 182 if (path.startsWith("/")) { 183 return new File(path).getCanonicalFile(); 184 } else { 185 return new File(home, path).getCanonicalFile(); 186 } 187 } 188 189 /** 190 * Fills the classloader with all jars found in the defined classpath. 191 * 192 * @return the list of bundle files. 193 */ 194 protected List<File> buildClassPath() throws IOException { 195 List<File> bundleFiles = new ArrayList<File>(); 196 String libsCp = (String) env.get(LIBS); 197 if (libsCp != null) { 198 buildLibsClassPath(libsCp); 199 } 200 String bundlesCp = (String) env.get(BUNDLES); 201 if (bundlesCp != null) { 202 buildBundlesClassPath(bundlesCp, bundleFiles); 203 } 204 extractNestedJars(bundleFiles, new File(home, "tmp/nested-jars")); 205 return bundleFiles; 206 } 207 208 protected void buildLibsClassPath(String libsCp) throws IOException { 209 String[] ar = libsCp.split(":"); 210 for (String entry : ar) { 211 File entryFile; 212 if (entry.endsWith("/*")) { 213 entryFile = newFile(entry.substring(0, entry.length() - 2)); 214 File[] files = entryFile.listFiles(); 215 if (files != null) { 216 for (File file : files) { 217 loader.addURL(file.toURI().toURL()); 218 } 219 } 220 } else { 221 entryFile = newFile(entry); 222 loader.addURL(entryFile.toURI().toURL()); 223 } 224 } 225 } 226 227 protected void buildBundlesClassPath(String bundlesCp, List<File> bundleFiles) throws IOException { 228 String[] ar = bundlesCp.split(":"); 229 for (String entry : ar) { 230 File entryFile; 231 if (entry.endsWith("/*")) { 232 entryFile = newFile(entry.substring(0, entry.length() - 2)); 233 File[] files = entryFile.listFiles(); 234 if (files != null) { 235 for (File file : files) { 236 String path = file.getPath(); 237 if (path.endsWith(".jar") || path.endsWith(".zip") || path.endsWith(".war") 238 || path.endsWith("rar")) { 239 bundleFiles.add(file); 240 loader.addURL(file.toURI().toURL()); 241 } 242 } 243 } 244 } else { 245 entryFile = newFile(entry); 246 bundleFiles.add(entryFile); 247 loader.addURL(entryFile.toURI().toURL()); 248 } 249 } 250 } 251 252 protected void extractNestedJars(List<File> bundleFiles, File dir) throws IOException { 253 if (!scanForNestedJars) { 254 return; 255 } 256 if (dir.isDirectory()) { 257 if (flushCache) { 258 deleteAll(dir); 259 } else { 260 File[] files = dir.listFiles(); 261 if (files != null) { 262 for (File f : files) { 263 loader.addURL(f.toURI().toURL()); 264 } 265 } 266 return; 267 } 268 } 269 dir.mkdirs(); 270 for (File f : bundleFiles) { 271 if (f.isFile()) { 272 extractNestedJars(f, dir); 273 } 274 } 275 } 276 277 protected void extractNestedJars(File file, File tmpDir) throws IOException { 278 try (JarFile jarFile = new JarFile(file)) { 279 String fileName = file.getName(); 280 Enumeration<JarEntry> entries = jarFile.entries(); 281 while (entries.hasMoreElements()) { 282 JarEntry entry = entries.nextElement(); 283 String path = entry.getName(); 284 if (entry.getName().endsWith(".jar")) { 285 String name = path.replace('/', '_'); 286 File dest = new File(tmpDir, fileName + '-' + name); 287 extractNestedJar(jarFile, entry, dest); 288 loader.addURL(dest.toURI().toURL()); 289 } 290 } 291 } 292 } 293 294 protected void extractNestedJar(JarFile file, ZipEntry entry, File dest) throws IOException { 295 try (InputStream in = file.getInputStream(entry)) { 296 copyToFile(in, dest); 297 } 298 } 299 300 public static void deleteAll(File file) { 301 if (file.isDirectory()) { 302 File[] files = file.listFiles(); 303 if (files != null) { 304 for (File f : files) { 305 deleteAll(f); 306 } 307 } 308 } 309 file.delete(); 310 } 311 312 public static void copyFile(File src, File file) throws IOException { 313 try (FileInputStream in = new FileInputStream(src)) { 314 copyToFile(in, file); 315 } 316 } 317 318 public static void copyToFile(InputStream in, File file) throws IOException { 319 try (OutputStream out = new FileOutputStream(file)) { 320 byte[] buffer = createBuffer(in.available()); 321 int read; 322 while ((read = in.read(buffer)) != -1) { 323 out.write(buffer, 0, read); 324 } 325 } 326 } 327 328 private static final int BUFFER_SIZE = 1024 * 64; // 64K 329 330 private static final int MAX_BUFFER_SIZE = 1024 * 1024; // 64K 331 332 private static final int MIN_BUFFER_SIZE = 1024 * 8; // 64K 333 334 private static byte[] createBuffer(int preferredSize) { 335 if (preferredSize < 1) { 336 preferredSize = BUFFER_SIZE; 337 } 338 if (preferredSize > MAX_BUFFER_SIZE) { 339 preferredSize = MAX_BUFFER_SIZE; 340 } else if (preferredSize < MIN_BUFFER_SIZE) { 341 preferredSize = MIN_BUFFER_SIZE; 342 } 343 return new byte[preferredSize]; 344 } 345 346 public static File findFileStartingWidth(File dir, String prefix) { 347 String[] names = dir.list(); 348 if (names != null) { 349 for (String name : names) { 350 if (name.startsWith(prefix)) { 351 return new File(dir, name); 352 } 353 } 354 } 355 return null; 356 } 357 358 public static void copyTree(File src, File dst) throws IOException { 359 if (src.isFile()) { 360 copyFile(src, dst); 361 } else if (src.isDirectory()) { 362 if (dst.exists()) { 363 dst = new File(dst, src.getName()); 364 dst.mkdir(); 365 } else { // allows renaming dest dir 366 dst.mkdirs(); 367 } 368 File[] files = src.listFiles(); 369 for (File file : files) { 370 copyTree(file, dst); 371 } 372 } 373 } 374 375}