Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 266ebcbddd57673ed2e0f69839554056cd90d19e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
/*******************************************************************************
 * Copyright (c) 2003, 2004, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.osgi.framework.internal.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.*;
import java.util.*;
import org.eclipse.osgi.framework.adaptor.BundleData;
import org.eclipse.osgi.framework.adaptor.BundleOperation;
import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.service.resolver.*;
import org.osgi.framework.*;
import org.osgi.framework.Version;

/**
 * This object is given out to bundles and wraps the internal Bundle object. It
 * is destroyed when a bundle is uninstalled and reused if a bundle is updated.
 * This class is abstract and is extended by BundleHost and BundleFragment.
 */
public abstract class AbstractBundle implements Bundle, Comparable, KeyedElement {
	/** The Framework this bundle is part of */
	protected Framework framework;
	/** The state of the bundle. */
	protected volatile int state;
	/** A flag to denote whether a bundle state change is in progress */
	protected volatile Thread stateChanging;
	/** Bundle's BundleData object */
	protected BundleData bundledata;
	/** Internal object used for state change synchronization */
	protected Object statechangeLock = new Object();
	/** ProtectionDomain for the bundle */
	protected BundleProtectionDomain domain;
	/* Single object for permission checks */
	BundleResourcePermission resourcePermission;

	/**
	 * This String captures the dependencies that could not be resolved
	 * as a result of a runtime error,  For example not having the proper 
	 * permissions or a singlton conflict.
	 * This information is collected by resolve, but an exception
	 * cannot be thrown during the resolve phase. It is saved here to be thrown
	 * later (by Bundle.start for example).
	 */
	protected String runtimeResolveError;
	protected ManifestLocalization manifestLocalization = null;

	/**
	 * Bundle object constructor. This constructor should not perform any real
	 * work.
	 * 
	 * @param bundledata
	 *            BundleData for this bundle
	 * @param framework
	 *            Framework this bundle is running in
	 */
	protected static AbstractBundle createBundle(BundleData bundledata, Framework framework) throws BundleException {
		if (bundledata.isFragment())
			return new BundleFragment(bundledata, framework);
		else
			return new BundleHost(bundledata, framework);
	}

	/**
	 * Bundle object constructor. This constructor should not perform any real
	 * work.
	 * 
	 * @param bundledata
	 *            BundleData for this bundle
	 * @param framework
	 *            Framework this bundle is running in
	 */
	protected AbstractBundle(BundleData bundledata, Framework framework) throws BundleException {
		state = INSTALLED;
		stateChanging = null;
		this.bundledata = bundledata;
		this.framework = framework;
		bundledata.setBundle(this);
	}

	/**
	 * Load the bundle.
	 * 
	 * @exception org.osgi.framework.BundleException
	 */
	protected abstract void load() throws BundleException;

	/**
	 * Reload from a new bundle. This method must be called while holding the
	 * bundles lock.
	 * 
	 * @param newBundle
	 *            Dummy Bundle which contains new data.
	 * @return true if an exported package is "in use". i.e. it has been
	 *         imported by a bundle
	 * @exception org.osgi.framework.BundleException
	 */
	protected abstract boolean reload(AbstractBundle newBundle) throws BundleException;

	/**
	 * Refresh the bundle. This is called by Framework.refreshPackages. This
	 * method must be called while holding the bundles lock.
	 * this.loader.unimportPackages must have already been called before
	 * calling this method!
	 * 
	 * @exception org.osgi.framework.BundleException
	 *                if an exported package is "in use". i.e. it has been
	 *                imported by a bundle
	 */
	protected abstract void refresh() throws BundleException;

	/**
	 * Unload the bundle. This method must be called while holding the bundles
	 * lock.
	 * 
	 * @return true if an exported package is "in use". i.e. it has been
	 *         imported by a bundle
	 */
	protected abstract boolean unload();

	/**
	 * Close the the Bundle's file.
	 *  
	 */
	protected void close() {
		if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
			if ((state & (INSTALLED)) == 0) {
				Debug.println("Bundle.close called when state != INSTALLED: " + this); //$NON-NLS-1$
				Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
			}
		}
		state = UNINSTALLED;
	}

	/** 
	 * Load and instantiate bundle's BundleActivator class
	 */
	protected BundleActivator loadBundleActivator() throws BundleException {
		/* load Bundle's BundleActivator if it has one */
		String activatorClassName = bundledata.getActivator();
		if (activatorClassName != null) {
			try {
				Class activatorClass = loadClass(activatorClassName, false);
				/* Create the activator for the bundle */
				return (BundleActivator) (activatorClass.newInstance());
			} catch (Throwable t) {
				if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
					Debug.printStackTrace(t);
				}
				throw new BundleException(Msg.formatter.getString("BUNDLE_INVALID_ACTIVATOR_EXCEPTION", activatorClassName, bundledata.getSymbolicName()), t); //$NON-NLS-1$
			}
		}
		return (null);
	}

	/**
	 * This method loads a class from the bundle.
	 * 
	 * @param name
	 *            the name of the desired Class.
	 * @param checkPermission
	 *            indicates whether a permission check should be done.
	 * @return the resulting Class
	 * @exception java.lang.ClassNotFoundException
	 *                if the class definition was not found.
	 */
	protected abstract Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException;

	/**
	 * Returns the current state of the bundle.
	 * 
	 * A bundle can only be in one state at any time.
	 * 
	 * @return bundle's state.
	 */
	public int getState() {
		return (state);
	}

	/**
	 * Return true if the bundle is starting or active.
	 *  
	 */
	protected boolean isActive() {
		return ((state & (ACTIVE | STARTING)) != 0);
	}

	/**
	 * Return true if the bundle is resolved.
	 *  
	 */
	protected boolean isResolved() {
		return (state & (INSTALLED | UNINSTALLED)) == 0;
	}

	/**
	 * Start this bundle.
	 * 
	 * If the current start level is less than this bundle's start level, then
	 * the Framework must persistently mark this bundle as started and delay
	 * the starting of this bundle until the Framework's current start level
	 * becomes equal or more than the bundle's start level.
	 * <p>
	 * Otherwise, the following steps are required to start a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this
	 * method returns immediately.
	 * <li>If the bundle is {@link #STOPPING}then this method may wait for
	 * the bundle to return to the {@link #RESOLVED}state before continuing.
	 * If this does not occur in a reasonable time, a {@link BundleException}
	 * is thrown to indicate the bundle was unable to be started.
	 * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to
	 * resolve the bundle. If the bundle cannot be resolved, a
	 * {@link BundleException}is thrown.
	 * <li>The state of the bundle is set to {@link #STARTING}.
	 * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's
	 * {@link BundleActivator}, if one is specified, is called. If the
	 * {@link BundleActivator}is invalid or throws an exception, the state of
	 * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if
	 * any, are removed, service's registered by the bundle, if any, are
	 * unregistered, and service's used by the bundle, if any, are released. A
	 * {@link BundleException}is then thrown.
	 * <li>It is recorded that this bundle has been started, so that when the
	 * framework is restarted, this bundle will be automatically started.
	 * <li>The state of the bundle is set to {@link #ACTIVE}.
	 * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is
	 * broadcast.
	 * </ol>
	 * 
	 * <h5>Preconditons</h5>
	 * <ul>
	 * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() in {{@link #ACTIVE}}.
	 * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called
	 * and did not throw an exception.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}.
	 * </ul>
	 * 
	 * @exception BundleException
	 *                If the bundle couldn't be started. This could be because
	 *                a code dependency could not be resolved or the specified
	 *                BundleActivator could not be loaded or threw an
	 *                exception.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle has been uninstalled or the bundle tries to
	 *                change its own state.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 */
	public void start() throws BundleException {
		framework.checkAdminPermission(this, AdminPermission.EXECUTE);
		checkValid();
		beginStateChange();
		try {
			startWorker(true);
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Internal worker to start a bundle.
	 * 
	 * @param persistent
	 *            if true persistently record the bundle was started.
	 */
	protected abstract void startWorker(boolean persistent) throws BundleException;

	/**
	 * Start this bundle w/o marking is persistently started.
	 * 
	 * <p>
	 * The following steps are followed to start a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this
	 * method returns immediately.
	 * <li>If the bundle is {@link #STOPPING}then this method may wait for
	 * the bundle to return to the {@link #RESOLVED}state before continuing.
	 * If this does not occur in a reasonable time, a {@link BundleException}
	 * is thrown to indicate the bundle was unable to be started.
	 * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to
	 * resolve the bundle. If the bundle cannot be resolved, a
	 * {@link BundleException}is thrown.
	 * <li>The state of the bundle is set to {@link #STARTING}.
	 * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's
	 * {@link BundleActivator}, if one is specified, is called. If the
	 * {@link BundleActivator}is invalid or throws an exception, the state of
	 * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if
	 * any, are removed, service's registered by the bundle, if any, are
	 * unregistered, and service's used by the bundle, if any, are released. A
	 * {@link BundleException}is then thrown.
	 * <li>The state of the bundle is set to {@link #ACTIVE}.
	 * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is
	 * broadcast.
	 * </ol>
	 * 
	 * <h5>Preconditons</h5>
	 * <ul>
	 * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() in {{@link #ACTIVE}}.
	 * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called
	 * and did not throw an exception.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}.
	 * </ul>
	 * 
	 * @exception BundleException
	 *                If the bundle couldn't be started. This could be because
	 *                a code dependency could not be resolved or the specified
	 *                BundleActivator could not be loaded or threw an
	 *                exception.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle tries to change its own state.
	 */
	protected void resume() throws BundleException {
		if (state == UNINSTALLED) {
			return;
		}
		beginStateChange();
		try {
			startWorker(false);
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Stop this bundle.
	 * 
	 * Any services registered by this bundle will be unregistered. Any
	 * services used by this bundle will be released. Any listeners registered
	 * by this bundle will be removed.
	 * 
	 * <p>
	 * The following steps are followed to stop a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or
	 * {@link #INSTALLED}then this method returns immediately.
	 * <li>If the bundle is {@link #STARTING}then this method may wait for
	 * the bundle to reach the {@link #ACTIVE}state before continuing. If this
	 * does not occur in a reasonable time, a {@link BundleException}is thrown
	 * to indicate the bundle was unable to be stopped.
	 * <li>The state of the bundle is set to {@link #STOPPING}.
	 * <li>It is recorded that this bundle has been stopped, so that when the
	 * framework is restarted, this bundle will not be automatically started.
	 * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's
	 * {@link BundleActivator}, if one is specified, is called. If the
	 * {@link BundleActivator}throws an exception, this method will continue
	 * to stop the bundle. A {@link BundleException}will be thrown after
	 * completion of the remaining steps.
	 * <li>The bundle's listeners, if any, are removed, service's registered
	 * by the bundle, if any, are unregistered, and service's used by the
	 * bundle, if any, are released.
	 * <li>The state of the bundle is set to {@link #RESOLVED}.
	 * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is
	 * broadcast.
	 * </ol>
	 * 
	 * <h5>Preconditons</h5>
	 * <ul>
	 * <li>getState() in {{@link #ACTIVE}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}.
	 * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called
	 * and did not throw an exception.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>None.
	 * </ul>
	 * 
	 * @exception BundleException
	 *                If the bundle's BundleActivator could not be loaded or
	 *                threw an exception.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle has been uninstalled or the bundle tries to
	 *                change its own state.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 */
	public void stop() throws BundleException {
		framework.checkAdminPermission(this, AdminPermission.EXECUTE);
		checkValid();
		beginStateChange();
		try {
			stopWorker(true);
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Internal worker to stop a bundle.
	 * 
	 * @param persistent
	 *            if true persistently record the bundle was stopped.
	 */
	protected abstract void stopWorker(boolean persistent) throws BundleException;

	/**
	 * Set the persistent status bit for the bundle.
	 * 
	 * @param mask
	 *            Mask for bit to set/clear
	 * @param state
	 *            true to set bit, false to clear bit
	 */
	protected void setStatus(final int mask, final boolean state) {
		try {
			AccessController.doPrivileged(new PrivilegedExceptionAction() {
				public Object run() throws BundleException, IOException {
					int status = bundledata.getStatus();
					boolean test = ((status & mask) != 0);
					if (test != state) {
						bundledata.setStatus(state ? (status | mask) : (status & ~mask));
						bundledata.save();
					}
					return null;
				}
			});
		} catch (PrivilegedActionException pae) {
			framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, pae.getException());
		}
	}

	/**
	 * Stop this bundle w/o marking is persistently stopped.
	 * 
	 * Any services registered by this bundle will be unregistered. Any
	 * services used by this bundle will be released. Any listeners registered
	 * by this bundle will be removed.
	 * 
	 * <p>
	 * The following steps are followed to stop a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or
	 * {@link #INSTALLED}then this method returns immediately.
	 * <li>If the bundle is {@link #STARTING}then this method may wait for
	 * the bundle to reach the {@link #ACTIVE}state before continuing. If this
	 * does not occur in a reasonable time, a {@link BundleException}is thrown
	 * to indicate the bundle was unable to be stopped.
	 * <li>The state of the bundle is set to {@link #STOPPING}.
	 * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's
	 * {@link BundleActivator}, if one is specified, is called. If the
	 * {@link BundleActivator}throws an exception, this method will continue
	 * to stop the bundle. A {@link BundleException}will be thrown after
	 * completion of the remaining steps.
	 * <li>The bundle's listeners, if any, are removed, service's registered
	 * by the bundle, if any, are unregistered, and service's used by the
	 * bundle, if any, are released.
	 * <li>The state of the bundle is set to {@link #RESOLVED}.
	 * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is
	 * broadcast.
	 * </ol>
	 * 
	 * <h5>Preconditons</h5>
	 * <ul>
	 * <li>getState() in {{@link #ACTIVE}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}.
	 * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called
	 * and did not throw an exception.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>None.
	 * </ul>
	 * 
	 * @param lock
	 *            true if state change lock should be held when returning from
	 *            this method.
	 * @exception BundleException
	 *                If the bundle's BundleActivator could not be loaded or
	 *                threw an exception.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle tries to change its own state.
	 */
	protected void suspend(boolean lock) throws BundleException {
		if (state == UNINSTALLED) {
			return;
		}
		beginStateChange();
		try {
			stopWorker(false);
		} finally {
			if (!lock) {
				completeStateChange();
			}
		}
	}

	/**
	 * Update this bundle. If the bundle is {@link #ACTIVE}, the bundle will
	 * be stopped before the update and started after the update successfully
	 * completes.
	 * 
	 * <p>
	 * The following steps are followed to update a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle
	 * is stopped as described in the {@link #stop()}method. If {@link #stop()}
	 * throws an exception, the exception is rethrown terminating the update.
	 * <li>The location for the new version of the bundle is determined from
	 * either the manifest header <code>Bundle-UpdateLocation</code> if
	 * available or the original location.
	 * <li>The location is interpreted in an implementation dependent way
	 * (typically as a URL) and the new version of the bundle is obtained from
	 * the location.
	 * <li>The new version of the bundle is installed. If the framework is
	 * unable to install the new version of the bundle, the original version of
	 * the bundle will be restored and a {@link BundleException}will be thrown
	 * after completion of the remaining steps.
	 * <li>The state of the bundle is set to {@link #INSTALLED}.
	 * <li>If the new version of the bundle was successfully installed, a
	 * {@link BundleEvent}of type {@link BundleEvent#UPDATED}is broadcast.
	 * <li>If the bundle was originally {@link #ACTIVE}, the updated bundle
	 * is started as described in the {@link #start()}method. If {@link #start()}
	 * throws an exception, a {@link FrameworkEvent}of type
	 * {@link FrameworkEvent#ERROR}is broadcast containing the exception.
	 * </ol>
	 * 
	 * <h5>Preconditions</h5>
	 * <ul>
	 * <li>getState() not in {{@link #UNINSTALLED}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED},
	 * {@link #ACTIVE}}.
	 * <li>The bundle has been updated.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED},
	 * {@link #ACTIVE}}.
	 * <li>Original bundle is still used, no update took place.
	 * </ul>
	 * 
	 * @exception BundleException
	 *                If the update fails.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle has been uninstalled or the bundle tries to
	 *                change its own state.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 * @see #stop()
	 * @see #start()
	 */
	public void update() throws BundleException {
		if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
			Debug.println("update location " + bundledata.getLocation()); //$NON-NLS-1$
		}
		framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
		checkValid();
		beginStateChange();
		try {
			final AccessControlContext callerContext = AccessController.getContext();
			//note AdminPermission is checked again after updated bundle is loaded
			updateWorker(new PrivilegedExceptionAction() {
				public Object run() throws BundleException {
					/* compute the update location */
					String updateLocation = bundledata.getLocation();
					if (bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION) != null) {
						updateLocation = (String) bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION);
						if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
							Debug.println("   from location: " + updateLocation); //$NON-NLS-1$
						}
					}
					/* Map the identity to a URLConnection */
					URLConnection source = framework.adaptor.mapLocationToURLConnection(updateLocation);
					/* call the worker */
					updateWorkerPrivileged(source, callerContext);
					return null;
				}
			});
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Update this bundle from an InputStream.
	 * 
	 * <p>
	 * This method performs all the steps listed in {@link #update()}, except
	 * the bundle will be read in through the supplied <code>InputStream</code>,
	 * rather than a <code>URL</code>.
	 * 
	 * @param in
	 *            The InputStream from which to read the new bundle.
	 * @see #update()
	 */
	public void update(final InputStream in) throws BundleException {
		if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
			Debug.println("update location " + bundledata.getLocation()); //$NON-NLS-1$
			Debug.println("   from: " + in); //$NON-NLS-1$
		}
		framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
		checkValid();
		beginStateChange();
		try {
			final AccessControlContext callerContext = AccessController.getContext();
			//note AdminPermission is checked again after updated bundle is loaded
			updateWorker(new PrivilegedExceptionAction() {
				public Object run() throws BundleException {
					/* Map the InputStream to a URLConnection */
					URLConnection source = new BundleSource(in);
					/* call the worker */
					updateWorkerPrivileged(source, callerContext);
					return null;
				}
			});
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Update worker. Assumes the caller has the state change lock.
	 */
	protected void updateWorker(PrivilegedExceptionAction action) throws BundleException {
		boolean bundleActive = false;
		if (!isFragment())
			bundleActive = (state == ACTIVE);
		if (bundleActive) {
			try {
				stopWorker(false);
			} catch (BundleException e) {
				framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
				if (state == ACTIVE) /* if the bundle is still active */{
					throw e;
				}
			}
		}
		try {
			AccessController.doPrivileged(action);
			framework.publishBundleEvent(BundleEvent.UPDATED, this);
		} catch (PrivilegedActionException pae) {
			if (pae.getException() instanceof RuntimeException)
				throw (RuntimeException) pae.getException();
			throw (BundleException) pae.getException();
		} finally {
			if (bundleActive) {
				try {
					startWorker(false);
				} catch (BundleException e) {
					framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
				}
			}
		}
	}

	/**
	 * Update worker. Assumes the caller has the state change lock.
	 */
	protected void updateWorkerPrivileged(URLConnection source, AccessControlContext callerContext) throws BundleException {
		AbstractBundle oldBundle = AbstractBundle.createBundle(bundledata, framework);
		boolean reloaded = false;
		BundleOperation storage = framework.adaptor.updateBundle(this.bundledata, source);
		BundleRepository bundles = framework.getBundles();
		try {
			BundleData newBundleData = storage.begin();
			// Must call framework createBundle to check execution environment.
			final AbstractBundle newBundle = framework.createAndVerifyBundle(newBundleData);
			String[] nativepaths = framework.selectNativeCode(newBundle);
			if (nativepaths != null) {
				bundledata.installNativeCode(nativepaths);
			}
			boolean exporting;
			int st = getState();
			synchronized (bundles) {
				exporting = reload(newBundle);
				manifestLocalization = null;
			}
			// indicate we have loaded from the new version of the bundle
			reloaded = true;
			if (System.getSecurityManager() != null && (bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION)) != 0) {
				// must check for AllPermission before allow a bundle extension to be installed
				hasPermission(new AllPermission());
			}
			try {
				AccessController.doPrivileged(new PrivilegedExceptionAction() {
					public Object run() throws Exception {
						framework.checkAdminPermission(newBundle, AdminPermission.LIFECYCLE);
						return null;
					}
				}, callerContext);
			} catch (PrivilegedActionException e) {
				throw e.getException();
			}
			// send out unresolved events outside synch block (defect #80610)
			if (st == RESOLVED)
				framework.publishBundleEvent(BundleEvent.UNRESOLVED, this);
			storage.commit(exporting);
		} catch (Throwable t) {
			try {
				storage.undo();
				if (reloaded) /*
				 * if we loaded from the new version of the
				 * bundle
				 */{
					synchronized (bundles) {
						reload(oldBundle); /* revert to old version */
					}
				}
			} catch (BundleException ee) {
				/* if we fail to revert then we are in big trouble */
				framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
			}
			if (t instanceof RuntimeException)
				throw (RuntimeException) t;
			if (t instanceof BundleException)
				throw (BundleException) t;
			throw new BundleException(t.getMessage(), t);
		}
	}

	/**
	 * Uninstall this bundle.
	 * <p>
	 * This method removes all traces of the bundle, including any data in the
	 * persistent storage area provided for the bundle by the framework.
	 * 
	 * <p>
	 * The following steps are followed to uninstall a bundle:
	 * <ol>
	 * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
	 * is thrown.
	 * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle
	 * is stopped as described in the {@link #stop()}method. If {@link #stop()}
	 * throws an exception, a {@link FrameworkEvent}of type
	 * {@link FrameworkEvent#ERROR}is broadcast containing the exception.
	 * <li>A {@link BundleEvent}of type {@link BundleEvent#UNINSTALLED}is
	 * broadcast.
	 * <li>The state of the bundle is set to {@link #UNINSTALLED}.
	 * <li>The bundle and the persistent storage area provided for the bundle
	 * by the framework, if any, is removed.
	 * </ol>
	 * 
	 * <h5>Preconditions</h5>
	 * <ul>
	 * <li>getState() not in {{@link #UNINSTALLED}}.
	 * </ul>
	 * <h5>Postconditons, no exceptions thrown</h5>
	 * <ul>
	 * <li>getState() in {{@link #UNINSTALLED}}.
	 * <li>The bundle has been uninstalled.
	 * </ul>
	 * <h5>Postconditions, when an exception is thrown</h5>
	 * <ul>
	 * <li>getState() not in {{@link #UNINSTALLED}}.
	 * <li>The Bundle has not been uninstalled.
	 * </ul>
	 * 
	 * @exception BundleException
	 *                If the uninstall failed.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle has been uninstalled or the bundle tries to
	 *                change its own state.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 * @see #stop()
	 */
	public void uninstall() throws BundleException {
		if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
			Debug.println("uninstall location: " + bundledata.getLocation()); //$NON-NLS-1$
		}
		framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
		checkValid();
		beginStateChange();
		try {
			uninstallWorker(new PrivilegedExceptionAction() {
				public Object run() throws BundleException {
					uninstallWorkerPrivileged();
					return null;
				}
			});
		} finally {
			completeStateChange();
		}
	}

	/**
	 * Uninstall worker. Assumes the caller has the state change lock.
	 */
	protected void uninstallWorker(PrivilegedExceptionAction action) throws BundleException {
		boolean bundleActive = false;
		if (!isFragment())
			bundleActive = (state == ACTIVE);
		if (bundleActive) {
			try {
				stopWorker(true);
			} catch (BundleException e) {
				framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
			}
		}
		try {
			AccessController.doPrivileged(action);
		} catch (PrivilegedActionException pae) {
			if (bundleActive) /* if we stopped the bundle */{
				try {
					startWorker(false);
				} catch (BundleException e) {
					/*
					 * if we fail to start the original bundle then we are in
					 * big trouble
					 */
					framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
				}
			}
			throw (BundleException) pae.getException();
		}
		framework.publishBundleEvent(BundleEvent.UNINSTALLED, this);
	}

	/**
	 * Uninstall worker. Assumes the caller has the state change lock.
	 */
	protected void uninstallWorkerPrivileged() throws BundleException {
		boolean unloaded = false;
		//cache the bundle's headers
		getHeaders();
		BundleOperation storage = framework.adaptor.uninstallBundle(this.bundledata);
		BundleRepository bundles = framework.getBundles();
		try {
			storage.begin();
			boolean exporting;
			int st = getState();
			synchronized (bundles) {
				bundles.remove(this); /* remove before calling unload */
				exporting = unload();
			}
			// send out unresolved events outside synch block (defect #80610)
			if (st == RESOLVED)
				framework.publishBundleEvent(BundleEvent.UNRESOLVED, this);
			unloaded = true;
			storage.commit(exporting);
			close();
		} catch (BundleException e) {
			try {
				storage.undo();
				if (unloaded) /* if we unloaded the bundle */{
					synchronized (bundles) {
						load(); /* reload the bundle */
						bundles.add(this);
					}
				}
			} catch (BundleException ee) {
				/*
				 * if we fail to load the original bundle then we are in big
				 * trouble
				 */
				framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
			}
			throw e;
		}
	}

	/**
	 * Return the bundle's manifest headers and values from the manifest's
	 * preliminary section. That is all the manifest's headers and values prior
	 * to the first blank line.
	 * 
	 * <p>
	 * Manifest header names are case-insensitive. The methods of the returned
	 * <code>Dictionary</code> object will operate on header names in a
	 * case-insensitive manner.
	 * 
	 * <p>
	 * For example, the following manifest headers and values are included if
	 * they are present in the manifest:
	 * 
	 * <pre>
	 *  Bundle-Name
	 *  Bundle-Vendor
	 *  Bundle-Version
	 *  Bundle-Description
	 *  Bundle-DocURL
	 *  Bundle-ContactAddress
	 * </pre>
	 * 
	 * <p>
	 * This method will continue to return this information when the bundle is
	 * in the {@link #UNINSTALLED}state.
	 * 
	 * @return A <code>Dictionary</code> object containing the bundle's
	 *         manifest headers and values.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 */
	public Dictionary getHeaders() {
		return getHeaders(Locale.getDefault().toString());
	}

	/**
	 * Returns this bundle's Manifest headers and values. This method returns
	 * all the Manifest headers and values from the main section of the
	 * bundle's Manifest file; that is, all lines prior to the first blank
	 * line.
	 * 
	 * <p>
	 * Manifest header names are case-insensitive. The methods of the returned
	 * <tt>Dictionary</tt> object will operate on header names in a
	 * case-insensitive manner.
	 * 
	 * If a Manifest header begins with a '%', it will be evaluated with the
	 * specified properties file for the specied Locale.
	 * 
	 * <p>
	 * For example, the following Manifest headers and values are included if
	 * they are present in the Manifest file:
	 * 
	 * <pre>
	 *  Bundle-Name
	 *  Bundle-Vendor
	 *  Bundle-Version
	 *  Bundle-Description
	 *  Bundle-DocURL
	 *  Bundle-ContactAddress
	 * </pre>
	 * 
	 * <p>
	 * This method will continue to return Manifest header information while
	 * this bundle is in the <tt>UNINSTALLED</tt> state.
	 * 
	 * @return A <tt>Dictionary</tt> object containing this bundle's Manifest
	 *         headers and values.
	 * 
	 * @exception java.lang.SecurityException
	 *                If the caller does not have the <tt>AdminPermission</tt>,
	 *                and the Java Runtime Environment supports permissions.
	 */
	public Dictionary getHeaders(String localeString) {
		framework.checkAdminPermission(this, AdminPermission.METADATA);
		try {
			initializeManifestLocalization();
		} catch (BundleException e) {
			framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
			// return an empty dictinary.
			return new Hashtable();
		}
		return manifestLocalization.getHeaders(localeString);
	}

	/**
	 * Retrieve the bundle's unique identifier, which the framework assigned to
	 * this bundle when it was installed.
	 * 
	 * <p>
	 * The unique identifier has the following attributes:
	 * <ul>
	 * <li>It is unique and persistent.
	 * <li>The identifier is a long.
	 * <li>Once its value is assigned to a bundle, that value is not reused
	 * for another bundle, even after the bundle is uninstalled.
	 * <li>Its value does not change as long as the bundle remains installed.
	 * <li>Its value does not change when the bundle is updated
	 * </ul>
	 * 
	 * <p>
	 * This method will continue to return the bundle's unique identifier when
	 * the bundle is in the {@link #UNINSTALLED}state.
	 * 
	 * @return This bundle's unique identifier.
	 */
	public long getBundleId() {
		return (bundledata.getBundleID());
	}

	/**
	 * Retrieve the location identifier of the bundle. This is typically the
	 * location passed to
	 * {@link BundleContextImpl#installBundle(String) BundleContext.installBundle}when the
	 * bundle was installed. The location identifier of the bundle may change
	 * during bundle update. Calling this method while framework is updating
	 * the bundle results in undefined behavior.
	 * 
	 * <p>
	 * This method will continue to return the bundle's location identifier
	 * when the bundle is in the {@link #UNINSTALLED}state.
	 * 
	 * @return A string that is the location identifier of the bundle.
	 * @exception java.lang.SecurityException
	 *                If the caller does not have {@link AdminPermission}
	 *                permission and the Java runtime environment supports
	 *                permissions.
	 */
	public String getLocation() {
		framework.checkAdminPermission(this, AdminPermission.METADATA);
		return (bundledata.getLocation());
	}

	/**
	 * Determine whether the bundle has the requested permission.
	 * 
	 * <p>
	 * If the Java runtime environment does not supports permissions this
	 * method always returns <code>true</code>. The permission parameter is
	 * of type <code>Object</code> to avoid referencing the <code>java.security.Permission</code>
	 * class directly. This is to allow the framework to be implemented in Java
	 * environments which do not support permissions.
	 * 
	 * @param permission
	 *            The requested permission.
	 * @return <code>true</code> if the bundle has the requested permission
	 *         or <code>false</code> if the bundle does not have the
	 *         permission or the permission parameter is not an <code>instanceof java.security.Permission</code>.
	 * @exception java.lang.IllegalStateException
	 *                If the bundle has been uninstalled.
	 */
	public boolean hasPermission(Object permission) {
		checkValid();
		if (domain != null) {
			if (permission instanceof Permission) {
				SecurityManager sm = System.getSecurityManager();
				if (sm instanceof FrameworkSecurityManager) {
					/*
					 * If the FrameworkSecurityManager is active, we need to do checks the "right" way.
					 * We can exploit our knowledge that the security context of FrameworkSecurityManager
					 * is an AccessControlContext to invoke it properly with the ProtectionDomain.
					 */
					AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] {domain});
					try {
						sm.checkPermission((Permission) permission, acc);
						return true;
					} catch (Exception e) {
						return false;
					}
				}
				return domain.implies((Permission) permission);
			}
			return false;
		}
		return true;
	}

	/**
	 * This method marks the bundle's state as changing so that other calls to
	 * start/stop/suspend/update/uninstall can wait until the state change is
	 * complete. If stateChanging is non-null when this method is called, we
	 * will wait for the state change to complete. If the timeout expires
	 * without changing state (this may happen if the state change is back up
	 * our call stack), a BundleException is thrown so that we don't wait
	 * forever.
	 * 
	 * A call to this method should be immediately followed by a try block
	 * whose finally block calls completeStateChange().
	 * 
	 * beginStateChange(); try { // change the bundle's state here... } finally {
	 * completeStateChange(); }
	 * 
	 * @exception org.osgi.framework.BundleException
	 *                if the bundles state is still changing after waiting for
	 *                the timeout.
	 */
	protected void beginStateChange() throws BundleException {
		synchronized (statechangeLock) {
			boolean doubleFault = false;
			while (true) {
				if (stateChanging == null) {
					stateChanging = Thread.currentThread();
					return;
				}
				if (doubleFault || (stateChanging == Thread.currentThread())) {
					throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION", getLocation(), stateChanging.getName())); //$NON-NLS-1$
				}
				try {
					if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
						Debug.println(" Waiting for state to change in bundle " + this); //$NON-NLS-1$
					}
					long start = 0;
					if (Debug.DEBUG)
						start = System.currentTimeMillis();
					statechangeLock.wait(5000); /*
					 * wait for other thread to
					 * finish changing state
					 */
					if (Debug.DEBUG) {
						long end = System.currentTimeMillis();
						if (end - start > 0) {
							System.out.println("Waiting... : " + getSymbolicName() + ' ' + (end - start)); //$NON-NLS-1$
						}
					}
				} catch (InterruptedException e) {
				}
				doubleFault = true;
			}
		}
	}

	/**
	 * This method completes the bundle state change by setting stateChanging
	 * to null and notifying one waiter that the state change has completed.
	 */
	protected void completeStateChange() {
		synchronized (statechangeLock) {
			if (stateChanging != null) {
				stateChanging = null;
				statechangeLock.notify(); /*
				 * notify one waiting thread that the
				 * state change is complete
				 */
			}
		}
	}

	/**
	 * Return a string representation of this bundle.
	 * 
	 * @return String
	 */
	public String toString() {
		return (bundledata.getLocation() + " [" + getBundleId() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Answers an integer indicating the relative positions of the receiver and
	 * the argument in the natural order of elements of the receiver's class.
	 * 
	 * @return int which should be <0 if the receiver should sort before the
	 *         argument, 0 if the receiver should sort in the same position as
	 *         the argument, and >0 if the receiver should sort after the
	 *         argument.
	 * @param obj
	 *            another Bundle an object to compare the receiver to
	 * @exception ClassCastException
	 *                if the argument can not be converted into something
	 *                comparable with the receiver.
	 */
	public int compareTo(Object obj) {
		int slcomp = getStartLevel() - ((AbstractBundle) obj).getStartLevel();
		if (slcomp != 0) {
			return slcomp;
		}
		long idcomp = getBundleId() - ((AbstractBundle) obj).getBundleId();
		return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0);
	}

	/**
	 * This method checks that the bundle is not uninstalled. If the bundle is
	 * uninstalled, an IllegalStateException is thrown.
	 * 
	 * @exception java.lang.IllegalStateException
	 *                If the bundle is uninstalled.
	 */
	protected void checkValid() {
		if (state == UNINSTALLED) {
			throw new IllegalStateException(Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION", getLocation())); //$NON-NLS-1$
		}
	}

	protected void checkResourcePermission() {
		SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
			if (resourcePermission == null)
				resourcePermission = new BundleResourcePermission(getBundleId());
			sm.checkPermission(resourcePermission);
		}
	}

	/**
	 * Get the bundle's ProtectionDomain.
	 * 
	 * @return bundle's ProtectionDomain.
	 */
	protected BundleProtectionDomain getProtectionDomain() {
		return domain;
	}

	/**
	 * The bundle must unresolve the permissions in these packages.
	 * 
	 * @param refreshedBundles
	 *            A list of bundles which have been refreshed as a result
	 *            of a packageRefresh
	 */
	protected void unresolvePermissions(AbstractBundle[] refreshedBundles) {
		if (domain != null) {
			BundlePermissionCollection collection = (BundlePermissionCollection) domain.getPermissions();
			if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
				Debug.println("Unresolving permissions in bundle " + this); //$NON-NLS-1$
			}
			collection.unresolvePermissions(refreshedBundles);
		}
	}

	protected Bundle[] getFragments() {
		checkValid();
		return null;
	}

	protected boolean isFragment() {
		return false;
	}

	protected BundleLoaderProxy[] getHosts() {
		checkValid();
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.framework.Bundle#findClass(java.lang.String)
	 */
	public Class loadClass(String classname) throws ClassNotFoundException {
		return loadClass(classname, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.framework.Bundle#getResourcePaths(java.lang.String)
	 */
	public Enumeration getEntryPaths(final String path) {
		try {
			framework.checkAdminPermission(this, AdminPermission.RESOURCE);
		} catch (SecurityException e) {
			return null;
		}
		checkValid();
		if (bundledata == null) {
			if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
				Debug.println("Bundle.getResourcePaths(" + path + ") called when bundledata == null: " + this); //$NON-NLS-1$ //$NON-NLS-2$
				Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
			}
			return (null);
		}
		return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				return bundledata.getEntryPaths(path);
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.osgi.framework.Bundle#getFile(java.lang.String)
	 */
	public URL getEntry(String fileName) {
		try {
			framework.checkAdminPermission(this, AdminPermission.RESOURCE);
		} catch (SecurityException e) {
			return null;
		}
		checkValid();
		if (bundledata == null) {
			if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
				Debug.println("Bundle.getFile(" + fileName + ") called when bundledata == null: " + this); //$NON-NLS-1$ //$NON-NLS-2$
				Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
			}
			return (null);
		}
		if (System.getSecurityManager() == null)
			return bundledata.getEntry(fileName);
		final String ffileName = fileName;
		return (URL) AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				return bundledata.getEntry(ffileName);
			}
		});
	}

	public String getSymbolicName() {
		return bundledata.getSymbolicName();
	}

	public long getLastModified() {
		return bundledata.getLastModified();
	}

	public BundleData getBundleData() {
		return bundledata;
	}

	public Version getVersion() {
		return bundledata.getVersion();
	}

	protected BundleDescription getBundleDescription() {
		return framework.adaptor.getState().getBundle(getBundleId());
	}

	protected int getStartLevel() {
		return bundledata.getStartLevel();
	}

	protected abstract BundleLoader getBundleLoader();

	/**
	 * Mark this bundle as resolved.
	 */
	protected void resolve() {
		if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
			if ((state & (INSTALLED)) == 0) {
				Debug.println("Bundle.resolve called when state != INSTALLED: " + this); //$NON-NLS-1$
				Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
			}
		}
		if (state == INSTALLED) {
			state = RESOLVED;
			// Do not publish RESOLVED event here.  This is done by caller 
			// to resolve if appropriate.
		}
	}

	/**
	 * Return the current context for this bundle.
	 * 
	 * @return BundleContext for this bundle.
	 */
	abstract protected BundleContextImpl getContext();

	protected String getResolutionFailureMessage() {
		String defaultMessage = Msg.formatter.getString("BUNDLE_UNRESOLVED_EXCEPTION"); //$NON-NLS-1$
		// don't spend time if debug info is not needed
		if (!Debug.DEBUG) {
			return defaultMessage;
		}
		if (runtimeResolveError != null) {
			return runtimeResolveError; // do not null this field out until a successful resolve is done.
		}
		BundleDescription bundleDescription = getBundleDescription();
		if (bundleDescription == null) {
			return defaultMessage;
		}
		// just a sanity check - this would be an inconsistency between the
		// framework and the state
		if (bundleDescription.isResolved()) {
			throw new IllegalStateException(Msg.formatter.getString("BUNDLE_UNRESOLVED_STATE_CONFLICT")); //$NON-NLS-1$
		}
		VersionConstraint[] unsatisfied = framework.adaptor.getPlatformAdmin().getStateHelper().getUnsatisfiedConstraints(bundleDescription);
		if (unsatisfied.length == 0) {
			return Msg.formatter.getString("BUNDLE_UNRESOLVED_NOT_CHOSEN_EXCEPTION"); //$NON-NLS-1$
		}
		StringBuffer missing = new StringBuffer();
		for (int i = 0; i < unsatisfied.length; i++) {
			if (unsatisfied[i] instanceof ImportPackageSpecification) {
				missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_PACKAGE", toString(unsatisfied[i]))); //$NON-NLS-1$
			} else if (unsatisfied[i] instanceof HostSpecification) {
				missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_HOST", toString(unsatisfied[i]))); //$NON-NLS-1$
			} else {
				missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_BUNDLE", toString(unsatisfied[i]))); //$NON-NLS-1$
			}
			missing.append(',');
		}
		missing.deleteCharAt(missing.length() - 1);
		return Msg.formatter.getString("BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION", missing.toString()); //$NON-NLS-1$
	}

	private String toString(VersionConstraint constraint) {
		org.eclipse.osgi.service.resolver.VersionRange versionRange = constraint.getVersionRange();
		if (versionRange == null)
			return constraint.getName();
		return constraint.getName() + '_' + versionRange;
	}

	public int getKeyHashCode() {
		return (int) getBundleId();
	}

	public boolean compare(KeyedElement other) {
		return getBundleId() == ((AbstractBundle) other).getBundleId();
	}

	public Object getKey() {
		return new Long(getBundleId());
	}

	/* This method is used by the Bundle Localization Service to obtain
	 * a ResourceBundle that resides in a bundle.  This is not an OSGi
	 * defined method for org.osgi.framework.Bundle
	 * 
	 */
	public ResourceBundle getResourceBundle(String localeString) {
		try {
			initializeManifestLocalization();
		} catch (BundleException ex) {
			return (null);
		}
		if (localeString == null) {
			localeString = Locale.getDefault().toString();
		}
		return manifestLocalization.getResourceBundle(localeString);
	}

	private void initializeManifestLocalization() throws BundleException {
		if (manifestLocalization == null) {
			Dictionary rawHeaders;
			rawHeaders = bundledata.getManifest();
			manifestLocalization = new ManifestLocalization(this, rawHeaders);
		}
	}

	public boolean testStateChanging(Object thread) {
		return stateChanging == thread;
	}

	public Object getStateChangeLock() {
		return statechangeLock;
	}

	public Thread getStateChanging() {
		return stateChanging;
	}
}

Back to the top