Cutelyst  2.3.0
application.cpp
1 /*
2  * Copyright (C) 2013-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "application_p.h"
19 
20 #include "config.h"
21 #include "common.h"
22 #include "context_p.h"
23 #include "enginerequest.h"
24 #include "request.h"
25 #include "request_p.h"
26 #include "controller.h"
27 #include "controller_p.h"
28 #include "response.h"
29 #include "response_p.h"
30 #include "dispatchtype.h"
31 #include "view.h"
32 #include "stats.h"
33 #include "utils.h"
34 
35 #include <QtCore/QDir>
36 #include <QtCore/QStringList>
37 #include <QtCore/QDataStream>
38 #include <QtCore/QCoreApplication>
39 #include <QtCore/QPluginLoader>
40 #include <QtCore/QTranslator>
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QLocale>
43 
44 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER, "cutelyst.dispatcher")
45 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH, "cutelyst.dispatcher.path")
46 Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED, "cutelyst.dispatcher.chained")
47 Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER, "cutelyst.controller")
48 Q_LOGGING_CATEGORY(CUTELYST_CORE, "cutelyst.core")
49 Q_LOGGING_CATEGORY(CUTELYST_ENGINE, "cutelyst.engine")
50 Q_LOGGING_CATEGORY(CUTELYST_UPLOAD, "cutelyst.upload")
51 Q_LOGGING_CATEGORY(CUTELYST_MULTIPART, "cutelyst.multipart")
52 Q_LOGGING_CATEGORY(CUTELYST_VIEW, "cutelyst.view")
53 Q_LOGGING_CATEGORY(CUTELYST_REQUEST, "cutelyst.request")
54 Q_LOGGING_CATEGORY(CUTELYST_RESPONSE, "cutelyst.response")
55 Q_LOGGING_CATEGORY(CUTELYST_STATS, "cutelyst.stats")
56 Q_LOGGING_CATEGORY(CUTELYST_COMPONENT, "cutelyst.component")
57 
58 using namespace Cutelyst;
59 
60 Application::Application(QObject *parent) :
61  QObject(parent),
62  d_ptr(new ApplicationPrivate)
63 {
64  Q_D(Application);
65 
66  d->q_ptr = this;
67  d->headers.setHeader(QStringLiteral("X_CUTELYST"), QStringLiteral(VERSION));
68 
69  qRegisterMetaType<ParamsMultiMap>();
70  qRegisterMetaTypeStreamOperators<ParamsMultiMap>("ParamsMultiMap");
71 
72  d->dispatcher = new Dispatcher(this);
73 
74  loadTranslations(QStringLiteral("cutelystcore"));
75 }
76 
77 Application::~Application()
78 {
79  delete d_ptr;
80 }
81 
83 {
84  qCDebug(CUTELYST_CORE) << "Default Application::init called on pid:" << QCoreApplication::applicationPid();
85  return true;
86 }
87 
89 {
90  qCDebug(CUTELYST_CORE) << "Default Application::postFork called on pid:" << QCoreApplication::applicationPid();
91  return true;
92 }
93 
95 {
96  Q_D(Application);
97  return d->headers;
98 }
99 
101 {
102  Q_D(Application);
103  if (d->plugins.contains(plugin)) {
104  return false;
105  }
106  d->plugins.append(plugin);
107  return true;
108 }
109 
111 {
112  Q_D(Application);
113  const auto name = QString::fromLatin1(controller->metaObject()->className());
114  if (d->controllersHash.contains(name)) {
115  return false;
116  }
117  d->controllersHash.insert(name, controller);
118  d->controllers.append(controller);
119  return true;
120 }
121 
123 {
124  Q_D(Application);
125  if (d->views.contains(view->name())) {
126  qCWarning(CUTELYST_CORE) << "Not registering View." << view->metaObject()->className()
127  << "There is already a view with this name:" << view->name();
128  return false;
129  }
130  d->views.insert(view->name(), view);
131  return true;
132 }
133 
135 {
136  Q_D(Application);
137  if (d->dispatchers.contains(dispatcher)) {
138  return false;
139  }
140  d->dispatchers.append(dispatcher);
141  return true;
142 }
143 
144 Component *Application::createComponentPlugin(const QString &name, QObject *parent)
145 {
146  Q_D(Application);
147  auto it = d->factories.constFind(name);
148  if (it != d->factories.constEnd()) {
149  ComponentFactory *factory = it.value();
150  if (factory) {
151  return factory->createComponent(parent);
152  } else {
153  return nullptr;
154  }
155  }
156 
157  const QByteArrayList dirs = QByteArrayList{ QByteArrayLiteral(CUTELYST_PLUGINS_DIR) } + qgetenv("CUTELYST_PLUGINS_DIR").split(';');
158  for (const QByteArray &dir : dirs) {
159  Component *component = d->createComponentPlugin(name, parent, QString::fromLocal8Bit(dir));
160  if (component) {
161  return component;
162  }
163  }
164 
165  return nullptr;
166 }
167 
169 {
170  return VERSION;
171 }
172 
173 QVector<Cutelyst::Controller *> Application::controllers() const
174 {
175  Q_D(const Application);
176  return d->controllers;
177 }
178 
179 View *Application::view(const QString &name) const
180 {
181  Q_D(const Application);
182  return d->views.value(name);
183 }
184 
185 QVariant Application::config(const QString &key, const QVariant &defaultValue) const
186 {
187  Q_D(const Application);
188  auto it = d->config.constFind(key);
189  if (it != d->config.constEnd()) {
190  return it.value();
191  }
192  return defaultValue;
193 }
194 
196 {
197  Q_D(const Application);
198  return d->dispatcher;
199 }
200 
201 QVector<Cutelyst::DispatchType *> Application::dispatchers() const
202 {
203  Q_D(const Application);
204  return d->dispatcher->dispatchers();
205 }
206 
207 QVector<Plugin *> Application::plugins() const
208 {
209  Q_D(const Application);
210  return d->plugins;
211 }
212 
213 QVariantMap Application::config() const
214 {
215  Q_D(const Application);
216  return d->config;
217 }
218 
219 QString Application::pathTo(const QString &path) const
220 {
221  QDir home = config(QStringLiteral("home")).toString();
222  return home.absoluteFilePath(path);
223 }
224 
225 QString Cutelyst::Application::pathTo(const QStringList &path) const
226 {
227  QDir home = config(QStringLiteral("home")).toString();
228  return home.absoluteFilePath(path.join(QLatin1Char('/')));
229 }
230 
232 {
233  Q_D(const Application);
234  return d->init;
235 }
236 
238 {
239  Q_D(const Application);
240  return d->engine;
241 }
242 
243 void Application::setConfig(const QString &key, const QVariant &value)
244 {
245  Q_D(Application);
246  d->config.insert(key, value);
247 }
248 
250 {
251  Q_D(Application);
252 
253  if (d->init) {
254  return true;
255  }
256  d->init = true;
257 
258  d->useStats = CUTELYST_STATS().isDebugEnabled();
259  d->engine = engine;
260  d->config = engine->config(QLatin1String("Cutelyst"));
261 
262  d->setupHome();
263 
264  // Call the virtual application init
265  // to setup Controllers plugins stuff
266  if (init()) {
267  d->setupChildren(children());
268 
269  bool zeroCore = engine->workerCore() == 0;
270 
271  QVector<QStringList> tablePlugins;
272  const auto plugins = d->plugins;
273  for (Plugin *plugin : plugins) {
274  if (plugin->objectName().isEmpty()) {
275  plugin->setObjectName(QString::fromLatin1(plugin->metaObject()->className()));
276  }
277  tablePlugins.append({ plugin->objectName() });
278  // Configure plugins
279  plugin->setup(this);
280  }
281 
282  if (zeroCore && !tablePlugins.isEmpty()) {
283  qCDebug(CUTELYST_CORE) << Utils::buildTable(tablePlugins, QStringList(),
284  QLatin1String("Loaded plugins:")).constData();
285  }
286 
287  if (zeroCore) {
288  QVector<QStringList> tableDataHandlers;
289  tableDataHandlers.append({ QLatin1String("application/x-www-form-urlencoded") });
290  tableDataHandlers.append({ QLatin1String("application/json") });
291  tableDataHandlers.append({ QLatin1String("multipart/form-data") });
292  qCDebug(CUTELYST_CORE) << Utils::buildTable(tableDataHandlers, QStringList(),
293  QLatin1String("Loaded Request Data Handlers:")).constData();
294 
295  qCDebug(CUTELYST_CORE) << "Loaded dispatcher" << QString::fromLatin1(d->dispatcher->metaObject()->className());
296  qCDebug(CUTELYST_CORE) << "Using engine" << QString::fromLatin1(d->engine->metaObject()->className());
297  }
298 
299  QString home = d->config.value(QLatin1String("home")).toString();
300  if (home.isEmpty()) {
301  if (zeroCore) {
302  qCDebug(CUTELYST_CORE) << "Couldn't find home";
303  }
304  } else {
305  QFileInfo homeInfo = home;
306  if (homeInfo.isDir()) {
307  if (zeroCore) {
308  qCDebug(CUTELYST_CORE) << "Found home" << home;
309  }
310  } else {
311  if (zeroCore) {
312  qCDebug(CUTELYST_CORE) << "Home" << home << "doesn't exist";
313  }
314  }
315  }
316 
317  QVector<QStringList> table;
318  QStringList controllerNames = d->controllersHash.keys();
319  controllerNames.sort();
320  for (const QString &controller : controllerNames) {
321  table.append({ controller, QLatin1String("Controller")});
322  }
323 
324  const auto views = d->views;
325  for (View *view : views) {
326  if (view->reverse().isEmpty()) {
327  const QString className = QString::fromLatin1(view->metaObject()->className()) + QLatin1String("->execute");
328  view->setReverse(className);
329  }
330  table.append({ view->reverse(), QLatin1String("View")});
331  }
332 
333  if (zeroCore && !table.isEmpty()) {
334  qCDebug(CUTELYST_CORE) << Utils::buildTable(table, {
335  QLatin1String("Class"), QLatin1String("Type")
336  },
337  QLatin1String("Loaded components:")).constData();
338  }
339 
340  const auto controllers = d->controllers;
341  for (Controller *controller : controllers) {
342  controller->d_ptr->init(this, d->dispatcher);
343  }
344 
345  d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
346 
347  if (zeroCore) {
348  qCInfo(CUTELYST_CORE) << qPrintable(QString::fromLatin1("%1 powered by Cutelyst %2, Qt %3.")
349  .arg(QCoreApplication::applicationName(),
350  QLatin1String(Application::cutelystVersion()),
351  QLatin1String(qVersion())));
352  }
353 
354  Q_EMIT preForked(this);
355 
356  return true;
357  }
358 
359  return false;
360 }
361 
363 {
364  Q_D(Application);
365 
366  Engine *engine = d->engine;
367 
368  auto priv = new ContextPrivate(this, engine, d->dispatcher, d->plugins);
369  auto c = new Context(priv);
370 
371  request->context = c;
372  priv->engineRequest = request;
373  priv->response = new Response(d->headers, request);
374  priv->request = new Request(request);
375 
376  Stats *stats = nullptr;
377  if (d->useStats) {
378  stats = new Stats(this);
379  priv->stats = stats;
380  }
381 
382  // Process request
383  bool skipMethod = false;
384  Q_EMIT beforePrepareAction(c, &skipMethod);
385 
386  if (!skipMethod) {
387  static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
388  if (log) {
389  d->logRequest(priv->request);
390  }
391 
392  d->dispatcher->prepareAction(c);
393 
394  Q_EMIT beforeDispatch(c);
395 
396  d->dispatcher->dispatch(c);
397 
398  Q_EMIT afterDispatch(c);
399  }
400 
401  request->finalize();
402 
403  if (stats) {
404  qCDebug(CUTELYST_STATS, "Response Code: %d; Content-Type: %s; Content-Length: %s",
405  c->response()->status(),
406  qPrintable(c->response()->headers().header(QStringLiteral("CONTENT_TYPE"), QStringLiteral("unknown"))),
407  qPrintable(c->response()->headers().header(QStringLiteral("CONTENT_LENGTH"), QStringLiteral("unknown"))));
408 
409  quint64 endOfRequest = engine->time();
410  double enlapsed = (endOfRequest - request->startOfRequest) / 1000000.0;
411  QString average;
412  if (enlapsed == 0.0) {
413  average = QStringLiteral("??");
414  } else {
415  average = QString::number(1.0 / enlapsed, 'f');
416  average.truncate(average.size() - 3);
417  }
418  qCInfo(CUTELYST_STATS) << qPrintable(QStringLiteral("Request took: %1s (%2/s)\n%3")
419  .arg(QString::number(enlapsed, 'f'), average, QString::fromLatin1(stats->report())));
420  delete stats;
421  }
422 }
423 
425 {
426  Q_D(Application);
427 
428  if (!postFork()) {
429  return false;
430  }
431 
432  const auto controllers = d->controllers;
433  for (Controller *controller : controllers) {
434  if (!controller->postFork(this)) {
435  return false;
436  }
437  }
438 
439  Q_EMIT postForked(this);
440 
441  return true;
442 }
443 
444 void Application::addTranslator(const QLocale &locale, QTranslator *translator)
445 {
446  Q_D(Application);
447  Q_ASSERT_X(translator, "add translator to application", "invalid QTranslator object");
448  auto it = d->translators.find(locale);
449  if (it != d->translators.end()) {
450  it.value().prepend(translator);
451  } else {
452  d->translators.insert(locale, QVector<QTranslator*>(1, translator));
453  }
454 }
455 
456 void Application::addTranslator(const QString &locale, QTranslator *translator)
457 {
458  addTranslator(QLocale(locale), translator);
459 }
460 
461 void Application::addTranslators(const QLocale &locale, const QVector<QTranslator *> &translators)
462 {
463  Q_D(Application);
464  Q_ASSERT_X(!translators.empty(), "add translators to application", "empty translators vector");
465  auto transIt = d->translators.find(locale);
466  if (transIt != d->translators.end()) {
467  for (auto it = translators.crbegin(); it != translators.crend(); ++it) {
468  transIt.value().prepend(*it);
469  }
470  } else {
471  d->translators.insert(locale, translators);
472  }
473 }
474 
475 static void replacePercentN(QString *result, int n)
476 {
477  if (n >= 0) {
478  auto percentPos = 0;
479  auto len = 0;
480  while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len)) != -1) {
481  len = 1;
482  QString fmt;
483  if (result->at(percentPos + len) == QLatin1Char('L')) {
484  ++len;
485  fmt = QStringLiteral("%L1");
486  } else {
487  fmt = QStringLiteral("%1");
488  }
489  if (result->at(percentPos + len) == QLatin1Char('n')) {
490  fmt = fmt.arg(n);
491  ++len;
492  result->replace(percentPos, len, fmt);
493  len = fmt.length();
494  }
495  }
496  }
497 }
498 
499 QString Application::translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation, int n) const
500 {
501  QString result;
502 
503  if (!sourceText) {
504  return result;
505  }
506 
507  Q_D(const Application);
508 
509  const QVector<QTranslator*> translators = d->translators.value(locale);
510  if (translators.empty()) {
511  result = QString::fromUtf8(sourceText);
512  replacePercentN(&result, n);
513  return result;
514  }
515 
516  for (QTranslator *translator : translators) {
517  result = translator->translate(context, sourceText, disambiguation, n);
518  if (!result.isEmpty()) {
519  break;
520  }
521  }
522 
523  if (result.isEmpty()) {
524  result = QString::fromUtf8(sourceText);
525  }
526 
527  replacePercentN(&result, n);
528  return result;
529 }
530 
531 void Application::loadTranslations(const QString &filename, const QString &directory, const QString &prefix, const QString &suffix)
532 {
533  loadTranslationsFromDir(filename, directory, prefix, suffix);
534 }
535 
536 QVector<QLocale> Application::loadTranslationsFromDir(const QString &filename, const QString &directory, const QString &prefix, const QString &suffix)
537 {
538  QVector<QLocale> locales;
539 
540  if (Q_LIKELY(!filename.isEmpty())) {
541  const QString _dir = directory.isEmpty() ? QStringLiteral(I18NDIR) : directory;
542  const QDir i18nDir(_dir);
543  if (Q_LIKELY(i18nDir.exists())) {
544  const QString _prefix = prefix.isEmpty() ? QStringLiteral(".") : prefix;
545  const QString _suffix = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
546  const QStringList namesFilter = QStringList({filename + _prefix + QLatin1Char('*') + _suffix});
547 
548  const QFileInfoList tsFiles = i18nDir.entryInfoList(namesFilter, QDir::Files);
549  if (Q_LIKELY(!tsFiles.empty())) {
550  locales.reserve(tsFiles.size());
551  for (const QFileInfo &ts : tsFiles) {
552  const QString fn = ts.fileName();
553  const int prefIdx = fn.indexOf(_prefix);
554  const QString locString = fn.mid(prefIdx + _prefix.length(), fn.length() - prefIdx - _suffix.length() - _prefix.length());
555  QLocale loc(locString);
556  if (Q_LIKELY(loc.language() != QLocale::C)) {
557  auto trans = new QTranslator(this);
558  if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
559  addTranslator(loc, trans);
560  locales.append(loc);
561  qCDebug(CUTELYST_CORE) << "Loaded translations for" << loc << "from" << ts.absoluteFilePath();
562  } else {
563  delete trans;
564  qCWarning(CUTELYST_CORE) << "Can not load translations for" << loc << "from" << ts.absoluteFilePath();
565  }
566  } else {
567  qCWarning(CUTELYST_CORE) << "Can not load translations for invalid locale string" << locString;
568  }
569  }
570  locales.squeeze();
571  } else {
572  qCWarning(CUTELYST_CORE) << "Can not find translation files for" << filename << "in directory" << _dir;
573  }
574  } else {
575  qCWarning(CUTELYST_CORE) << "Can not load translations from not existing directory:" << _dir;
576  }
577  } else {
578  qCWarning(CUTELYST_CORE) << "Can not load translations for empty file name.";
579  }
580 
581  return locales;
582 }
583 
584 QVector<QLocale> Application::loadTranslationsFromDirs(const QString &directory, const QString &filename)
585 {
586  QVector<QLocale> locales;
587 
588  if (Q_LIKELY(!directory.isEmpty() && !filename.isEmpty())) {
589  const QDir dir(directory);
590  if (Q_LIKELY(dir.exists())) {
591  const auto dirs = dir.entryList(QDir::AllDirs);
592  if (Q_LIKELY(!dirs.empty())) {
593  locales.reserve(dirs.size());
594  for (const QString &subDir : dirs) {
595  const QString relFn = subDir + QLatin1Char('/') + filename;
596  if (dir.exists(relFn)) {
597  const QLocale l(subDir);
598  if (Q_LIKELY(l.language() != QLocale::C)) {
599  auto trans = new QTranslator(this);
600  const QFileInfo fi(dir, relFn);
601  if (Q_LIKELY(trans->load(l, fi.baseName(), QString(), fi.absolutePath(), fi.suffix()))) {
602  addTranslator(l, trans);
603  locales.append(l);
604  qCDebug(CUTELYST_CORE) << "Loaded translations for" << l << "from" << fi.absoluteFilePath();
605  } else {
606  delete trans;
607  qCWarning(CUTELYST_CORE) << "Can not load translations for" << l << "from" << fi.absoluteFilePath();
608  }
609  } else {
610  qCWarning(CUTELYST_CORE) << "Can not load translations for invalid locale string:" << subDir;
611  }
612  }
613  }
614  locales.squeeze();
615  } else {
616  qCWarning(CUTELYST_CORE) << "Can not find locale dirs under" << directory;
617  }
618  } else {
619  qCWarning(CUTELYST_CORE) << "Can not load translations from not existing directory:" << directory;
620  }
621  } else {
622  qCWarning(CUTELYST_CORE) << "Can not load translations for empty file name or directory name";
623  }
624 
625  return locales;
626 }
627 
628 void Cutelyst::ApplicationPrivate::setupHome()
629 {
630  // Hook the current directory in config if "home" is not set
631  if (!config.contains(QLatin1String("home"))) {
632  config.insert(QStringLiteral("home"), QDir::currentPath());
633  }
634 
635  if (!config.contains(QLatin1String("root"))) {
636  QDir home = config.value(QLatin1String("home")).toString();
637  config.insert(QStringLiteral("root"), home.absoluteFilePath(QLatin1String("root")));
638  }
639 }
640 
641 void ApplicationPrivate::setupChildren(const QObjectList &children)
642 {
643  Q_Q(Application);
644  for (QObject *child : children) {
645  auto controller = qobject_cast<Controller *>(child);
646  if (controller) {
647  q->registerController(controller);
648  continue;
649  }
650 
651  auto plugin = qobject_cast<Plugin *>(child);
652  if (plugin) {
653  q->registerPlugin(plugin);
654  continue;
655  }
656 
657  auto view = qobject_cast<View *>(child);
658  if (view) {
659  q->registerView(view);
660  continue;
661  }
662 
663  auto dispatchType = qobject_cast<DispatchType *>(child);
664  if (dispatchType) {
665  q->registerDispatcher(dispatchType);
666  continue;
667  }
668  }
669 }
670 
671 void Cutelyst::ApplicationPrivate::logRequest(Request *req)
672 {
673  QString path = req->path();
674  if (path.isEmpty()) {
675  path = QStringLiteral("/");
676  }
677  qCDebug(CUTELYST_REQUEST) << req->method() << "request for" << path << "from" << req->addressString();
678 
679  ParamsMultiMap params = req->queryParameters();
680  if (!params.isEmpty()) {
681  logRequestParameters(params, QLatin1String("Query Parameters are:"));
682  }
683 
684  params = req->bodyParameters();
685  if (!params.isEmpty()) {
686  logRequestParameters(params, QLatin1String("Body Parameters are:"));
687  }
688 
689  const auto uploads = req->uploads();
690  if (!uploads.isEmpty()) {
691  logRequestUploads(uploads);
692  }
693 }
694 
695 void Cutelyst::ApplicationPrivate::logRequestParameters(const ParamsMultiMap &params, const QString &title)
696 {
697  QVector<QStringList> table;
698  auto it = params.constBegin();
699  while (it != params.constEnd()) {
700  table.append({ it.key(), it.value() });
701  ++it;
702  }
703  qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table, {
704  QLatin1String("Parameter"),
705  QLatin1String("Value"),
706  },
707  title).constData();
708 }
709 
710 void Cutelyst::ApplicationPrivate::logRequestUploads(const QVector<Cutelyst::Upload *> &uploads)
711 {
712  QVector<QStringList> table;
713  for (Upload *upload : uploads) {
714  table.append({ upload->name(),
715  upload->filename(),
716  upload->contentType(),
717  QString::number(upload->size())
718  });
719  }
720  qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table, {
721  QLatin1String("Parameter"),
722  QLatin1String("Filename"),
723  QLatin1String("Type"),
724  QLatin1String("Size"),
725  },
726  QLatin1String("File Uploads are:")).constData();
727 }
728 
729 Component *ApplicationPrivate::createComponentPlugin(const QString &name, QObject *parent, const QString &directory)
730 {
731  Component *component = nullptr;
732 
733  QDir pluginsDir(directory);
734  QPluginLoader loader;
735  ComponentFactory *factory = nullptr;
736  const auto plugins = pluginsDir.entryList(QDir::Files);
737  for (const QString &fileName : plugins) {
738  loader.setFileName(pluginsDir.absoluteFilePath(fileName));
739  const QJsonObject json = loader.metaData()[QLatin1String("MetaData")].toObject();
740  if (json[QLatin1String("name")].toString() == name) {
741  QObject *plugin = loader.instance();
742  if (plugin) {
743  factory = qobject_cast<ComponentFactory *>(plugin);
744  if (!factory) {
745  qCCritical(CUTELYST_CORE) << "Could not create a factory for" << loader.fileName();
746  } else {
747  component = factory->createComponent(parent);
748  }
749  break;
750  } else {
751  qCCritical(CUTELYST_CORE) << "Could not load plugin" << loader.fileName() << loader.errorString();
752  }
753  }
754  }
755 
756  if (factory) {
757  factories.insert(name, factory);
758  }
759 
760  return component;
761 }
762 
763 #include "moc_application.cpp"
QMap< QString, QString > ParamsMultiMap
void setConfig(const QString &key, const QVariant &value)
View * view(const QString &name=QString()) const
void setReverse(const QString &reverse)
Definition: component.cpp:57
void afterDispatch(Context *c)
Engine * engine() const
QVector< Controller * > controllers() const
bool registerView(View *view)
void loadTranslations(const QString &filename, const QString &directory=QString(), const QString &prefix=QString(), const QString &suffix=QString())
T plugin()
Returns the registered plugin that casts to the template type T.
Definition: application.h:115
bool setup(Engine *engine)
Called by the Engine to setup the internal data.
void addTranslators(const QLocale &locale, const QVector< QTranslator *> &translators)
QVariantMap config() const
The Cutelyst Component base class.
Definition: component.h:38
Dispatcher * dispatcher() const
void handleRequest(Cutelyst::EngineRequest *request)
Called by the Engine to handle a new Request object.
QVector< Plugin * > plugins() const
Cutelyst Upload handles file upload request
Definition: upload.h:35
ParamsMultiMap bodyParameters() const
Definition: request.cpp:209
Headers & defaultHeaders()
Definition: application.cpp:94
The Cutelyst Context.
Definition: context.h:50
Cutelyst Controller base class
Definition: controller.h:102
QVector< Upload * > uploads() const
Definition: request.cpp:339
bool registerController(Controller *controller)
QString addressString() const
Definition: request.cpp:48
virtual bool init()
Definition: application.cpp:82
QString name() const
Definition: component.cpp:39
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
Context * context
The Cutelyst::Context of this request.
QVariantMap config(const QString &entity) const
user configuration for the application
Definition: engine.cpp:323
void addTranslator(const QLocale &locale, QTranslator *translator)
QString pathTo(const QString &path) const
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
void beforePrepareAction(Context *c, bool *skipMethod)
QString reverse() const
Definition: component.cpp:51
void beforeDispatch(Context *c)
bool registerDispatcher(DispatchType *dispatcher)
void postForked(Application *app)
ParamsMultiMap queryParameters() const
Definition: request.cpp:245
bool enginePostFork()
Called by the Engine once post fork happened.
void preForked(Application *app)
virtual QByteArray report()
Definition: stats.cpp:62
virtual bool postFork()
Definition: application.cpp:88
Cutelyst View abstract view component
Definition: view.h:33
QVector< DispatchType * > dispatchers() const
virtual quint64 time()
Definition: engine.cpp:159
The Cutelyst Application.
Definition: application.h:55
quint64 startOfRequest
The timestamp of the start of headers.
bool registerPlugin(Plugin *plugin)
virtual Component * createComponent(QObject *parent=nullptr)=0
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
The Cutelyst Engine.
Definition: engine.h:33
QVector< QLocale > loadTranslationsFromDirs(const QString &directory, const QString &filename)
The Cutelyst Dispatcher.
Definition: dispatcher.h:40
int workerCore() const
Each worker process migth have a number of worker cores (threads), a single process with two worker t...
Definition: engine.cpp:122
static const char * cutelystVersion()
void finalize()
Called by Application to deal with finalizing cookies, headers and body.
QString translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const