/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2019, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2019 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #ifndef _TfRevModule_h_Included_ #define _TfRevModule_h_Included_ #include "zmq.h" #include "OdaCommon.h" #include "Ed/EdCommandStack.h" #include "RxModule.h" #include "StaticRxObject.h" #include "DbDatabase.h" #define STL_USING_MAP #define STL_USING_VECTOR #include "OdaSTL.h" #include "Tf/TfRevisionControl.h" #include "RxDynamicModule.h" #include "DbCommandContext.h" #include "DbHistoryManager.h" #include "FlatMemStream.h" //#include "TfDwgFiler.h" //#include "../../Drawing/Source/Tf/TfModule.h" #include "StaticRxObject.h" #include "ExSystemServices.h" #include "OdFileBuf.h" #include "ExHostAppServices.h" #ifdef OD_HAVE_CONSOLE_H_FILE #include #endif #include "DebugStuff.h" #define SHA_SIZE 20 // 172.16.64.103 #define SERVER_CONF "tcp://172.16.64.103:4040" // "tcp://172.16.64.192:4040" static void internalRemove(const OdString& testFile) { #ifdef OD_HAVE_REMOVE_FUNC remove(testFile); #else DeleteFile(testFile.c_str()); #endif } class OdRcModule; enum OdRcCodes { kOdRcSha = 1, kOdRcPull, kOdRcPush, kOdRcClone, kOdRcStop, kOdRcOk, kOdRcNotOk, kRCDoNothink, kOdRcNeedPull, kOdRcReset }; class MyServices : public ExSystemServices, public ExHostAppServices { ODRX_USING_HEAP_OPERATORS(ExSystemServices); public: OdDbHostAppProgressMeter* newProgressMeter() { return NULL; } }; struct OdRcCommit : OdEdCommand { const OdString groupName() const { return L"Revision control git"; } const OdString globalName() const { return L"Commit"; } void execute(OdEdCommandContext* pCmdCtx); virtual OdInt32 flags() const { return OdEdCommand::flags() | OdEdCommand::kNoUndoMarker; } OdRcModule* _module; }; struct OdRcPush : OdEdCommand { const OdString groupName() const { return L"Revision control git"; } const OdString globalName() const { return L"Push"; } void execute(OdEdCommandContext* pCmdCtx); virtual OdInt32 flags() const { return OdEdCommand::flags() | OdEdCommand::kNoUndoMarker; } OdRcModule* _module; }; struct OdRcPull : OdEdCommand { const OdString groupName() const { return L"Revision control git"; } const OdString globalName() const { return L"Pull"; } void execute(OdEdCommandContext* pCmdCtx); virtual OdInt32 flags() const { return OdEdCommand::flags() | OdEdCommand::kNoUndoMarker; } OdRcModule* _module; }; struct OdRcStop : OdEdCommand { const OdString groupName() const { return L"Revision control git"; } const OdString globalName() const { return L"Stop_server"; } void execute(OdEdCommandContext* pCmdCtx); virtual OdInt32 flags() const { return OdEdCommand::flags() | OdEdCommand::kNoUndoMarker; } OdRcModule* _module; }; void initZMQ(void* &context, void* &socket) { context = zmq_ctx_new(); if (!context) throw OdException(L"Can't get context"); socket = zmq_socket(context, ZMQ_REQ); if (!socket) throw OdException(L"Can't get socket"); int conn = zmq_connect(socket, SERVER_CONF); if (conn != 0) throw OdException(OdString(L"Can't connect ") + SERVER_CONF); } template static void send(void* const socket, const T data, const OdInt8 &flag = 0, char *buf = 0, OdUInt16 countBytes = 0) { countBytes = countBytes ? countBytes : sizeof(T); zmq_msg_t message; OdInt8 res = zmq_msg_init_size(&message, countBytes); ODA_ASSERT(res == 0); if(buf) memcpy(zmq_msg_data(&message), buf, countBytes); else memcpy(zmq_msg_data(&message), &data, countBytes); OdInt32 countSendBytes = zmq_msg_send(&message, socket, flag); ODA_ASSERT(countSendBytes == countBytes); res = zmq_msg_close(&message); ODA_ASSERT(res == 0); } template static void receive(void* const socket, T& data, const OdUInt16 countBytes = 0, const OdInt8 &flag = 0) { zmq_msg_t reply; OdInt8 res = zmq_msg_init(&reply); ODA_ASSERT(res == 0); int len = zmq_msg_recv(&reply, socket, flag); ODA_ASSERT(len != -1); size_t length = zmq_msg_size(&reply); ODA_ASSERT(len == length); memcpy(&data, zmq_msg_data(&reply), length); res = zmq_msg_close(&reply); ODA_ASSERT(res == 0); } static void sendBigData(void* const socket, OdStreamBufPtr &buf) { OdUInt64 N = buf->length(); buf->rewind(); send(socket, N, ZMQ_SNDMORE); for (OdUInt64 n = 0; ;) { char subBuf[128]; if (n + 128 < N) { buf->getBytes(subBuf, 128); send(socket, 0, ZMQ_SNDMORE, subBuf, 128); n += 128; } else { buf->getBytes(subBuf, N - n); send(socket, 0, 0, subBuf, N - n); break; } } } struct GitLikeServer { OdString _path; OdDbDatabasePtr _db; OdDbHostAppServices* _appServices; void handler() { internalRemove(L"repository_s.dsf"); if (_path.isEmpty()) {//create new sqlite base _db = _appServices->createDatabase(true); OdTfRevisionControl::import(odrxSystemServices()->createFile(L"repository_s.dsf", Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kCreateAlways), _db); } else if(_path.right(4).iCompare(L".dsf") == 0) {//open created OdStreamBufPtr s = odrxSystemServices()->createFile(_path, Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kOpenAlways); _db = OdTfRevisionControl::checkout(s, _appServices, L"master");//TODO to expand } else if (_path.right(4).iCompare(L".dwg") == 0) { _db = _appServices->readFile(_path); OdTfRevisionControl::import(odrxSystemServices()->createFile(L"repository_s.dsf", Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kCreateAlways), _db); } else { throw OdError(L"Can't create/open repository"); } void* context = zmq_ctx_new(); if (!context) throw OdException(L"Can't get context"); void* socket = zmq_socket(context, ZMQ_REP); if (!socket) throw OdException(L"Can't get socket"); OdInt8 conn = zmq_bind(socket, SERVER_CONF); if (conn != 0) throw OdException(OdString(L"Can't bind to ") + SERVER_CONF); bool bStop = false; while (true) { if (bStop) break; OdRcCodes code; if(!reciveRcCode(socket, code)) continue; switch (code) { case kOdRcStop: { bStop = true; break; } case kOdRcSha: { ODA_FAIL(); break; } case kOdRcPush: { odPrintConsoleString(L"Start push\n"); OdTfDigest digest; OdTfRevisionControl::getBranchTip(_db, L"master", digest); send(socket, digest, 0, digest.data, sizeof(digest.data)); OdRcCodes code; if (!reciveRcCode(socket, code)) continue; if (code == kOdRcNeedPull) { odPrintConsoleString(L"Branch is ahead, need update on client\n"); send(socket, kOdRcNotOk); continue; //doPull(socket);//TODO do pull for client } if (code != kOdRcOk) { if (code == kOdRcReset) { ODA_FAIL(); } else { send(socket, kOdRcNotOk); ODA_FAIL(); continue; } } //TODO pattern work - need send to recive ??? OdUInt64 N = 0; receive(socket, N); OdStreamBufPtr sBuf = OdFlatMemStreamManaged::createNew(N); reciveBigData(N, socket, sBuf); send(socket, kOdRcOk);//TODO pattern of work: query - answer OdDbHandleArray hArr; OdTfRevisionControl::applyPatchViaBranch(_db, sBuf, hArr, kOdTfMergePreferTheirs, L"master"); odPrintConsoleString(L"End push\n"); odPrintConsoleString(L"- - - Show logs - - -\n"); OdTfDigest dig; OdTfRevisionControl::getBranchTip(_db, L"master", dig); { OdTfCommitInfo c; showLog(c, dig); } odPrintConsoleString(L"\n\n"); break; } case kOdRcPull: { doPull(socket); break; } case kOdRcClone: { odPrintConsoleString(L"Start clone\n"); _db->release(); _db.detach(); { OdStreamBufPtr pFile; try { if (_path.isEmpty()) pFile = odrxSystemServices()->createFile(L"repository_s.dsf", Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kOpenAlways); else if (_path.right(4).iCompare(L".dsf") == 0) pFile = odrxSystemServices()->createFile(_path, Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kOpenAlways); else if (_path.right(4).iCompare(L".dwg") == 0) pFile = odrxSystemServices()->createFile(L"repository_s.dsf", Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kOpenAlways); else throw OdError(L"Repository is lock"); } catch (OdError exp) { OdRcCodes code = kOdRcNotOk; send(socket, code); throw exp; } OdRcCodes code = kOdRcOk; send(socket, code, ZMQ_SNDMORE); sendBigData(socket, pFile); } OdStreamBufPtr s = odrxSystemServices()->createFile(L"repository_s.dsf", Oda::FileAccessMode(Oda::kFileWrite | Oda::kFileRead), Oda::kShareDenyWrite, Oda::kOpenAlways); _db = OdTfRevisionControl::checkout(s, _appServices, L"master"); odPrintConsoleString(L"End clone\n\n"); break; } } } zmq_close(socket); zmq_ctx_destroy(context); }; void showLog(OdTfCommitInfo c, OdTfDigest dig) { c = OdTfRevisionControl::getCommitInfo(_db, dig); //odPrintConsoleString(L"Branch '%10ls', Message '%10ls', Parents '%d':\n", c.author.c_str(), c.message.c_str(), c.parents.size());// -%c%c%c\n", c.author.c_str(), c.message.c_str(), c.parents.size(), dig.data[0], dig.data[1], dig.data[2]); odPrintConsoleString(L"Branch '%10ls', Message '%10ls', Parents '%d': -%c%c%c\n", c.author.c_str(), c.message.c_str(), c.parents.size(), dig.data[0], dig.data[1], dig.data[2]); for (unsigned int i = 0; i < c.parents.size(); ++i) { dig = c.parents[i]; showLog(c, dig); } }; void doPull(void* socket) { odPrintConsoleString(L"Start pull\n"); OdTfDigest shaRoot, shaTop; receive(socket, shaRoot.data, sizeof(shaRoot.data)); OdString currentBranch; OdTfRevisionControl::getBranchTip(_db, L"master", shaTop); if (shaTop == shaRoot || shaRoot.isNull() || shaTop.isNull()) { odPrintConsoleString(L"Pull no need. Heads are equals\n"); send(socket, kOdRcNotOk); odPrintConsoleString(L"End pull\n\n"); return; } send(socket, kOdRcOk, ZMQ_SNDMORE); OdStreamBufPtr sBuf = OdTfRevisionControl::makePatch(_db, shaRoot, shaTop); sendBigData(socket, sBuf); odPrintConsoleString(L"End pull\n\n"); } bool reciveBigData(OdUInt64 N, void* socket, OdStreamBufPtr& sBuf) { size_t length; zmq_msg_t reply; for (OdUInt32 n = 0; N - n;) { zmq_msg_init(&reply); zmq_msg_recv(&reply, socket, 0); length = zmq_msg_size(&reply); if (length < 1) { odPrintConsoleString(L"Something wrong on receive bigData\n"); ODA_ASSERT(length); zmq_msg_close(&reply); return false; } sBuf->putBytes(zmq_msg_data(&reply), (OdUInt32)length); n += (OdUInt32)length; } zmq_msg_close(&reply); sBuf->rewind(); return true; } bool reciveRcCode(void* socket, OdRcCodes& code) { zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, socket, 0); size_t length = zmq_msg_size(&reply); if (length != sizeof(OdRcCodes)) { odPrintConsoleString(L"Wrong command\n"); zmq_msg_close(&reply); return false; } memcpy(&code, zmq_msg_data(&reply), sizeof(OdRcCodes)); zmq_msg_close(&reply); return true; } }; namespace Git { static void startServer(const OdString& path, ExHostAppServices* appServ) { GitLikeServer serv; serv._path = path; serv._appServices = appServ; serv.handler(); }; } class OdRcModule : public OdRxModule { OdStaticRxObject m_RCPush; OdStaticRxObject m_RCUpdate; OdStaticRxObject m_RCStop; OdStaticRxObject m_RCCommit; OdTfDigest shaLastPull; OdTfDigest shaLastPush; public: OdTfDigest getShaLastPull() const { return shaLastPull; } void setShaLastPull(OdTfDigest val) { shaLastPull = val; } OdTfDigest getShaLastPush() const { return shaLastPush; } void setShaLastPush(OdTfDigest val) { shaLastPush = val; } void initApp(); void uninitApp(); }; #endif //_TfRevModule_h_Included_