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