001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors. 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 * 019 * $Id$ 020 */ 021 022package org.nuxeo.osgi.application; 023 024import java.io.File; 025import java.io.IOException; 026import java.net.MalformedURLException; 027import java.net.URI; 028import java.net.URISyntaxException; 029import java.net.URL; 030import java.util.ArrayList; 031import java.util.List; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.common.Environment; 036import org.nuxeo.common.utils.StringUtils; 037import org.nuxeo.osgi.BundleFile; 038import org.nuxeo.osgi.BundleImpl; 039import org.nuxeo.osgi.DirectoryBundleFile; 040import org.nuxeo.osgi.JarBundleFile; 041import org.nuxeo.osgi.OSGiAdapter; 042import org.nuxeo.osgi.SystemBundle; 043import org.osgi.framework.BundleException; 044import org.osgi.framework.FrameworkEvent; 045 046/** 047 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 048 */ 049public class StandaloneApplication extends OSGiAdapter { 050 051 public static final String MAIN_TASK = "org.nuxeo.osgi.application.main.task"; 052 053 private static final Log log = LogFactory.getLog(StandaloneApplication.class); 054 055 private static StandaloneApplication instance; 056 057 private static CommandLineOptions options; // TODO should be remove 058 059 private static String[] args; 060 061 private static Runnable mainTask; 062 063 protected final SharedClassLoader classLoader; 064 065 protected final Environment env; 066 067 protected boolean isStarted; 068 069 protected File home; 070 071 protected List<File> classPath; 072 073 protected boolean scanForNestedJARs = true; // by default true 074 075 // a list of class path prefixes that contains JARS that should not be 076 // treated as bundles. 077 protected String[] libdirs; 078 079 public static StandaloneApplication getInstance() { 080 return instance; 081 } 082 083 public static StandaloneApplication createInstance(SharedClassLoader cl) throws IOException { 084 if (instance != null) { 085 throw new IllegalStateException("Application already instantiated"); 086 } 087 // create application environment 088 Environment env = createEnvironment(); 089 Environment.setDefault(env); 090 instance = new StandaloneApplication(cl, env); 091 String val = options.getOption("scanForNestedJARs"); 092 if (val != null) { 093 StandaloneApplication.instance.scanForNestedJARs = Boolean.parseBoolean(val); 094 } 095 // hack to avoid deploying all jars in classpath as bundles 096 String javaLibsProp = System.getProperty("org.nuxeo.launcher.libdirs"); 097 if (javaLibsProp != null) { 098 String[] ar = StringUtils.split(javaLibsProp, ',', false); 099 if (ar.length > 0) { 100 instance.libdirs = ar; 101 File wd = instance.getWorkingDir(); 102 for (int i = 0; i < ar.length; i++) { 103 if (!ar[i].startsWith("/")) { 104 instance.libdirs[i] = new File(wd, ar[i]).getCanonicalFile().getAbsolutePath(); 105 } 106 } 107 } 108 } 109 // end hack 110 return instance; 111 } 112 113 private StandaloneApplication(SharedClassLoader cl, Environment env) { 114 super(env.getHome(), env.getData(), env.getProperties()); 115 classLoader = cl; 116 this.env = env; 117 } 118 119 public SharedClassLoader getSharedClassLoader() { 120 return classLoader; 121 } 122 123 public Environment getEnvironment() { 124 return env; 125 } 126 127 public void start() throws IOException, BundleException { 128 if (isStarted) { 129 throw new IllegalStateException("OSGi Application is already started"); 130 } 131 List<BundleFile> preBundles = loadUserBundles("pre-bundles"); 132 List<BundleFile> postBundles = loadUserBundles("post-bundles"); 133 // start level 1 134 // start bundles that are specified in the osgi.bundles property 135 if (preBundles != null) { 136 startBundles(preBundles); 137 } 138 // start level 2 139 // if needed install all discovered bundles (the one that are located in 140 // bundles dir) 141 autoInstallBundles(); 142 // start level 3 143 if (postBundles != null) { 144 startBundles(postBundles); 145 } 146 fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, getSystemBundle(), null)); 147 isStarted = true; 148 } 149 150 public boolean isStarted() { 151 return isStarted; 152 } 153 154 @Override 155 public void shutdown() throws IOException { 156 if (!isStarted) { 157 throw new IllegalStateException("OSGi Application was not started"); 158 } 159 try { 160 super.shutdown(); 161 } finally { 162 isStarted = false; 163 } 164 } 165 166 protected void startBundles(List<BundleFile> bundles) throws BundleException { 167 for (BundleFile bf : bundles) { 168 this.install(new BundleImpl(this, bf, classLoader.getLoader())); 169 } 170 } 171 172 protected List<BundleFile> loadUserBundles(String key) throws IOException { 173 if (options == null) { 174 return null; 175 } 176 String bundlesString = options.getOption(key); 177 if (bundlesString == null) { 178 return null; // no bundles to load 179 } 180 List<BundleFile> bundles = new ArrayList<BundleFile>(); 181 String[] ar = StringUtils.split(bundlesString, ':', true); 182 for (String entry : ar) { 183 File file; 184 if (entry.contains("file:")) { 185 try { 186 URL url = new URL(entry); 187 file = new File(url.toURI()); 188 } catch (MalformedURLException e) { 189 throw new IOException(e); 190 } catch (URISyntaxException e) { 191 throw new IOException(e); 192 } 193 } else { 194 file = new File(entry); 195 } 196 BundleFile bf; 197 if (file.isDirectory()) { 198 bf = new DirectoryBundleFile(file); 199 } else { 200 bf = new JarBundleFile(file); 201 } 202 classLoader.addURL(bf.getURL()); 203 bundles.add(bf); 204 } 205 return bundles; 206 } 207 208 public List<File> getClassPath() { 209 return classPath; 210 } 211 212 public void setClassPath(List<File> classPath) { 213 this.classPath = classPath; 214 } 215 216 protected void autoInstallBundles() throws IOException, BundleException { 217 List<File> cp = getClassPath(); 218 if (cp == null || cp.isEmpty()) { 219 return; 220 } 221 boolean clear = hasCommandLineOption("clear"); 222 ClassPath cpath = new ClassPath(classLoader, new File(env.getData(), "nested-jars")); 223 File cache = new File(env.getData(), "bundles.cache"); 224 if (!clear && cache.exists()) { 225 try { 226 cpath.restore(cache); 227 } catch (IOException e) { // rebuild cache 228 cpath.scan(classPath, scanForNestedJARs, libdirs); 229 cpath.store(cache); 230 } 231 } else { 232 cpath.scan(classPath, scanForNestedJARs, libdirs); 233 cpath.store(cache); 234 } 235 installAll(cpath.getBundles()); 236 // new ApplicationBundleLoader(this, !clear).loadBundles(classPath); 237 } 238 239 public void install(BundleFile bf) throws BundleException { 240 install(new BundleImpl(this, bf, classLoader.getLoader())); 241 } 242 243 public void installAll(List<BundleFile> bundles) throws BundleException { 244 for (BundleFile bf : bundles) { 245 install(new BundleImpl(this, bf, classLoader.getLoader())); 246 } 247 } 248 249 /** 250 * Creates the system bundle from the jar specified by the nuxeo.osgi.system.bundle property. 251 */ 252 public static BundleFile createSystemBundle(URL systemBundle) throws IOException { 253 URI uri; 254 try { 255 uri = systemBundle.toURI(); 256 } catch (URISyntaxException e) { 257 throw new IOException(e); 258 } 259 File file = new File(uri); 260 BundleFile sysbf = null; 261 if (file.isFile()) { 262 sysbf = new JarBundleFile(file); 263 } else { 264 sysbf = new DirectoryBundleFile(file); 265 } 266 return sysbf; 267 } 268 269 public static CommandLineOptions getComandLineOptions() { 270 return options; 271 } 272 273 public static boolean hasCommandLineOption(String option) { 274 return options != null && options.hasOption(option); 275 } 276 277 public static Environment createEnvironment() throws IOException { 278 if (options != null) { 279 String val = options.getOption("home"); 280 if (val == null) { 281 val = System.getProperty(Environment.HOME_DIR); 282 if (val == null) { 283 val = "."; 284 } 285 } 286 File home = new File(val); 287 home = home.getCanonicalFile(); 288 Environment env = new Environment(home); 289 env.setCommandLineArguments(args); 290 val = options.getOption("data"); 291 if (val != null) { 292 env.setData(new File(val).getCanonicalFile()); 293 } 294 val = options.getOption("log"); 295 if (val != null) { 296 env.setLog(new File(val).getCanonicalFile()); 297 } 298 val = options.getOption("config"); 299 if (val != null) { 300 env.setConfig(new File(val).getCanonicalFile()); 301 } 302 val = options.getOption("web"); 303 if (val != null) { 304 env.setWeb(new File(val).getCanonicalFile()); 305 } 306 val = options.getOption("tmp"); 307 if (val != null) { 308 env.setTemp(new File(val).getCanonicalFile()); 309 } 310 val = options.getOption("bundles"); 311 if (val != null) { 312 env.setProperty(Environment.BUNDLES, val); 313 } 314 env.setHostApplicationName(Environment.NXSERVER_HOST); 315 env.setHostApplicationVersion("1.0.0"); 316 env.getData().mkdirs(); 317 env.getLog().mkdirs(); 318 env.getTemp().mkdirs(); 319 return env; 320 } else { 321 return new Environment(new File("").getCanonicalFile()); 322 } 323 } 324 325 public static void setMainTask(Runnable mainTask) { 326 StandaloneApplication.mainTask = mainTask; 327 } 328 329 public static Runnable getMainTask() { 330 return mainTask; 331 } 332 333 public static void main(URL systemBundle, List<File> classPath, String[] args) throws Exception { 334 SharedClassLoader classLoader = (SharedClassLoader) Thread.currentThread().getContextClassLoader(); 335 long startTime = System.currentTimeMillis(); 336 // parse command line args 337 StandaloneApplication.args = args; 338 options = new CommandLineOptions(args); 339 // start framework 340 StandaloneApplication app = createInstance(classLoader); 341 // start level 0 342 app.setClassPath(classPath); 343 app.setSystemBundle(new SystemBundle(app, createSystemBundle(systemBundle), classLoader.getLoader())); 344 // start level 1 345 app.start(); 346 log.info("Framework started in " + ((System.currentTimeMillis() - startTime) / 1000) + " sec."); 347 if (mainTask != null) { 348 mainTask.run(); 349 } 350 } 351 352}