Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Class Members | Related Pages

OpenmisOscarBundleArchive.java

00001 /*
00002  * Oscar - An implementation of the OSGi framework.
00003  * Copyright (c) 2004, Richard S. Hall
00004  * All rights reserved.
00005  *  
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *  
00010  *   * Redistributions of source code must retain the above copyright
00011  *     notice, this list of conditions and the following disclaimer.
00012  *   * Redistributions in binary form must reproduce the above copyright
00013  *     notice, this list of conditions and the following disclaimer in
00014  *     the documentation and/or other materials provided with the
00015  *     distribution.
00016  *   * Neither the name of the ungoverned.org nor the names of its
00017  *     contributors may be used to endorse or promote products derived
00018  *     from this software without specific prior written permission.
00019  *  
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00023  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00024  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00026  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00027  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00028  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00030  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  * 
00032  * Contact: Richard S. Hall (heavy@ungoverned.org)
00033  * Contributor(s):
00034  *
00035 **/
00036 package org.openmobileis.oscar.utils;
00037 
00038 import java.io.*;
00039 import java.security.AccessController;
00040 import java.security.PrivilegedActionException;
00041 import java.security.PrivilegedExceptionAction;
00042 import java.util.HashMap;
00043 import java.util.Iterator;
00044 import java.util.Map;
00045 import java.util.jar.JarFile;
00046 import java.util.jar.Manifest;
00047 import java.util.zip.ZipEntry;
00048 
00049 import org.osgi.framework.Bundle;
00050 import org.osgi.framework.BundleActivator;
00051 import org.osgi.service.log.LogService;
00052 import org.ungoverned.oscar.cache.BundleArchive;
00053 import org.ungoverned.oscar.util.*;
00054 
00062 public class OpenmisOscarBundleArchive implements BundleArchive
00063 {
00064     private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
00065     private static final transient String BUNDLE_LOCATION_FILE = "bundle.location";
00066     private static final transient String BUNDLE_STATE_FILE = "bundle.state";
00067     private static final transient String BUNDLE_START_LEVEL_FILE = "bundle.startlevel";
00068     private static final transient String REFRESH_COUNTER_FILE = "refresh.counter";
00069     private static final transient String BUNDLE_ACTIVATOR_FILE = "bundle.activator";
00070 
00071     private static final transient String REVISION_DIRECTORY = "version";
00072     private static final transient String EMBEDDED_DIRECTORY = "embedded";
00073     private static final transient String LIBRARY_DIRECTORY = "lib";
00074     private static final transient String DATA_DIRECTORY = "data";
00075 
00076     private static final transient String ACTIVE_STATE = "active";
00077     private static final transient String INSTALLED_STATE = "installed";
00078     private static final transient String UNINSTALLED_STATE = "uninstalled";
00079 
00080     private LogService m_logger = null;
00081     private long m_id = -1;
00082     private File m_dir = null;
00083     private String m_location = null;
00084     private int m_persistentState = -1;
00085     private int m_startLevel = -1;
00086     private Map m_currentHeader = null;
00087 
00088     private long m_refreshCount = -1;
00089     private int m_revisionCount = -1;
00090 
00091     public OpenmisOscarBundleArchive(LogService logger, File dir, long id, String location, InputStream is)    
00092         throws Exception
00093     {
00094         this(logger, dir, id);
00095         m_location = location;
00096 
00097         // Try to save and pre-process the bundle JAR.
00098         try
00099         {
00100             initialize(is);
00101         }
00102         catch (Exception ex)
00103         {
00104             if (!deleteDirectoryTree(dir))
00105             {
00106                 m_logger.log(
00107                     LogService.LOG_ERROR,
00108                     "Unable to delete the archive directory: " + id);
00109             }
00110             throw ex;
00111         }
00112     }
00113 
00114     public OpenmisOscarBundleArchive(LogService logger, File dir, long id)
00115     {
00116         m_logger = logger;
00117         m_dir = dir;
00118         m_id = id;
00119         if (m_id <= 0)
00120         {
00121             throw new IllegalArgumentException(
00122                 "Bundle ID cannot be less than or equal to zero.");
00123         }
00124     }
00125 
00126     private void initialize(InputStream is)
00127         throws Exception
00128     {
00129         if (System.getSecurityManager() != null)
00130         {
00131             try
00132             {
00133                 AccessController.doPrivileged(
00134                     new PrivilegedAction(
00135                         PrivilegedAction.INITIALIZE_ACTION, this, is));
00136             }
00137             catch (PrivilegedActionException ex)
00138             {
00139                 throw ((PrivilegedActionException) ex).getException();
00140             }
00141         }
00142         else
00143         {
00144             initializeUnchecked(is);
00145         }
00146     }
00147 
00148     private void initializeUnchecked(InputStream is)
00149         throws Exception
00150     {
00151         FileWriter fw = null;
00152         BufferedWriter bw = null;
00153 
00154         try
00155         {
00156             // Create archive directory.
00157             if (!m_dir.mkdir())
00158             {
00159                 m_logger.log(
00160                     LogService.LOG_ERROR,
00161                     "DefaultBundleArchive: Unable to create archive directory.");
00162                 throw new IOException("Unable to create archive directory.");
00163             }
00164 
00165             // Save location string.
00166             File file = new File(m_dir, BUNDLE_LOCATION_FILE);
00167             fw = new FileWriter(file);
00168             bw = new BufferedWriter(fw);
00169             bw.write(m_location, 0, m_location.length());
00170 
00171             // Create version/revision directory for bundle JAR.
00172             // Since this is only called when the bundle JAR is
00173             // first saved, the update and revision will always
00174             // be "0.0" for the directory name.
00175             File revisionDir = new File(m_dir, REVISION_DIRECTORY + "0.0");
00176             if (!revisionDir.mkdir())
00177             {
00178                 m_logger.log(
00179                     LogService.LOG_ERROR,
00180                     "DefaultBundleArchive: Unable to create revision directory.");
00181                 throw new IOException("Unable to create revision directory.");
00182             }
00183 
00184             // Save the bundle jar file.
00185             file = new File(revisionDir, BUNDLE_JAR_FILE);
00186             copy(is, file);
00187 
00188             // This will always be revision zero.
00189             preprocessBundleJar(0, revisionDir);
00190 
00191         }
00192         finally
00193         {
00194             if (is != null) is.close();
00195             if (bw != null) bw.close();
00196             if (fw != null) fw.close();
00197         }
00198     }
00199 
00200     public File getDirectory()
00201     {
00202         return m_dir;
00203     }
00204 
00205     public long getId()
00206     {
00207         return m_id;
00208     }
00209 
00210     public String getLocation()
00211         throws Exception
00212     {
00213         if (m_location != null)
00214         {
00215             return m_location;
00216         }
00217         else if (System.getSecurityManager() != null)
00218         {
00219             try
00220             {
00221                 return (String) AccessController.doPrivileged(
00222                     new PrivilegedAction(
00223                         PrivilegedAction.GET_LOCATION_ACTION, this));
00224             }
00225             catch (PrivilegedActionException ex)
00226             {
00227                 throw ((PrivilegedActionException) ex).getException();
00228             }
00229         }
00230         else
00231         {
00232             return getLocationUnchecked();
00233         }
00234     }
00235 
00236     private String getLocationUnchecked()
00237         throws Exception
00238     {
00239         // Get bundle location file.
00240         File locFile = new File(m_dir, BUNDLE_LOCATION_FILE);
00241 
00242         // Read bundle location.
00243         FileReader fr = null;
00244         BufferedReader br = null;
00245         try
00246         {
00247             fr = new FileReader(locFile);
00248             br = new BufferedReader(fr);
00249             m_location = br.readLine();
00250             return m_location;
00251         }
00252         finally
00253         {
00254             if (br != null) br.close();
00255             if (fr != null) fr.close();
00256         }
00257     }
00258 
00259     public int getPersistentState()
00260         throws Exception
00261     {
00262         if (m_persistentState >= 0)
00263         {
00264             return m_persistentState;
00265         }
00266         else if (System.getSecurityManager() != null)
00267         {
00268             try
00269             {
00270                 return ((Integer) AccessController.doPrivileged(
00271                     new PrivilegedAction(
00272                         PrivilegedAction.GET_PERSISTENT_STATE_ACTION, this))).intValue();
00273             }
00274             catch (PrivilegedActionException ex)
00275             {
00276                 throw ((PrivilegedActionException) ex).getException();
00277             }
00278         }
00279         else
00280         {
00281             return getPersistentStateUnchecked();
00282         }
00283     }
00284 
00285     private int getPersistentStateUnchecked()
00286         throws Exception
00287     {
00288         // Get bundle state file.
00289         File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
00290 
00291         // If the state file doesn't exist, then
00292         // assume the bundle was installed.
00293         if (!stateFile.exists())
00294         {
00295             return Bundle.INSTALLED;
00296         }
00297 
00298         // Read the bundle state.
00299         FileReader fr = null;
00300         BufferedReader br= null;
00301         try
00302         {
00303             fr = new FileReader(stateFile);
00304             br = new BufferedReader(fr);
00305             String s = br.readLine();
00306             if (s.equals(ACTIVE_STATE))
00307             {
00308                 m_persistentState = Bundle.ACTIVE;
00309             }
00310             else if (s.equals(UNINSTALLED_STATE))
00311             {
00312                 m_persistentState = Bundle.UNINSTALLED;
00313             }
00314             else
00315             {
00316                 m_persistentState = Bundle.INSTALLED;
00317             }
00318             return m_persistentState;
00319         }
00320         finally
00321         {
00322             if (br != null) br.close();
00323             if (fr != null) fr.close();
00324         }
00325     }
00326 
00327     public void setPersistentState(int state)
00328         throws Exception
00329     {
00330         if (System.getSecurityManager() != null)
00331         {
00332             try
00333             {
00334                 AccessController.doPrivileged(
00335                     new PrivilegedAction(
00336                         PrivilegedAction.SET_PERSISTENT_STATE_ACTION, this, state));
00337             }
00338             catch (PrivilegedActionException ex)
00339             {
00340                 throw ((PrivilegedActionException) ex).getException();
00341             }
00342         }
00343         else
00344         {
00345             setPersistentStateUnchecked(state);
00346         }
00347     }
00348 
00349     private void setPersistentStateUnchecked(int state)
00350         throws Exception
00351     {
00352         // Get bundle state file.
00353         File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
00354 
00355         // Write the bundle state.
00356         FileWriter fw = null;
00357         BufferedWriter bw= null;
00358         try
00359         {
00360             fw = new FileWriter(stateFile);
00361             bw = new BufferedWriter(fw);
00362             String s = null;
00363             switch (state)
00364             {
00365                 case Bundle.ACTIVE:
00366                     s = ACTIVE_STATE;
00367                     break;
00368                 case Bundle.UNINSTALLED:
00369                     s = UNINSTALLED_STATE;
00370                     break;
00371                 default:
00372                     s = INSTALLED_STATE;
00373                     break;
00374             }
00375             bw.write(s, 0, s.length());
00376             m_persistentState = state;
00377         }
00378         catch (IOException ex)
00379         {
00380             m_logger.log(
00381                 LogService.LOG_ERROR,
00382                 "DefaultBundleArchive: Unable to record state: " + ex);
00383             throw ex;
00384         }
00385         finally
00386         {
00387             if (bw != null) bw.close();
00388             if (fw != null) fw.close();
00389         }
00390     }
00391 
00392     public int getStartLevel()
00393         throws Exception
00394     {
00395         if (m_startLevel >= 0)
00396         {
00397             return m_startLevel;
00398         }
00399         else if (System.getSecurityManager() != null)
00400         {
00401             try
00402             {
00403                 return ((Integer) AccessController.doPrivileged(
00404                     new PrivilegedAction(
00405                         PrivilegedAction.GET_START_LEVEL_ACTION, this))).intValue();
00406             }
00407             catch (PrivilegedActionException ex)
00408             {
00409                 throw ((PrivilegedActionException) ex).getException();
00410             }
00411         }
00412         else
00413         {
00414             return getStartLevelUnchecked();
00415         }
00416     }
00417 
00418     private int getStartLevelUnchecked()
00419         throws Exception
00420     {
00421         // Get bundle start level file.
00422         File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
00423 
00424         // If the start level file doesn't exist, then
00425         // return an error.
00426         if (!levelFile.exists())
00427         {
00428             return -1;
00429         }
00430 
00431         // Read the bundle start level.
00432         FileReader fr = null;
00433         BufferedReader br= null;
00434         try
00435         {
00436             fr = new FileReader(levelFile);
00437             br = new BufferedReader(fr);
00438             m_startLevel = Integer.parseInt(br.readLine());
00439             return m_startLevel;
00440         }
00441         finally
00442         {
00443             if (br != null) br.close();
00444             if (fr != null) fr.close();
00445         }
00446     }
00447 
00448     public void setStartLevel(int level)
00449         throws Exception
00450     {
00451         if (System.getSecurityManager() != null)
00452         {
00453             try
00454             {
00455                 AccessController.doPrivileged(
00456                     new PrivilegedAction(
00457                         PrivilegedAction.SET_START_LEVEL_ACTION, this, level));
00458             }
00459             catch (PrivilegedActionException ex)
00460             {
00461                 throw ((PrivilegedActionException) ex).getException();
00462             }
00463         }
00464         else
00465         {
00466             setStartLevelUnchecked(level);
00467         }
00468     }
00469 
00470     private void setStartLevelUnchecked(int level)
00471         throws Exception
00472     {
00473         // Get bundle start level file.
00474         File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
00475 
00476         // Write the bundle start level.
00477         FileWriter fw = null;
00478         BufferedWriter bw = null;
00479         try
00480         {
00481             fw = new FileWriter(levelFile);
00482             bw = new BufferedWriter(fw);
00483             String s = Integer.toString(level);
00484             bw.write(s, 0, s.length());
00485             m_startLevel = level;
00486         }
00487         catch (IOException ex)
00488         {
00489             m_logger.log(
00490                 LogService.LOG_ERROR,
00491                 "DefaultBundleArchive: Unable to record start leel: " + ex);
00492             throw ex;
00493         }
00494         finally
00495         {
00496             if (bw != null) bw.close();
00497             if (fw != null) fw.close();
00498         }
00499     }
00500 
00501     public File getDataFile(String fileName)
00502         throws Exception
00503     {
00504         // Do some sanity checking.
00505         if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
00506             throw new IllegalArgumentException("The data file path must be relative, not absolute.");
00507         else if (fileName.indexOf("..") >= 0)
00508             throw new IllegalArgumentException("The data file path cannot contain a reference to the \"..\" directory.");
00509 
00510         // Get bundle data directory.
00511         File dataDir = new File(m_dir, DATA_DIRECTORY);
00512 
00513         if (System.getSecurityManager() != null)
00514         {
00515             try
00516             {
00517                 AccessController.doPrivileged(
00518                     new PrivilegedAction(
00519                         PrivilegedAction.CREATE_DATA_DIR_ACTION, this, dataDir));
00520             }
00521             catch (PrivilegedActionException ex)
00522             {
00523                 throw ((PrivilegedActionException) ex).getException();
00524             }
00525         }
00526         else
00527         {
00528             createDataDirectoryUnchecked(dataDir);
00529         }
00530 
00531         // Return the data file.
00532         return new File(dataDir, fileName);
00533     }
00534 
00535     private void createDataDirectoryUnchecked(File dir)
00536         throws Exception
00537     {
00538         // Create data directory if necessary.
00539         if (!dir.exists())
00540         {
00541             if (!dir.mkdir())
00542             {
00543                 throw new IOException("Unable to create bundle data directory.");
00544             }
00545         }
00546     }
00547 
00548     public BundleActivator getActivator(ClassLoader loader)
00549         throws Exception
00550     {
00551         if (System.getSecurityManager() != null)
00552         {
00553             try
00554             {
00555                 return (BundleActivator) AccessController.doPrivileged(
00556                     new PrivilegedAction(
00557                         PrivilegedAction.GET_ACTIVATOR_ACTION, this, loader));
00558             }
00559             catch (PrivilegedActionException ex)
00560             {
00561                 throw ((PrivilegedActionException) ex).getException();
00562             }
00563         }
00564         else
00565         {
00566             return getActivatorUnchecked(loader);
00567         }
00568     }
00569 
00570     private BundleActivator getActivatorUnchecked(ClassLoader loader)
00571         throws Exception
00572     {
00573         // Get bundle activator file.
00574         File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
00575         // If the activator file doesn't exist, then
00576         // assume there isn't one.
00577         if (!activatorFile.exists())
00578             return null;
00579 
00580         // Deserialize the activator object.
00581         InputStream is = null;
00582         ObjectInputStreamX ois = null;
00583         try
00584         {
00585             is = new FileInputStream(activatorFile);
00586             ois = new ObjectInputStreamX(is, loader);
00587             Object o = ois.readObject();
00588             return (BundleActivator) o;
00589         }
00590         catch (Exception ex)
00591         {
00592             m_logger.log(
00593                 LogService.LOG_ERROR,
00594                 "DefaultBundleArchive: Trying to deserialize - " + ex);
00595         }
00596         finally
00597         {
00598             if (ois != null) ois.close();
00599             if (is != null) is.close();
00600         }
00601 
00602         return null;
00603     }
00604 
00605     public void setActivator(Object obj)
00606         throws Exception
00607     {
00608         if (System.getSecurityManager() != null)
00609         {
00610             try
00611             {
00612                 AccessController.doPrivileged(
00613                     new PrivilegedAction(
00614                         PrivilegedAction.SET_ACTIVATOR_ACTION, this, obj));
00615             }
00616             catch (PrivilegedActionException ex)
00617             {
00618                 throw ((PrivilegedActionException) ex).getException();
00619             }
00620         }
00621         else
00622         {
00623             setActivatorUnchecked(obj);
00624         }
00625     }
00626 
00627     private void setActivatorUnchecked(Object obj)
00628         throws Exception
00629     {
00630         if (!(obj instanceof Serializable))
00631         {
00632             return;
00633         }
00634 
00635         // Get bundle activator file.
00636         File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
00637 
00638         // Serialize the activator object.
00639         OutputStream os = null;
00640         ObjectOutputStream oos = null;
00641         try
00642         {
00643             os = new FileOutputStream(activatorFile);
00644             oos = new ObjectOutputStream(os);
00645             oos.writeObject(obj);
00646         }
00647         catch (IOException ex)
00648         {
00649             m_logger.log(
00650                 LogService.LOG_ERROR,
00651                 "DefaultBundleArchive: Unable to serialize activator - " + ex);
00652             throw ex;
00653         }
00654         finally
00655         {
00656             if (oos != null) oos.close();
00657             if (os != null) os.close();
00658         }
00659     }
00660 
00661     public int getRevisionCount()
00662         throws Exception
00663     {
00664         if (System.getSecurityManager() != null)
00665         {
00666             try
00667             {
00668                 return ((Integer) AccessController.doPrivileged(
00669                     new PrivilegedAction(
00670                         PrivilegedAction.GET_REVISION_COUNT_ACTION, this))).intValue();
00671             }
00672             catch (PrivilegedActionException ex)
00673             {
00674                 throw ((PrivilegedActionException) ex).getException();
00675             }
00676         }
00677         else
00678         {
00679             return getRevisionCountUnchecked();
00680         }
00681     }
00682 
00683     public int getRevisionCountUnchecked()
00684     {
00685         // We should always have at least one revision
00686         // directory, so try to count them if the value
00687         // has not been initialized yet.
00688         if (m_revisionCount <= 0)
00689         {
00690             m_revisionCount = 0;
00691             File[] children = m_dir.listFiles();
00692             for (int i = 0; (children != null) && (i < children.length); i++)
00693             {
00694                 if (children[i].getName().startsWith(REVISION_DIRECTORY))
00695                 {
00696                     m_revisionCount++;
00697                 }
00698             }
00699         }
00700         return m_revisionCount;
00701     }
00702 
00703     public Map getManifestHeader(int revision)
00704         throws Exception
00705     {
00706         // If the request is for the current revision header,
00707         // then return the cached copy if it is present.
00708         if ((revision == (getRevisionCount() - 1)) && (m_currentHeader != null))
00709         {
00710             return m_currentHeader;
00711         }
00712 
00713         // Get the revision directory.
00714         File revisionDir = new File(
00715             m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
00716 
00717         // Get the embedded resource.
00718         JarFile jarFile = null;
00719 
00720         try
00721         {
00722             // Create JarFile object using privileged block.
00723             if (System.getSecurityManager() != null)
00724             {
00725                 jarFile = (JarFile) AccessController.doPrivileged(
00726                     new PrivilegedAction(
00727                         PrivilegedAction.OPEN_BUNDLE_JAR_ACTION, this, revisionDir));
00728             }
00729             else
00730             {
00731                 jarFile = openBundleJarUnchecked(revisionDir);
00732             }
00733 
00734             // Error if no jar file.
00735             if (jarFile == null)
00736             {
00737                 throw new IOException("No JAR file found.");
00738             }
00739 
00740             // Get manifest.
00741             Manifest mf = jarFile.getManifest();
00742             // Create a case insensitive map of manifest attributes.
00743             Map map = new CaseInsensitiveMap(mf.getMainAttributes());
00744             // If the request is for the current revision's header,
00745             // then cache it.
00746             if (revision == (getRevisionCount() - 1))
00747             {
00748                 m_currentHeader = map;
00749             }
00750             return map;
00751 
00752         } catch (PrivilegedActionException ex) {
00753             throw ((PrivilegedActionException) ex).getException();
00754         } finally {
00755             if (jarFile != null) jarFile.close();
00756         }
00757     }
00758 
00759     private JarFile openBundleJarUnchecked(File revisionDir)
00760         throws Exception
00761     {
00762         // Get bundle jar file.
00763         File bundleJar = new File(revisionDir, BUNDLE_JAR_FILE);
00764         // Get bundle jar file.
00765         return new JarFile(bundleJar);
00766     }
00767 
00768     public String[] getClassPath(int revision)
00769         throws Exception
00770     {
00771         if (System.getSecurityManager() != null)
00772         {
00773             try
00774             {
00775                 return (String []) AccessController.doPrivileged(
00776                     new PrivilegedAction(
00777                         PrivilegedAction.GET_CLASS_PATH_ACTION, this, revision));
00778             }
00779             catch (PrivilegedActionException ex)
00780             {
00781                 throw ((PrivilegedActionException) ex).getException();
00782             }
00783         }
00784         else
00785         {
00786             return getClassPathUnchecked(revision);
00787         }
00788     }
00789 
00790     private String[] getClassPathUnchecked(int revision)
00791         throws Exception
00792     {
00793         // Get the revision directory.
00794         File revisionDir = new File(
00795             m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
00796 
00797         // Get the bundle's manifest header.
00798         Map map = getManifestHeader(revision);
00799         if (map == null)
00800         {
00801             map = new HashMap();
00802         }
00803 
00804         // Find class path meta-data.
00805         String classPath = null;
00806         Iterator iter = map.entrySet().iterator();
00807         while ((classPath == null) && iter.hasNext())
00808         {
00809             Map.Entry entry = (Map.Entry) iter.next();
00810             if (entry.getKey().toString().toLowerCase().equals(
00811                 OscarConstants.BUNDLE_CLASSPATH.toLowerCase()))
00812             {
00813                 classPath = entry.getValue().toString();
00814             }
00815         }
00816 
00817         // Parse the class path into strings.
00818         String[] classPathStrings = Util.parseDelimitedString(
00819             classPath, OscarConstants.CLASS_PATH_SEPARATOR);
00820 
00821         if (classPathStrings == null)
00822         {
00823             classPathStrings = new String[0];
00824         }
00825 
00826         // Now, check for "." in the class path.
00827         boolean includeDot = false;
00828         for (int i = 0; !includeDot && (i < classPathStrings.length); i++)
00829         {
00830             if (classPathStrings[i].equals(OscarConstants.CLASS_PATH_DOT))
00831             {
00832                 includeDot = true;
00833             }
00834         }
00835 
00836         // Include all JARs in the embedded jar directory, since they
00837         // were extracted when the bundle was initially saved.
00838         File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
00839         String[] paths = null;
00840         if (embedDir.exists())
00841         {
00842             // The size of the paths array is the number of
00843             // embedded JAR files plus one, if we need to include
00844             // ".", otherwise it is just the number of JAR files.
00845             // If "." is included, then it will be added to the
00846             // first place in the path array below.
00847             File[] children = embedDir.listFiles();
00848             int size = (children == null) ? 0 : children.length;
00849             size = (includeDot) ? size + 1 : size;
00850             paths = new String[size];
00851             for (int i = 0; i < children.length; i++)
00852             {
00853                 // If we are including "." then skip the first slot,
00854                 // because this is where we will put the bundle JAR file.
00855                 paths[(includeDot) ? i + 1 : i] = children[i].getPath();
00856             }
00857         }
00858 
00859         // If there is nothing on the class path, then include
00860         // "." by default, as per the spec.
00861         if ((paths == null) || (paths.length == 0))
00862         {
00863             includeDot = true;
00864             paths = new String[1];
00865         }
00866 
00867         // Put the bundle jar file first, if included.
00868         if (includeDot)
00869         {
00870             paths[0] = revisionDir + File.separator + BUNDLE_JAR_FILE;
00871         }
00872 
00873         return paths;
00874     }
00875 
00876 //  TODO: This will need to consider security.
00877     public String findLibrary(int revision, String libName)
00878         throws Exception
00879     {
00880         return findLibraryUnchecked(revision, libName);
00881     }
00882 
00883     private String findLibraryUnchecked(int revision, String libName)
00884         throws Exception
00885     {
00886         // Get the revision directory.
00887         File revisionDir = new File(
00888             m_dir.getAbsoluteFile(),
00889             REVISION_DIRECTORY + getRefreshCount() + "." + revision);
00890 
00891         // Get bundle lib directory.
00892         File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
00893         // Get lib file.
00894         File libFile = new File(libDir, File.separatorChar + libName);
00895         // Make sure that the library's parent directory exists;
00896         // it may be in a sub-directory.
00897         libDir = libFile.getParentFile();
00898         if (!libDir.exists())
00899         {
00900             if (!libDir.mkdirs())
00901             {
00902                 throw new IOException("Unable to create library directory.");
00903             }
00904         }
00905         // Extract the library from the JAR file if it does not
00906         // already exist.
00907         if (!libFile.exists())
00908         {
00909             JarFile jarFile = null;
00910             InputStream is = null;
00911 
00912             try
00913             {
00914                 jarFile = openBundleJarUnchecked(revisionDir);
00915                 ZipEntry ze = jarFile.getEntry(libName);
00916                 if (ze == null)
00917                 {
00918                     throw new IOException("No JAR entry: " + libName);
00919                 }
00920                 is = new BufferedInputStream(
00921                     jarFile.getInputStream(ze), OpenmisOscarBundleCache.BUFSIZE);
00922                 if (is == null)
00923                 {
00924                     throw new IOException("No input stream: " + libName);
00925                 }
00926 
00927                 // Create the file.
00928                 copy(is, libFile);
00929 
00930             }
00931             finally
00932             {
00933                 if (jarFile != null) jarFile.close();
00934                 if (is != null) is.close();
00935             }
00936         }
00937 
00938         return libFile.toString();
00939     }
00940 
00955     private long getRefreshCount()
00956         throws Exception
00957     {
00958         // If we have already read the update counter file,
00959         // then just return the result.
00960         if (m_refreshCount >= 0)
00961         {
00962             return m_refreshCount;
00963         }
00964 
00965         // Get update counter file.
00966         File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
00967 
00968         // If the update counter file doesn't exist, then
00969         // assume the counter is at zero.
00970         if (!counterFile.exists())
00971         {
00972             return 0;
00973         }
00974 
00975         // Read the bundle update counter.
00976         FileReader fr = null;
00977         BufferedReader br = null;
00978         try
00979         {
00980             fr = new FileReader(counterFile);
00981             br = new BufferedReader(fr);
00982             long counter = Long.parseLong(br.readLine());
00983             return counter;
00984         }
00985         finally
00986         {
00987             if (br != null) br.close();
00988             if (fr != null) fr.close();
00989         }
00990     }
00991 
01006     private void setRefreshCount(long counter)
01007         throws Exception
01008     {
01009         // Get update counter file.
01010         File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
01011 
01012         // Write the update counter.
01013         FileWriter fw = null;
01014         BufferedWriter bw = null;
01015         try
01016         {
01017             fw = new FileWriter(counterFile);
01018             bw = new BufferedWriter(fw);
01019             String s = Long.toString(counter);
01020             bw.write(s, 0, s.length());
01021             m_refreshCount = counter;
01022         }
01023         catch (IOException ex)
01024         {
01025             m_logger.log(
01026                 LogService.LOG_ERROR,
01027                 "DefaultBundleArchive: Unable to write counter: " + ex);
01028             throw ex;
01029         }
01030         finally
01031         {
01032             if (bw != null) bw.close();
01033             if (fw != null) fw.close();
01034         }
01035     }
01036 
01037     //
01038     // File-oriented utility methods.
01039     //
01040 
01041     protected static boolean deleteDirectoryTree(File target)
01042     {
01043         if (!target.exists())
01044         {
01045             return true;
01046         }
01047 
01048         if (target.isDirectory())
01049         {
01050             File[] files = target.listFiles();
01051             for (int i = 0; i < files.length; i++)
01052             {
01053                 deleteDirectoryTree(files[i]);
01054             }
01055         }
01056 
01057         return target.delete();
01058     }
01059 
01068     private void copy(InputStream is, File outputFile)
01069         throws IOException
01070     {
01071         OutputStream os = null;
01072 
01073         try
01074         {
01075             os = new BufferedOutputStream(
01076                 new FileOutputStream(outputFile), OpenmisOscarBundleCache.BUFSIZE);
01077             byte[] b = new byte[OpenmisOscarBundleCache.BUFSIZE];
01078             int len = 0;
01079             while ((len = is.read(b)) != -1)
01080                 os.write(b, 0, len);
01081         }
01082         finally
01083         {
01084             if (is != null) is.close();
01085             if (os != null) os.close();
01086         }
01087     }
01088 
01095     private void preprocessBundleJar(int revision, File revisionDir)
01096         throws Exception
01097     {
01098         //
01099         // Create special directories so that we can avoid checking
01100         // for their existence all the time.
01101         //
01102 
01103         File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
01104         if (!embedDir.exists())
01105         {
01106             if (!embedDir.mkdir())
01107             {
01108                 throw new IOException("Could not create embedded JAR directory.");
01109             }
01110         }
01111 
01112         File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
01113         if (!libDir.exists())
01114         {
01115             if (!libDir.mkdir())
01116             {
01117                 throw new IOException("Unable to create native library directory.");
01118             }
01119         }
01120 
01121         //
01122         // This block extracts all embedded JAR files.
01123         //
01124 
01125         try
01126         {
01127             // Get the bundle's manifest header.
01128             Map map = getManifestHeader(revision);
01129             if (map == null)
01130             {
01131                 map = new HashMap();
01132             }
01133 
01134             // Find class path meta-data.
01135             String classPath = null;
01136             Iterator iter = map.entrySet().iterator();
01137             while ((classPath == null) && iter.hasNext())
01138             {
01139                 Map.Entry entry = (Map.Entry) iter.next();
01140                 if (entry.getKey().toString().toLowerCase().equals(
01141                     OscarConstants.BUNDLE_CLASSPATH.toLowerCase()))
01142                 {
01143                     classPath = entry.getValue().toString();
01144                 }
01145             }
01146 
01147             // Parse the class path into strings.
01148             String[] classPathStrings = Util.parseDelimitedString(
01149                 classPath, OscarConstants.CLASS_PATH_SEPARATOR);
01150 
01151             if (classPathStrings == null)
01152             {
01153                 classPathStrings = new String[0];
01154             }
01155 
01156             for (int i = 0; i < classPathStrings.length; i++)
01157             {
01158                 if (!classPathStrings[i].equals(OscarConstants.CLASS_PATH_DOT))
01159                 {
01160                     extractEmbeddedJar(revisionDir, classPathStrings[i]);
01161                 }
01162             }
01163 
01164         }
01165         catch (PrivilegedActionException ex)
01166         {
01167             throw ((PrivilegedActionException) ex).getException();
01168         }
01169     }
01170 
01180     private void extractEmbeddedJar(File revisionDir, String jarPath)
01181         throws Exception
01182     {
01183         // Remove leading slash if present.
01184         jarPath = (jarPath.charAt(0) == '/') ? jarPath.substring(1) : jarPath;
01185         // Get only the JAR file name.
01186         String jarName = (jarPath.lastIndexOf('/') >= 0)
01187             ? jarPath.substring(jarPath.lastIndexOf('/') + 1) : jarPath;
01188 
01189         // If JAR is already extracted, then don't
01190         // re-extract it...
01191         File embedFile = new File(
01192             revisionDir, EMBEDDED_DIRECTORY + File.separatorChar + jarName);
01193         if (!embedFile.exists())
01194         {
01195             JarFile jarFile = null;
01196             InputStream is = null;
01197 
01198             try
01199             {
01200                 jarFile = openBundleJarUnchecked(revisionDir);
01201                 ZipEntry ze = jarFile.getEntry(jarPath);
01202                 if (ze == null)
01203                 {
01204                     throw new IOException("No JAR entry: " + jarPath);
01205                 }
01206                 is = new BufferedInputStream(jarFile.getInputStream(ze), OpenmisOscarBundleCache.BUFSIZE);
01207                 if (is == null)
01208                 {
01209                     throw new IOException("No input stream: " + jarPath);
01210                 }
01211 
01212                 // Create the file.
01213                 copy(is, embedFile);
01214 
01215             }
01216             finally
01217             {
01218                 if (jarFile != null) jarFile.close();
01219                 if (is != null) is.close();
01220             }
01221         }
01222     }
01223 
01224     // INCREASES THE REVISION COUNT.    
01225     protected void update(InputStream is) throws Exception
01226     {
01227         if (System.getSecurityManager() != null)
01228         {
01229             try
01230             {
01231                 AccessController.doPrivileged(
01232                     new PrivilegedAction(
01233                         PrivilegedAction.UPDATE_ACTION, this, is));
01234             }
01235             catch (PrivilegedActionException ex)
01236             {
01237                 throw ((PrivilegedActionException) ex).getException();
01238             }
01239         }
01240         else
01241         {
01242             updateUnchecked(is);
01243         }
01244     }
01245 
01246     // INCREASES THE REVISION COUNT.    
01247     private void updateUnchecked(InputStream is) throws Exception
01248     {
01249         File revisionDir = null;
01250 
01251         try
01252         {
01253             // Create the new revision directory.
01254             int revision = getRevisionCountUnchecked();
01255             revisionDir = new File(
01256                 m_dir, REVISION_DIRECTORY
01257                 + getRefreshCount() + "." + revision);
01258             if (!revisionDir.mkdir())
01259             {
01260                 throw new IOException("Unable to create revision directory.");
01261             }
01262 
01263             // Save the new revision bundle jar file.
01264             File file = new File(revisionDir, BUNDLE_JAR_FILE);
01265             copy(is, file);
01266 
01267             preprocessBundleJar(revision, revisionDir);
01268         }
01269         catch (Exception ex)
01270         {
01271             if ((revisionDir != null) && revisionDir.exists())
01272             {
01273                 try
01274                 {
01275                     deleteDirectoryTree(revisionDir);
01276                 }
01277                 catch (Exception ex2)
01278                 {
01279                     // There is very little we can do here.
01280                     m_logger.log(
01281                         LogService.LOG_ERROR,
01282                         "Unable to remove partial revision directory.", ex2);
01283                 }
01284             }
01285             throw ex;
01286         }
01287 
01288         // If everything was successful, then update
01289         // the revision count.
01290         m_revisionCount++;
01291         // Clear the cached revision header, since it is
01292         // no longer the current revision.
01293         m_currentHeader = null;
01294     }
01295 
01296     // DECREASES THE REVISION COUNT.    
01297     protected void purge() throws Exception
01298     {
01299         if (System.getSecurityManager() != null)
01300         {
01301             try
01302             {
01303                 AccessController.doPrivileged(
01304                     new PrivilegedAction(
01305                         PrivilegedAction.PURGE_ACTION, this));
01306             }
01307             catch (PrivilegedActionException ex)
01308             {
01309                 throw ((PrivilegedActionException) ex).getException();
01310             }
01311         }
01312         else
01313         {
01314             purgeUnchecked();
01315         }
01316     }
01317     
01318     // DECREASES THE REVISION COUNT.    
01319     private void purgeUnchecked() throws Exception
01320     {
01321         // Get the current update count.
01322         long update = getRefreshCount();
01323         // Get the current revision count.
01324         int count = getRevisionCountUnchecked();
01325 
01326         File revisionDir = null;
01327         for (int i = 0; i < count - 1; i++)
01328         {
01329             revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + i);
01330             if (revisionDir.exists())
01331             {
01332                 deleteDirectoryTree(revisionDir);
01333             }
01334         }
01335         // Increment the update count.
01336         setRefreshCount(update + 1);
01337 
01338         // Rename the current revision to be the current update.
01339         File currentDir = new File(m_dir, REVISION_DIRECTORY + (update + 1) + ".0");
01340         revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + (count - 1));
01341         revisionDir.renameTo(currentDir);
01342         
01343         // If everything is successful, then set the revision
01344         // count to one.
01345         m_revisionCount = 1;
01346         // Although the cached current header should stay the same
01347         // here, clear it for consistency.
01348         m_currentHeader = null;
01349     }
01350 
01351     protected void remove() throws Exception
01352     {
01353         if (System.getSecurityManager() != null)
01354         {
01355             try
01356             {
01357                 AccessController.doPrivileged(
01358                     new PrivilegedAction(
01359                         PrivilegedAction.REMOVE_ACTION, this));
01360             }
01361             catch (PrivilegedActionException ex)
01362             {
01363                 throw ((PrivilegedActionException) ex).getException();
01364             }
01365         }
01366         else
01367         {
01368             removeUnchecked();
01369         }
01370     }
01371     
01372     private void removeUnchecked() throws Exception
01373     {
01374         deleteDirectoryTree(m_dir);
01375     }
01376 
01377     //
01378     // Utility class for performing privileged actions.
01379     //
01380 
01381     private static class PrivilegedAction implements PrivilegedExceptionAction
01382     {
01383         private static final int INITIALIZE_ACTION = 0;
01384         private static final int UPDATE_ACTION = 1;
01385         private static final int PURGE_ACTION = 2;
01386         private static final int REMOVE_ACTION = 3;
01387         private static final int GET_REVISION_COUNT_ACTION = 4;
01388         private static final int GET_LOCATION_ACTION = 5;
01389         private static final int GET_PERSISTENT_STATE_ACTION = 6;
01390         private static final int SET_PERSISTENT_STATE_ACTION = 7;
01391         private static final int GET_START_LEVEL_ACTION = 8;
01392         private static final int SET_START_LEVEL_ACTION = 9;
01393         private static final int OPEN_BUNDLE_JAR_ACTION = 10;
01394         private static final int CREATE_DATA_DIR_ACTION = 11;
01395         private static final int GET_CLASS_PATH_ACTION = 12;
01396         private static final int GET_ACTIVATOR_ACTION = 13;
01397         private static final int SET_ACTIVATOR_ACTION = 14;
01398 
01399         private int m_action = 0;
01400         private OpenmisOscarBundleArchive m_archive = null;
01401         private InputStream m_isArg = null;
01402         private String m_strArg = null;
01403         private int m_intArg = 0;
01404         private File m_fileArg = null;
01405         private ClassLoader m_loaderArg = null;
01406         private Object m_objArg = null;
01407 
01408         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive)
01409         {
01410             m_action = action;
01411             m_archive = archive;
01412         }
01413 
01414         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive, InputStream isArg)
01415         {
01416             m_action = action;
01417             m_archive = archive;
01418             m_isArg = isArg;
01419         }
01420 
01421         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive, int intArg)
01422         {
01423             m_action = action;
01424             m_archive = archive;
01425             m_intArg = intArg;
01426         }
01427 
01428         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive, File fileArg)
01429         {
01430             m_action = action;
01431             m_archive = archive;
01432             m_fileArg = fileArg;
01433         }
01434 
01435         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive, ClassLoader loaderArg)
01436         {
01437             m_action = action;
01438             m_archive = archive;
01439             m_loaderArg = loaderArg;
01440         }
01441 
01442         public PrivilegedAction(int action, OpenmisOscarBundleArchive archive, Object objArg)
01443         {
01444             m_action = action;
01445             m_archive = archive;
01446             m_objArg = objArg;
01447         }
01448 
01449         public Object run() throws Exception
01450         {
01451             switch (m_action)
01452             {
01453                 case INITIALIZE_ACTION:
01454                     m_archive.initializeUnchecked(m_isArg);
01455                     return null;
01456                 case UPDATE_ACTION:
01457                     m_archive.updateUnchecked(m_isArg);
01458                     return null;
01459                 case PURGE_ACTION:
01460                     m_archive.purgeUnchecked();
01461                     return null;
01462                 case REMOVE_ACTION:
01463                     m_archive.removeUnchecked();
01464                     return null;
01465                 case GET_REVISION_COUNT_ACTION:
01466                     return new Integer(m_archive.getRevisionCountUnchecked());
01467                 case GET_LOCATION_ACTION:
01468                     return m_archive.getLocationUnchecked();
01469                 case GET_PERSISTENT_STATE_ACTION:
01470                     return new Integer(m_archive.getPersistentStateUnchecked());
01471                 case SET_PERSISTENT_STATE_ACTION:
01472                     m_archive.setPersistentStateUnchecked(m_intArg);
01473                     return null;
01474                 case GET_START_LEVEL_ACTION:
01475                     return new Integer(m_archive.getStartLevelUnchecked());
01476                 case SET_START_LEVEL_ACTION:
01477                     m_archive.setStartLevelUnchecked(m_intArg);
01478                     return null;
01479                 case OPEN_BUNDLE_JAR_ACTION:
01480                     return m_archive.openBundleJarUnchecked(m_fileArg);
01481                 case CREATE_DATA_DIR_ACTION:
01482                     m_archive.createDataDirectoryUnchecked(m_fileArg);
01483                     return null;
01484                 case GET_CLASS_PATH_ACTION:
01485                     return m_archive.getClassPathUnchecked(m_intArg);
01486                 case GET_ACTIVATOR_ACTION:
01487                     return m_archive.getActivatorUnchecked(m_loaderArg);
01488                 case SET_ACTIVATOR_ACTION:
01489                     m_archive.setActivatorUnchecked(m_objArg);
01490                     return null;
01491             }
01492 
01493             throw new IllegalArgumentException("Invalid action specified.");
01494         }
01495     }
01496 }

Generated on Wed Dec 14 21:05:34 2005 for OpenMobileIS by  doxygen 1.4.4