/* * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * bstefanescu, jcarsique */ package org.nuxeo.osgi.application; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import javax.management.JMException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class FrameworkBootstrap implements LoaderConstants { protected static final String DEFAULT_BUNDLES_CP = "bundles/*:plugins/*"; protected static final String DEFAULT_LIBS_CP = "lib/*:.:config"; private static final Log log = LogFactory.getLog(FrameworkBootstrap.class); protected File home; protected MutableClassLoader loader; protected Map<String, Object> env; protected Class<?> frameworkLoaderClass; protected long startTime; protected boolean scanForNestedJars = true; protected boolean flushCache = false; public FrameworkBootstrap(ClassLoader cl, File home) throws IOException { this(new MutableClassLoaderDelegate(cl), home); } public FrameworkBootstrap(MutableClassLoader loader, File home) throws IOException { this.home = home.getCanonicalFile(); this.loader = loader; initializeEnvironment(); } public void setHostName(String value) { env.put(HOST_NAME, value); } public void setHostVersion(String value) { env.put(HOST_VERSION, value); } public void setDoPreprocessing(boolean doPreprocessing) { env.put(PREPROCESSING, Boolean.toString(doPreprocessing)); } public void setDevMode(String devMode) { env.put(DEVMODE, devMode); } public void setFlushCache(boolean flushCache) { this.flushCache = flushCache; } public void setScanForNestedJars(boolean scanForNestedJars) { this.scanForNestedJars = scanForNestedJars; } public Map<String, Object> env() { return env; } public MutableClassLoader getLoader() { return loader; } public ClassLoader getClassLoader() { return loader.getClassLoader(); } public File getHome() { return home; } public void initialize() throws ReflectiveOperationException, IOException { startTime = System.currentTimeMillis(); List<File> bundleFiles = buildClassPath(); frameworkLoaderClass = getClassLoader().loadClass("org.nuxeo.osgi.application.loader.FrameworkLoader"); Method init = frameworkLoaderClass.getMethod("initialize", ClassLoader.class, File.class, List.class, Map.class); init.invoke(null, loader.getClassLoader(), home, bundleFiles, env); } public void start(MutableClassLoader cl) throws ReflectiveOperationException, IOException, JMException { } public void stop(MutableClassLoader cl) throws ReflectiveOperationException, JMException { } public String installBundle(File f) throws ReflectiveOperationException { if (frameworkLoaderClass == null) { throw new IllegalStateException("Framework Loader was not initialized. Call initialize() method first"); } Method install = frameworkLoaderClass.getMethod("install", File.class); return (String) install.invoke(null, f); } public void uninstallBundle(String name) throws ReflectiveOperationException { if (frameworkLoaderClass == null) { throw new IllegalStateException("Framework Loader was not initialized. Call initialize() method first"); } Method uninstall = frameworkLoaderClass.getMethod("uninstall", String.class); uninstall.invoke(null, name); } @SuppressWarnings({ "unchecked", "rawtypes" }) protected void initializeEnvironment() throws IOException { System.setProperty(HOME_DIR, home.getAbsolutePath()); env = new HashMap<String, Object>(); // initialize with default values env.put(BUNDLES, DEFAULT_BUNDLES_CP); env.put(LIBS, DEFAULT_LIBS_CP); // load launcher.properties file if exists to overwrite default values File file = new File(home, "launcher.properties"); if (!file.isFile()) { return; } Properties p = new Properties(); FileInputStream in = new FileInputStream(file); try { p.load(in); env.putAll((Map) p); String v = (String) env.get(SCAN_FOR_NESTED_JARS); if (v != null) { scanForNestedJars = Boolean.parseBoolean(v); } v = (String) env.get(FLUSH_CACHE); if (v != null) { flushCache = Boolean.parseBoolean(v); } } finally { in.close(); } } protected void printStartedMessage() { log.info("Framework started in " + ((System.currentTimeMillis() - startTime) / 1000) + " sec."); } protected File newFile(String path) throws IOException { if (path.startsWith("/")) { return new File(path).getCanonicalFile(); } else { return new File(home, path).getCanonicalFile(); } } /** * Fills the classloader with all jars found in the defined classpath. * * @return the list of bundle files. */ protected List<File> buildClassPath() throws IOException { List<File> bundleFiles = new ArrayList<File>(); String libsCp = (String) env.get(LIBS); if (libsCp != null) { buildLibsClassPath(libsCp); } String bundlesCp = (String) env.get(BUNDLES); if (bundlesCp != null) { buildBundlesClassPath(bundlesCp, bundleFiles); } extractNestedJars(bundleFiles, new File(home, "tmp/nested-jars")); return bundleFiles; } protected void buildLibsClassPath(String libsCp) throws IOException { String[] ar = libsCp.split(":"); for (String entry : ar) { File entryFile; if (entry.endsWith("/*")) { entryFile = newFile(entry.substring(0, entry.length() - 2)); File[] files = entryFile.listFiles(); if (files != null) { for (File file : files) { loader.addURL(file.toURI().toURL()); } } } else { entryFile = newFile(entry); loader.addURL(entryFile.toURI().toURL()); } } } protected void buildBundlesClassPath(String bundlesCp, List<File> bundleFiles) throws IOException { String[] ar = bundlesCp.split(":"); for (String entry : ar) { File entryFile; if (entry.endsWith("/*")) { entryFile = newFile(entry.substring(0, entry.length() - 2)); File[] files = entryFile.listFiles(); if (files != null) { for (File file : files) { String path = file.getPath(); if (path.endsWith(".jar") || path.endsWith(".zip") || path.endsWith(".war") || path.endsWith("rar")) { bundleFiles.add(file); loader.addURL(file.toURI().toURL()); } } } } else { entryFile = newFile(entry); bundleFiles.add(entryFile); loader.addURL(entryFile.toURI().toURL()); } } } protected void extractNestedJars(List<File> bundleFiles, File dir) throws IOException { if (!scanForNestedJars) { return; } if (dir.isDirectory()) { if (flushCache) { deleteAll(dir); } else { File[] files = dir.listFiles(); if (files != null) { for (File f : files) { loader.addURL(f.toURI().toURL()); } } return; } } dir.mkdirs(); for (File f : bundleFiles) { if (f.isFile()) { extractNestedJars(f, dir); } } } protected void extractNestedJars(File file, File tmpDir) throws IOException { JarFile jarFile = new JarFile(file); String fileName = file.getName(); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String path = entry.getName(); if (entry.getName().endsWith(".jar")) { String name = path.replace('/', '_'); File dest = new File(tmpDir, fileName + '-' + name); extractNestedJar(jarFile, entry, dest); loader.addURL(dest.toURI().toURL()); } } } protected void extractNestedJar(JarFile file, ZipEntry entry, File dest) throws IOException { InputStream in = null; try { in = file.getInputStream(entry); copyToFile(in, dest); } finally { if (in != null) { in.close(); } } } public static void deleteAll(File file) { if (file.isDirectory()) { File[] files = file.listFiles(); if (files != null) { for (File f : files) { deleteAll(f); } } } file.delete(); } public static void copyFile(File src, File file) throws IOException { FileInputStream in = new FileInputStream(src); try { copyToFile(in, file); } finally { in.close(); } } public static void copyToFile(InputStream in, File file) throws IOException { OutputStream out = null; try { out = new FileOutputStream(file); byte[] buffer = createBuffer(in.available()); int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } } finally { if (out != null) { out.close(); } } } private static final int BUFFER_SIZE = 1024 * 64; // 64K private static final int MAX_BUFFER_SIZE = 1024 * 1024; // 64K private static final int MIN_BUFFER_SIZE = 1024 * 8; // 64K private static byte[] createBuffer(int preferredSize) { if (preferredSize < 1) { preferredSize = BUFFER_SIZE; } if (preferredSize > MAX_BUFFER_SIZE) { preferredSize = MAX_BUFFER_SIZE; } else if (preferredSize < MIN_BUFFER_SIZE) { preferredSize = MIN_BUFFER_SIZE; } return new byte[preferredSize]; } public static File findFileStartingWidth(File dir, String prefix) { String[] names = dir.list(); if (names != null) { for (String name : names) { if (name.startsWith(prefix)) { return new File(dir, name); } } } return null; } public static void copyTree(File src, File dst) throws IOException { if (src.isFile()) { copyFile(src, dst); } else if (src.isDirectory()) { if (dst.exists()) { dst = new File(dst, src.getName()); dst.mkdir(); } else { // allows renaming dest dir dst.mkdirs(); } File[] files = src.listFiles(); for (File file : files) { copyTree(file, dst); } } } }