/** * 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 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.net.URL; import java.security.ProtectionDomain; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; import java.util.List; //import org.osgi.framework.ServiceListener; //import org.osgi.framework.ServiceReference; /***OSGI Bundle implementation **/ public final class BundleImpl implements Bundle { static final Logger log; Archive archive; final File bundleDir; BundleClassLoader classloader; int currentStartlevel; ProtectionDomain domain; Hashtable<String, String> headers = new Hashtable<String, String>(); final String location; boolean persistently; List<BundleListener> registeredBundleListeners; List<FrameworkListener> registeredFrameworkListeners; int state = 0; boolean isValid=true; static { log = LoggerFactory.getInstance("BundleImpl"); } BundleImpl(File bundleDir, String location, InputStream archiveInputStream, File archiveFile, boolean isInstall) throws BundleException, IOException { this.persistently = false; this.domain = null; this.registeredFrameworkListeners = null; this.registeredBundleListeners = null; long currentTimeMillis = System.currentTimeMillis(); this.location = location; this.currentStartlevel = Framework.startlevel; 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(BundleEvent.INSTALLED, this); } if (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) { log.info("Framework: Bundle " + toString() + " created. " + (System.currentTimeMillis() - currentTimeMillis) + " ms"); } } BundleImpl(File file) 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(); 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 != BundleEvent.STOPPED) { if (this.classloader == null) { this.classloader = new BundleClassLoader(this); } if (recursive) { this.classloader.resolveBundle(true, new HashSet(0)); this.state = BundleEvent.STOPPED; } else if (this.classloader.resolveBundle(false, null)) { this.state = BundleEvent.STOPPED; } Framework.notifyBundleListeners(BundleEvent.LOADED, 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; } /*** * @return BundleClassLoader BundleClassLoader **/ public ClassLoader getClassLoader() { return this.classloader; } @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 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 { isValid = true; 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 msg = "Error starting bundle " + toString(); log.error(msg,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 (Framework.DEBUG_BUNDLES && log.isInfoEnabled()) { log.info("Framework: Bundle " + toString() + " stopped."); } Framework.clearBundleTrace(this); this.state = BundleEvent.STOPPED; Framework.notifyBundleListeners(BundleEvent.STOPPED, this); isValid = false; } catch (Throwable th) { Framework.clearBundleTrace(this); this.state = BundleEvent.STOPPED; Framework.notifyBundleListeners(BundleEvent.STOPPED, this); 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(); this.classloader.cleanup(true); this.classloader = null; Framework.bundles.remove(this); Framework.notifyBundleListeners(BundleEvent.UNINSTALLED, this); isValid = false; } @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()); } boolean isResolved=false; if (this.state == BundleEvent.RESOLVED) { stopBundle(); isResolved=true; } try { this.archive = new BundleArchive(this.location, this.bundleDir); BundleClassLoader bundleClassLoader = new BundleClassLoader(this); 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 (isResolved) { startBundle(); } } catch (BundleException e) { throw e; } catch (Throwable e) { throw new BundleException("Could not refresh bundle " + toString(), e); } } 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; } }