1 /******************************************************************************* 2 * Copyright (c) 2003, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Rob Harrop - SpringSource Inc. (bug 247522) 14 *******************************************************************************/ 15 package org.eclipse.osgi.internal.resolver; 16 17 import java.io.BufferedInputStream; 18 import java.io.DataInputStream; 19 import java.io.File; 20 import java.io.IOException; 21 import java.lang.reflect.Constructor; 22 import java.nio.charset.StandardCharsets; 23 import java.security.AccessController; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.Dictionary; 27 import java.util.HashMap; 28 import java.util.Hashtable; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import org.eclipse.osgi.framework.util.ObjectPool; 34 import org.eclipse.osgi.framework.util.SecureAction; 35 import org.eclipse.osgi.service.resolver.BaseDescription; 36 import org.eclipse.osgi.service.resolver.BundleDescription; 37 import org.eclipse.osgi.service.resolver.BundleSpecification; 38 import org.eclipse.osgi.service.resolver.DisabledInfo; 39 import org.eclipse.osgi.service.resolver.ExportPackageDescription; 40 import org.eclipse.osgi.service.resolver.GenericDescription; 41 import org.eclipse.osgi.service.resolver.GenericSpecification; 42 import org.eclipse.osgi.service.resolver.ImportPackageSpecification; 43 import org.eclipse.osgi.service.resolver.NativeCodeSpecification; 44 import org.eclipse.osgi.service.resolver.StateWire; 45 import org.eclipse.osgi.service.resolver.VersionRange; 46 import org.osgi.framework.Constants; 47 import org.osgi.framework.InvalidSyntaxException; 48 import org.osgi.framework.Version; 49 50 /** 51 * This class is internally threadsafe and supports client locking. Clients must <strong>not</strong> hold the monitor for 52 * any {@link StateImpl} or {@link BundleDescriptionImpl} object when calling into the public methods of this class to prevent 53 * possible deadlock. 54 */ 55 final class StateReader { 56 public static final String STATE_FILE = ".state"; //$NON-NLS-1$ 57 public static final String LAZY_FILE = ".lazy"; //$NON-NLS-1$ 58 private static final int BUFFER_SIZE_LAZY = 4096; 59 private static final int BUFFER_SIZE_FULLYREAD = 16384; 60 private static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction()); 61 62 // objectTable will be a hashmap of objects. The objects will be things 63 // like BundleDescription, ExportPackageDescription, Version etc.. The integer 64 // index value will be used in the cache to allow cross-references in the 65 // cached state. 66 final Map<Integer, Object> objectTable = Collections.synchronizedMap(new HashMap<Integer, Object>()); 67 68 private volatile File stateFile; 69 private volatile File lazyFile; 70 71 private volatile boolean lazyLoad = true; 72 private volatile int numBundles; 73 private volatile boolean accessedFlag = false; 74 75 public static final byte STATE_CACHE_VERSION = 38; 76 public static final byte NULL = 0; 77 public static final byte OBJECT = 1; 78 public static final byte INDEX = 2; 79 public static final byte LONG_STRING = 3; 80 StateReader()81 public StateReader() //TODO - deprecated 82 { 83 lazyLoad = false; 84 } 85 StateReader(File stateDirectory)86 public StateReader(File stateDirectory) { 87 if (!stateDirectory.exists()) 88 stateDirectory.mkdirs(); 89 this.stateFile = new File(stateDirectory, STATE_FILE); 90 this.lazyFile = new File(stateDirectory, LAZY_FILE); 91 this.lazyLoad = false; 92 } 93 StateReader(File stateFile, File lazyFile, boolean lazyLoad)94 public StateReader(File stateFile, File lazyFile, boolean lazyLoad) { 95 this.stateFile = stateFile; 96 this.lazyFile = lazyFile; 97 this.lazyLoad = lazyLoad; 98 } 99 addToObjectTable(Object object, int index)100 private void addToObjectTable(Object object, int index) { 101 objectTable.put(Integer.valueOf(index), object); 102 } 103 getFromObjectTable(int index)104 private Object getFromObjectTable(int index) { 105 Object result = objectTable.get(Integer.valueOf(index)); 106 if (result == null) 107 throw new IllegalStateException("Expected to find an object at table index: " + index); //$NON-NLS-1$ 108 return result; 109 } 110 readState(StateImpl state, long expectedTimestamp)111 private boolean readState(StateImpl state, long expectedTimestamp) throws IOException { 112 DataInputStream in = new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(stateFile), BUFFER_SIZE_FULLYREAD)); 113 DataInputStream lazyIn = null; 114 try { 115 if (in.readByte() != STATE_CACHE_VERSION) 116 return false; 117 byte tag = readTag(in); 118 if (tag != OBJECT) 119 return false; 120 int index = in.readInt(); 121 long timestampRead = in.readLong(); 122 if (expectedTimestamp >= 0 && timestampRead != expectedTimestamp) 123 return false; 124 addToObjectTable(state, index); 125 // read the platform property keys 126 String[] platformPropKeys = (String[]) readPlatformProp(in); 127 state.addPlatformPropertyKeys(platformPropKeys); 128 int numSets = in.readInt(); 129 Dictionary<?, ?>[] platformProps = new Dictionary[numSets]; 130 for (int i = 0; i < numSets; i++) { 131 Hashtable<Object, Object> props = new Hashtable<>(platformPropKeys.length); 132 int numProps = in.readInt(); 133 for (int j = 0; j < numProps; j++) { 134 Object value = readPlatformProp(in); 135 if (value != null && j < platformPropKeys.length) 136 props.put(platformPropKeys[j], value); 137 } 138 platformProps[i] = props; 139 } 140 state.setPlatformProperties(platformProps, false); 141 numBundles = in.readInt(); 142 for (int i = 0; i < numBundles; i++) { 143 BundleDescriptionImpl bundle = readBundleDescription(in); 144 state.basicAddBundle(bundle); 145 if (bundle.isResolved()) 146 state.addResolvedBundle(bundle); 147 } 148 // read the DisabledInfos 149 int numDisableInfos = in.readInt(); 150 for (int i = 0; i < numDisableInfos; i++) { 151 DisabledInfo info = readDisabledInfo(in); 152 state.addDisabledInfo(info); 153 } 154 state.setTimeStamp(timestampRead); 155 state.setResolved(in.readBoolean()); 156 if (lazyLoad) 157 return true; 158 //read in from lazy data file; using the fully read buffer size because we are reading the complete file in. 159 lazyIn = new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(lazyFile), BUFFER_SIZE_FULLYREAD)); 160 for (int i = 0; i < numBundles; i++) 161 readBundleDescriptionLazyData(lazyIn, 0); 162 } finally { 163 in.close(); 164 if (lazyIn != null) 165 try { 166 lazyIn.close(); 167 } catch (IOException e) { 168 // ignore 169 } 170 } 171 return true; 172 } 173 readStateDeprecated(StateImpl state, DataInputStream in, long expectedTimestamp)174 private boolean readStateDeprecated(StateImpl state, DataInputStream in, long expectedTimestamp) throws IOException { 175 if (in.readByte() != STATE_CACHE_VERSION) 176 return false; 177 byte tag = readTag(in); 178 if (tag != OBJECT) 179 return false; 180 int index = in.readInt(); 181 long timestampRead = in.readLong(); 182 if (expectedTimestamp >= 0 && timestampRead != expectedTimestamp) 183 return false; 184 addToObjectTable(state, index); 185 // read the platform property keys 186 String[] platformPropKeys = (String[]) readPlatformProp(in); 187 state.addPlatformPropertyKeys(platformPropKeys); 188 int numSets = in.readInt(); 189 Dictionary<?, ?>[] platformProps = new Dictionary[numSets]; 190 for (int i = 0; i < numSets; i++) { 191 Hashtable<Object, Object> props = new Hashtable<>(platformPropKeys.length); 192 int numProps = in.readInt(); 193 for (int j = 0; j < numProps; j++) { 194 Object value = readPlatformProp(in); 195 if (value != null && j < platformPropKeys.length) 196 props.put(platformPropKeys[j], value); 197 } 198 platformProps[i] = props; 199 } 200 state.setPlatformProperties(platformProps); 201 numBundles = in.readInt(); 202 if (numBundles == 0) 203 return true; 204 for (int i = 0; i < numBundles; i++) { 205 BundleDescriptionImpl bundle = readBundleDescription(in); 206 state.basicAddBundle(bundle); 207 if (bundle.isResolved()) 208 state.addResolvedBundle(bundle); 209 } 210 state.setTimeStamp(timestampRead); 211 state.setResolved(in.readBoolean()); 212 in.readInt(); // skip past the old offset 213 if (lazyLoad) 214 return true; 215 for (int i = 0; i < numBundles; i++) 216 readBundleDescriptionLazyData(in, 0); 217 return true; 218 } 219 readPlatformProp(DataInputStream in)220 private Object readPlatformProp(DataInputStream in) throws IOException { 221 byte type = in.readByte(); 222 if (type == NULL) 223 return null; 224 int num = in.readInt(); 225 if (num == 1) 226 return readString(in, false); 227 String[] result = new String[num]; 228 for (int i = 0; i < result.length; i++) 229 result[i] = readString(in, false); 230 return result; 231 } 232 readBundleDescription(DataInputStream in)233 private BundleDescriptionImpl readBundleDescription(DataInputStream in) throws IOException { 234 byte tag = readTag(in); 235 if (tag == NULL) 236 return null; 237 if (tag == INDEX) 238 return (BundleDescriptionImpl) getFromObjectTable(in.readInt()); 239 // first read in non-lazy loaded data 240 BundleDescriptionImpl result = new BundleDescriptionImpl(); 241 addToObjectTable(result, in.readInt()); 242 243 result.setBundleId(in.readLong()); 244 readBaseDescription(result, in); 245 result.setLazyDataOffset(in.readInt()); 246 result.setLazyDataSize(in.readInt()); 247 result.setStateBit(BundleDescriptionImpl.RESOLVED, in.readBoolean()); 248 result.setStateBit(BundleDescriptionImpl.SINGLETON, in.readBoolean()); 249 result.setStateBit(BundleDescriptionImpl.HAS_DYNAMICIMPORT, in.readBoolean()); 250 result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, in.readBoolean()); 251 result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, in.readBoolean()); 252 String[] mandatory = readList(in); 253 if (mandatory != null) 254 result.setDirective(Constants.MANDATORY_DIRECTIVE, mandatory); 255 result.setAttributes(readMap(in)); 256 result.setArbitraryDirectives(readMap(in)); 257 result.setHost(readHostSpec(in)); 258 259 // set the bundle dependencies from imports and requires and hosts. 260 int numDeps = in.readInt(); 261 if (numDeps > 0) { 262 BundleDescription[] deps = new BundleDescription[numDeps]; 263 for (int i = 0; i < numDeps; i++) 264 deps[i] = readBundleDescription(in); 265 result.addDependencies(deps, false); // no need to check dups; we already know there are none when we resolved (bug 152900) 266 } 267 // No need to set the dependencies between fragment and hosts; that was already done in the above loop (bug 152900) 268 // but we do need to set the dependencies between hosts and fragment. 269 HostSpecificationImpl hostSpec = (HostSpecificationImpl) result.getHost(); 270 if (hostSpec != null) { 271 BundleDescription[] hosts = hostSpec.getHosts(); 272 if (hosts != null) { 273 for (BundleDescription host : hosts) { 274 ((BundleDescriptionImpl) host).addDependency(result, false); 275 } 276 } 277 } 278 // the rest is lazy loaded data 279 result.setFullyLoaded(false); 280 return result; 281 } 282 283 @SuppressWarnings("unchecked") readBundleDescriptionLazyData(DataInputStream in, int skip)284 private BundleDescriptionImpl readBundleDescriptionLazyData(DataInputStream in, int skip) throws IOException { 285 if (skip > 0) 286 in.skipBytes(skip); 287 int index = in.readInt(); 288 BundleDescriptionImpl result = (BundleDescriptionImpl) getFromObjectTable(index); 289 if (result.isFullyLoaded()) { 290 in.skipBytes(result.getLazyDataSize() - 4); // skip to the end subtract 4 for the int read already 291 return result; 292 } 293 294 result.setLocation(readString(in, false)); 295 result.setPlatformFilter(readString(in, false)); 296 297 int exportCount = in.readInt(); 298 if (exportCount > 0) { 299 ExportPackageDescription[] exports = new ExportPackageDescription[exportCount]; 300 for (int i = 0; i < exports.length; i++) 301 exports[i] = readExportPackageDesc(in); 302 result.setExportPackages(exports); 303 } 304 305 int importCount = in.readInt(); 306 if (importCount > 0) { 307 ImportPackageSpecification[] imports = new ImportPackageSpecification[importCount]; 308 for (int i = 0; i < imports.length; i++) 309 imports[i] = readImportPackageSpec(in); 310 result.setImportPackages(imports); 311 } 312 313 int requiredBundleCount = in.readInt(); 314 if (requiredBundleCount > 0) { 315 BundleSpecification[] requiredBundles = new BundleSpecification[requiredBundleCount]; 316 for (int i = 0; i < requiredBundles.length; i++) 317 requiredBundles[i] = readBundleSpec(in); 318 result.setRequiredBundles(requiredBundles); 319 } 320 321 int selectedCount = in.readInt(); 322 if (selectedCount > 0) { 323 ExportPackageDescription[] selected = new ExportPackageDescription[selectedCount]; 324 for (int i = 0; i < selected.length; i++) 325 selected[i] = readExportPackageDesc(in); 326 result.setSelectedExports(selected); 327 } 328 329 int substitutedCount = in.readInt(); 330 if (substitutedCount > 0) { 331 ExportPackageDescription[] selected = new ExportPackageDescription[substitutedCount]; 332 for (int i = 0; i < selected.length; i++) 333 selected[i] = readExportPackageDesc(in); 334 result.setSubstitutedExports(selected); 335 } 336 337 int resolvedCount = in.readInt(); 338 if (resolvedCount > 0) { 339 ExportPackageDescription[] resolved = new ExportPackageDescription[resolvedCount]; 340 for (int i = 0; i < resolved.length; i++) 341 resolved[i] = readExportPackageDesc(in); 342 result.setResolvedImports(resolved); 343 } 344 345 int resolvedRequiredCount = in.readInt(); 346 if (resolvedRequiredCount > 0) { 347 BundleDescription[] resolved = new BundleDescription[resolvedRequiredCount]; 348 for (int i = 0; i < resolved.length; i++) 349 resolved[i] = readBundleDescription(in); 350 result.setResolvedRequires(resolved); 351 } 352 353 int eeCount = in.readInt(); 354 if (eeCount > 0) { 355 String[] ee = new String[eeCount]; 356 for (int i = 0; i < ee.length; i++) 357 ee[i] = readString(in, false); 358 result.setExecutionEnvironments(ee); 359 } 360 361 int dynamicPkgCnt = in.readInt(); 362 if (dynamicPkgCnt > 0) { 363 HashMap<String, Long> dynamicStamps = new HashMap<>(dynamicPkgCnt); 364 for (int i = 0; i < dynamicPkgCnt; i++) { 365 String pkg = readString(in, false); 366 Long stamp = new Long(in.readLong()); 367 dynamicStamps.put(pkg, stamp); 368 } 369 result.setDynamicStamps(dynamicStamps); 370 } 371 372 int genericCapCnt = in.readInt(); 373 if (genericCapCnt > 0) { 374 GenericDescription[] capabilities = new GenericDescription[genericCapCnt]; 375 for (int i = 0; i < capabilities.length; i++) 376 capabilities[i] = readGenericDescription(in); 377 result.setGenericCapabilities(capabilities); 378 } 379 380 int genericReqCnt = in.readInt(); 381 if (genericReqCnt > 0) { 382 GenericSpecification[] reqs = new GenericSpecification[genericReqCnt]; 383 for (int i = 0; i < reqs.length; i++) 384 reqs[i] = readGenericSpecification(in); 385 result.setGenericRequires(reqs); 386 } 387 388 int selectedGenCapCnt = in.readInt(); 389 if (selectedGenCapCnt > 0) { 390 GenericDescription[] capabilities = new GenericDescription[selectedGenCapCnt]; 391 for (int i = 0; i < capabilities.length; i++) 392 capabilities[i] = readGenericDescription(in); 393 result.setSelectedCapabilities(capabilities); 394 } 395 396 int resolvedGenCapCnt = in.readInt(); 397 if (resolvedGenCapCnt > 0) { 398 GenericDescription[] capabilities = new GenericDescription[resolvedGenCapCnt]; 399 for (int i = 0; i < capabilities.length; i++) 400 capabilities[i] = readGenericDescription(in); 401 result.setResolvedCapabilities(capabilities); 402 } 403 404 result.setNativeCodeSpecification(readNativeCode(in)); 405 406 @SuppressWarnings("rawtypes") 407 Map raw = readMap(in); 408 result.setStateWires(raw); 409 410 result.setFullyLoaded(true); // set fully loaded before setting the dependencies 411 // No need to add bundle dependencies for hosts, imports or requires; 412 // This is done by readBundleDescription 413 return result; 414 } 415 readBundleSpec(DataInputStream in)416 private BundleSpecificationImpl readBundleSpec(DataInputStream in) throws IOException { 417 byte tag = readTag(in); 418 if (tag == NULL) 419 return null; 420 if (tag == INDEX) 421 return (BundleSpecificationImpl) getFromObjectTable(in.readInt()); 422 BundleSpecificationImpl result = new BundleSpecificationImpl(); 423 int tableIndex = in.readInt(); 424 addToObjectTable(result, tableIndex); 425 readVersionConstraint(result, in); 426 result.setSupplier(readBundleDescription(in)); 427 result.setExported(in.readBoolean()); 428 result.setOptional(in.readBoolean()); 429 result.setAttributes(readMap(in)); 430 result.setArbitraryDirectives(readMap(in)); 431 return result; 432 } 433 readExportPackageDesc(DataInputStream in)434 private ExportPackageDescriptionImpl readExportPackageDesc(DataInputStream in) throws IOException { 435 byte tag = readTag(in); 436 if (tag == NULL) 437 return null; 438 if (tag == INDEX) 439 return (ExportPackageDescriptionImpl) getFromObjectTable(in.readInt()); 440 ExportPackageDescriptionImpl exportPackageDesc = new ExportPackageDescriptionImpl(); 441 int tableIndex = in.readInt(); 442 addToObjectTable(exportPackageDesc, tableIndex); 443 readBaseDescription(exportPackageDesc, in); 444 exportPackageDesc.setExporter(readBundleDescription(in)); 445 exportPackageDesc.setAttributes(readMap(in)); 446 exportPackageDesc.setDirectives(readMap(in)); 447 exportPackageDesc.setArbitraryDirectives(readMap(in)); 448 exportPackageDesc.setFragmentDeclaration(readExportPackageDesc(in)); 449 return exportPackageDesc; 450 } 451 readDisabledInfo(DataInputStream in)452 private DisabledInfo readDisabledInfo(DataInputStream in) throws IOException { 453 return new DisabledInfo(readString(in, false), readString(in, false), readBundleDescription(in)); 454 } 455 readMap(DataInputStream in)456 private Map<String, Object> readMap(DataInputStream in) throws IOException { 457 int count = in.readInt(); 458 if (count == 0) 459 return null; 460 HashMap<String, Object> result = new HashMap<>(count); 461 for (int i = 0; i < count; i++) { 462 String key = readString(in, false); 463 Object value = null; 464 byte type = in.readByte(); 465 if (type == 0) 466 value = readString(in, false); 467 else if (type == 1) 468 value = readList(in); 469 else if (type == 2) 470 value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE; 471 else if (type == 3) 472 value = Integer.valueOf(in.readInt()); 473 else if (type == 4) 474 value = new Long(in.readLong()); 475 else if (type == 5) 476 value = new Double(in.readDouble()); 477 else if (type == 6) 478 value = readVersion(in); 479 else if (type == 7) { 480 value = readString(in, false); 481 try { 482 Class<?> uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$ 483 Constructor<?> constructor = uriClazz.getConstructor(new Class[] {String.class}); 484 value = constructor.newInstance(new Object[] {value}); 485 } catch (ClassNotFoundException e) { 486 // oh well cannot support; just use the string 487 } catch (RuntimeException e) { // got some reflection exception 488 throw e; 489 } catch (Exception e) { 490 throw new RuntimeException(e.getMessage(), e); 491 } 492 } else if (type == 8) { 493 int listType = in.readByte(); 494 int size = in.readInt(); 495 List<Object> list = new ArrayList<>(size); 496 for (int j = 0; j < size; j++) { 497 switch (listType) { 498 case 0 : 499 list.add(readString(in, false)); 500 break; 501 case 3 : 502 list.add(Integer.valueOf(in.readInt())); 503 break; 504 case 4 : 505 list.add(new Long(in.readLong())); 506 break; 507 case 5 : 508 list.add(new Double(in.readDouble())); 509 break; 510 case 6 : 511 list.add(readVersion(in)); 512 break; 513 case 7 : 514 list.add(readStateWire(in)); 515 break; 516 default : 517 throw new IOException("Invalid type: " + listType); //$NON-NLS-1$ 518 } 519 } 520 value = list; 521 } 522 result.put(key, value); 523 } 524 return result; 525 } 526 readStateWire(DataInputStream in)527 private Object readStateWire(DataInputStream in) throws IOException { 528 VersionConstraintImpl requirement; 529 BundleDescription requirementHost; 530 BaseDescription capability; 531 BundleDescription capabilityHost; 532 533 byte wireType = in.readByte(); 534 switch (wireType) { 535 case 0 : 536 requirement = readImportPackageSpec(in); 537 capability = readExportPackageDesc(in); 538 break; 539 case 1 : 540 requirement = readBundleSpec(in); 541 capability = readBundleDescription(in); 542 break; 543 case 2 : 544 requirement = readHostSpec(in); 545 capability = readBundleDescription(in); 546 break; 547 case 3 : 548 requirement = readGenericSpecification(in); 549 capability = readGenericDescription(in); 550 break; 551 default : 552 throw new IOException("Invalid wire type: " + wireType); //$NON-NLS-1$ 553 } 554 555 requirementHost = readBundleDescription(in); 556 capabilityHost = readBundleDescription(in); 557 558 if (requirement.getBundle() == null) { 559 // Need to fix up dynamic imports added by weaving hook (bug 359394) 560 requirement.setBundle(requirementHost); 561 } 562 return new StateWire(requirementHost, requirement, capabilityHost, capability); 563 } 564 readList(DataInputStream in)565 private String[] readList(DataInputStream in) throws IOException { 566 int count = in.readInt(); 567 if (count == 0) 568 return null; 569 String[] result = new String[count]; 570 for (int i = 0; i < count; i++) 571 result[i] = readString(in, false); 572 return result; 573 } 574 readBaseDescription(BaseDescriptionImpl root, DataInputStream in)575 private void readBaseDescription(BaseDescriptionImpl root, DataInputStream in) throws IOException { 576 root.setName(readString(in, false)); 577 root.setVersion(readVersion(in)); 578 } 579 readImportPackageSpec(DataInputStream in)580 private ImportPackageSpecificationImpl readImportPackageSpec(DataInputStream in) throws IOException { 581 byte tag = readTag(in); 582 if (tag == NULL) 583 return null; 584 if (tag == INDEX) 585 return (ImportPackageSpecificationImpl) getFromObjectTable(in.readInt()); 586 ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl(); 587 int tableIndex = in.readInt(); 588 addToObjectTable(result, tableIndex); 589 readVersionConstraint(result, in); 590 result.setSupplier(readExportPackageDesc(in)); 591 result.setBundleSymbolicName(readString(in, false)); 592 result.setBundleVersionRange(readVersionRange(in)); 593 result.setAttributes(readMap(in)); 594 result.setDirectives(readMap(in)); 595 result.setArbitraryDirectives(readMap(in)); 596 return result; 597 } 598 readHostSpec(DataInputStream in)599 private HostSpecificationImpl readHostSpec(DataInputStream in) throws IOException { 600 byte tag = readTag(in); 601 if (tag == NULL) 602 return null; 603 if (tag == INDEX) 604 return (HostSpecificationImpl) getFromObjectTable(in.readInt()); 605 HostSpecificationImpl result = new HostSpecificationImpl(); 606 int tableIndex = in.readInt(); 607 addToObjectTable(result, tableIndex); 608 readVersionConstraint(result, in); 609 int hostCount = in.readInt(); 610 if (hostCount > 0) { 611 BundleDescription[] hosts = new BundleDescription[hostCount]; 612 for (int i = 0; i < hosts.length; i++) 613 hosts[i] = readBundleDescription(in); 614 result.setHosts(hosts); 615 } 616 result.setAttributes(readMap(in)); 617 result.setArbitraryDirectives(readMap(in)); 618 return result; 619 } 620 621 @SuppressWarnings("unchecked") readGenericDescription(DataInputStream in)622 private GenericDescription readGenericDescription(DataInputStream in) throws IOException { 623 byte tag = readTag(in); 624 if (tag == NULL) 625 return null; 626 if (tag == INDEX) 627 return (GenericDescription) getFromObjectTable(in.readInt()); 628 int tableIndex = in.readInt(); 629 GenericDescriptionImpl result = new GenericDescriptionImpl(); 630 addToObjectTable(result, tableIndex); 631 readBaseDescription(result, in); 632 result.setSupplier(readBundleDescription(in)); 633 result.setType(readString(in, false)); 634 Map<String, Object> mapAttrs = readMap(in); 635 Dictionary<String, Object> attrs = new Hashtable<>(); 636 if (mapAttrs != null) { 637 for (String key : mapAttrs.keySet()) { 638 attrs.put(key, mapAttrs.get(key)); 639 } 640 } 641 result.setAttributes(attrs); 642 @SuppressWarnings("rawtypes") 643 Map directives = readMap(in); 644 if (directives != null) 645 result.setDirectives(directives); 646 result.setFragmentDeclaration(readGenericDescription(in)); 647 return result; 648 } 649 readGenericSpecification(DataInputStream in)650 private GenericSpecificationImpl readGenericSpecification(DataInputStream in) throws IOException { 651 byte tag = readTag(in); 652 if (tag == NULL) 653 return null; 654 if (tag == INDEX) 655 return (GenericSpecificationImpl) getFromObjectTable(in.readInt()); 656 GenericSpecificationImpl result = new GenericSpecificationImpl(); 657 int tableIndex = in.readInt(); 658 addToObjectTable(result, tableIndex); 659 readVersionConstraint(result, in); 660 result.setType(readString(in, false)); 661 int num = in.readInt(); 662 GenericDescription[] suppliers = num == 0 ? null : new GenericDescription[num]; 663 for (int i = 0; i < num; i++) 664 suppliers[i] = readGenericDescription(in); 665 result.setSupplers(suppliers); 666 result.setResolution(in.readInt()); 667 try { 668 result.setMatchingFilter(readString(in, false), false); 669 } catch (InvalidSyntaxException e) { 670 // do nothing this filter was tested before 671 } 672 result.setAttributes(readMap(in)); 673 result.setArbitraryDirectives(readMap(in)); 674 return result; 675 } 676 readNativeCode(DataInputStream in)677 private NativeCodeSpecification readNativeCode(DataInputStream in) throws IOException { 678 if (!in.readBoolean()) 679 return null; 680 NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl(); 681 result.setOptional(in.readBoolean()); 682 int numNativeDesc = in.readInt(); 683 NativeCodeDescriptionImpl[] nativeDescs = new NativeCodeDescriptionImpl[numNativeDesc]; 684 for (int i = 0; i < numNativeDesc; i++) 685 nativeDescs[i] = readNativeCodeDescription(in); 686 result.setPossibleSuppliers(nativeDescs); 687 int supplierIndex = in.readInt(); 688 if (supplierIndex >= 0) 689 result.setSupplier(nativeDescs[supplierIndex]); 690 return result; 691 } 692 readNativeCodeDescription(DataInputStream in)693 private NativeCodeDescriptionImpl readNativeCodeDescription(DataInputStream in) throws IOException { 694 NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl(); 695 readBaseDescription(result, in); 696 result.setSupplier(readBundleDescription(in)); 697 try { 698 result.setFilter(readString(in, false)); 699 } catch (InvalidSyntaxException e) { 700 // do nothing, this filter was tested before 701 } 702 result.setLanguages(readStringArray(in)); 703 result.setNativePaths(readStringArray(in)); 704 result.setOSNames(readStringArray(in)); 705 result.setOSVersions(readVersionRanges(in)); 706 result.setProcessors(readStringArray(in)); 707 result.setInvalidNativePaths(in.readBoolean()); 708 return result; 709 } 710 readVersionRanges(DataInputStream in)711 private VersionRange[] readVersionRanges(DataInputStream in) throws IOException { 712 int num = in.readInt(); 713 if (num == 0) 714 return null; 715 VersionRange[] result = new VersionRange[num]; 716 for (int i = 0; i < num; i++) 717 result[i] = readVersionRange(in); 718 return result; 719 } 720 readStringArray(DataInputStream in)721 private String[] readStringArray(DataInputStream in) throws IOException { 722 int num = in.readInt(); 723 if (num == 0) 724 return null; 725 String[] result = new String[num]; 726 for (int i = 0; i < num; i++) 727 result[i] = readString(in, false); 728 return result; 729 } 730 731 // called by readers for VersionConstraintImpl subclasses readVersionConstraint(VersionConstraintImpl version, DataInputStream in)732 private void readVersionConstraint(VersionConstraintImpl version, DataInputStream in) throws IOException { 733 version.setName(readString(in, false)); 734 version.setVersionRange(readVersionRange(in)); 735 } 736 readVersion(DataInputStream in)737 private Version readVersion(DataInputStream in) throws IOException { 738 byte tag = readTag(in); 739 if (tag == NULL) 740 return Version.emptyVersion; 741 int majorComponent = in.readInt(); 742 int minorComponent = in.readInt(); 743 int serviceComponent = in.readInt(); 744 String qualifierComponent = readString(in, false); 745 Version result = ObjectPool.intern(new Version(majorComponent, minorComponent, serviceComponent, qualifierComponent)); 746 return result; 747 } 748 readVersionRange(DataInputStream in)749 private VersionRange readVersionRange(DataInputStream in) throws IOException { 750 byte tag = readTag(in); 751 if (tag == NULL) 752 return null; 753 return new VersionRange(readVersion(in), in.readBoolean(), readVersion(in), in.readBoolean()); 754 } 755 756 /** 757 * expectedTimestamp is the expected value for the timestamp. or -1, if 758 * no checking should be performed 759 */ loadStateDeprecated(StateImpl state, DataInputStream input, long expectedTimestamp)760 public synchronized boolean loadStateDeprecated(StateImpl state, DataInputStream input, long expectedTimestamp) throws IOException { 761 try { 762 return readStateDeprecated(state, input, expectedTimestamp); 763 } finally { 764 input.close(); 765 } 766 } 767 768 /** 769 * expectedTimestamp is the expected value for the timestamp. or -1, if 770 * no checking should be performed 771 */ loadState(StateImpl state, long expectedTimestamp)772 public synchronized boolean loadState(StateImpl state, long expectedTimestamp) throws IOException { 773 return readState(state, expectedTimestamp); 774 } 775 readString(DataInputStream in, boolean intern)776 private String readString(DataInputStream in, boolean intern) throws IOException { 777 byte type = in.readByte(); 778 if (type == NULL) 779 return null; 780 781 if (type == LONG_STRING) { 782 int length = in.readInt(); 783 byte[] data = new byte[length]; 784 in.readFully(data); 785 String string = new String(data, StandardCharsets.UTF_8); 786 787 if (intern) 788 return string.intern(); 789 return ObjectPool.intern(string); 790 } 791 792 if (intern) 793 return in.readUTF().intern(); 794 return ObjectPool.intern(in.readUTF()); 795 } 796 readTag(DataInputStream in)797 private byte readTag(DataInputStream in) throws IOException { 798 return in.readByte(); 799 } 800 openLazyFile()801 private DataInputStream openLazyFile() throws IOException { 802 if (lazyFile == null) 803 throw new IOException(); // TODO error message here! 804 return new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(lazyFile), BUFFER_SIZE_LAZY)); 805 } 806 isLazyLoaded()807 boolean isLazyLoaded() { 808 return lazyLoad; 809 } 810 getAccessedFlag()811 boolean getAccessedFlag() { 812 return accessedFlag; 813 } 814 setAccessedFlag(boolean accessedFlag)815 void setAccessedFlag(boolean accessedFlag) { 816 this.accessedFlag = accessedFlag; 817 } 818 fullyLoad()819 void fullyLoad() { 820 setAccessedFlag(true); 821 DataInputStream in = null; 822 try { 823 in = openLazyFile(); 824 for (int i = 0; i < numBundles; i++) 825 readBundleDescriptionLazyData(in, 0); 826 } catch (IOException ioe) { 827 throw new RuntimeException(ioe.getMessage(), ioe); // TODO need error message here 828 } finally { 829 if (in != null) 830 try { 831 in.close(); 832 } catch (IOException e) { 833 // nothing we can do now 834 } 835 } 836 } 837 fullyLoad(BundleDescriptionImpl target)838 void fullyLoad(BundleDescriptionImpl target) throws IOException { 839 setAccessedFlag(true); 840 DataInputStream in = null; 841 try { 842 in = openLazyFile(); 843 // get the set of bundles that must be loaded according to dependencies 844 List<BundleDescriptionImpl> toLoad = new ArrayList<>(); 845 addDependencies(target, toLoad); 846 int skipBytes[] = getSkipBytes(toLoad); 847 // look for the lazy data of the toLoad list 848 for (int i = 0; i < skipBytes.length; i++) 849 readBundleDescriptionLazyData(in, skipBytes[i]); 850 } finally { 851 if (in != null) 852 in.close(); 853 } 854 } 855 addDependencies(BundleDescriptionImpl target, List<BundleDescriptionImpl> toLoad)856 private void addDependencies(BundleDescriptionImpl target, List<BundleDescriptionImpl> toLoad) { 857 if (toLoad.contains(target) || target.isFullyLoaded()) 858 return; 859 Iterator<BundleDescriptionImpl> load = toLoad.iterator(); 860 int i = 0; 861 while (load.hasNext()) { 862 // insert the target into the list sorted by lazy data offsets 863 BundleDescriptionImpl bundle = load.next(); 864 if (target.getLazyDataOffset() < bundle.getLazyDataOffset()) 865 break; 866 i++; 867 } 868 if (i >= toLoad.size()) 869 toLoad.add(target); 870 else 871 toLoad.add(i, target); 872 List<BundleDescription> deps = target.getBundleDependencies(); 873 for (Iterator<BundleDescription> iter = deps.iterator(); iter.hasNext();) 874 addDependencies((BundleDescriptionImpl) iter.next(), toLoad); 875 } 876 getSkipBytes(List<BundleDescriptionImpl> toLoad)877 private int[] getSkipBytes(List<BundleDescriptionImpl> toLoad) { 878 int[] skipBytes = new int[toLoad.size()]; 879 for (int i = 0; i < skipBytes.length; i++) { 880 BundleDescriptionImpl current = toLoad.get(i); 881 if (i == 0) { 882 skipBytes[i] = current.getLazyDataOffset(); 883 continue; 884 } 885 BundleDescriptionImpl previous = toLoad.get(i - 1); 886 skipBytes[i] = current.getLazyDataOffset() - previous.getLazyDataOffset() - previous.getLazyDataSize(); 887 } 888 return skipBytes; 889 } 890 flushLazyObjectCache()891 void flushLazyObjectCache() { 892 for (Iterator<Entry<Integer, Object>> entries = objectTable.entrySet().iterator(); entries.hasNext();) { 893 Map.Entry<Integer, Object> entry = entries.next(); 894 Object value = entry.getValue(); 895 if (value instanceof ExportPackageDescription || value instanceof GenericDescription || value instanceof ImportPackageSpecification || value instanceof BundleSpecification || value instanceof GenericSpecification) 896 entries.remove(); 897 } 898 } 899 } 900