//---------------------------------------------------------------------------- ctkPlugin::State ctkPluginPrivate::getUpdatedState_unlocked() { if (state & ctkPlugin::INSTALLED) { try { if (state == ctkPlugin::INSTALLED) { operation.fetchAndStoreOrdered(RESOLVING); fwCtx->resolvePlugin(this); state = ctkPlugin::RESOLVED; // TODO plugin threading //bundleThread().bundleChanged(new BundleEvent(BundleEvent.RESOLVED, this)); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, this->q_func())); operation.fetchAndStoreOrdered(IDLE); } } catch (const ctkPluginException& pe) { if (resolveFailException) delete resolveFailException; resolveFailException = new ctkPluginException(pe); this->fwCtx->listeners.frameworkError(this->q_func(), pe); } } return state; }
//---------------------------------------------------------------------------- void ctkPluginFrameworkPrivate::shutdown0(bool restart, bool wasActive) { try { { Locker sync(&lock); waitOnOperation(&lock, QString("Framework::") + (restart ? "update" : "stop"), true); operation = DEACTIVATING; state = ctkPlugin::STOPPING; } fwCtx->listeners.emitPluginChanged( ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func())); if (wasActive) { stopAllPlugins(); } { Locker sync(&lock); fwCtx->uninit(); shuttingDown.fetchAndStoreOrdered(0); shutdownDone_unlocked(restart); } if (restart) { if (wasActive) { q_func().toStrongRef()->start(); } else { init(); } } } catch (const ctkException& e) { shuttingDown.fetchAndStoreOrdered(0); systemShuttingdownDone(ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::PLUGIN_ERROR, this->q_func(), e)); } }
//---------------------------------------------------------------------------- void ctkPluginPrivate::setStateInstalled(bool sendEvent) { Locker sync(&operationLock); // Make sure that the context is invalid if (pluginContext != 0) { pluginContext->d_func()->invalidate(); pluginContext.reset(); } state = ctkPlugin::INSTALLED; if (sendEvent) { operation.fetchAndStoreOrdered(UNRESOLVING); // TODO: plugin thread //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this)); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, this->q_func())); } operation.fetchAndStoreOrdered(IDLE); }
//---------------------------------------------------------------------------- const ctkRuntimeException* ctkPluginPrivate::stop0() { wasStarted = state == ctkPlugin::ACTIVE; // 5: state = ctkPlugin::STOPPING; operation.fetchAndStoreOrdered(DEACTIVATING); // 6-13: // TODO plugin threading //const ctkRuntimeException* savedException = pluginThread().callStop1(this); const ctkRuntimeException* savedException = stop1(); if (state != ctkPlugin::UNINSTALLED) { state = ctkPlugin::RESOLVED; // TODO plugin threading //bundleThread().bundleChanged(new BundleEvent(BundleEvent.STOPPED, this)); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func())); operationLock.wakeAll(); operation.fetchAndStoreOrdered(IDLE); } return savedException; }
void ctkPlugin::uninstall() { bool wasResolved = false; Q_D(ctkPlugin); if (d->archive) { try { d->archive->setStartLevel(-2); // Mark as uninstalled } catch (...) { } } d->cachedHeaders = getHeaders(); switch (d->state) { case UNINSTALLED: throw std::logic_error("Plugin is in UNINSTALLED state"); case STARTING: // Lazy start case ACTIVE: case STOPPING: try { //TODO: If activating or deactivating, wait a litle // we don't use mutliple threads to start plugins for now //d->waitOnActivation(fwCtx.packages, "Bundle.uninstall", false); if (d->state & (ACTIVE | STARTING)) { try { d->stop0(d->state == ACTIVE); } catch (const std::exception& exception) { // NYI! not call inside lock d->fwCtx->listeners.frameworkError(this, exception); } } } catch (const std::exception& e) { d->deactivating = false; //fwCtx.packages.notifyAll(); d->fwCtx->listeners.frameworkError(this, e); } // Fall through case RESOLVED: wasResolved = true; // Fall through case INSTALLED: d->fwCtx->plugins->remove(d->location); d->pluginActivator = 0; if (d->pluginDir.exists()) { if (!ctkPluginFrameworkUtil::removeDir(d->pluginDir.absolutePath())) { // Plugin dir is not deleted completely, make sure we mark // it as uninstalled for next framework restart if (d->archive) { try { d->archive->setStartLevel(-2); // Mark as uninstalled } catch (const std::exception& e) { // NYI! Generate FrameworkError if dir still exists!? qDebug() << "Failed to mark plugin" << d->id << "as uninstalled," << d->pluginDir.absoluteFilePath() << "must be deleted manually:" << e.what(); } } } d->pluginDir.setFile(""); } if (d->archive) { d->archive->purge(); } // id, location and headers survive after uninstall. // TODO: UNRESOLVED must be sent out during installed state // This needs to be reviewed. See OSGi bug #1374 d->state = INSTALLED; d->modified(); // Broadcast events if (wasResolved) { d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, d->q_ptr)); } d->state = UNINSTALLED; d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNINSTALLED, d->q_ptr)); break; } }
//---------------------------------------------------------------------------- void ctkPluginPrivate::update0(const QUrl& updateLocation, bool wasActive) { const bool wasResolved = state == ctkPlugin::RESOLVED; const int oldStartLevel = getStartLevel(); QSharedPointer<ctkPluginArchive> newArchive; operation.fetchAndStoreOrdered(UPDATING); try { // New plugin as stream supplied? QUrl updateUrl(updateLocation); if (updateUrl.isEmpty()) { // Try Plugin-UpdateLocation QString update = archive != 0 ? archive->getAttribute(ctkPluginConstants::PLUGIN_UPDATELOCATION) : QString(); if (update.isEmpty()) { // Take original location updateUrl = location; } } if(updateUrl.scheme() != "file") { QString msg = "Unsupported update URL:"; msg += updateUrl.toString(); throw ctkPluginException(msg); } newArchive = fwCtx->storage->updatePluginArchive(archive, updateUrl, updateUrl.toLocalFile()); //checkCertificates(newArchive); checkManifestHeaders(); newArchive->setStartLevel(oldStartLevel); fwCtx->storage->replacePluginArchive(archive, newArchive); } catch (const std::exception& e) { if (!newArchive.isNull()) { newArchive->purge(); } operation.fetchAndStoreOrdered(IDLE); if (wasActive) { try { this->q_func().data()->start(); } catch (const ctkPluginException& pe) { fwCtx->listeners.frameworkError(this->q_func(), pe); } } try { const ctkPluginException& pe = dynamic_cast<const ctkPluginException&>(e); throw pe; } catch (std::bad_cast) { throw ctkPluginException(QString("Failed to get update plugin: ") + e.what(), ctkPluginException::UNSPECIFIED); } } bool purgeOld = false; // TODO: check if dependent plug-ins are started. If not, set purgeOld to true. // Activate new plug-in QSharedPointer<ctkPluginArchive> oldArchive = archive; archive = newArchive; cachedRawHeaders.clear(); state = ctkPlugin::INSTALLED; // Purge old archive if (purgeOld) { //secure.purge(this, oldProtectionDomain); if (oldArchive != 0) { oldArchive->purge(); } } // Broadcast events if (wasResolved) { // TODO: use plugin threading //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this)); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, this->q_func())); } //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UPDATED, this)); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UPDATED, this->q_func())); operation.fetchAndStoreOrdered(IDLE); // Restart plugin previously stopped in the operation if (wasActive) { try { this->q_func().data()->start(); } catch (const ctkPluginException& pe) { fwCtx->listeners.frameworkError(this->q_func(), pe); } } }
//---------------------------------------------------------------------------- const ctkRuntimeException* ctkPluginPrivate::stop1() { const ctkRuntimeException* res = 0; //6: fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, q_func())); //7: if (wasStarted && pluginActivator) { try { pluginActivator->stop(pluginContext.data()); if (state != ctkPlugin::STOPPING) { if (state == ctkPlugin::UNINSTALLED) { return new ctkIllegalStateException("Plug-in is uninstalled"); } else { return new ctkIllegalStateException("Plug-in changed state because of refresh during stop"); } } } catch (const ctkException& e) { res = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed", ctkPluginException::ACTIVATOR_ERROR, e); } catch (...) { res = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed", ctkPluginException::ACTIVATOR_ERROR); } pluginActivator = 0; } if (operation.fetchAndAddOrdered(0) == DEACTIVATING) { // Call hooks after we've called PluginActivator::stop(), but before we've // cleared all resources if (pluginContext) { // TODO service listener hooks //fwCtx->listeners.serviceListeners.hooksBundleStopped(this); //8-10: removePluginResources(); pluginContext->d_func()->invalidate(); pluginContext.reset(); } } // This would unload the shared library and delete the activator if // there are no dependencies. However, objects from the plug-in might // have been created via C-function symbol lookups. Hence we cannot // safely unload the DLL. Maybe implement a in-DLL counter later // (http://stackoverflow.com/questions/460809/c-dll-unloading-issue and // http://boost.2283326.n4.nabble.com/shared-ptr-A-smarter-smart-pointer-proposal-for-dynamic-libraries-td2649749.html). // The activator itself will be delete during program termination // (by the QPluginLoader instance). //pluginLoader.unload(); return res; }
//---------------------------------------------------------------------------- QSharedPointer<ctkPlugin> ctkPlugins::install(const QUrl& location, QIODevice* in) { if (!fwCtx) { // This ctkPlugins instance has been closed! throw std::logic_error("ctkPlugins::install(location, inputStream) called on closed plugins object."); } { QWriteLocker lock(&pluginsLock); QHash<QString, QSharedPointer<ctkPlugin> >::const_iterator it = plugins.find(location.toString()); if (it != plugins.end()) { return it.value(); } // install new plugin ctkPluginArchive* pa = 0; QString localPluginPath; try { if (!in) { // extract the input stream from the given location // //TODO Support for http proxy authentication // //TODO put in update as well // String auth = fwCtx.props.getProperty("http.proxyAuth"); // if (auth != null && !"".equals(auth)) { // if ("http".equals(url.getProtocol()) || // "https".equals(url.getProtocol())) { // String base64 = Util.base64Encode(auth); // conn.setRequestProperty("Proxy-Authorization", // "Basic " + base64); // } // } // // Support for http basic authentication // String basicAuth = fwCtx.props.getProperty("http.basicAuth"); // if (basicAuth != null && !"".equals(basicAuth)) { // if ("http".equals(url.getProtocol()) || // "https".equals(url.getProtocol())) { // String base64 = Util.base64Encode(basicAuth); // conn.setRequestProperty("Authorization", // "Basic " +base64); // } // } if (location.scheme() != "file") { throw std::runtime_error(std::string("Unsupported url scheme: ") + qPrintable(location.scheme())); } else { qDebug() << QString("Trying to install file:") << location.path(); localPluginPath = location.toLocalFile(); } } else { //TODO copy the QIODevice to a local cache } pa = fwCtx->storage->insertPlugin(location, localPluginPath); QSharedPointer<ctkPlugin> res(new ctkPlugin()); res->init(res, fwCtx, pa); plugins.insert(location.toString(), res); fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::INSTALLED, res)); return res; } catch (const std::exception& e) { if (pa) { pa->purge(); } // if (dynamic_cast<const SecurityException&>(e)) { // throw; // } // else // { throw ctkPluginException(QString("Failed to install plugin: ") + QString(e.what()), ctkPluginException::UNSPECIFIED, &e); // } } } }