초기화와 종료 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();


};
void triggerExit ( int32_t reason_code , const void * reason_arg , const char * reason_string )
OasisAPI.h
Oasis가 예약된 자원이나 네트워크 프로토콜의 심각한 오류 등으로 종료해야 하는 경우에 이 함수가 호출 됩니다. 응용 프로그램은 모든 자원을 해제하고 종료하거나, 문제 원인을 해결하고 종료없이 계속 동작할 수 있습니다.
매개변수
reason_code  종료 이유 코드입니다. 아래 trigger_exit API 함수 참고합니다.
reason_arg  종류 이유에 따른 상세 데이터 값입니다.
reason_string  종류 이유 코드에 대한 설명입니다.
bool queryExit ( )
OasisAPI.h
응응 프로그램에게 종료를 해도 되는지 여부를 묻습니다. 만일 응용프로그램이 백그라운드나 서비스로 동작할 경우, false를 리턴하여 종료 요청을 무시할 수 있습니다.
리턴값
  • true: 종료해도 좋습니다.
  • false: 종료하면 안 됩니다.

함수

int32_t initialize ( key_value_map_t & parameters )
OasisAPI.h
Oasis를 초기화 합니다.
매개변수
parameters  초기화에 필요한 key-value map 입니다.
리턴값
  • 0: 성공
  • -1: 실패

oasis::initialize 함수의 매개변수로 전달할 수 있는 key-value map은 아래와 같습니다.

기본값
필수
설명
offs-qsize-max
10240
 
파일시스템 내부의 쓰기용 Q 최대 크기를 지정합니다. 실제 메모리를 할당하지는 않습니다. KBytes 단위입니다.
offs-disable
0
 
파일시스템을 사용하지 않습니다. "1"인 경우, 녹화나 재생 등의 컴포넌트에서 파일 저장과 읽기를 할 수 없습니다. Oasis 파일 시스템을 사용하지 않더라도 미디어 파일 읽기와 쓰기가 필요한 경우, "1"로 설정합니다.
offs-overwrite-if-exist
1
 
같은 파일명의 파일이 있으면 덮어쓸지 여부를 지정합니다. 1값이면 덮어씁니다. 덮어쓰지 않을 경우, 이름을 변경하여 생성합니다.
offs-cache-size
0
 
파일시스템 내부의 읽기용 캐쉬 크기를 지정합니다. 메모리를 할당합니다. KBytes 단위입니다. 0 값이면 사용하지 않습니다.
offs-log-flags
0
 
파일시스템 내부 로그를 활성화합니다.
media-cache-size
4096
 
녹화용 미디어 데이터 임시 저장 캐쉬 크기를 지정합니다. 녹화 시 이벤트 발생 시점 이전 시간의 데이터 보관 용도입니다. 녹화 트랙 개수와 오디오 비디오 인코딩 비트레이트, 메타데이터 크기 등을 고려하여 충분한 공간이 필요합니다. Kbytes 단위입니다.
oasis-log-flags
0
 
Oasis 컴포넌트 로그를 활성화합니다. OasisAPI.h에 정의된 OASIS_LOG_DEBUG(0x02)가 있습니다.
system-font-path
 
Oasis가 사용하는 폰트 파일 경로입니다. TrueType 폰트를 지원합니다.
system-font-size
10
 
Oasis가 사용하는 폰트 기본 크기입니다. 포인트 단위입니다.

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);
int32_t initialize ( key_value_map_t & parameters , const std::shared_ptr<OasisAppDelegate> & app_delegate )
OasisAPI.h
OasisAppDelegate로 부터 유도된 사용자 정의 객체와 함께 Oasis를 초기화합니다. Oasis 컴포넌트가 어떠한 사유로 재시작이나 종료를 해야 할 경우 OasisAppDelegate로 콜백 함수를 호출합니다.
매개변수
parameters  초기화에 필요한 key-value map 입니다.
app_delegate  OasisAppDelegate로 부터 유도된 사용자 정의 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t finalize ( )
Oasis를 해제합니다.
리턴값
  • 0: 성공
  • -1: 실패
void trigger_exit ( int32_t reason_code , const void * reason_arg , const char * reason_string )
OasisAPI.h
응용 프로그램이 Oasis 초기화 시에 OasisAppDelgate로 유도된 사용자 정의 객체를 인수로 전달할 경우, OasisAppDelgate::triggerExit 콜백 함수를 호출합니다. 응용 프로그램은 이 콜백함수에서 프로그램 종료 조치를 취할 수 있습니다. 만일 OasisAppDelgate로 유도된 사용자 정의 객체 없이 Oasis를 초기화한 경우, 이 함수는 아무 동작없이 바로 리턴됩니다.
매개변수
reason_code  종료 이유 코드입니다.
reason_arg  종료 이유 코드에 따른 상세 데이터입니다. nullptr 일 수 있습니다.
reason_string  종료 이유 코드에 따른 상세 설명입니다. nullptr 일 수 있습니다.

종료 이유 코드는 아래와 같습니다.

enum {
    kTriggerExitReasonCodeQuit = 0,
    kTriggerExitReasonCodeRestart = 1,
    kTriggerExitReasonCodeUpdated = 2,
    kTriggerExitReasonCodeOutOfResource = 3,
};

Note

kTriggerExitReasonCodeOutOfResource 상황이 발생할 경우, 예를들어, 네트워크 버퍼를 할당할 수 없거나, 포트를 더 이상 할당할 수 없을 경우 등이 발생하면, query_exit 호출없이, 바로 trigger_exit를 호출할 수 있습니다. 실제 프로그램 종료는 응용 프로그램의 역할이기 때문에, 응용 프로그램은 실제 프로그램 종료없이, 자원 고갈 문제 원인을 해결할 수도 있습니다.

bool query_exit ( )
OasisAPI.h
응용 프로그램이 Oasis 초기화 시에 OasisAppDelgate로 유도된 사용자 정의 객체를 인수로 전달할 경우, OasisAppDelgate::queryExit 콜백 함수를 호출합니다. 응용 프로그램은 이 콜백함수에서 프로그램의 종료 여부를 체크할 수 있습니다. 만일 OasisAppDelgate로 유도된 사용자 정의 객체 없이 Oasis를 초기화한 경우, 이 함수는 아무 동작없이 바로 리턴됩니다.
리턴값
  • true: 프로그램을 종료해도 좋습니다.
  • false: 프로그램 종료가 허용되지 않습니다.

Caution

trigger_exitquery_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