StartLevelImpl.java

00001 /*
00002  * Copyright (c) 2003-2005, KNOPFLERFISH project
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following
00007  * conditions are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  *
00012  * - Redistributions in binary form must reproduce the above
00013  *   copyright notice, this list of conditions and the following
00014  *   disclaimer in the documentation and/or other materials
00015  *   provided with the distribution.
00016  *
00017  * - Neither the name of the KNOPFLERFISH project nor the names of its
00018  *   contributors may be used to endorse or promote products derived
00019  *   from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00027  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00028  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00030  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00031  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00032  * OF THE POSSIBILITY OF SUCH DAMAGE.
00033  */
00034 
00035 package org.knopflerfish.framework;
00036 
00037 import org.osgi.framework.*;
00038 import org.osgi.service.startlevel.*;
00039 import java.util.Vector;
00040 import java.util.List;
00041 import java.util.Iterator;
00042 
00043 import java.io.File;
00044 
00045 
00050 public class StartLevelImpl implements StartLevel, Runnable {
00051   Thread        wc;
00052   long          wcDelay  = 2000;
00053   boolean       bRun     = false;
00054   Queue         jobQueue = new Queue(100);
00055 
00056   final static int START_MIN = 0;
00057   final static int START_MAX = Integer.MAX_VALUE;
00058 
00059   final static String LEVEL_FILE = "currentlevel";
00060 
00061   int currentLevel     = 0;
00062   int initStartLevel   = 1;
00063   int targetStartLevel = currentLevel;
00064   boolean acceptChanges = true;
00065 
00066   Framework framework;
00067 
00068   FileTree storage;
00069 
00070   // Set to true indicates startlevel compatability mode.
00071   // all bundles and current start level will be 1
00072   boolean  bCompat /*= false*/;
00073 
00074   public static final String SPEC_VERSION = "1.0";
00075   
00076 
00077   public StartLevelImpl(Framework framework) {
00078     this.framework = framework;
00079     
00080     storage = Util.getFileStorage("startlevel");
00081   }
00082   
00083   void open() {
00084     
00085     if(Debug.startlevel) {
00086       Debug.println("startlevel: open");
00087     }
00088 
00089     if (jobQueue.isEmpty()) {
00090       setStartLevel0(1, false, false, true);
00091     }
00092     Runnable firstJob = (Runnable)jobQueue.firstElement();
00093     wc   = new Thread(this, "startlevel job thread");
00094     synchronized (firstJob) {
00095       bRun = true;
00096       wc.start();
00097       if (!acceptChanges) {
00098         acceptChanges = true;
00099         restoreState();
00100       }
00101       // Wait for first job to complete before return
00102       try {
00103         firstJob.wait();
00104       } catch (InterruptedException _ignore) { }
00105     }
00106   }
00107 
00120   void restoreState() {
00121     if (Debug.startlevel) {
00122       Debug.println("startlevel: restoreState");
00123     }
00124     // Skip level load in mem storage since bundle levels
00125     // isn't saved anyway
00126     if (!Framework.bIsMemoryStorage) {
00127       try {
00128         String s = Util.getContent(new File(storage, LEVEL_FILE));
00129         if (s != null) {
00130           int oldStartLevel = Integer.parseInt(s);
00131           if (oldStartLevel != -1) {
00132             setStartLevel0(oldStartLevel, false, false, true);
00133           }
00134         }
00135       } catch (Exception _ignored) { }
00136     }
00137   }
00138 
00139 
00140   void close() {
00141     if (Debug.startlevel) {
00142       Debug.println("*** closing startlevel service");
00143     }
00144 
00145     bRun = false;
00146     if(wc != null) {
00147       try {
00148         wc.join(wcDelay * 2);
00149       } catch (Exception ignored) { 
00150       }
00151       wc = null;
00152     }
00153   }
00154 
00155   void shutdown() {
00156     acceptChanges = false;
00157     setStartLevel0(0, false, true, false);
00158     while (currentLevel > 1) {
00159       synchronized (wc) {
00160         try { wc.wait(); } catch (Exception e) {}
00161       }
00162     }
00163     close();
00164   }
00165 
00166   public void run() {
00167     while(bRun) {
00168       try {
00169         Runnable job = (Runnable)jobQueue.removeWait((float)(wcDelay / 1000.0));        
00170         if (job != null) {
00171           job.run();
00172           synchronized (job) {
00173             job.notify();
00174           }
00175         }
00176       } catch (Exception ignored) { 
00177         ignored.printStackTrace();
00178       }
00179     }
00180   }
00181 
00182   
00183   public int getStartLevel() {
00184     return currentLevel;
00185   }
00186   
00187 
00188   public void setStartLevel(final int startLevel) {
00189     framework.perm.checkStartLevelAdminPerm();
00190     if(startLevel <= 0) {
00191       throw new IllegalArgumentException("Initial start level must be > 0, is " + startLevel);
00192     }
00193     if (acceptChanges) {
00194       setStartLevel0(startLevel, framework.active, false, true);
00195     }
00196   }
00197 
00198 
00199   private void setStartLevel0(final int startLevel, final boolean notifyFw, final boolean notifyWC, final boolean storeLevel) {
00200     if (Debug.startlevel) {
00201       Debug.println("startlevel: setStartLevel " + startLevel);
00202     }
00203 
00204     jobQueue.insert(new Runnable() {
00205         public void run() {
00206           int sl = bCompat ? 1 : startLevel;
00207           targetStartLevel = sl;
00208 
00209           while (targetStartLevel > currentLevel) {
00210             increaseStartLevel();
00211           }
00212 
00213           while (targetStartLevel < currentLevel) {
00214             decreaseStartLevel();
00215           }
00216 
00217           // Skip level save in mem storage since bundle levels
00218           // won't be saved anyway
00219           if (storeLevel && !Framework.bIsMemoryStorage) {
00220             try {
00221               Util.putContent(new File(storage, LEVEL_FILE), 
00222                               Integer.toString(currentLevel));
00223             } catch (Exception e) {
00224               e.printStackTrace();
00225             }
00226           }
00227           if (notifyFw) {
00228             notifyFramework();
00229           }
00230           if (notifyWC && wc != null) {
00231             synchronized (wc) {
00232               wc.notifyAll();
00233             }
00234           }
00235         }
00236       });
00237   }
00238 
00239 
00240   Object lock = new Object();
00241 
00242 
00243   void increaseStartLevel() {
00244     synchronized(lock) {
00245 
00246       currentLevel++;
00247 
00248       if (Debug.startlevel) {
00249         Debug.println("startlevel: increaseStartLevel currentLevel=" + currentLevel);
00250       }
00251       Vector set = new Vector();
00252 
00253       List bundles = framework.bundles.getBundles();
00254 
00255       for (Iterator i = bundles.iterator(); i.hasNext(); ) {
00256         BundleImpl bs  = (BundleImpl)i.next();
00257 
00258         if (canStart(bs)) {
00259           if (bs.getStartLevel() == currentLevel) {
00260             if (bs.archive.isPersistent()) {
00261               set.addElement(bs);
00262             }
00263           }
00264         } else {
00265 
00266         }
00267       }
00268 
00269       Util.sort(set, BSComparator, false);
00270 
00271       for(int i = 0; i < set.size(); i++) {
00272         BundleImpl bs = (BundleImpl)set.elementAt(i);
00273         try {
00274           if (bs.archive.isPersistent()) {
00275             if (Debug.startlevel) {
00276               Debug.println("startlevel: start " + bs);
00277             }
00278             bs.start();
00279           }
00280         } catch (Exception e) {
00281           framework.listeners.frameworkError(bs, e);
00282         }
00283       }
00284     }
00285   }
00286 
00287   
00288   void decreaseStartLevel() {
00289     synchronized(lock) {
00290       currentLevel--;
00291 
00292       Vector set = new Vector();
00293       
00294       List bundles = framework.bundles.getBundles();
00295 
00296       for (Iterator i = bundles.iterator(); i.hasNext(); ) {
00297         BundleImpl bs  = (BundleImpl)i.next();
00298 
00299         if (bs.getState() == Bundle.ACTIVE) {
00300           if (bs.getStartLevel() == currentLevel + 1) {
00301             set.addElement(bs);
00302           }
00303         }
00304       }
00305 
00306       Util.sort(set, BSComparator, true);
00307 
00308       for (int i = 0; i < set.size(); i++) {
00309         BundleImpl bs = (BundleImpl)set.elementAt(i);
00310         BundleException saved = null;
00311         synchronized (bs) {
00312           if (bs.getState() == Bundle.ACTIVE) {
00313             if (Debug.startlevel) {
00314               Debug.println("startlevel: stop " + bs);
00315             }
00316             saved = bs.stop0(false);
00317           }
00318         }
00319         if (saved != null) {
00320           framework.listeners.frameworkError(bs, saved);
00321         }
00322       }
00323     }
00324   }
00325 
00326 
00327   boolean canStart(BundleImpl b) {
00328     return b.getState() != Bundle.UNINSTALLED;
00329   }
00330 
00331 
00332   static final Util.Comparator BSComparator = new Util.Comparator() {
00333       public int compare(Object o1, Object o2) {
00334         BundleImpl b1 = (BundleImpl)o1;
00335         BundleImpl b2 = (BundleImpl)o2;
00336         
00337         int res = b1.getStartLevel() - b2.getStartLevel();
00338         if (res == 0) {
00339           res = (int)(b1.getBundleId() - b2.getBundleId());
00340         }
00341         return res;
00342       }
00343     };
00344 
00345 
00346   public int getBundleStartLevel(Bundle bundle) {
00347     if(bundle.getBundleId() == 0) {
00348       return 0;
00349     }
00350     BundleImpl bs = (BundleImpl)bundle;
00351     return bs.getStartLevel();
00352   }
00353 
00354 
00355   public void setBundleStartLevel(Bundle bundle, final int startLevel) {
00356     framework.perm.checkExecuteAdminPerm(bundle);
00357           
00358     if(startLevel <= 0) {
00359       throw new IllegalArgumentException("Initial start level must be > 0, is " + startLevel);
00360     }
00361 
00362     if(bundle.getBundleId() == 0) {
00363       throw new IllegalArgumentException("System bundle start level cannot be changed");
00364     }
00365     
00366     final BundleImpl bs = (BundleImpl)bundle;
00367 
00368     if(bs.getState() == Bundle.UNINSTALLED) {
00369       throw new IllegalArgumentException("uninstalled bundle start level cannot be changed");
00370     }
00371 
00372     jobQueue.insert(new Runnable() {
00373         public void run() {
00374           int sl  = bCompat ? 1 : startLevel;
00375 
00376           bs.setStartLevel(sl);
00377           syncStartLevel(bs);
00378         }
00379       });
00380   }
00381   
00382 
00383   void syncStartLevel(BundleImpl bs) {
00384     synchronized(lock) {
00385 
00386       if (bs.getStartLevel() <= currentLevel) {
00387         if (canStart(bs)) {
00388           if (bs.archive.isPersistent() ||  (bs.getState() == Bundle.RESOLVED)) {
00389             try {
00390               if (Debug.startlevel) {
00391                 Debug.println("startlevel: start " + bs);
00392               }
00393               bs.start();
00394             } catch (Exception e) {
00395               framework.listeners.frameworkError(bs, e);
00396             }
00397           } else {
00398             bs.bDelayedStart = true;
00399           }
00400         }
00401       } else if (bs.getStartLevel() > currentLevel) {
00402         BundleException saved = null;
00403         synchronized (bs) {
00404           if (bs.getState() == Bundle.ACTIVE) {
00405             if (Debug.startlevel) {
00406               Debug.println("startlevel: stop " + bs);
00407             }
00408             saved = bs.stop0(false);
00409           }
00410         }
00411         if (saved != null) {
00412           framework.listeners.frameworkError(bs, saved);
00413         }
00414       }
00415     }
00416   }
00417 
00418   
00419   public int getInitialBundleStartLevel() {
00420     return initStartLevel;
00421   }
00422   
00423 
00424   public void setInitialBundleStartLevel(int startLevel) {
00425     framework.perm.checkStartLevelAdminPerm();  
00426           
00427     if(startLevel <= 0) {
00428       throw new IllegalArgumentException("Initial start level must be > 0, is " + startLevel);
00429     }
00430     initStartLevel = bCompat ? 1 : startLevel;
00431   }
00432 
00433   
00434   public boolean isBundlePersistentlyStarted(Bundle bundle) {
00435     return ((BundleImpl)bundle).isPersistent();
00436   }
00437 
00438  
00439   private void notifyFramework() {
00440     framework.listeners.frameworkEvent(new FrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, framework.systemBundle, null));
00441   }
00442 }
00443 
00444 

Generated on Mon Jan 11 21:19:17 2010 for OpenMobileIS by  doxygen 1.5.4