초기화와 종료 API¶
헤더 파일¶
OasisAPI.h
OasisAppDelgate 인터페이스¶
Oasis 초기화 시 OasisAppDelgate 인터페이스를 정의하여 사용할 수 있습니다. Oasis 컴포넌트가 어떠한 사유로 재시작이나 종료를 해야 경우에 OasisAppDelgate 인터페이스의 콜백 함수를 호출합니다.
Note
Oasis 초기화에 반드시 필요한 인터페이스는 아닙니다. 이 인터페이스는 현재 DDS 게이트웨이 프로세스에서만 사용되고 있습니다. 따라서 DDS 게이트웨이 프로세스를 사용하지 않는 응용 프로그램은 Oasis 초기화 시 이 인터페이스가 필요하지 않습니다.
OasisAppDelgate는 아래와 같이 정의되어 있습니다.
class OasisAppDelegate
{
public:
OasisAppDelegate();
virtual ~OasisAppDelegate();
virtual void triggerExit(int32_t reason_code, const void *reason_arg, const char *reason_string);
virtual bool queryExit();
};
- true: 종료해도 좋습니다.
- false: 종료하면 안 됩니다.
함수¶
- 0: 성공
- -1: 실패
oasis::initialize 함수의 매개변수로 전달할 수 있는 key-value map은 아래와 같습니다.
offs-log-flags 는 아래와 같은 값을 OR'ing 합니다.
#define OFFS_LOG_IO_WRITE_BEGIN 0x00000001
#define OFFS_LOG_IO_WRITE_END 0x00000002
#define OFFS_LOG_IO_READ_BEGIN 0x00000004
#define OFFS_LOG_IO_READ_END 0x00000008
#define OFFS_LOG_IO_VERBOSE 0x00000010
#define OFFS_LOG_IO_DEBUG 0x00000020
예로 Oasis 파일시스템에 읽거나 쓰는 로그를 출력하려면, 아래와 같이 설정합니다.
parameters["offs-log-flags"] = std::to_string(OFFS_LOG_IO_WRITE_BEGIN|OFFS_LOG_IO_WRITE_END|OFFS_LOG_IO_READ_BEGIN|OFFS_LOG_IO_READ_END);
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
종료 이유 코드는 아래와 같습니다.
enum {
kTriggerExitReasonCodeQuit = 0,
kTriggerExitReasonCodeRestart = 1,
kTriggerExitReasonCodeUpdated = 2,
kTriggerExitReasonCodeOutOfResource = 3,
};
Note
kTriggerExitReasonCodeOutOfResource 상황이 발생할 경우, 예를들어, 네트워크 버퍼를 할당할 수 없거나, 포트를 더 이상 할당할 수 없을 경우 등이 발생하면, query_exit 호출없이, 바로 trigger_exit를 호출할 수 있습니다. 실제 프로그램 종료는 응용 프로그램의 역할이기 때문에, 응용 프로그램은 실제 프로그램 종료없이, 자원 고갈 문제 원인을 해결할 수도 있습니다.
- true: 프로그램을 종료해도 좋습니다.
- false: 프로그램 종료가 허용되지 않습니다.
Caution
trigger_exit와 query_exit는 Oasis 컴포넌트 내부에서 사용됩니다. 응용 프로그램에서 MediaObserver나 HttpUpStreamDelegate 등 Oasis 인터페이스로 부터 유도된 사용자 정의 인터페이스의 콜백함수에서 이 함수들을 사용할 필요는 없습니다.
예제¶
#include "OasisAPI.h"
#include "OasisLog.h"
// oasis 네임스페이스를 명시하지 않으려면, 아래 줄의 코멘트를 제거합니다.
// using namespace oasis;
#define OFFS_QUEUE_SIZE_KBYTES (40*1024)
#define OFFS_CACHE_SIZE_KBYTES (1024)
#define MEDIA_CACHE_SIZE_KBYTES (40*1024)
int main(int argc, char *argv[])
{
int32_t err;
oasis::key_value_map_t parameters;
parameters["offs-qsize-max"] = std::to_string(OFFS_QUEUE_SIZE_KBYTES);
parameters["offs-overwrite-if-exist"] = "1";
parameters["offs-cache-size"] = std::to_string(OFFS_CACHE_SIZE_KBYTES);
parameters["media-cache-size"] = std::to_string(MEDIA_CACHE_SIZE_KBYTES);
parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG);
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
/*
여기에 Oasis 컴포넌트를 추가로 초기화하고 사용합니다.
*/
oasis::finalize();
return 0;
}
OasisAppDelegate 사용예¶
아래 예는 웹 클라이언트가
-
"/restart" URL에 접속하면
kTriggerExitReasonCodeRestart코드로 응응 프로그램을 종료하고,if(query_exit()) { trigger_exit(kTriggerExitReasonCodeRestart, nullptr, nullptr); netMessagePost(res); } else { netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request"); netMessagePost(res); } -
"/upload" URL 에 접속하여 파일을 업로드 할 경우,
kTriggerExitReasonCodeUpdated코드로 응응 프로그램을 종료합니다.if(query_exit()) { trigger_exit(kTriggerExitReasonCodeUpdated, nullptr, "file updated"); netMessagePost(res); } else { netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request"); netMessagePost(res); }
응응 프로그램 종료 시 종료 코드에 따라서 응용 프로그램 동작을 모니터링하는 관리 프로세스에서 그에 상응하는 조치를 취할 수 있습니다. 예를 들어, 아래 예에서 종료코드가 "2" 일 경우, 관리 프로세스는 사용자가 전송한 파일을 이용하여 업데이트를 하고, 응용 프로그램을 재시작할 수 있습니다.
Note
이 예는 OasisAppDelegate 사용 예를 보여주는 것으로 실제 응용 프로그램단에서 재시작이나 업데이트 용도로 OasisAppDelegate 정의하여 사용할 필요는 없습니다. 재시작이나 업데이트 등은 OasisAppDelegate 없이도 논리적으로 처리가 가능합니다.
#include "OasisAPI.h"
#include "OasisWeb.h"
#include "OasisLog.h"
#include "OasisUtil.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <signal.h>
#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>
#define DLOG_THIS 0x00080000
#undef DLOG_FLAGS
#define DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_THIS/**/)
#undef DLOG_TAG
#define DLOG_TAG "APP-DELEGATE"
using namespace oasis;
static bool continue_running = true;
static int32_t app_exit_code = 0; // 1: restart, 2: update
void cancel_handler(int signum)
{
continue_running = false;
}
class MyAppDelegate : public OasisAppDelegate
{
public:
MyAppDelegate(bool run_in_background);
virtual ~MyAppDelegate();
virtual void triggerExit(int32_t reason_code, const void *reason_arg, const char *reason_string);
virtual bool queryExit();
protected:
bool run_in_background_;
};
MyAppDelegate::MyAppDelegate(bool run_in_background)
: run_in_background_(run_in_background)
{
}
MyAppDelegate::~MyAppDelegate()
{
}
void MyAppDelegate::triggerExit(int32_t reason_code, const void *reason_arg, const char *reason_string)
{
if(reason_code == kTriggerExitReasonCodeRestart) {
app_exit_code = 1;
} else if(reason_code == kTriggerExitReasonCodeUpdated) {
app_exit_code = 2;
} else {
app_exit_code = -1;
}
continue_running = false;
}
bool MyAppDelegate::queryExit()
{
if(!run_in_background_) {
TRACE0("Can not exit because it runs in terminal.\n");
}
return run_in_background_;
}
class MyHttpUpStreamDelegate : public HttpUpStreamDelegate
{
public:
MyHttpUpStreamDelegate() { }
virtual ~MyHttpUpStreamDelegate() { }
public:
virtual void onRequest(const NetMessageRef &msg) {
std::string url;
netMessageUriGetPath(msg, url);
ASSERT(netMessageIsRequest(msg));
if(!netMessageIsRequest(msg)) {
return;
}
NetMessageRef res = netMessageCreate(msg, false);
if(res == nullptr) {
netMessageAbortConnection(msg);
return;
}
std::string method;
netMessageGetMethod(msg, method);
fprintf(stdout, "onRequest [%s]: %s\n", method.c_str(), url.c_str());
netMessageSetStatusCodeAndReasonString(res, 200, "OK");
netMessageSetHeaderField(res, "Connection", "close");
if(url == "/upload") {
//send the upload data form.
std::string upload_form = R"(
<form action="/update-now" method="POST" enctype="multipart/form-data">
<label for="file-upload">Select a file:</label>
<input type="file" id="file-upload" name="myFile">
<button type="submit">Upload</button>
</form>)";
netMessageSetContent(res, "text/html", upload_form.c_str(), upload_form.size());
netMessagePost(res);
} else if(url == "/update-now") {
std::string content_type, filename;
char *filedata;
ssize_t size = netMessageGetFormDataFileData(msg, "myFile", content_type, filename, &filedata);
if(size <= 0) {
netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
netMessagePost(res);
return;
}
fprintf(stdout, "myFile: %s, content type %s, size %zd\n", filename.c_str(), content_type.c_str(), size);
//save to the data folder
if(query_exit()) {
trigger_exit(kTriggerExitReasonCodeUpdated, nullptr, "file updated");
netMessagePost(res);
} else {
netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
netMessagePost(res);
}
} else if(url == "/restart") {
if(query_exit()) {
trigger_exit(kTriggerExitReasonCodeRestart, nullptr, nullptr);
netMessagePost(res);
} else {
netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
netMessagePost(res);
}
} else {
netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
netMessagePost(res);
}
}
virtual void onResponse(const NetMessageRef &msg) {
}
};
void printUsage(const char *pgname)
{
fprintf(stderr, "USAGE: %s [-p <port number>]\n", pgname);
}
int main(int argc, char* argv[])
{
int32_t err;
int c;
int32_t http_port = 80;
std::shared_ptr<MyHttpUpStreamDelegate> http_delegate;
HttpServerRef http_server;
//assume this app is running in background so that the /restart request is granted.
std::shared_ptr<MyAppDelegate> app_delegate = std::make_shared<MyAppDelegate>(true);
opterr = 0;
while ((c = getopt(argc, argv, "p:h")) != -1) {
switch (c) {
case 'p':
if(optarg == nullptr) {
fprintf(stderr, "error: bad option argument '%c'\n", optopt);
return -1;
}
http_port = atoi(optarg);
if(http_port <= 0 || http_port > 65535) {
fprintf(stderr, "bad port number: %d\n", http_port);
return -1;
}
break;
case 'h':
default:
printUsage(argv[0]);
return -1;
}
}
key_value_map_t parameters;
parameters["offs-disable"] = "1";
if(oasis::initialize(parameters, app_delegate) < 0) {
fprintf(stderr, "Oasis init failed\n");
return -1;
}
parameters.clear();
parameters["port"] = std::to_string(http_port);
parameters["root-dir"] = "/tmp";
parameters["content-length-max"] = std::to_string(200*1024*1024);
parameters["cache-dir"] = "/tmp";
parameters["cached-content-types"] = "application/octet-stream,application/zip";
http_delegate = std::make_shared<MyHttpUpStreamDelegate>();
http_server = oasis::createHttpServer(parameters, http_delegate);
if (http_server) {
err = oasis::startHttpServer(http_server);
ASSERT(err == 0);
if(err < 0) {
fprintf(stderr, "HTTPS server failed\n");
goto done;
}
fprintf(stdout, "HTTP running at %d...\n", http_port);
}
do {
usleep(100000);
} while(continue_running);
done:
if(http_server) {
oasis::destroyHttpServer(http_server);
}
oasis::finalize();
fprintf(stdout, "app exit code: %d\n", (uint8_t)(app_exit_code&0xff));
return app_exit_code;
}
아래는 실행 로그 예입니다.
OFFS disabled (virtual file size max.: 1024 MBytes)
HTTP running at 80...
onRequest [GET]: /upload
onRequest [GET]: /favicon.ico
onRequest [POST]: /update-now
myFile: app_v101.zip, content type application/zip, size 2301259
app exit code: 2