Using QNetworkAccessManager across dll - qt

I have a Qt5 application which uses QNetworkAccessManager for network requests which is accessible via a singleton and QPluginLoader to load extensions which add the functionality to the program. Currently I'm using static linking for plugins and everything works just fine.
However I want to switch to using dynamic libraries to separate the core functionality from other parts of the app. I've added the necessary declspec's via macro, and made necessary adjustments in my .pro files.
The problem is that very often (like, 3 of 4 starts) QNetworkAccessManager when used from dlls just returns an empty request or a null pointer. No data, no error string, no headers.
This is the code I'm using for loading plugins:
template <typename PluginType>
static QList<PluginType*> loadModules() {
QList<PluginType*> loadedModules;
foreach (QObject* instance, QPluginLoader::staticInstances()) {
PluginType* plugin = qobject_cast<PluginType*>(instance);
if (plugin) {
loadedModules << plugin;
}
}
QDir modulesDir(qApp->applicationDirPath() + "/modules");
foreach (QString fileName, modulesDir.entryList(QDir::Files)) {
QPluginLoader loader(modulesDir.absoluteFilePath(fileName));
QObject *instance = loader.instance();
PluginType* plugin = qobject_cast<PluginType*>(instance);
if (plugin) {
loadedModules << plugin;
}
}
return loadedModules;
}
Which is used in this non-static non-template overload called during the startup:
bool AppController::loadModules() {
m_window = new AppWindow();
/* some unimportant connection and splashscreen updating */
QList <ModuleInterface*> loadedModules = loadModules<ModuleInterface>();
foreach (ModuleInterface* module, loadedModules) {
m_splash->showMessage(tr("Initializing module: %1").arg(module->getModuleName()),
Qt::AlignBottom | Qt::AlignRight, Qt::white);
module->preinit();
QApplication::processEvents();
// [1]
ControllerInterface *controller = module->getMainController();
m_window->addModule(module->getModuleName(),
QIcon(module->getIconPath()),
controller->primaryWidget(),
controller->settingsWidget());
m_moduleControllers << controller;
}
m_window->addGeneralSettings((new GeneralSettingsController(m_window))->settingsWidget());
m_window->enableSettings();
/* restoring window geometry & showing it */
return true;
}
However, if I insert QThread::sleep(1); into the line marked 1, it works okay, but the loading slows down and I highly doubt it is a stable solution that will work everywhere.
Also, the site I'm sending requests to is MyAnimeList.

All right, now I have finally debugged it. Turned out I deleted internal QNetworkAccessManager in one of the classes that needed unsync access. That, and updating to Qt5.3 seem to have solved my problem.

Related

QSettings of ini file from internet

Im using this function so far:
bool MSSQL::checkSettings()
{
QString settingsFile = "someDir/setup.ini";
QString fileName(settingsFile);
QFile file(fileName);
if(QFileInfo::exists(fileName)) {
return true;
}
else {
qDebug() << "Error: No INI File Found on path:" << settingsFile;
return false;
}
}
which works fine.
However when I wish to use an online ini file which is reachable (because can be opened via browser link), then its a false, for example if i use:
QString settingsFile = "http://localhost/something/setup.ini";
which QT should be able to read, then it doesnt work...
any ideas?
I dont see where you are using QSettings at all. You are using QFile, which is representation of a local file.
To your question:
QSettings does not able to read/write setting from URL. You have to download it manually before instantiating QSettings, See QNetworkAccessManager.
Or you may try to make your own settings provider, by using: QSettings::registerFormat() static function.

library module upgrade to Glide 4, where should the AppGlideModule be put in

In library module to upgrade to Glide 4.9.0.
api "com.github.bumptech.glide:glide:4.9.0"
api "com.github.bumptech.glide:annotations:4.9.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
and having a kotlin extension
fun ImageView.loadImg(imageUrl: String) {
// 4.+ code
var requestOptions : RequestOptions = RequestOptions()
.placeholder(ColorDrawable(Color.LTGRAY))
.diskCacheStrategy(DiskCacheStrategy.ALL)
if (!TextUtils.isEmpty(imageUrl)) {
Glide.with(context)
.setDefaultRequestOptions(requestOptions) // or use .apply(requestOptions) but after the .load()
.asBitmap()
.load(imageUrl)
.into(this)
}
}
but it crashes
java.lang.AbstractMethodError: abstract method "void com.bumptech.glide.module.RegistersComponents.registerComponents(android.content.Context, com.bumptech.glide.Glide, com.bumptech.glide.Registry)"
at com.bumptech.glide.Glide.initializeGlide(Glide.java:270)
at com.bumptech.glide.Glide.initializeGlide(Glide.java:223)
at com.bumptech.glide.Glide.checkAndInitializeGlide(Glide.java:184)
at com.bumptech.glide.Glide.get(Glide.java:168)
at com.bumptech.glide.Glide.getRetriever(Glide.java:689)
at com.bumptech.glide.Glide.with(Glide.java:716)
at com.common.extentions.ExtensionsKt.loadImg(Extensions.kt:44)
After adding
#GlideModule
class TheAppGlideModule : AppGlideModule() {
override fun isManifestParsingEnabled(): Boolean {
return false
}
}
to the library module does not help, or adding it to hosting app only does not work either,
but after adding it to both the library module and the hosting app the crash goes away.
according to documentation https://bumptech.github.io/glide/doc/generatedapi.html,
isnt it that it not supposed to have this class defined in the library module?
anyone has same experience?
* For now the API is only generated when a properly annotated AppGlideModule is found.
* There can only be one AppGlideModule per application.
* As a result it’s not possible to generate the API for a library without precluding any application
* that uses the library from using the generated API.
Resolved, it has missed
api "com.github.bumptech.glide:annotations:$versions.glide"
in the application side (not sure why adding single one in the module did not work and why with both it worked, maybe didnt do clear/rebuild after change?)

Extensible ASP.NET Core 2 application

I need to create an ASP.NET Core 2 application that can be extensible.
An extension, is a project that reference the main assembly and can extends it adding new controllers, models (with EF migrations), views, razor pages, etc.
Because the extensions need use the main application base classes like base controller, base model or view/page layout, the main application cannot reference the module project (avoid circular references).
I'm not sure how can I achieve this, but the idea is an installation of the main project, can add new functionality simple putting the modules DLL (or by online market in the main application, that download the DLL).
In my research, I found Applications parts, but my problem here is I need specify the part assembly in Startup class, and I need in installed the capacity of install modules without doing any changes in the code.
Some modules, need be extensible too, for example, an accounting module, need to connect with bank, and it have an interface that defines the methods of working with the bank, for example:
public interface IBankingOperation
{
public async void PayInvoiceAsync(Invoice invoice);
// More methods...
}
Then, differents projects can reference the banking assembly and provide implementation for differents banks.
In the main application, this modules can be installed like other modules, but checking the base module is intalled, for example, I can install the Santander module like other module, but only if banking module is installed (module dependency).
In conclusion, I need to create a modular ASP.NET Core 2 application, but the main assembly cannot reference the modules, the modules must reference the main assembly. The modules can contain Controllers, Models, Views, Pages, Etc.
In the main web app you would call a class which loads the extensions in the memory
ModuleManager.LoadModules(Path.Combine(_env.ContentRootPath, "Modules"));
this is the load modules function
public static void LoadModules(string modulesFolder)
{
var appsFolders = new DirectoryInfo(modulesFolder).GetDirectories();
foreach (var appFolder in appsFolders)
{
var binFolder = new DirectoryInfo(Path.Combine(appFolder.FullName, "bin"));
if (!binFolder.Exists)
{
continue;
}
var assemblies = AssemblyProvider.GetAssemblies(binFolder.FullName);
foreach (var assembly in assemblies)
{
var iModuleClass = assembly.GetTypes().FirstOrDefault(type => type.GetInterfaces().Contains(typeof(IModule))
&& type.GetConstructor(Type.EmptyTypes) != null);
if (iModuleClass != null)
{
var module = Activator.CreateInstance(iModuleClass) as IModule;
module.ModuleFolder = appFolder;
Modules.Add(module);
Assemblies.Add(assembly);
break;
}
}
}
}
then you should have an interface which should be implemented by each module the class which implement this interface should do the work of registering services and database models and all staff needed and you will load them as follows
public static IEnumerable<IExtensionRegister> GetModuleRegistrars()
{
lock (Modules)
{
return Modules.Select(item => item.Registrar).Where(item=>item!=null).ToList();
}
}

How do I do the equivalent of "git repack -ad" with jgit?

I have implemented a DfsRepository using jgit-2.0.0.201206130900. It works great but I want to repack it so that I only have one packfile. How do I do that via jgit?
Got this working. DfsGarbageCollector basically does the equivalent of repack -d. To get the repack -a behavior, use DfsPackCompactor:
void repack(DfsRepository repo) throws IOException {
DfsGarbageCollector gc = new DfsGarbageCollector(repo);
gc.pack(null);
// update the list of packs for getPacks() below
// otherwise not all packs are compacted
repo.scanForRepoChanges();
// only compact if there are multiple pack files
DfsPackFile[] packs = repo.getObjectDatabase().getPacks();
if (packs.length > 1) {
DfsPackCompactor compactor = new DfsPackCompactor(repo);
for (DfsPackFile pack : packs) {
compactor.add(pack);
}
compactor.compact(null);
}
}
That's not quite all though.
DfsGarbageCollector creates a separate packfile for the garbage.
The easiest way I found to "delete" the garbage packfile was to return a DfsOutputStream from my DfsObjDatabase.writePackFile() implementation that simply threw away the data if the pack file's source was PackSource.UNREACHABLE_GARBAGE.

Qt OpenSSL problem - blocked (?) on some computers

I write an app i qt which uses OpenSSL. All was alright, since yesterday. I compiled app and sent to my friend. On his computer application can open https. I open on other computer and it doesn't work. So I gave it to other friend and he can't open https websites. I was confused and gave other guy and on his computer my app is working. I don't understand situation. Previous versions worked without bugs. But i ran previous version which worked and it doesn't work too. I turned off all my firewalls. Nothing changed.
Any suggestions?
We all have 7 x64. I tested on XP HE and it works, bou on 7 x64 doesn't work. On my friend's computer 7 x64 works, but on XP HE doesn't works. IMO Operating System hasn't got any mean.
By default Qt doesn't contain implementation of OpenSSL, but uses libraries already installed into system.
Installing Win32 OpenSSL will make it work.
Another option is to build Qt with OpenSSL. Some info here.
In case you have still no solution to the error - I just ran over the same issue. It seems to be a problem with the CA certficate chain on the Windows computer. The details can be found at https://bugreports.qt-project.org/browse/QTBUG-20012.
Here's also a little class which fixes the ca chain so the error should not occur in the application.
#ifndef OPENSSLFIX_H
#define OPENSSLFIX_H
#include <QSslConfiguration>
/* this class fixes a problem with qt/openssl and expired ca certificates.
* the idea is taken from https://bugreports.qt-project.org/browse/QTBUG-20012
* which describes the problem and the workaround further. the workaround is
* scheduled for qt5, but will not be introduced into qt4.x.
*
* to use this fix just call it in main() before doing any network related
* stuff
*
* OpenSslFix::fixCaCertificates();
*
* it will go through the certificates and remove invalid certs from the chain,
* thus avoiding the error to arise.
*/
class OpenSslFix {
public:
static void fixCaCertificates()
{
QSslConfiguration config(QSslConfiguration::defaultConfiguration());
QList<QSslCertificate> in(config.caCertificates());
QList<QSslCertificate> out;
for (int i=0, size=in.size(); i<size; ++i) {
const QSslCertificate &c(in[i]);
if (c.isValid()) {
/* not expired -> add */
out << c;
continue;
}
/* check if the cert is already present in the output */
bool found = false;
for (int j=0, size=out.size(); j<size; ++j) {
if (isCertificateSameName(c, out[j])) {
/* already present... */
found = true;
break;
}
}
if (!found)
out << c;
}
/* now set the new list as the default */
config.setCaCertificates(out);
QSslConfiguration::setDefaultConfiguration(config);
}
private:
static inline bool isCertificateSameName(const QSslCertificate &cert1,
const QSslCertificate &cert2)
{
return cert1.subjectInfo(QSslCertificate::Organization) ==
cert2.subjectInfo(QSslCertificate::Organization) &&
cert1.subjectInfo(QSslCertificate::CommonName) ==
cert2.subjectInfo(QSslCertificate::CommonName) &&
cert1.subjectInfo(QSslCertificate::LocalityName) ==
cert2.subjectInfo(QSslCertificate::LocalityName) &&
cert1.subjectInfo(QSslCertificate::OrganizationalUnitName) ==
cert2.subjectInfo(QSslCertificate::OrganizationalUnitName) &&
cert1.subjectInfo(QSslCertificate::StateOrProvinceName) ==
cert2.subjectInfo(QSslCertificate::StateOrProvinceName) &&
cert1.subjectInfo(QSslCertificate::CountryName) ==
cert2.subjectInfo(QSslCertificate::CountryName);
}
};
#endif // OPENSSLFIX_H
Try to use QSslSocket::ignoreSslErrors() method.
I also had such problems and using this function solved them for me.

Resources