/**
* 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.framework.bundlestorage.Archive;
import com.openatlas.framework.bundlestorage.BundleArchive;
import com.openatlas.log.Logger;
import com.openatlas.log.LoggerFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.*;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
public final class BundleImpl implements Bundle {
static final Logger log;
Archive archive;
final File bundleDir;
BundleClassLoader classloader;
private final BundleContextImpl context;
int currentStartlevel;
ProtectionDomain domain;
Hashtable<String, String> headers = new Hashtable<String, String>();
final String location;
boolean persistently;
List<BundleListener> registeredBundleListeners;
List<FrameworkListener> registeredFrameworkListeners;
List<ServiceListener> registeredServiceListeners;
List<ServiceReference> registeredServices;
Package[] staleExportedPackages;
int state = 0;
static {
log = LoggerFactory.getInstance("BundleImpl");
}
BundleImpl(File bundleDir, String location, BundleContextImpl bundleContextImpl,
InputStream archiveInputStream, File archiveFile, boolean isInstall)
throws BundleException, IOException {
this.persistently = false;
this.domain = null;
this.registeredServices = null;
this.registeredFrameworkListeners = null;
this.registeredBundleListeners = null;
this.registeredServiceListeners = null;
this.staleExportedPackages = null;
long currentTimeMillis = System.currentTimeMillis();
this.location = location;
bundleContextImpl.bundle = this;
this.context = bundleContextImpl;
this.currentStartlevel = Framework.initStartlevel;
this.bundleDir = bundleDir;
if (archiveInputStream != null) {
// try {
this.archive = new BundleArchive(location, bundleDir, archiveInputStream);
// } catch (Throwable e) {
// Framework.deleteDirectory(bundleDir);
// throw new BundleException("Could not install bundle " + location, e);
// }
} else if (archiveFile != null) {
try {
this.archive = new BundleArchive(location, bundleDir, archiveFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.state = BundleEvent.STARTED;
updateMetadata();
if (isInstall) {
Framework.bundles.put(location, this);
resolveBundle(false);
Framework.notifyBundleListeners(1, this);
}
if (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) {
log.info("Framework: Bundle " + toString() + " created. "
+ (System.currentTimeMillis() - currentTimeMillis) + " ms");
}
}
BundleImpl(File file, BundleContextImpl bundleContextImpl) throws Exception {
long currentTimeMillis = System.currentTimeMillis();
DataInputStream dataInputStream = new DataInputStream(new FileInputStream(new File(file, "meta")));
this.location = dataInputStream.readUTF();
this.currentStartlevel = dataInputStream.readInt();
this.persistently = dataInputStream.readBoolean();
dataInputStream.close();
bundleContextImpl.bundle = this;
this.context = bundleContextImpl;
this.bundleDir = file;
this.state = BundleEvent.STARTED;
try {
this.archive = new BundleArchive(this.location, file);
resolveBundle(false);
Framework.bundles.put(this.location, this);
Framework.notifyBundleListeners(1, this);
if (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) {
log.info("Framework: Bundle " + toString() + " loaded. " + (System.currentTimeMillis() - currentTimeMillis) + " ms");
}
} catch (Exception e) {
throw new BundleException("Could not load bundle " + this.location, e.getCause());
}
}
private synchronized void resolveBundle(boolean recursive) throws BundleException {
if (this.state != 4) {
if (this.classloader == null) {
this.classloader = new BundleClassLoader(this);
}
if (recursive) {
this.classloader.resolveBundle(true, new HashSet(0));
this.state = 4;
} else if (this.classloader.resolveBundle(false, null)) {
this.state = 4;
}
Framework.notifyBundleListeners(0, this);
}
}
@Override
public long getBundleId() {
return 0;
}
@Override
public Dictionary<String, String> getHeaders() {
return this.headers;
}
@Override
public String getLocation() {
return this.location;
}
public Archive getArchive() {
return this.archive;
}
public ClassLoader getClassLoader() {
return this.classloader;
}
@Override
public ServiceReference[] getRegisteredServices() {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Bundle " + toString()
+ "has been unregistered.");
} else if (this.registeredServices == null) {
return null;
} else {
return this.registeredServices
.toArray(new ServiceReference[this.registeredServices
.size()]);
}
}
@Override
public URL getResource(String name) {
if (this.state != BundleEvent.INSTALLED) {
return this.classloader.getResource(name);
}
throw new IllegalStateException("Bundle " + toString()
+ " has been uninstalled");
}
@Override
public ServiceReference[] getServicesInUse() {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Bundle " + toString()
+ "has been unregistered.");
}
ArrayList<ServiceReferenceImpl> arrayList = new ArrayList<ServiceReferenceImpl>();
ServiceReferenceImpl[] serviceReferenceImplArr = Framework.services
.toArray(new ServiceReferenceImpl[Framework.services.size()]);
int i = 0;
while (i < serviceReferenceImplArr.length) {
synchronized (serviceReferenceImplArr[i].useCounters) {
if (serviceReferenceImplArr[i].useCounters.get(this) != null) {
arrayList.add(serviceReferenceImplArr[i]);
}
}
i++;
}
return arrayList
.toArray(new ServiceReference[arrayList.size()]);
}
@Override
public int getState() {
return this.state;
}
@Override
public boolean hasPermission(Object permission) {
if (this.state != BundleEvent.INSTALLED) {
return true;
}
throw new IllegalStateException("Bundle " + toString()
+ "has been unregistered.");
}
@Override
public synchronized void start() throws BundleException {
this.persistently = true;
updateMetadata();
if (this.currentStartlevel <= Framework.startlevel) {
startBundle();
}
}
public synchronized void startBundle() throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Cannot start uninstalled bundle "
+ toString());
} else if (this.state != BundleEvent.RESOLVED) {
if (this.state == BundleEvent.STARTED) {
resolveBundle(true);
}
this.state = BundleEvent.UPDATED;
try {
this.context.isValid = true;
// if (!(this.classloader.activatorClassName == null ||
// StringUtils
// .isBlank(this.classloader.activatorClassName))) {
// Class<?> loadClass = this.classloader
// .loadClass(this.classloader.activatorClassName);
// if (loadClass == null) {
// throw new ClassNotFoundException(
// this.classloader.activatorClassName);
// }
// this.classloader.activator = (BundleActivator) loadClass
// .newInstance();
// this.classloader.activator.start(this.context);
//
// }
this.state = BundleEvent.RESOLVED;
Framework.notifyBundleListeners(BundleEvent.STARTED, this);
if (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) {
log.info("Framework: Bundle " + toString() + " started.");
}
} catch (Throwable th) {
Framework.clearBundleTrace(this);
this.state = BundleEvent.STOPPED;
String str = "Error starting bundle " + toString();
BundleException bundleException = new BundleException(str, th);
}
}
}
@Override
public synchronized void stop() throws BundleException {
this.persistently = false;
updateMetadata();
stopBundle();
}
public synchronized void stopBundle() throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Cannot stop uninstalled bundle "
+ toString());
} else if (this.state == BundleEvent.RESOLVED) {
this.state = BundleEvent.UNINSTALLED;
try {
// if (this.classloader.activator != null) {
// this.classloader.activator.stop(this.context);
// }
if (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) {
log.info("Framework: Bundle " + toString() + " stopped.");
}
// this.classloader.activator = null;
Framework.clearBundleTrace(this);
this.state = BundleEvent.STOPPED;
Framework.notifyBundleListeners(BundleEvent.STOPPED, this);
this.context.isValid = false;
} catch (Throwable th) {
// this.classloader.activator = null;
Framework.clearBundleTrace(this);
this.state = BundleEvent.STOPPED;
Framework.notifyBundleListeners(BundleEvent.STOPPED, this);
this.context.isValid = false;
}
}
}
@Override
public synchronized void uninstall() throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Bundle " + toString() + " is already uninstalled.");
}
if (this.state == BundleEvent.RESOLVED) {
try {
stopBundle();
} catch (Throwable th) {
Framework.notifyFrameworkListeners(BundleEvent.STARTED, this, th);
}
}
this.state = BundleEvent.INSTALLED;
new File(this.bundleDir, "meta").delete();
if (this.classloader.originalExporter != null) {
this.classloader.originalExporter.cleanup(true);
this.classloader.originalExporter = null;
}
this.classloader.cleanup(true);
this.classloader = null;
Framework.bundles.remove(this);
Framework.notifyBundleListeners(BundleEvent.UNINSTALLED, this);
this.context.isValid = false;
this.context.bundle = null;
}
@Override
public synchronized void update() throws BundleException {
String locationUpdate = this.headers.get(Constants.BUNDLE_UPDATELOCATION);
try {
if (locationUpdate == null) {
locationUpdate = this.location;
}
update(new URL(locationUpdate).openConnection().getInputStream());
} catch (Throwable e) {
throw new BundleException("Could not update " + toString()
+ " from " + locationUpdate, e);
}
}
@Override
public synchronized void update(InputStream inputStream)
throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Cannot update uninstalled bundle "
+ toString());
}
try {
this.archive
.newRevision(this.location, this.bundleDir, inputStream);
} catch (Throwable e) {
throw new BundleException("Could not update bundle " + toString(),
e);
}
}
@Override
public synchronized void update(File bundleFile) throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException("Cannot update uninstalled bundle "
+ toString());
}
try {
this.archive.newRevision(this.location, this.bundleDir, bundleFile);
} catch (Throwable e) {
throw new BundleException("Could not update bundle " + toString(),
e);
}
}
public synchronized void refresh() throws BundleException {
if (this.state == BundleEvent.INSTALLED) {
throw new IllegalStateException(
"Cannot refresh uninstalled bundle " + toString());
}
Object obj;
if (this.state == BundleEvent.RESOLVED) {
stopBundle();
obj = 1;
} else {
obj = null;
}
try {
this.archive = new BundleArchive(this.location, this.bundleDir);
BundleClassLoader bundleClassLoader = new BundleClassLoader(this);
String[] strArr = this.classloader.exports;
if (strArr.length > 0) {
int i = 0;
Object obj2 = null;
while (i < strArr.length) {
Object obj3;
Package packageR = Framework.exportedPackages
.get(new Package(strArr[i], null, false));
if (packageR.importingBundles == null
|| packageR.classloader != this.classloader) {
obj3 = obj2;
} else {
packageR.removalPending = true;
obj3 = 1;
}
i++;
obj2 = obj3;
}
if (obj2 != null) {
if (this.classloader.originalExporter != null) {
bundleClassLoader.originalExporter = this.classloader.originalExporter;
} else {
bundleClassLoader.originalExporter = this.classloader;
}
}
}
this.classloader.cleanup(true);
this.classloader = bundleClassLoader;
if (this.classloader.resolveBundle(false, null)) {
this.state = BundleEvent.STOPPED;
} else {
this.state = BundleEvent.STARTED;
}
Framework.notifyBundleListeners(BundleEvent.UPDATED, this);
if (obj != null) {
startBundle();
}
} catch (BundleException e) {
throw e;
} catch (Throwable e2) {
throw new BundleException("Could not refresh bundle " + toString(),
e2);
}
}
public synchronized void optDexFile() {
getArchive().optDexFile();
}
public synchronized void purge() throws BundleException {
try {
getArchive().purge();
} catch (Throwable e) {
throw new BundleException("Could not purge bundle " + toString(), e);
}
}
void updateMetadata() {
File file = new File(this.bundleDir, "meta");
DataOutputStream dataOutputStream = null;
try {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
dataOutputStream = new DataOutputStream(fileOutputStream);
dataOutputStream.writeUTF(this.location);
dataOutputStream.writeInt(this.currentStartlevel);
dataOutputStream.writeBoolean(this.persistently);
dataOutputStream.flush();
fileOutputStream.getFD().sync();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public String toString() {
return this.location;
}
}