ServerContext.h
ServerIndex index_;
IStorageArea& area_;
StorageCache storageCache_;
ServerContext.cpp
ServerContext::ServerContext(IDatabaseWrapper& database,
IStorageArea& area,
bool unitTesting,
size_t maxCompletedJobs) :
index_(*this, database, (unitTesting ? 20 : 500)),
area_(area),
compressionEnabled_(false),
storeMD5_(true),
largeDicomThrottler_(1),
dicomCache_(DICOM_CACHE_SIZE),
mainLua_(*this),
filterLua_(*this),
luaListener_(*this),
jobsEngine_(maxCompletedJobs),
#if ORTHANC_ENABLE_PLUGINS == 1
plugins_(NULL),
#endif
done_(false),
haveJobsChanged_(false),
isJobsEngineUnserialized_(false),
metricsRegistry_(new MetricsRegistry),
isHttpServerSecure_(true),
isExecuteLuaEnabled_(false),
overwriteInstances_(false),
dcmtkTranscoder_(new DcmtkTranscoder),
isIngestTranscoding_(false),
ingestTranscodingOfUncompressed_(true),
ingestTranscodingOfCompressed_(true),
preferredTransferSyntax_(DicomTransferSyntax_LittleEndianExplicit),
deidentifyLogs_(false)
{
try
{
unsigned int lossyQuality;
{
OrthancConfiguration::ReaderLock lock;
queryRetrieveArchive_.reset(
new SharedArchive(lock.GetConfiguration().GetUnsignedIntegerParameter("QueryRetrieveSize", 100)));
mediaArchive_.reset(
new SharedArchive(lock.GetConfiguration().GetUnsignedIntegerParameter("MediaArchiveSize", 1)));
defaultLocalAet_ = lock.GetConfiguration().GetOrthancAET();
jobsEngine_.SetWorkersCount(lock.GetConfiguration().GetUnsignedIntegerParameter("ConcurrentJobs", 2));
saveJobs_ = lock.GetConfiguration().GetBooleanParameter("SaveJobs", true);
metricsRegistry_->SetEnabled(lock.GetConfiguration().GetBooleanParameter("MetricsEnabled", true));
// New configuration options in Orthanc 1.5.1
findStorageAccessMode_ = StringToFindStorageAccessMode(lock.GetConfiguration().GetStringParameter("StorageAccessOnFind", "Always"));
limitFindInstances_ = lock.GetConfiguration().GetUnsignedIntegerParameter("LimitFindInstances", 0);
limitFindResults_ = lock.GetConfiguration().GetUnsignedIntegerParameter("LimitFindResults", 0);
// New configuration option in Orthanc 1.6.0
storageCommitmentReports_.reset(new StorageCommitmentReports(lock.GetConfiguration().GetUnsignedIntegerParameter("StorageCommitmentReportsSize", 100)));
// New options in Orthanc 1.7.0
transcodeDicomProtocol_ = lock.GetConfiguration().GetBooleanParameter("TranscodeDicomProtocol", true);
builtinDecoderTranscoderOrder_ = StringToBuiltinDecoderTranscoderOrder(lock.GetConfiguration().GetStringParameter("BuiltinDecoderTranscoderOrder", "After"));
lossyQuality = lock.GetConfiguration().GetUnsignedIntegerParameter("DicomLossyTranscodingQuality", 90);
std::string s;
if (lock.GetConfiguration().LookupStringParameter(s, "IngestTranscoding"))
{
if (LookupTransferSyntax(ingestTransferSyntax_, s))
{
isIngestTranscoding_ = true;
LOG(WARNING) << "Incoming DICOM instances will automatically be transcoded to "
<< "transfer syntax: " << GetTransferSyntaxUid(ingestTransferSyntax_);
// New options in Orthanc 1.8.2
ingestTranscodingOfUncompressed_ = lock.GetConfiguration().GetBooleanParameter("IngestTranscodingOfUncompressed", true);
ingestTranscodingOfCompressed_ = lock.GetConfiguration().GetBooleanParameter("IngestTranscodingOfCompressed", true);
LOG(WARNING) << " Ingest transcoding will "
<< (ingestTranscodingOfUncompressed_ ? "be applied" : "*not* be applied")
<< " to uncompressed transfer syntaxes (Little Endian Implicit/Explicit, Big Endian Explicit)";
LOG(WARNING) << " Ingest transcoding will "
<< (ingestTranscodingOfCompressed_ ? "be applied" : "*not* be applied")
<< " to compressed transfer syntaxes";
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Unknown transfer syntax for ingest transcoding: " + s);
}
}
else
{
isIngestTranscoding_ = false;
LOG(INFO) << "Automated transcoding of incoming DICOM instances is disabled";
}
// New options in Orthanc 1.8.2
if (lock.GetConfiguration().GetBooleanParameter("DeidentifyLogs", true))
{
deidentifyLogs_ = true;
CLOG(INFO, DICOM) << "Deidentification of log contents (notably for DIMSE queries) is enabled";
DicomVersion version = StringToDicomVersion(
lock.GetConfiguration().GetStringParameter("DeidentifyLogsDicomVersion", "2021b"));
CLOG(INFO, DICOM) << "Version of DICOM standard used for deidentification is "
<< EnumerationToString(version);
logsDeidentifierRules_.SetupAnonymization(version);
}
else
{
deidentifyLogs_ = false;
CLOG(INFO, DICOM) << "Deidentification of log contents (notably for DIMSE queries) is disabled";
}
// New options in Orthanc 1.9.0
if (lock.GetConfiguration().LookupStringParameter(s, "DicomScuPreferredTransferSyntax") &&
!LookupTransferSyntax(preferredTransferSyntax_, s))
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Unknown preferred transfer syntax: " + s);
}
CLOG(INFO, DICOM) << "Preferred transfer syntax for Orthanc C-STORE SCU: "
<< GetTransferSyntaxUid(preferredTransferSyntax_);
lock.GetConfiguration().GetAcceptedTransferSyntaxes(acceptedTransferSyntaxes_);
isUnknownSopClassAccepted_ = lock.GetConfiguration().GetBooleanParameter("UnknownSopClassAccepted", false);
}
jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200);
listeners_.push_back(ServerListener(luaListener_, "Lua"));
changeThread_ = boost::thread(ChangeThread, this, (unitTesting ? 20 : 100));
dynamic_cast<DcmtkTranscoder&>(*dcmtkTranscoder_).SetLossyQuality(lossyQuality);
}
catch (OrthancException&)
{
Stop();
throw;
}
}
ServerContext::~ServerContext()
{
if (!done_)
{
LOG(ERROR) << "INTERNAL ERROR: ServerContext::Stop() should be invoked manually to avoid mess in the destruction order!";
Stop();
}
}
main.cpp
static bool ConfigureServerContext(IDatabaseWrapper& database,
IStorageArea& storageArea,
OrthancPlugins *plugins,
bool loadJobsFromDatabase)
{
size_t maxCompletedJobs;
{
OrthancConfiguration::ReaderLock lock;
// These configuration options must be set before creating the
// ServerContext, otherwise the possible Lua scripts will not be
// able to properly issue HTTP/HTTPS queries
HttpClient::ConfigureSsl(lock.GetConfiguration().GetBooleanParameter("HttpsVerifyPeers", true),
lock.GetConfiguration().InterpretStringParameterAsPath
(lock.GetConfiguration().GetStringParameter("HttpsCACertificates", "")));
HttpClient::SetDefaultVerbose(lock.GetConfiguration().GetBooleanParameter("HttpVerbose", false));
// The value "0" below makes the class HttpClient use its default
// value (DEFAULT_HTTP_TIMEOUT = 60 seconds in Orthanc 1.5.7)
HttpClient::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpTimeout", 0));
HttpClient::SetDefaultProxy(lock.GetConfiguration().GetStringParameter("HttpProxy", ""));
DicomAssociationParameters::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScuTimeout", 10));
maxCompletedJobs = lock.GetConfiguration().GetUnsignedIntegerParameter("JobsHistorySize", 10);
if (maxCompletedJobs == 0)
{
LOG(WARNING) << "Setting option \"JobsHistorySize\" to zero is not recommended";
}
// Configuration of DICOM TLS for Orthanc SCU (since Orthanc 1.9.0)
DicomAssociationParameters::SetDefaultOwnCertificatePath(
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_PRIVATE_KEY, ""),
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_CERTIFICATE, ""));
DicomAssociationParameters::SetDefaultTrustedCertificatesPath(
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_TRUSTED_CERTIFICATES, ""));
DicomAssociationParameters::SetDefaultMaximumPduLength(
lock.GetConfiguration().GetUnsignedIntegerParameter(KEY_MAXIMUM_PDU_LENGTH, 16384));
// New option in Orthanc 1.9.3
DicomAssociationParameters::SetDefaultRemoteCertificateRequired(
lock.GetConfiguration().GetBooleanParameter(KEY_DICOM_TLS_REMOTE_CERTIFICATE_REQUIRED, true));
}
ServerContext context(database, storageArea, false /* not running unit tests */, maxCompletedJobs);
{
OrthancConfiguration::ReaderLock lock;
context.SetCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("StorageCompression", false));
context.SetStoreMD5ForAttachments(lock.GetConfiguration().GetBooleanParameter("StoreMD5ForAttachments", true));
// New option in Orthanc 1.4.2
context.SetOverwriteInstances(lock.GetConfiguration().GetBooleanParameter("OverwriteInstances", false));
try
{
context.GetIndex().SetMaximumPatientCount(lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumPatientCount", 0));
}
catch (...)
{
context.GetIndex().SetMaximumPatientCount(0);
}
try
{
uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageSize", 0);
context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
}
catch (...)
{
context.GetIndex().SetMaximumStorageSize(0);
}
try
{
std::string mode = lock.GetConfiguration().GetStringParameter("MaximumStorageMode", "Recycle");
context.GetIndex().SetMaximumStorageMode(StringToMaxStorageMode(mode));
}
catch (...)
{
context.GetIndex().SetMaximumStorageMode(MaxStorageMode_Recycle);
}
try
{
uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageCacheSize", 128);
context.SetMaximumStorageCacheSize(size * 1024 * 1024);
}
catch (...)
{
context.SetMaximumStorageCacheSize(128);
}
}
{
ServerContextConfigurator configurator(context, plugins); // This calls "OrthancConfiguration::SetServerIndex()"
{
OrthancConfiguration::WriterLock lock;
lock.GetConfiguration().LoadModalitiesAndPeers();
}
return ConfigureHttpHandler(context, plugins, loadJobsFromDatabase);
}
}
static bool ConfigureServerContext(IDatabaseWrapper& database,
IStorageArea& storageArea,
OrthancPlugins *plugins,
bool loadJobsFromDatabase)
{
size_t maxCompletedJobs;
{
OrthancConfiguration::ReaderLock lock;
// These configuration options must be set before creating the
// ServerContext, otherwise the possible Lua scripts will not be
// able to properly issue HTTP/HTTPS queries
HttpClient::ConfigureSsl(lock.GetConfiguration().GetBooleanParameter("HttpsVerifyPeers", true),
lock.GetConfiguration().InterpretStringParameterAsPath
(lock.GetConfiguration().GetStringParameter("HttpsCACertificates", "")));
HttpClient::SetDefaultVerbose(lock.GetConfiguration().GetBooleanParameter("HttpVerbose", false));
// The value "0" below makes the class HttpClient use its default
// value (DEFAULT_HTTP_TIMEOUT = 60 seconds in Orthanc 1.5.7)
HttpClient::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpTimeout", 0));
HttpClient::SetDefaultProxy(lock.GetConfiguration().GetStringParameter("HttpProxy", ""));
DicomAssociationParameters::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScuTimeout", 10));
maxCompletedJobs = lock.GetConfiguration().GetUnsignedIntegerParameter("JobsHistorySize", 10);
if (maxCompletedJobs == 0)
{
LOG(WARNING) << "Setting option \"JobsHistorySize\" to zero is not recommended";
}
// Configuration of DICOM TLS for Orthanc SCU (since Orthanc 1.9.0)
DicomAssociationParameters::SetDefaultOwnCertificatePath(
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_PRIVATE_KEY, ""),
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_CERTIFICATE, ""));
DicomAssociationParameters::SetDefaultTrustedCertificatesPath(
lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_TRUSTED_CERTIFICATES, ""));
DicomAssociationParameters::SetDefaultMaximumPduLength(
lock.GetConfiguration().GetUnsignedIntegerParameter(KEY_MAXIMUM_PDU_LENGTH, 16384));
// New option in Orthanc 1.9.3
DicomAssociationParameters::SetDefaultRemoteCertificateRequired(
lock.GetConfiguration().GetBooleanParameter(KEY_DICOM_TLS_REMOTE_CERTIFICATE_REQUIRED, true));
}
ServerContext context(database, storageArea, false /* not running unit tests */, maxCompletedJobs);
{
OrthancConfiguration::ReaderLock lock;
context.SetCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("StorageCompression", false));
context.SetStoreMD5ForAttachments(lock.GetConfiguration().GetBooleanParameter("StoreMD5ForAttachments", true));
// New option in Orthanc 1.4.2
context.SetOverwriteInstances(lock.GetConfiguration().GetBooleanParameter("OverwriteInstances", false));
try
{
context.GetIndex().SetMaximumPatientCount(lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumPatientCount", 0));
}
catch (...)
{
context.GetIndex().SetMaximumPatientCount(0);
}
try
{
uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageSize", 0);
context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
}
catch (...)
{
context.GetIndex().SetMaximumStorageSize(0);
}
try
{
std::string mode = lock.GetConfiguration().GetStringParameter("MaximumStorageMode", "Recycle");
context.GetIndex().SetMaximumStorageMode(StringToMaxStorageMode(mode));
}
catch (...)
{
context.GetIndex().SetMaximumStorageMode(MaxStorageMode_Recycle);
}
try
{
uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageCacheSize", 128);
context.SetMaximumStorageCacheSize(size * 1024 * 1024);
}
catch (...)
{
context.SetMaximumStorageCacheSize(128);
}
}
{
ServerContextConfigurator configurator(context, plugins); // This calls "OrthancConfiguration::SetServerIndex()"
{
OrthancConfiguration::WriterLock lock;
lock.GetConfiguration().LoadModalitiesAndPeers();
}
return ConfigureHttpHandler(context, plugins, loadJobsFromDatabase);
}
}
static bool ConfigureDatabase(IDatabaseWrapper& database,
IStorageArea& storageArea,
OrthancPlugins *plugins,
bool upgradeDatabase,
bool loadJobsFromDatabase)
{
database.Open();
unsigned int currentVersion = database.GetDatabaseVersion();
if (upgradeDatabase)
{
UpgradeDatabase(database, storageArea);
return false; // Stop and don't restart Orthanc (cf. issue 29)
}
else if (currentVersion != ORTHANC_DATABASE_VERSION)
{
throw OrthancException(ErrorCode_IncompatibleDatabaseVersion,
"The database schema must be upgraded from version " +
boost::lexical_cast<std::string>(currentVersion) + " to " +
boost::lexical_cast<std::string>(ORTHANC_DATABASE_VERSION) +
": Please run Orthanc with the \"--upgrade\" argument");
}
{
static const char* const CHECK_REVISIONS = "CheckRevisions";
OrthancConfiguration::ReaderLock lock;
if (lock.GetConfiguration().GetBooleanParameter(CHECK_REVISIONS, false))
{
if (database.HasRevisionsSupport())
{
LOG(INFO) << "Handling of revisions is enabled, and the custom database back-end *has* "
<< "support for revisions of metadata and attachments";
}
else
{
LOG(WARNING) << "The custom database back-end has *no* support for revisions of metadata and attachments, "
<< "but configuration option \"" << CHECK_REVISIONS << "\" is set to \"true\"";
}
static const char* const STORE_MD5 = "StoreMD5ForAttachments";
if (!lock.GetConfiguration().GetBooleanParameter(STORE_MD5, true))
{
throw OrthancException(
ErrorCode_ParameterOutOfRange,
"The revision system is enabled by configuration option \"" + std::string(CHECK_REVISIONS) +
"\", but won't work properly for attachments if \"" + std::string(STORE_MD5) + "\" is set to \"false\"");
}
}
}
bool success = ConfigureServerContext
(database, storageArea, plugins, loadJobsFromDatabase);
database.Close();
return success;
}
static bool ConfigurePlugins(int argc,
char* argv[],
bool upgradeDatabase,
bool loadJobsFromDatabase)
{
std::unique_ptr<IDatabaseWrapper> databasePtr;
std::unique_ptr<IStorageArea> storage;
#if ORTHANC_ENABLE_PLUGINS == 1
std::string databaseServerIdentifier;
{
OrthancConfiguration::ReaderLock lock;
databaseServerIdentifier = lock.GetConfiguration().GetDatabaseServerIdentifier();
}
OrthancPlugins plugins(databaseServerIdentifier);
plugins.SetCommandLineArguments(argc, argv);
LoadPlugins(plugins);
IDatabaseWrapper* database = NULL;
if (plugins.HasDatabaseBackend())
{
LOG(WARNING) << "Using a custom database from plugins";
database = &plugins.GetDatabaseBackend();
}
else
{
databasePtr.reset(CreateDatabaseWrapper());
database = databasePtr.get();
}
if (plugins.HasStorageArea())
{
LOG(WARNING) << "Using a custom storage area from plugins";
storage.reset(plugins.CreateStorageArea());
}
else
{
storage.reset(CreateStorageArea());
}
assert(database != NULL);
assert(storage.get() != NULL);
return ConfigureDatabase(*database, *storage, &plugins,
upgradeDatabase, loadJobsFromDatabase);
#elif ORTHANC_ENABLE_PLUGINS == 0
// The plugins are disabled
databasePtr.reset(CreateDatabaseWrapper());
storage.reset(CreateStorageArea());
assert(databasePtr.get() != NULL);
assert(storage.get() != NULL);
return ConfigureDatabase(*databasePtr, *storage, NULL,
upgradeDatabase, loadJobsFromDatabase);
#else
# error The macro ORTHANC_ENABLE_PLUGINS must be set to 0 or 1
#endif
}