001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 package org.apache.felix.main; 020 021 import java.io.File; 022 import java.util.*; 023 import org.osgi.framework.*; 024 import org.osgi.service.startlevel.*; 025 026 public class AutoProcessor 027 { 028 /** 029 * The property name used for the bundle directory. 030 **/ 031 public static final String AUTO_DEPLOY_DIR_PROPERY = "felix.auto.deploy.dir"; 032 /** 033 * The default name used for the bundle directory. 034 **/ 035 public static final String AUTO_DEPLOY_DIR_VALUE = "bundle"; 036 /** 037 * The property name used to specify auto-deploy actions. 038 **/ 039 public static final String AUTO_DEPLOY_ACTION_PROPERY = "felix.auto.deploy.action"; 040 /** 041 * The name used for the auto-deploy install action. 042 **/ 043 public static final String AUTO_DEPLOY_INSTALL_VALUE = "install"; 044 /** 045 * The name used for the auto-deploy start action. 046 **/ 047 public static final String AUTO_DEPLOY_START_VALUE = "start"; 048 /** 049 * The name used for the auto-deploy update action. 050 **/ 051 public static final String AUTO_DEPLOY_UPDATE_VALUE = "update"; 052 /** 053 * The name used for the auto-deploy uninstall action. 054 **/ 055 public static final String AUTO_DEPLOY_UNINSTALL_VALUE = "uninstall"; 056 /** 057 * The property name prefix for the launcher's auto-install property. 058 **/ 059 public static final String AUTO_INSTALL_PROP = "felix.auto.install"; 060 /** 061 * The property name prefix for the launcher's auto-start property. 062 **/ 063 public static final String AUTO_START_PROP = "felix.auto.start"; 064 065 /** 066 * Used to instigate auto-deploy directory process and auto-install/auto-start 067 * configuration property processing during. 068 * @param configMap Map of configuration properties. 069 * @param context The system bundle context. 070 **/ 071 public static void process(Map configMap, BundleContext context) 072 { 073 configMap = (configMap == null) ? new HashMap() : configMap; 074 processAutoDeploy(configMap, context); 075 processAutoProperties(configMap, context); 076 } 077 078 /** 079 * <p> 080 * Processes bundles in the auto-deploy directory, installing and then 081 * starting each one. 082 * </p> 083 */ 084 private static void processAutoDeploy(Map configMap, BundleContext context) 085 { 086 // Determine if auto deploy actions to perform. 087 String action = (String) configMap.get(AUTO_DEPLOY_ACTION_PROPERY); 088 action = (action == null) ? "" : action; 089 List actionList = new ArrayList(); 090 StringTokenizer st = new StringTokenizer(action, ","); 091 while (st.hasMoreTokens()) 092 { 093 String s = st.nextToken().trim().toLowerCase(); 094 if (s.equals(AUTO_DEPLOY_INSTALL_VALUE) 095 || s.equals(AUTO_DEPLOY_START_VALUE) 096 || s.equals(AUTO_DEPLOY_UPDATE_VALUE) 097 || s.equals(AUTO_DEPLOY_UNINSTALL_VALUE)) 098 { 099 actionList.add(s); 100 } 101 } 102 103 // Perform auto-deploy actions. 104 if (actionList.size() > 0) 105 { 106 // Get list of already installed bundles as a map. 107 Map installedBundleMap = new HashMap(); 108 Bundle[] bundles = context.getBundles(); 109 for (int i = 0; i < bundles.length; i++) 110 { 111 installedBundleMap.put(bundles[i].getLocation(), bundles[i]); 112 } 113 114 // Get the auto deploy directory. 115 String autoDir = (String) configMap.get(AUTO_DEPLOY_DIR_PROPERY); 116 autoDir = (autoDir == null) ? AUTO_DEPLOY_DIR_VALUE : autoDir; 117 // Look in the specified bundle directory to create a list 118 // of all JAR files to install. 119 File[] files = new File(autoDir).listFiles(); 120 List jarList = new ArrayList(); 121 if (files != null) 122 { 123 Arrays.sort(files); 124 for (int i = 0; i < files.length; i++) 125 { 126 if (files[i].getName().endsWith(".jar")) 127 { 128 jarList.add(files[i]); 129 } 130 } 131 } 132 133 // Install bundle JAR files and remember the bundle objects. 134 final List startBundleList = new ArrayList(); 135 for (int i = 0; i < jarList.size(); i++) 136 { 137 // Look up the bundle by location, removing it from 138 // the map of installed bundles so the remaining bundles 139 // indicate which bundles may need to be uninstalled. 140 Bundle b = (Bundle) installedBundleMap.remove( 141 ((File) jarList.get(i)).toURI().toString()); 142 try 143 { 144 // If the bundle is not already installed, then install it 145 // if the 'install' action is present. 146 if ((b == null) && actionList.contains(AUTO_DEPLOY_INSTALL_VALUE)) 147 { 148 b = context.installBundle( 149 ((File) jarList.get(i)).toURI().toString()); 150 } 151 // If the bundle is already installed, then update it 152 // if the 'update' action is present. 153 else if (actionList.contains(AUTO_DEPLOY_UPDATE_VALUE)) 154 { 155 b.update(); 156 } 157 158 // If we have found and/or successfully installed a bundle, 159 // then add it to the list of bundles to potentially start. 160 if (b != null) 161 { 162 startBundleList.add(b); 163 } 164 } 165 catch (BundleException ex) 166 { 167 System.err.println("Auto-deploy install: " 168 + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); 169 } 170 } 171 172 // Uninstall all bundles not in the auto-deploy directory if 173 // the 'uninstall' action is present. 174 if (actionList.contains(AUTO_DEPLOY_UNINSTALL_VALUE)) 175 { 176 for (Iterator it = installedBundleMap.entrySet().iterator(); it.hasNext(); ) 177 { 178 Map.Entry entry = (Map.Entry) it.next(); 179 Bundle b = (Bundle) entry.getValue(); 180 if (b.getBundleId() != 0) 181 { 182 try 183 { 184 b.uninstall(); 185 } 186 catch (BundleException ex) 187 { 188 System.err.println("Auto-deploy uninstall: " 189 + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); 190 } 191 } 192 } 193 } 194 195 // Start all installed and/or updated bundles if the 'start' 196 // action is present. 197 if (actionList.contains(AUTO_DEPLOY_START_VALUE)) 198 { 199 for (int i = 0; i < startBundleList.size(); i++) 200 { 201 try 202 { 203 if (!isFragment((Bundle) startBundleList.get(i))) 204 { 205 ((Bundle) startBundleList.get(i)).start(); 206 } 207 } 208 catch (BundleException ex) 209 { 210 System.err.println("Auto-deploy start: " 211 + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); 212 } 213 } 214 } 215 } 216 } 217 218 /** 219 * <p> 220 * Processes the auto-install and auto-start properties from the 221 * specified configuration properties. 222 * </p> 223 */ 224 private static void processAutoProperties(Map configMap, BundleContext context) 225 { 226 // Retrieve the Start Level service, since it will be needed 227 // to set the start level of the installed bundles. 228 StartLevel sl = (StartLevel) context.getService( 229 context.getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName())); 230 231 // Retrieve all auto-install and auto-start properties and install 232 // their associated bundles. The auto-install property specifies a 233 // space-delimited list of bundle URLs to be automatically installed 234 // into each new profile, while the auto-start property specifies 235 // bundles to be installed and started. The start level to which the 236 // bundles are assigned is specified by appending a ".n" to the 237 // property name, where "n" is the desired start level for the list 238 // of bundles. If no start level is specified, the default start 239 // level is assumed. 240 for (Iterator i = configMap.keySet().iterator(); i.hasNext(); ) 241 { 242 String key = ((String) i.next()).toLowerCase(); 243 244 // Ignore all keys that are not an auto property. 245 if (!key.startsWith(AUTO_INSTALL_PROP) && !key.startsWith(AUTO_START_PROP)) 246 { 247 continue; 248 } 249 250 // If the auto property does not have a start level, 251 // then assume it is the default bundle start level, otherwise 252 // parse the specified start level. 253 int startLevel = sl.getInitialBundleStartLevel(); 254 if (!key.equals(AUTO_INSTALL_PROP) && !key.equals(AUTO_START_PROP)) 255 { 256 try 257 { 258 startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1)); 259 } 260 catch (NumberFormatException ex) 261 { 262 System.err.println("Invalid property: " + key); 263 } 264 } 265 266 // Parse and install the bundles associated with the key. 267 StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true); 268 for (String location = nextLocation(st); location != null; location = nextLocation(st)) 269 { 270 try 271 { 272 Bundle b = context.installBundle(location, null); 273 sl.setBundleStartLevel(b, startLevel); 274 } 275 catch (Exception ex) 276 { 277 System.err.println("Auto-properties install: " + location + " (" 278 + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")"); 279 if (ex.getCause() != null) 280 ex.printStackTrace(); 281 } 282 } 283 } 284 285 // Now loop through the auto-start bundles and start them. 286 for (Iterator i = configMap.keySet().iterator(); i.hasNext(); ) 287 { 288 String key = ((String) i.next()).toLowerCase(); 289 if (key.startsWith(AUTO_START_PROP)) 290 { 291 StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true); 292 for (String location = nextLocation(st); location != null; location = nextLocation(st)) 293 { 294 // Installing twice just returns the same bundle. 295 try 296 { 297 Bundle b = context.installBundle(location, null); 298 if (b != null) 299 { 300 b.start(); 301 } 302 } 303 catch (Exception ex) 304 { 305 System.err.println("Auto-properties start: " + location + " (" 306 + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")"); 307 } 308 } 309 } 310 } 311 } 312 313 private static String nextLocation(StringTokenizer st) 314 { 315 String retVal = null; 316 317 if (st.countTokens() > 0) 318 { 319 String tokenList = "\" "; 320 StringBuffer tokBuf = new StringBuffer(10); 321 String tok = null; 322 boolean inQuote = false; 323 boolean tokStarted = false; 324 boolean exit = false; 325 while ((st.hasMoreTokens()) && (!exit)) 326 { 327 tok = st.nextToken(tokenList); 328 if (tok.equals("\"")) 329 { 330 inQuote = ! inQuote; 331 if (inQuote) 332 { 333 tokenList = "\""; 334 } 335 else 336 { 337 tokenList = "\" "; 338 } 339 340 } 341 else if (tok.equals(" ")) 342 { 343 if (tokStarted) 344 { 345 retVal = tokBuf.toString(); 346 tokStarted=false; 347 tokBuf = new StringBuffer(10); 348 exit = true; 349 } 350 } 351 else 352 { 353 tokStarted = true; 354 tokBuf.append(tok.trim()); 355 } 356 } 357 358 // Handle case where end of token stream and 359 // still got data 360 if ((!exit) && (tokStarted)) 361 { 362 retVal = tokBuf.toString(); 363 } 364 } 365 366 return retVal; 367 } 368 369 private static boolean isFragment(Bundle bundle) 370 { 371 return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null; 372 } 373 }