KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > oscar > util > DefaultBundleArchive


1 /*
2  * Oscar - An implementation of the OSGi framework.
3  * Copyright (c) 2004, Richard S. Hall
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the ungoverned.org nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Contact: Richard S. Hall (heavy@ungoverned.org)
33  * Contributor(s):
34  *
35 **/

36 package org.ungoverned.oscar.util;
37
38 import java.io.*;
39 import java.security.AccessController JavaDoc;
40 import java.security.PrivilegedActionException JavaDoc;
41 import java.security.PrivilegedExceptionAction JavaDoc;
42 import java.util.*;
43 import java.util.jar.JarFile JavaDoc;
44 import java.util.jar.Manifest JavaDoc;
45 import java.util.zip.ZipEntry JavaDoc;
46
47 import org.osgi.framework.Bundle;
48 import org.osgi.framework.BundleActivator;
49 import org.ungoverned.oscar.BundleArchive;
50 import org.ungoverned.oscar.Oscar;
51
52 /**
53  * <p>
54  * This class, combined with <tt>DefaultBundleCache</tt>, implements the
55  * default file system-based bundle cache for Oscar.
56  * </p>
57  * @see org.ungoverned.oscar.util.DefaultBundleCache
58 **/

59 public class DefaultBundleArchive implements BundleArchive
60 {
61     private static final transient String JavaDoc BUNDLE_JAR_FILE = "bundle.jar";
62     private static final transient String JavaDoc BUNDLE_LOCATION_FILE = "bundle.location";
63     private static final transient String JavaDoc BUNDLE_STATE_FILE = "bundle.state";
64     private static final transient String JavaDoc BUNDLE_START_LEVEL_FILE = "bundle.startlevel";
65     private static final transient String JavaDoc REFRESH_COUNTER_FILE = "refresh.counter";
66     private static final transient String JavaDoc BUNDLE_ACTIVATOR_FILE = "bundle.activator";
67
68     private static final transient String JavaDoc REVISION_DIRECTORY = "version";
69     private static final transient String JavaDoc EMBEDDED_DIRECTORY = "embedded";
70     private static final transient String JavaDoc LIBRARY_DIRECTORY = "lib";
71     private static final transient String JavaDoc DATA_DIRECTORY = "data";
72
73     private static final transient String JavaDoc ACTIVE_STATE = "active";
74     private static final transient String JavaDoc INSTALLED_STATE = "installed";
75     private static final transient String JavaDoc UNINSTALLED_STATE = "uninstalled";
76
77     private long m_id = -1;
78     private File JavaDoc m_dir = null;
79     private String JavaDoc m_location = null;
80     private int m_persistentState = -1;
81     private int m_startLevel = -1;
82     private Map m_currentHeader = null;
83
84     private long m_refreshCount = -1;
85     private int m_revisionCount = -1;
86
87     public DefaultBundleArchive(File JavaDoc dir, long id, String JavaDoc location, InputStream is)
88         throws Exception JavaDoc
89     {
90         this(dir, id);
91         m_location = location;
92
93         // Try to save and pre-process the bundle JAR.
94
try
95         {
96             initialize(is);
97         }
98         catch (Exception JavaDoc ex)
99         {
100             if (!deleteDirectoryTree(dir))
101             {
102                 Oscar.error("Unable to delete the archive directory.");
103             }
104             throw ex;
105         }
106     }
107
108     public DefaultBundleArchive(File JavaDoc dir, long id)
109     {
110         m_dir = dir;
111         m_id = id;
112         if (m_id <= 0)
113         {
114             throw new IllegalArgumentException JavaDoc(
115                 "Bundle ID cannot be less than or equal to zero.");
116         }
117     }
118
119     private void initialize(InputStream is)
120         throws Exception JavaDoc
121     {
122         if (System.getSecurityManager() != null)
123         {
124             try
125             {
126                 AccessController.doPrivileged(
127                     new PrivilegedAction(
128                         PrivilegedAction.INITIALIZE_ACTION, this, is));
129             }
130             catch (PrivilegedActionException JavaDoc ex)
131             {
132                 throw ((PrivilegedActionException JavaDoc) ex).getException();
133             }
134         }
135         else
136         {
137             initializeUnchecked(is);
138         }
139     }
140
141     private void initializeUnchecked(InputStream is)
142         throws Exception JavaDoc
143     {
144         FileWriter fw = null;
145         BufferedWriter bw = null;
146
147         try
148         {
149             // Create archive directory.
150
if (!m_dir.mkdir())
151             {
152                 Oscar.error("DefaultBundleArchive: Unable to create archive directory.");
153                 throw new IOException("Unable to create archive directory.");
154             }
155
156             // Save location string.
157
File JavaDoc file = new File JavaDoc(m_dir, BUNDLE_LOCATION_FILE);
158             fw = new FileWriter(file);
159             bw = new BufferedWriter(fw);
160             bw.write(m_location, 0, m_location.length());
161
162             // Create version/revision directory for bundle JAR.
163
// Since this is only called when the bundle JAR is
164
// first saved, the update and revision will always
165
// be "0.0" for the directory name.
166
File JavaDoc revisionDir = new File JavaDoc(m_dir, REVISION_DIRECTORY + "0.0");
167             if (!revisionDir.mkdir())
168             {
169                 Oscar.error("DefaultBundleArchive: Unable to create revision directory.");
170                 throw new IOException("Unable to create revision directory.");
171             }
172
173             // Save the bundle jar file.
174
file = new File JavaDoc(revisionDir, BUNDLE_JAR_FILE);
175             copy(is, file);
176
177             // This will always be revision zero.
178
preprocessBundleJar(0, revisionDir);
179
180         }
181         finally
182         {
183             if (is != null) is.close();
184             if (bw != null) bw.close();
185             if (fw != null) fw.close();
186         }
187     }
188
189     public File JavaDoc getDirectory()
190     {
191         return m_dir;
192     }
193
194     public long getId()
195     {
196         return m_id;
197     }
198
199     public String JavaDoc getLocation()
200         throws Exception JavaDoc
201     {
202         if (m_location != null)
203         {
204             return m_location;
205         }
206         else if (System.getSecurityManager() != null)
207         {
208             try
209             {
210                 return (String JavaDoc) AccessController.doPrivileged(
211                     new PrivilegedAction(
212                         PrivilegedAction.GET_LOCATION_ACTION, this));
213             }
214             catch (PrivilegedActionException JavaDoc ex)
215             {
216                 throw ((PrivilegedActionException JavaDoc) ex).getException();
217             }
218         }
219         else
220         {
221             return getLocationUnchecked();
222         }
223     }
224
225     private String JavaDoc getLocationUnchecked()
226         throws Exception JavaDoc
227     {
228         // Get bundle location file.
229
File JavaDoc locFile = new File JavaDoc(m_dir, BUNDLE_LOCATION_FILE);
230
231         // Read bundle location.
232
FileReader fr = null;
233         BufferedReader br = null;
234         try
235         {
236             fr = new FileReader(locFile);
237             br = new BufferedReader(fr);
238             m_location = br.readLine();
239             return m_location;
240         }
241         finally
242         {
243             if (br != null) br.close();
244             if (fr != null) fr.close();
245         }
246     }
247
248     public int getPersistentState()
249         throws Exception JavaDoc
250     {
251         if (m_persistentState >= 0)
252         {
253             return m_persistentState;
254         }
255         else if (System.getSecurityManager() != null)
256         {
257             try
258             {
259                 return ((Integer JavaDoc) AccessController.doPrivileged(
260                     new PrivilegedAction(
261                         PrivilegedAction.GET_PERSISTENT_STATE_ACTION, this))).intValue();
262             }
263             catch (PrivilegedActionException JavaDoc ex)
264             {
265                 throw ((PrivilegedActionException JavaDoc) ex).getException();
266             }
267         }
268         else
269         {
270             return getPersistentStateUnchecked();
271         }
272     }
273
274     private int getPersistentStateUnchecked()
275         throws Exception JavaDoc
276     {
277         // Get bundle state file.
278
File JavaDoc stateFile = new File JavaDoc(m_dir, BUNDLE_STATE_FILE);
279
280         // If the state file doesn't exist, then
281
// assume the bundle was installed.
282
if (!stateFile.exists())
283         {
284             return Bundle.INSTALLED;
285         }
286
287         // Read the bundle state.
288
FileReader fr = null;
289         BufferedReader br= null;
290         try
291         {
292             fr = new FileReader(stateFile);
293             br = new BufferedReader(fr);
294             String JavaDoc s = br.readLine();
295             if (s.equals(ACTIVE_STATE))
296             {
297                 m_persistentState = Bundle.ACTIVE;
298             }
299             else if (s.equals(UNINSTALLED_STATE))
300             {
301                 m_persistentState = Bundle.UNINSTALLED;
302             }
303             else
304             {
305                 m_persistentState = Bundle.INSTALLED;
306             }
307             return m_persistentState;
308         }
309         finally
310         {
311             if (br != null) br.close();
312             if (fr != null) fr.close();
313         }
314     }
315
316     public void setPersistentState(int state)
317         throws Exception JavaDoc
318     {
319         if (System.getSecurityManager() != null)
320         {
321             try
322             {
323                 AccessController.doPrivileged(
324                     new PrivilegedAction(
325                         PrivilegedAction.SET_PERSISTENT_STATE_ACTION, this, state));
326             }
327             catch (PrivilegedActionException JavaDoc ex)
328             {
329                 throw ((PrivilegedActionException JavaDoc) ex).getException();
330             }
331         }
332         else
333         {
334             setPersistentStateUnchecked(state);
335         }
336     }
337
338     private void setPersistentStateUnchecked(int state)
339         throws Exception JavaDoc
340     {
341         // Get bundle state file.
342
File JavaDoc stateFile = new File JavaDoc(m_dir, BUNDLE_STATE_FILE);
343
344         // Write the bundle state.
345
FileWriter fw = null;
346         BufferedWriter bw= null;
347         try
348         {
349             fw = new FileWriter(stateFile);
350             bw = new BufferedWriter(fw);
351             String JavaDoc s = null;
352             switch (state)
353             {
354                 case Bundle.ACTIVE:
355                     s = ACTIVE_STATE;
356                     break;
357                 case Bundle.UNINSTALLED:
358                     s = UNINSTALLED_STATE;
359                     break;
360                 default:
361                     s = INSTALLED_STATE;
362                     break;
363             }
364             bw.write(s, 0, s.length());
365             m_persistentState = state;
366         }
367         catch (IOException ex)
368         {
369             Oscar.error("DefaultBundleArchive: Unable to record state: " + ex);
370             throw ex;
371         }
372         finally
373         {
374             if (bw != null) bw.close();
375             if (fw != null) fw.close();
376         }
377     }
378
379     public int getStartLevel()
380         throws Exception JavaDoc
381     {
382         if (m_startLevel >= 0)
383         {
384             return m_startLevel;
385         }
386         else if (System.getSecurityManager() != null)
387         {
388             try
389             {
390                 return ((Integer JavaDoc) AccessController.doPrivileged(
391                     new PrivilegedAction(
392                         PrivilegedAction.GET_START_LEVEL_ACTION, this))).intValue();
393             }
394             catch (PrivilegedActionException JavaDoc ex)
395             {
396                 throw ((PrivilegedActionException JavaDoc) ex).getException();
397             }
398         }
399         else
400         {
401             return getStartLevelUnchecked();
402         }
403     }
404
405     private int getStartLevelUnchecked()
406         throws Exception JavaDoc
407     {
408         // Get bundle start level file.
409
File JavaDoc levelFile = new File JavaDoc(m_dir, BUNDLE_START_LEVEL_FILE);
410
411         // If the start level file doesn't exist, then
412
// return an error.
413
if (!levelFile.exists())
414         {
415             return -1;
416         }
417
418         // Read the bundle start level.
419
FileReader fr = null;
420         BufferedReader br= null;
421         try
422         {
423             fr = new FileReader(levelFile);
424             br = new BufferedReader(fr);
425             m_startLevel = Integer.parseInt(br.readLine());
426             return m_startLevel;
427         }
428         finally
429         {
430             if (br != null) br.close();
431             if (fr != null) fr.close();
432         }
433     }
434
435     public void setStartLevel(int level)
436         throws Exception JavaDoc
437     {
438         if (System.getSecurityManager() != null)
439         {
440             try
441             {
442                 AccessController.doPrivileged(
443                     new PrivilegedAction(
444                         PrivilegedAction.SET_START_LEVEL_ACTION, this, level));
445             }
446             catch (PrivilegedActionException JavaDoc ex)
447             {
448                 throw ((PrivilegedActionException JavaDoc) ex).getException();
449             }
450         }
451         else
452         {
453             setStartLevelUnchecked(level);
454         }
455     }
456
457     private void setStartLevelUnchecked(int level)
458         throws Exception JavaDoc
459     {
460         // Get bundle start level file.
461
File JavaDoc levelFile = new File JavaDoc(m_dir, BUNDLE_START_LEVEL_FILE);
462
463         // Write the bundle start level.
464
FileWriter fw = null;
465         BufferedWriter bw = null;
466         try
467         {
468             fw = new FileWriter(levelFile);
469             bw = new BufferedWriter(fw);
470             String JavaDoc s = Integer.toString(level);
471             bw.write(s, 0, s.length());
472             m_startLevel = level;
473         }
474         catch (IOException ex)
475         {
476             Oscar.error("DefaultBundleArchive: Unable to record start leel: " + ex);
477             throw ex;
478         }
479         finally
480         {
481             if (bw != null) bw.close();
482             if (fw != null) fw.close();
483         }
484     }
485
486     public File JavaDoc getDataFile(String JavaDoc fileName)
487         throws Exception JavaDoc
488     {
489         // Do some sanity checking.
490
if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
491             throw new IllegalArgumentException JavaDoc("The data file path must be relative, not absolute.");
492         else if (fileName.indexOf("..") >= 0)
493             throw new IllegalArgumentException JavaDoc("The data file path cannot contain a reference to the \"..\" directory.");
494
495         // Get bundle data directory.
496
File JavaDoc dataDir = new File JavaDoc(m_dir, DATA_DIRECTORY);
497
498         if (System.getSecurityManager() != null)
499         {
500             try
501             {
502                 AccessController.doPrivileged(
503                     new PrivilegedAction(
504                         PrivilegedAction.CREATE_DATA_DIR_ACTION, this, dataDir));
505             }
506             catch (PrivilegedActionException JavaDoc ex)
507             {
508                 throw ((PrivilegedActionException JavaDoc) ex).getException();
509             }
510         }
511         else
512         {
513             createDataDirectoryUnchecked(dataDir);
514         }
515
516         // Return the data file.
517
return new File JavaDoc(dataDir, fileName);
518     }
519
520     private void createDataDirectoryUnchecked(File JavaDoc dir)
521         throws Exception JavaDoc
522     {
523         // Create data directory if necessary.
524
if (!dir.exists())
525         {
526             if (!dir.mkdir())
527             {
528                 throw new IOException("Unable to create bundle data directory.");
529             }
530         }
531     }
532
533     public BundleActivator getActivator(ClassLoader JavaDoc loader)
534         throws Exception JavaDoc
535     {
536         if (System.getSecurityManager() != null)
537         {
538             try
539             {
540                 return (BundleActivator) AccessController.doPrivileged(
541                     new PrivilegedAction(
542                         PrivilegedAction.GET_ACTIVATOR_ACTION, this, loader));
543             }
544             catch (PrivilegedActionException JavaDoc ex)
545             {
546                 throw ((PrivilegedActionException JavaDoc) ex).getException();
547             }
548         }
549         else
550         {
551             return getActivatorUnchecked(loader);
552         }
553     }
554
555     private BundleActivator getActivatorUnchecked(ClassLoader JavaDoc loader)
556         throws Exception JavaDoc
557     {
558         // Get bundle activator file.
559
File JavaDoc activatorFile = new File JavaDoc(m_dir, BUNDLE_ACTIVATOR_FILE);
560         // If the activator file doesn't exist, then
561
// assume there isn't one.
562
if (!activatorFile.exists())
563             return null;
564
565         // Deserialize the activator object.
566
InputStream is = null;
567         ObjectInputStreamX ois = null;
568         try
569         {
570             is = new FileInputStream(activatorFile);
571             ois = new ObjectInputStreamX(is, loader);
572             Object JavaDoc o = ois.readObject();
573             return (BundleActivator) o;
574         }
575         catch (Exception JavaDoc ex)
576         {
577             Oscar.error("DefaultBundleArchive: Trying to deserialize.");
578             Oscar.error("DefaultBundleArchive: " + ex);
579         }
580         finally
581         {
582             if (ois != null) ois.close();
583             if (is != null) is.close();
584         }
585
586         return null;
587     }
588
589     public void setActivator(Object JavaDoc obj)
590         throws Exception JavaDoc
591     {
592         if (System.getSecurityManager() != null)
593         {
594             try
595             {
596                 AccessController.doPrivileged(
597                     new PrivilegedAction(
598                         PrivilegedAction.SET_ACTIVATOR_ACTION, this, obj));
599             }
600             catch (PrivilegedActionException JavaDoc ex)
601             {
602                 throw ((PrivilegedActionException JavaDoc) ex).getException();
603             }
604         }
605         else
606         {
607             setActivatorUnchecked(obj);
608         }
609     }
610
611     private void setActivatorUnchecked(Object JavaDoc obj)
612         throws Exception JavaDoc
613     {
614         if (!(obj instanceof Serializable))
615         {
616             return;
617         }
618
619         // Get bundle activator file.
620
File JavaDoc activatorFile = new File JavaDoc(m_dir, BUNDLE_ACTIVATOR_FILE);
621
622         // Serialize the activator object.
623
OutputStream os = null;
624         ObjectOutputStream oos = null;
625         try
626         {
627             os = new FileOutputStream(activatorFile);
628             oos = new ObjectOutputStream(os);
629             oos.writeObject(obj);
630         }
631         catch (IOException ex)
632         {
633             Oscar.error("DefaultBundleArchive: Unable to serialize activator.");
634             Oscar.error("DefaultBundleArchive: " + ex);
635             throw ex;
636         }
637         finally
638         {
639             if (oos != null) oos.close();
640             if (os != null) os.close();
641         }
642     }
643
644     public int getRevisionCount()
645         throws Exception JavaDoc
646     {
647         if (System.getSecurityManager() != null)
648         {
649             try
650             {
651                 return ((Integer JavaDoc) AccessController.doPrivileged(
652                     new PrivilegedAction(
653                         PrivilegedAction.GET_REVISION_COUNT_ACTION, this))).intValue();
654             }
655             catch (PrivilegedActionException JavaDoc ex)
656             {
657                 throw ((PrivilegedActionException JavaDoc) ex).getException();
658             }
659         }
660         else
661         {
662             return getRevisionCountUnchecked();
663         }
664     }
665
666     public int getRevisionCountUnchecked()
667     {
668         // We should always have at least one revision
669
// directory, so try to count them if the value
670
// has not been initialized yet.
671
if (m_revisionCount <= 0)
672         {
673             m_revisionCount = 0;
674             File JavaDoc[] children = m_dir.listFiles();
675             for (int i = 0; (children != null) && (i < children.length); i++)
676             {
677                 if (children[i].getName().startsWith(REVISION_DIRECTORY))
678                 {
679                     m_revisionCount++;
680                 }
681             }
682         }
683         return m_revisionCount;
684     }
685
686     public Map getManifestHeader(int revision)
687         throws Exception JavaDoc
688     {
689         // If the request is for the current revision header,
690
// then return the cached copy if it is present.
691
if ((revision == (getRevisionCount() - 1)) && (m_currentHeader != null))
692         {
693             return m_currentHeader;
694         }
695
696         // Get the revision directory.
697
File JavaDoc revisionDir = new File JavaDoc(
698             m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
699
700         // Get the embedded resource.
701
JarFile JavaDoc jarFile = null;
702
703         try
704         {
705             // Create JarFile object using privileged block.
706
if (System.getSecurityManager() != null)
707             {
708                 jarFile = (JarFile JavaDoc) AccessController.doPrivileged(
709                     new PrivilegedAction(
710                         PrivilegedAction.OPEN_BUNDLE_JAR_ACTION, this, revisionDir));
711             }
712             else
713             {
714                 jarFile = openBundleJarUnchecked(revisionDir);
715             }
716
717             // Error if no jar file.
718
if (jarFile == null)
719             {
720                 throw new IOException("No JAR file found.");
721             }
722
723             // Get manifest.
724
Manifest JavaDoc mf = jarFile.getManifest();
725             // Create a case insensitive map of manifest attributes.
726
Map map = new CaseInsensitiveMap(mf.getMainAttributes());
727             // If the request is for the current revision's header,
728
// then cache it.
729
if (revision == (getRevisionCount() - 1))
730             {
731                 m_currentHeader = map;
732             }
733             return map;
734
735         } catch (PrivilegedActionException JavaDoc ex) {
736             throw ((PrivilegedActionException JavaDoc) ex).getException();
737         } finally {
738             if (jarFile != null) jarFile.close();
739         }
740     }
741
742     private JarFile JavaDoc openBundleJarUnchecked(File JavaDoc revisionDir)
743         throws Exception JavaDoc
744     {
745         // Get bundle jar file.
746
File JavaDoc bundleJar = new File JavaDoc(revisionDir, BUNDLE_JAR_FILE);
747         // Get bundle jar file.
748
return new JarFile JavaDoc(bundleJar);
749     }
750
751     public String JavaDoc[] getClassPath(int revision)
752         throws Exception JavaDoc
753     {
754         if (System.getSecurityManager() != null)
755         {
756             try
757             {
758                 return (String JavaDoc []) AccessController.doPrivileged(
759                     new PrivilegedAction(
760                         PrivilegedAction.GET_CLASS_PATH_ACTION, this, revision));
761             }
762             catch (PrivilegedActionException JavaDoc ex)
763             {
764                 throw ((PrivilegedActionException JavaDoc) ex).getException();
765             }
766         }
767         else
768         {
769             return getClassPathUnchecked(revision);
770         }
771     }
772
773     private String JavaDoc[] getClassPathUnchecked(int revision)
774         throws Exception JavaDoc
775     {
776         // Get the revision directory.
777
File JavaDoc revisionDir = new File JavaDoc(
778             m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
779
780         // Get the bundle's manifest header.
781
Map map = getManifestHeader(revision);
782         if (map == null)
783         {
784             map = new HashMap();
785         }
786
787         // Find class path meta-data.
788
String JavaDoc classPath = null;
789         Iterator iter = map.entrySet().iterator();
790         while ((classPath == null) && iter.hasNext())
791         {
792             Map.Entry entry = (Map.Entry) iter.next();
793             if (entry.getKey().toString().toLowerCase().equals(
794                 OscarConstants.BUNDLE_CLASSPATH.toLowerCase()))
795             {
796                 classPath = entry.getValue().toString();
797             }
798         }
799
800         // Parse the class path into strings.
801
String JavaDoc[] classPathStrings = TextUtil.parseCommaDelimitedString(classPath);
802
803         if (classPathStrings == null)
804         {
805             classPathStrings = new String JavaDoc[0];
806         }
807
808         // Now, check for "." in the class path.
809
boolean includeDot = false;
810         for (int i = 0; !includeDot && (i < classPathStrings.length); i++)
811         {
812             if (classPathStrings[i].equals(OscarConstants.CLASS_PATH_DOT))
813             {
814                 includeDot = true;
815             }
816         }
817
818         // Include all JARs in the embedded jar directory, since they
819
// were extracted when the bundle was initially saved.
820
File JavaDoc embedDir = new File JavaDoc(revisionDir, EMBEDDED_DIRECTORY);
821         String JavaDoc[] paths = null;
822         if (embedDir.exists())
823         {
824             // The size of the paths array is the number of
825
// embedded JAR files plus one, if we need to include
826
// ".", otherwise it is just the number of JAR files.
827
// If "." is included, then it will be added to the
828
// first place in the path array below.
829
File JavaDoc[] children = embedDir.listFiles();
830             int size = (children == null) ? 0 : children.length;
831             size = (includeDot) ? size + 1 : size;
832             paths = new String JavaDoc[size];
833             for (int i = 0; i < children.length; i++)
834             {
835                 // If we are including "." then skip the first slot,
836
// because this is where we will put the bundle JAR file.
837
paths[(includeDot) ? i + 1 : i] = children[i].getPath();
838             }
839         }
840
841         // If there is nothing on the class path, then include
842
// "." by default, as per the spec.
843
if ((paths == null) || (paths.length == 0))
844         {
845             includeDot = true;
846             paths = new String JavaDoc[1];
847         }
848
849         // Put the bundle jar file first, if included.
850
if (includeDot)
851         {
852             paths[0] = revisionDir + File.separator + BUNDLE_JAR_FILE;
853         }
854
855         return paths;
856     }
857
858 // TODO: This will need to consider security.
859
public String JavaDoc findLibrary(int revision, String JavaDoc libName)
860         throws Exception JavaDoc
861     {
862         return findLibraryUnchecked(revision, libName);
863     }
864
865     private String JavaDoc findLibraryUnchecked(int revision, String JavaDoc libName)
866         throws Exception JavaDoc
867     {
868         // Get the revision directory.
869
File JavaDoc revisionDir = new File JavaDoc(
870             m_dir.getAbsoluteFile(),
871             REVISION_DIRECTORY + getRefreshCount() + "." + revision);
872
873         // Get bundle lib directory.
874
File JavaDoc libDir = new File JavaDoc(revisionDir, LIBRARY_DIRECTORY);
875         // Get lib file.
876
File JavaDoc libFile = new File JavaDoc(libDir, File.separatorChar + libName);
877         // Make sure that the library's parent directory exists;
878
// it may be in a sub-directory.
879
libDir = libFile.getParentFile();
880         if (!libDir.exists())
881         {
882             if (!libDir.mkdirs())
883             {
884                 throw new IOException("Unable to create library directory.");
885             }
886         }
887         // Extract the library from the JAR file if it does not
888
// already exist.
889
if (!libFile.exists())
890         {
891             JarFile JavaDoc jarFile = null;
892             InputStream is = null;
893
894             try
895             {
896                 jarFile = openBundleJarUnchecked(revisionDir);
897                 ZipEntry JavaDoc ze = jarFile.getEntry(libName);
898                 if (ze == null)
899                 {
900                     throw new IOException("No JAR entry: " + libName);
901                 }
902                 is = new BufferedInputStream(
903                     jarFile.getInputStream(ze), DefaultBundleCache.BUFSIZE);
904                 if (is == null)
905                 {
906                     throw new IOException("No input stream: " + libName);
907                 }
908
909                 // Create the file.
910
copy(is, libFile);
911
912             }
913             finally
914             {
915                 if (jarFile != null) jarFile.close();
916                 if (is != null) is.close();
917             }
918         }
919
920         return libFile.toString();
921     }
922
923     /**
924      * This utility method is used to retrieve the current refresh
925      * counter value for the bundle. This value is used when generating
926      * the bundle JAR directory name where native libraries are extracted.
927      * This is necessary because Sun's JVM requires a one-to-one mapping
928      * between native libraries and class loaders where the native library
929      * is uniquely identified by its absolute path in the file system. This
930      * constraint creates a problem when a bundle is refreshed, because it
931      * gets a new class loader. Using the refresh counter to generate the name
932      * of the bundle JAR directory resolves this problem because each time
933      * bundle is refresh, the native library will have a unique name.
934      * As a result of the unique name, the JVM will then reload the
935      * native library without a problem.
936     **/

937     private long getRefreshCount()
938         throws Exception JavaDoc
939     {
940         // If we have already read the update counter file,
941
// then just return the result.
942
if (m_refreshCount >= 0)
943         {
944             return m_refreshCount;
945         }
946
947         // Get update counter file.
948
File JavaDoc counterFile = new File JavaDoc(m_dir, REFRESH_COUNTER_FILE);
949
950         // If the update counter file doesn't exist, then
951
// assume the counter is at zero.
952
if (!counterFile.exists())
953         {
954             return 0;
955         }
956
957         // Read the bundle update counter.
958
FileReader fr = null;
959         BufferedReader br = null;
960         try
961         {
962             fr = new FileReader(counterFile);
963             br = new BufferedReader(fr);
964             long counter = Long.parseLong(br.readLine());
965             return counter;
966         }
967         finally
968         {
969             if (br != null) br.close();
970             if (fr != null) fr.close();
971         }
972     }
973
974     /**
975      * This utility method is used to retrieve the current refresh
976      * counter value for the bundle. This value is used when generating
977      * the bundle JAR directory name where native libraries are extracted.
978      * This is necessary because Sun's JVM requires a one-to-one mapping
979      * between native libraries and class loaders where the native library
980      * is uniquely identified by its absolute path in the file system. This
981      * constraint creates a problem when a bundle is refreshed, because it
982      * gets a new class loader. Using the refresh counter to generate the name
983      * of the bundle JAR directory resolves this problem because each time
984      * bundle is refresh, the native library will have a unique name.
985      * As a result of the unique name, the JVM will then reload the
986      * native library without a problem.
987     **/

988     private void setRefreshCount(long counter)
989         throws Exception JavaDoc
990     {
991         // Get update counter file.
992
File JavaDoc counterFile = new File JavaDoc(m_dir, REFRESH_COUNTER_FILE);
993
994         // Write the update counter.
995
FileWriter fw = null;
996         BufferedWriter bw = null;
997         try
998         {
999             fw = new FileWriter(counterFile);
1000            bw = new BufferedWriter(fw);
1001            String JavaDoc s = Long.toString(counter);
1002            bw.write(s, 0, s.length());
1003            m_refreshCount = counter;
1004        }
1005        catch (IOException ex)
1006        {
1007            Oscar.error("DefaultBundleArchive: Unable to write counter: " + ex);
1008            throw ex;
1009        }
1010        finally
1011        {
1012            if (bw != null) bw.close();
1013            if (fw != null) fw.close();
1014        }
1015    }
1016
1017    //
1018
// File-oriented utility methods.
1019
//
1020

1021    protected static boolean deleteDirectoryTree(File JavaDoc target)
1022    {
1023        if (!target.exists())
1024        {
1025            return true;
1026        }
1027
1028        if (target.isDirectory())
1029        {
1030            File JavaDoc[] files = target.listFiles();
1031            for (int i = 0; i < files.length; i++)
1032            {
1033                deleteDirectoryTree(files[i]);
1034            }
1035        }
1036
1037        return target.delete();
1038    }
1039
1040    /**
1041     * This method copies an input stream to the specified file.
1042     * <p>
1043     * Security: This method must be called from within a <tt>doPrivileged()</tt>
1044     * block since it accesses the disk.
1045     * @param is the input stream to copy.
1046     * @param outputFile the file to which the input stream should be copied.
1047    **/

1048    private void copy(InputStream is, File JavaDoc outputFile)
1049        throws IOException
1050    {
1051        OutputStream os = null;
1052
1053        try
1054        {
1055            os = new BufferedOutputStream(
1056                new FileOutputStream(outputFile), DefaultBundleCache.BUFSIZE);
1057            byte[] b = new byte[DefaultBundleCache.BUFSIZE];
1058            int len = 0;
1059            while ((len = is.read(b)) != -1)
1060                os.write(b, 0, len);
1061        }
1062        finally
1063        {
1064            if (is != null) is.close();
1065            if (os != null) os.close();
1066        }
1067    }
1068
1069    /**
1070     * This method pre-processes a bundle JAR file making it ready
1071     * for use. This entails extracting all embedded JAR files and
1072     * all native libraries.
1073     * @throws java.lang.Exception if any error occurs while processing JAR file.
1074    **/

1075    private void preprocessBundleJar(int revision, File JavaDoc revisionDir)
1076        throws Exception JavaDoc
1077    {
1078        //
1079
// Create special directories so that we can avoid checking
1080
// for their existence all the time.
1081
//
1082

1083        File JavaDoc embedDir = new File JavaDoc(revisionDir, EMBEDDED_DIRECTORY);
1084        if (!embedDir.exists())
1085        {
1086            if (!embedDir.mkdir())
1087            {
1088                throw new IOException("Could not create embedded JAR directory.");
1089            }
1090        }
1091
1092        File JavaDoc libDir = new File JavaDoc(revisionDir, LIBRARY_DIRECTORY);
1093        if (!libDir.exists())
1094        {
1095            if (!libDir.mkdir())
1096            {
1097                throw new IOException("Unable to create native library directory.");
1098            }
1099        }
1100
1101        //
1102
// This block extracts all embedded JAR files.
1103
//
1104

1105        try
1106        {
1107            // Get the bundle's manifest header.
1108
Map map = getManifestHeader(revision);
1109            if (map == null)
1110            {
1111                map = new HashMap();
1112            }
1113
1114            // Find class path meta-data.
1115
String JavaDoc classPath = null;
1116            Iterator iter = map.entrySet().iterator();
1117            while ((classPath == null) && iter.hasNext())
1118            {
1119                Map.Entry entry = (Map.Entry) iter.next();
1120                if (entry.getKey().toString().toLowerCase().equals(
1121                    OscarConstants.BUNDLE_CLASSPATH.toLowerCase()))
1122                {
1123                    classPath = entry.getValue().toString();
1124                }
1125            }
1126
1127            // Parse the class path into strings.
1128
String JavaDoc[] classPathStrings = TextUtil.parseCommaDelimitedString(classPath);
1129
1130            if (classPathStrings == null)
1131            {
1132                classPathStrings = new String JavaDoc[0];
1133            }
1134
1135            for (int i = 0; i < classPathStrings.length; i++)
1136            {
1137                if (!classPathStrings[i].equals(OscarConstants.CLASS_PATH_DOT))
1138                {
1139                    extractEmbeddedJar(revisionDir, classPathStrings[i]);
1140                }
1141            }
1142
1143        }
1144        catch (PrivilegedActionException JavaDoc ex)
1145        {
1146            throw ((PrivilegedActionException JavaDoc) ex).getException();
1147        }
1148    }
1149
1150    /**
1151     * This method extracts an embedded JAR file from the bundle's
1152     * JAR file.
1153     * <p>
1154     * Security: This method must be called from within a <tt>doPrivileged()</tt>
1155     * block since it accesses the disk.
1156     * @param id the identifier of the bundle that owns the embedded JAR file.
1157     * @param jarPath the path to the embedded JAR file inside the bundle JAR file.
1158    **/

1159    private void extractEmbeddedJar(File JavaDoc revisionDir, String JavaDoc jarPath)
1160        throws Exception JavaDoc
1161    {
1162        // Remove leading slash if present.
1163
jarPath = (jarPath.charAt(0) == '/') ? jarPath.substring(1) : jarPath;
1164        // Get only the JAR file name.
1165
String JavaDoc jarName = (jarPath.lastIndexOf('/') >= 0)
1166            ? jarPath.substring(jarPath.lastIndexOf('/') + 1) : jarPath;
1167
1168        // If JAR is already extracted, then don't
1169
// re-extract it...
1170
File JavaDoc embedFile = new File JavaDoc(
1171            revisionDir, EMBEDDED_DIRECTORY + File.separatorChar + jarName);
1172        if (!embedFile.exists())
1173        {
1174            JarFile JavaDoc jarFile = null;
1175            InputStream is = null;
1176
1177            try
1178            {
1179                jarFile = openBundleJarUnchecked(revisionDir);
1180                ZipEntry JavaDoc ze = jarFile.getEntry(jarPath);
1181                if (ze == null)
1182                {
1183                    throw new IOException("No JAR entry: " + jarPath);
1184                }
1185                is = new BufferedInputStream(jarFile.getInputStream(ze), DefaultBundleCache.BUFSIZE);
1186                if (is == null)
1187                {
1188                    throw new IOException("No input stream: " + jarPath);
1189                }
1190
1191                // Create the file.
1192
copy(is, embedFile);
1193
1194            }
1195            finally
1196            {
1197                if (jarFile != null) jarFile.close();
1198                if (is != null) is.close();
1199            }
1200        }
1201    }
1202
1203    // INCREASES THE REVISION COUNT.
1204
protected void update(InputStream is) throws Exception JavaDoc
1205    {
1206        if (System.getSecurityManager() != null)
1207        {
1208            try
1209            {
1210                AccessController.doPrivileged(
1211                    new PrivilegedAction(
1212                        PrivilegedAction.UPDATE_ACTION, this, is));
1213            }
1214            catch (PrivilegedActionException JavaDoc ex)
1215            {
1216                throw ((PrivilegedActionException JavaDoc) ex).getException();
1217            }
1218        }
1219        else
1220        {
1221            updateUnchecked(is);
1222        }
1223    }
1224
1225    // INCREASES THE REVISION COUNT.
1226
private void updateUnchecked(InputStream is) throws Exception JavaDoc
1227    {
1228        File JavaDoc revisionDir = null;
1229
1230        try
1231        {
1232            // Create the new revision directory.
1233
int revision = getRevisionCountUnchecked();
1234            revisionDir = new File JavaDoc(
1235                m_dir, REVISION_DIRECTORY
1236                + getRefreshCount() + "." + revision);
1237            if (!revisionDir.mkdir())
1238            {
1239                throw new IOException("Unable to create revision directory.");
1240            }
1241
1242            // Save the new revision bundle jar file.
1243
File JavaDoc file = new File JavaDoc(revisionDir, BUNDLE_JAR_FILE);
1244            copy(is, file);
1245
1246            preprocessBundleJar(revision, revisionDir);
1247        }
1248        catch (Exception JavaDoc ex)
1249        {
1250            if ((revisionDir != null) && revisionDir.exists())
1251            {
1252                try
1253                {
1254                    deleteDirectoryTree(revisionDir);
1255                }
1256                catch (Exception JavaDoc ex2)
1257                {
1258                    // There is very little we can do here.
1259
Oscar.error("Unable to remove partial revision directory.", ex2);
1260                }
1261            }
1262            throw ex;
1263        }
1264
1265        // If everything was successful, then update
1266
// the revision count.
1267
m_revisionCount++;
1268        // Clear the cached revision header, since it is
1269
// no longer the current revision.
1270
m_currentHeader = null;
1271    }
1272
1273    // DECREASES THE REVISION COUNT.
1274
protected void purge() throws Exception JavaDoc
1275    {
1276        if (System.getSecurityManager() != null)
1277        {
1278            try
1279            {
1280                AccessController.doPrivileged(
1281                    new PrivilegedAction(
1282                        PrivilegedAction.PURGE_ACTION, this));
1283            }
1284            catch (PrivilegedActionException JavaDoc ex)
1285            {
1286                throw ((PrivilegedActionException JavaDoc) ex).getException();
1287            }
1288        }
1289        else
1290        {
1291            purgeUnchecked();
1292        }
1293    }
1294    
1295    // DECREASES THE REVISION COUNT.
1296
private void purgeUnchecked() throws Exception JavaDoc
1297    {
1298        // Get the current update count.
1299
long update = getRefreshCount();
1300        // Get the current revision count.
1301
int count = getRevisionCountUnchecked();
1302
1303        File JavaDoc revisionDir = null;
1304        for (int i = 0; i < count - 1; i++)
1305        {
1306            revisionDir = new File JavaDoc(m_dir, REVISION_DIRECTORY + update + "." + i);
1307            if (revisionDir.exists())
1308            {
1309                deleteDirectoryTree(revisionDir);
1310            }
1311        }
1312        // Increment the update count.
1313
setRefreshCount(update + 1);
1314
1315        // Rename the current revision to be the current update.
1316
File JavaDoc currentDir = new File JavaDoc(m_dir, REVISION_DIRECTORY + (update + 1) + ".0");
1317        revisionDir = new File JavaDoc(m_dir, REVISION_DIRECTORY + update + "." + (count - 1));
1318        revisionDir.renameTo(currentDir);
1319        
1320        // If everything is successful, then set the revision
1321
// count to one.
1322
m_revisionCount = 1;
1323        // Although the cached current header should stay the same
1324
// here, clear it for consistency.
1325
m_currentHeader = null;
1326    }
1327
1328    protected void remove() throws Exception JavaDoc
1329    {
1330        if (System.getSecurityManager() != null)
1331        {
1332            try
1333            {
1334                AccessController.doPrivileged(
1335                    new PrivilegedAction(
1336                        PrivilegedAction.REMOVE_ACTION, this));
1337            }
1338            catch (PrivilegedActionException JavaDoc ex)
1339            {
1340                throw ((PrivilegedActionException JavaDoc) ex).getException();
1341            }
1342        }
1343        else
1344        {
1345            removeUnchecked();
1346        }
1347    }
1348    
1349    private void removeUnchecked() throws Exception JavaDoc
1350    {
1351        deleteDirectoryTree(m_dir);
1352    }
1353
1354    //
1355
// Utility class for performing privileged actions.
1356
//
1357

1358    private static class PrivilegedAction implements PrivilegedExceptionAction JavaDoc
1359    {
1360        private static final int INITIALIZE_ACTION = 0;
1361        private static final int UPDATE_ACTION = 1;
1362        private static final int PURGE_ACTION = 2;
1363        private static final int REMOVE_ACTION = 3;
1364        private static final int GET_REVISION_COUNT_ACTION = 4;
1365        private static final int GET_LOCATION_ACTION = 5;
1366        private static final int GET_PERSISTENT_STATE_ACTION = 6;
1367        private static final int SET_PERSISTENT_STATE_ACTION = 7;
1368        private static final int GET_START_LEVEL_ACTION = 8;
1369        private static final int SET_START_LEVEL_ACTION = 9;
1370        private static final int OPEN_BUNDLE_JAR_ACTION = 10;
1371        private static final int CREATE_DATA_DIR_ACTION = 11;
1372        private static final int GET_CLASS_PATH_ACTION = 12;
1373        private static final int GET_ACTIVATOR_ACTION = 13;
1374        private static final int SET_ACTIVATOR_ACTION = 14;
1375
1376        private int m_action = 0;
1377        private DefaultBundleArchive m_archive = null;
1378        private InputStream m_isArg = null;
1379        private String JavaDoc m_strArg = null;
1380        private int m_intArg = 0;
1381        private File JavaDoc m_fileArg = null;
1382        private ClassLoader JavaDoc m_loaderArg = null;
1383        private Object JavaDoc m_objArg = null;
1384
1385        public PrivilegedAction(int action, DefaultBundleArchive archive)
1386        {
1387            m_action = action;
1388            m_archive = archive;
1389        }
1390
1391        public PrivilegedAction(int action, DefaultBundleArchive archive, InputStream isArg)
1392        {
1393            m_action = action;
1394            m_archive = archive;
1395            m_isArg = isArg;
1396        }
1397
1398        public PrivilegedAction(int action, DefaultBundleArchive archive, int intArg)
1399        {
1400            m_action = action;
1401            m_archive = archive;
1402            m_intArg = intArg;
1403        }
1404
1405        public PrivilegedAction(int action, DefaultBundleArchive archive, File JavaDoc fileArg)
1406        {
1407            m_action = action;
1408            m_archive = archive;
1409            m_fileArg = fileArg;
1410        }
1411
1412        public PrivilegedAction(int action, DefaultBundleArchive archive, ClassLoader JavaDoc loaderArg)
1413        {
1414            m_action = action;
1415            m_archive = archive;
1416            m_loaderArg = loaderArg;
1417        }
1418
1419        public PrivilegedAction(int action, DefaultBundleArchive archive, Object JavaDoc objArg)
1420        {
1421            m_action = action;
1422            m_archive = archive;
1423            m_objArg = objArg;
1424        }
1425
1426        public Object JavaDoc run() throws Exception JavaDoc
1427        {
1428            switch (m_action)
1429            {
1430                case INITIALIZE_ACTION:
1431                    m_archive.initializeUnchecked(m_isArg);
1432                    return null;
1433                case UPDATE_ACTION:
1434                    m_archive.updateUnchecked(m_isArg);
1435                    return null;
1436                case PURGE_ACTION:
1437                    m_archive.purgeUnchecked();
1438                    return null;
1439                case REMOVE_ACTION:
1440                    m_archive.removeUnchecked();
1441                    return null;
1442                case GET_REVISION_COUNT_ACTION:
1443                    return new Integer JavaDoc(m_archive.getRevisionCountUnchecked());
1444                case GET_LOCATION_ACTION:
1445                    return m_archive.getLocationUnchecked();
1446                case GET_PERSISTENT_STATE_ACTION:
1447                    return new Integer JavaDoc(m_archive.getPersistentStateUnchecked());
1448                case SET_PERSISTENT_STATE_ACTION:
1449                    m_archive.setPersistentStateUnchecked(m_intArg);
1450                    return null;
1451                case GET_START_LEVEL_ACTION:
1452                    return new Integer JavaDoc(m_archive.getStartLevelUnchecked());
1453                case SET_START_LEVEL_ACTION:
1454                    m_archive.setStartLevelUnchecked(m_intArg);
1455                    return null;
1456                case OPEN_BUNDLE_JAR_ACTION:
1457                    return m_archive.openBundleJarUnchecked(m_fileArg);
1458                case CREATE_DATA_DIR_ACTION:
1459                    m_archive.createDataDirectoryUnchecked(m_fileArg);
1460                    return null;
1461                case GET_CLASS_PATH_ACTION:
1462                    return m_archive.getClassPathUnchecked(m_intArg);
1463                case GET_ACTIVATOR_ACTION:
1464                    return m_archive.getActivatorUnchecked(m_loaderArg);
1465                case SET_ACTIVATOR_ACTION:
1466                    m_archive.setActivatorUnchecked(m_objArg);
1467                    return null;
1468            }
1469
1470            throw new IllegalArgumentException JavaDoc("Invalid action specified.");
1471        }
1472    }
1473}
Popular Tags