/**
* OpenAtlasForAndroid Project
* The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author BunnyBlue
**/
package com.openatlas.framework;
import com.openatlas.boot.PlatformConfigure;
import com.openatlas.framework.bundlestorage.Archive;
import com.openatlas.framework.bundlestorage.BundleArchiveRevision.DexLoadException;
import com.openatlas.hack.OpenAtlasHacks;
import com.openatlas.log.Logger;
import com.openatlas.log.LoggerFactory;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
public final class BundleClassLoader extends ClassLoader {
private static final List<URL> EMPTY_LIST;
static final HashSet<String> FRAMEWORK_PACKAGES;
static final Logger log;
// @Deprecated
// BundleActivator activator;
// @Deprecated
// String activatorClassName;
final Archive archive;
BundleImpl bundle;
private String[] dynamicImports;
String[] exports;
Map<String, BundleClassLoader> importDelegations;
String[] imports;
private File[] nativeLibraryDirectories;
BundleClassLoader originalExporter;
String[] requires;
/***
* remove next version
*********/
@SuppressWarnings("unused")
@Deprecated
private static final class BundleURLHandler extends URLStreamHandler {
private final InputStream input;
class AnonymousClass_1 extends InputStream {
final InputStream stream;
AnonymousClass_1(InputStream inputStream) {
this.stream = inputStream;
}
@Override
public int read() throws IOException {
return this.stream.read();
}
@Override
public int read(byte[] bArr) throws IOException {
return this.stream.read(bArr);
}
}
class AnonymousClass_2 extends URLConnection {
AnonymousClass_2(URL url) {
super(url);
}
@Override
public InputStream getInputStream() throws IOException {
return BundleURLHandler.this.input;
}
@Override
public void connect() throws IOException {
}
}
private BundleURLHandler(InputStream inputStream) {
this.input = new AnonymousClass_1(inputStream);
}
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new AnonymousClass_2(url);
}
@Override
protected int hashCode(URL url) {
return this.input.hashCode();
}
}
static {
log = LoggerFactory.getInstance("BundleClassLoader");
FRAMEWORK_PACKAGES = new HashSet<String>();
FRAMEWORK_PACKAGES.add(PlatformConfigure.OPENATLAS_FRAMEWORK_PACKAGE);
FRAMEWORK_PACKAGES.add("org.osgi.framework");
FRAMEWORK_PACKAGES.add("org.osgi.service.packageadmin");
FRAMEWORK_PACKAGES.add("org.osgi.service.startlevel");
EMPTY_LIST = new ArrayList<URL>();
}
BundleClassLoader(BundleImpl bundleImpl) throws BundleException {
super(Object.class.getClassLoader());
this.exports = new String[0];
this.imports = new String[0];
this.requires = new String[0];
// this.activatorClassName = null;
// this.activator = null;
this.dynamicImports = null;
this.originalExporter = null;
this.bundle = bundleImpl;
this.archive = bundleImpl.archive;
if (this.archive == null) {
throw new BundleException("Not Component valid bundle: " + bundleImpl.location);
}
try {
processManifest(this.archive.getManifest());
} catch (IOException e) {
e.printStackTrace();
throw new BundleException("Not Component valid bundle: " + bundleImpl.location);
}
}
public BundleImpl getBundle() {
return this.bundle;
}
private void processManifest(Manifest manifest) throws BundleException {
Attributes mainAttributes;
if (manifest != null) {
mainAttributes = manifest.getMainAttributes();
} else {
mainAttributes = new Attributes();
}
checkEE(readProperty(mainAttributes,
Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT),
splitString(System.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT)));
this.exports = readProperty(mainAttributes, Constants.EXPORT_PACKAGE);
this.imports = readProperty(mainAttributes, Constants.IMPORT_PACKAGE);
this.dynamicImports = readProperty(mainAttributes, Constants.DYNAMICIMPORT_PACKAGE);
this.requires = readProperty(mainAttributes, Constants.REQUIRE_BUNDLE);
// this.activatorClassName = mainAttributes
// .getValue(Constants.BUNDLE_ACTIVATOR);
Hashtable<String, String> hashtable = new Hashtable<String, String>(mainAttributes.size());
Object[] toArray = mainAttributes.keySet().toArray(
new Object[mainAttributes.keySet().size()]);
for (int i = 0; i < toArray.length; i++) {
hashtable.put(toArray[i].toString(), mainAttributes.get(toArray[i]).toString());
}
this.bundle.headers = hashtable;
}
private void checkEE(String[] strArr, String[] strArr2)
throws BundleException {
if (strArr.length != 0) {
Set hashSet = new HashSet(Arrays.asList(strArr2));
int i = 0;
while (i < strArr.length) {
if (!hashSet.contains(strArr[i])) {
i++;
} else {
return;
}
}
throw new BundleException("Platform does not provide EEs " + Arrays.asList(strArr));
}
}
boolean resolveBundle(boolean resolve, HashSet<BundleClassLoader> hashSet) throws BundleException {
int i;
if (Framework.DEBUG_CLASSLOADING && log.isInfoEnabled()) {
log.info("BundleClassLoader: Resolving " + this.bundle + (resolve ? " (critical)" : " (not critical)"));
}
HashSet hashSet2;
if (this.exports.length > 0) {
HashSet hashSet3 = new HashSet(this.exports.length);
for (String parsePackageString : this.exports) {
hashSet3.add(Package.parsePackageString(parsePackageString)[0]);
}
hashSet2 = hashSet3;
} else {
hashSet2 = null;
}
if (this.imports.length > 0) {
if (this.importDelegations == null) {
this.importDelegations = new HashMap(this.imports.length);
}
for (int i2 = 0; i2 < this.imports.length; i2++) {
String obj = Package.parsePackageString(this.imports[i2])[0];
if (!FRAMEWORK_PACKAGES.contains(obj)
&& this.importDelegations.get(obj) == null
&& (hashSet2 == null || !hashSet2.contains(obj))) {
BundleClassLoader bundleClassLoader = Framework.getImport(
this.bundle, this.imports[i2], resolve, hashSet);
if (bundleClassLoader != null) {
if (bundleClassLoader != this) {
this.importDelegations.put(obj, bundleClassLoader);
}
} else if (resolve) {
throw new BundleException("Unsatisfied import "
+ this.imports[i2] + " for bundle "
+ this.bundle.toString(),
new ClassNotFoundException(
"Unsatisfied import "
+ this.imports[i2]));
} else {
if (this.exports.length > 0) {
Framework.export(this, this.exports, false);
}
if (!Framework.DEBUG_CLASSLOADING
|| !log.isInfoEnabled()) {
return false;
}
log.info("BundleClassLoader: Missing import "
+ this.imports[i2]
+ ". Resolving attempt terminated unsuccessfully.");
return false;
}
}
}
}
if (this.exports.length > 0) {
if (this.importDelegations == null) {
this.importDelegations = new HashMap(this.imports.length);
}
for (i = 0; i < this.exports.length; i++) {
BundleClassLoader bundleClassLoader2 = Framework.getImport(
this.bundle,
Package.parsePackageString(this.exports[i])[0], false,
null);
if (!(bundleClassLoader2 == null || bundleClassLoader2 == this)) {
this.importDelegations.put(
Package.parsePackageString(this.exports[i])[0],
bundleClassLoader2);
}
}
}
if (this.exports.length > 0) {
Framework.export(this, this.exports, true);
}
return true;
}
void cleanup(boolean z) {
ArrayList arrayList = new ArrayList();
for (String str : this.exports) {
Package packageR = Framework.exportedPackages
.get(new Package(str, null, false));
if (packageR != null) {
if (packageR.importingBundles == null) {
Framework.exportedPackages.remove(packageR);
packageR.importingBundles = null;
} else {
packageR.removalPending = true;
arrayList.add(packageR);
}
}
}
if (this.bundle != null) {
if (z) {
this.bundle.staleExportedPackages = (Package[]) arrayList
.toArray(new Package[arrayList.size()]);
} else {
this.bundle.staleExportedPackages = null;
}
}
if (this.importDelegations != null) {
String[] strArr = this.importDelegations.keySet()
.toArray(new String[this.importDelegations.size()]);
for (String str2 : strArr) {
Package packageR2 = Framework.exportedPackages
.get(new Package(str2, null, false));
if (!(packageR2 == null || packageR2.importingBundles == null)) {
packageR2.importingBundles.remove(this.bundle);
if (packageR2.importingBundles.isEmpty()) {
packageR2.importingBundles = null;
if (packageR2.removalPending) {
Framework.exportedPackages.remove(packageR2);
}
}
}
}
}
this.importDelegations = null;
// this.activator = null;
this.originalExporter = null;
if (z) {
if (arrayList.size() == 0) {
this.bundle = null;
}
// this.activatorClassName = null;
this.imports = null;
this.dynamicImports = null;
}
}
@Override
protected Class<?> findClass(String str) throws ClassNotFoundException {
if (FRAMEWORK_PACKAGES.contains(packageOf(str))) {
return Framework.systemClassLoader.loadClass(str);
}
Class<?> findOwnClass = findOwnClass(str);
if (findOwnClass != null) {
return findOwnClass;
}
if (this.dynamicImports.length > 0) {
for (int i = 0; i < this.dynamicImports.length; i++) {
if (this.dynamicImports[i].indexOf("version") > -1) {
Package[] packageArr = Framework.exportedPackages
.keySet().toArray(
new Package[Framework.exportedPackages
.size()]);
for (int i2 = 0; i2 < packageArr.length; i2++) {
if (packageArr[i2].matches(this.dynamicImports[i])) {
Class<?> findDelegatedClass = findDelegatedClass(
packageArr[i2].classloader, str);
if (findDelegatedClass != null) {
return findDelegatedClass;
}
}
}
continue;
} else {
Package packageR = Framework.exportedPackages
.get(new Package(packageOf(str), null, false));
if (packageR != null) {
findOwnClass = findDelegatedClass(packageR.classloader,
str);
if (findOwnClass != null) {
return findOwnClass;
}
} else {
continue;
}
}
}
}
if (this.importDelegations != null) {
BundleClassLoader bundleClassLoader = this.importDelegations
.get(packageOf(str));
if (bundleClassLoader != null) {
findOwnClass = findDelegatedClass(bundleClassLoader, str);
if (findOwnClass != null) {
return findOwnClass;
}
}
}
try {
findOwnClass = Framework.systemClassLoader.loadClass(str);
if (findOwnClass != null) {
return findOwnClass;
}
} catch (Exception e) {
}
throw new ClassNotFoundException("Can't find class " + str
+ " in BundleClassLoader: " + this.bundle.getLocation());
}
private Class<?> findOwnClass(String str) {
try {
return this.archive.findClass(str, this);
} catch (Exception e) {
if (!(e instanceof DexLoadException)) {
return null;
}
throw ((DexLoadException) e);
}
}
private static Class<?> findDelegatedClass(
BundleClassLoader bundleClassLoader, String str) {
Class<?> findLoadedClass;
synchronized (bundleClassLoader) {
findLoadedClass = bundleClassLoader.findLoadedClass(str);
if (findLoadedClass == null) {
findLoadedClass = bundleClassLoader.findOwnClass(str);
}
}
return findLoadedClass;
}
@Override
protected URL findResource(String str) {
String stripTrailing = stripTrailing(str);
List findOwnResources = findOwnResources(stripTrailing, false);
if (findOwnResources.size() > 0) {
return (URL) findOwnResources.get(0);
}
List findImportedResources = findImportedResources(stripTrailing, false);
return findImportedResources.size() > 0 ? (URL) findImportedResources
.get(0) : null;
}
@Override
protected Enumeration<URL> findResources(String str) {
String stripTrailing = stripTrailing(str);
Collection findOwnResources = findOwnResources(stripTrailing, true);
findOwnResources.addAll(findImportedResources(stripTrailing, true));
return Collections.enumeration(findOwnResources);
}
private List<URL> findOwnResources(String str, boolean z) {
try {
return this.archive.getResources(str);
} catch (IOException e) {
e.printStackTrace();
return EMPTY_LIST;
}
}
private List<URL> findImportedResources(String str, boolean z) {
if (this.bundle.state == BundleEvent.STARTED || this.importDelegations == null) {
return EMPTY_LIST;
}
BundleClassLoader bundleClassLoader = this.importDelegations
.get(packageOf(pseudoClassname(str)));
if (bundleClassLoader == null) {
return EMPTY_LIST;
}
return bundleClassLoader.originalExporter == null ? bundleClassLoader
.findOwnResources(str, z) : bundleClassLoader.originalExporter
.findOwnResources(str, z);
}
@Override
protected String findLibrary(String nickname) {
String mapLibraryName = System.mapLibraryName(nickname);
if (this.nativeLibraryDirectories != null) {
for (File file : this.nativeLibraryDirectories) {
File file2 = new File(file, mapLibraryName);
if (file2.canRead()) {
return file2.getAbsolutePath();
}
}
}
File findLibrary = this.archive.findLibrary(mapLibraryName);
if (findLibrary != null) {
return findLibrary.getAbsolutePath();
}
try {
return (String) OpenAtlasHacks.ClassLoader_findLibrary.invoke(
Framework.systemClassLoader, nickname);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "BundleClassLoader[Bundle" + this.bundle + "]";
}
private static String[] readProperty(Attributes attributes, String name)
throws BundleException {
String value = attributes.getValue(name);
if (value == null || !value.equals("")) {
return splitString(value);
}
return new String[0];
}
private static String[] splitString(String str) {
int i = 0;
if (str == null) {
return new String[0];
}
StringTokenizer stringTokenizer = new StringTokenizer(str, ",");
if (stringTokenizer.countTokens() == 0) {
return new String[]{str};
}
String[] strArr = new String[stringTokenizer.countTokens()];
while (i < strArr.length) {
strArr[i] = stringTokenizer.nextToken().trim();
i++;
}
return strArr;
}
private static String stripTrailing(String str) {
return (str.startsWith("/") || str.startsWith("\\")) ? str.substring(1)
: str;
}
private static String packageOf(String str) {
int lastIndexOf = str.lastIndexOf(46);
return lastIndexOf > -1 ? str.substring(0, lastIndexOf) : "";
}
private static String pseudoClassname(String str) {
return stripTrailing(str).replace('.', '-').replace('/', '.')
.replace('\\', '.');
}
}