RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
js/rtb_router_js.cc
00001 /* rtb_router_js.cc
00002    Jeremy Barnes, 5 April 2011
00003    Copyright (c) 2011 Datacratic.  All rights reserved.
00004 
00005    JS bindings for RTB router.
00006 */
00007 
00008 #include "rtbkit/core/router/router_stack.h"
00009 #include "rtb_js.h"
00010 #include "auction_js.h"
00011 #include "banker_js.h"
00012 #include "v8.h"
00013 #include "node.h"
00014 #include "soa/js/js_value.h"
00015 #include "soa/js/js_utils.h"
00016 #include "soa/js/js_wrapped.h"
00017 #include "soa/js/js_call.h"
00018 #include "soa/js/js_registry.h"
00019 #include "jml/arch/timers.h"
00020 #include "soa/sigslot/slot.h"
00021 #include "jml/utils/guard.h"
00022 #include <boost/make_shared.hpp>
00023 #include "rtbkit/plugins/bidding_agent/bidding_agent.h"
00024 #include "node/uv.h"
00025 #include "soa/types/js/id_js.h"
00026 #include "bid_request_js.h"
00027 #include "currency_js.h"
00028 
00029 using namespace std;
00030 using namespace v8;
00031 using namespace node;
00032 using namespace ML;
00033 using namespace RTBKIT;
00034 
00035 namespace Datacratic {
00036 namespace JS {
00037 
00038 extern Registry registry;
00039 
00040 
00041 /*****************************************************************************/
00042 /* RTB ROUTER JS                                                             */
00043 /*****************************************************************************/
00044 
00051 const char * RTBRouterStackName = "Router";
00052 
00053 struct RTBRouterStackJS
00054     : public JSWrapped2<RTBKIT::RouterStack, RTBRouterStackJS, RTBRouterStackName,
00055                         rtbModule, false> {
00056 
00057     RTBRouterStackJS(const v8::Handle<v8::Object> & This,
00058                       const std::shared_ptr<RTBKIT::RouterStack> & router
00059                           = std::shared_ptr<RTBKIT::RouterStack>())
00060     {
00061         wrap(This, router);
00062     }
00063 
00064     static Handle<v8::Value>
00065     New(const Arguments & args)
00066     {
00067         try {
00068             new RTBRouterStackJS
00069                 (args.This(),
00070                  std::shared_ptr<RTBKIT::RouterStack>
00071                  (new RTBKIT::RouterStack(std::make_shared<ServiceProxies>(),
00072                                   getArg(args, 2, "router", "routerName"),
00073                                   getArg(args, 0, 2.0,
00074                                          "secondsUntilLossAssumed"))));
00075             return args.This();
00076         } HANDLE_JS_EXCEPTIONS;
00077     }
00078 
00079     static void Initialize()
00080     {
00081         Persistent<FunctionTemplate> t = Register(New);
00082 
00083         // Instance methods
00084         //NODE_SET_PROTOTYPE_METHOD(t, "initEndpoints", initEndpoints);
00085         NODE_SET_PROTOTYPE_METHOD(t, "createProxy", createProxy);
00086         NODE_SET_PROTOTYPE_METHOD(t, "bindAgents", bindAgents);
00087         NODE_SET_PROTOTYPE_METHOD(t, "start", start);
00088         NODE_SET_PROTOTYPE_METHOD(t, "sleepUntilIdle", sleepUntilIdle);
00089         NODE_SET_PROTOTYPE_METHOD(t, "shutdown", shutdown);
00090         NODE_SET_PROTOTYPE_METHOD(t, "injectAuction", injectAuction);
00091         NODE_SET_PROTOTYPE_METHOD(t, "injectWin", injectWin);
00092         NODE_SET_PROTOTYPE_METHOD(t, "injectLoss", injectLoss);
00093         NODE_SET_PROTOTYPE_METHOD(t, "injectCampaignEvent", injectCampaignEvent);
00094         NODE_SET_PROTOTYPE_METHOD(t, "numAuctionsInProgress",
00095                                   numAuctionsInProgress);
00096         NODE_SET_PROTOTYPE_METHOD(t, "getStats", getStats);
00097 
00098 #if 0
00099 
00100         t->InstanceTemplate()
00101             ->SetAccessor(String::NewSymbol("banker"),
00102                           bankerGetter, bankerSetter);
00103 #endif
00104 
00105 #if 0
00106         registerRWPropertyGetterSetter(&RTBKIT::RouterStack::getBanker,
00107                                        &RTBKIT::RouterStack::setBanker,
00108                                        "banker", v8::DontDelete);
00109 #endif
00110         registerMemberFn(&RTBKIT::RouterStack::notifyFinishedAuction,
00111                          "notifyFinishedAuction");
00112         registerMemberFn(&RTBKIT::RouterStack::notifyFinishedSpot,
00113                          "notifyFinishedSpot");
00114         registerMemberFn(&RTBKIT::RouterStack::init, "init");
00115     }
00116 
00117 #if 0
00118     static Handle<v8::Value>
00119     initEndpoints(const Arguments & args)
00120     {
00121         try {
00122             getShared(args)
00123                 ->initEndpoints(getArg(args, 0, -1, "bidPort"),
00124                                 //getArg(args, 1, -1, "backchannelPort"),
00125                                 getArg(args, 2, "localhost", "hostname"),
00126                                 getArg(args, 3,  1, "nThreads"));
00127             return args.This();
00128         } HANDLE_JS_EXCEPTIONS;
00129     }
00130 #endif
00131 
00132     static Handle<v8::Value>
00133     createProxy(const Arguments & args)
00134     {
00135         try {
00136 
00137             return JS::toJS
00138                 (std::make_shared<RTBKIT::BiddingAgent>
00139                  (getShared(args)->getServices(), "bidding_agent"));
00140         } HANDLE_JS_EXCEPTIONS;
00141     }
00142 
00143     static Handle<v8::Value>
00144     bindAgents(const Arguments & args)
00145     {
00146         try {
00147             getShared(args)
00148                 ->router.bindAgents(getArg(args, 0, "agentsUri"));
00149             return args.This();
00150         } HANDLE_JS_EXCEPTIONS;
00151     }
00152 
00153     static Handle<v8::Value>
00154     start(const Arguments & args)
00155     {
00156         try {
00157             // Make sure Node doesn't exit and we don't get GCd when the
00158             // event loop is running.
00159 
00160             struct ev_loop * loop = ev_default_loop(0);
00161             ev_ref(loop);
00162             v8::Persistent<v8::Object> phandle
00163                 = v8::Persistent<v8::Object>::New(args.This());
00164 
00165             auto cleanup = [=] ()
00166                 {
00167                     //cerr << "calling cleanup" << endl;
00168 
00169                     v8::Persistent<v8::Object> handle = phandle;
00170                     ev_unref(loop);
00171 
00172                     //cerr << "done loop unref" << endl;
00173                     //cerr << "depth " << ev_depth(loop) << endl;
00174                     //cerr << "iter " << ev_iteration(loop) << endl;
00175                     //cerr << "watching " << ev_pending_count(loop) << endl;
00176                     //ev_break(loop, EVBREAK_ONE);
00177 
00178                     handle.Clear();
00179                     handle.Dispose();
00180 
00181                 };
00182 
00183             getShared(args)->start(cleanup);
00184 
00185             // TODO: add something to node to stop it from returning?
00186 
00187             return args.This();
00188         } HANDLE_JS_EXCEPTIONS;
00189     }
00190 
00191     static Handle<v8::Value>
00192     shutdown(const Arguments & args)
00193     {
00194         try {
00195             getShared(args)->shutdown();
00196             //cerr << "finished shutdown" << endl;
00197             return args.This();
00198         } HANDLE_JS_EXCEPTIONS;
00199     }
00200 
00201     static Handle<v8::Value>
00202     sleepUntilIdle(const Arguments & args)
00203     {
00204         try {
00205             getShared(args)->sleepUntilIdle();
00206             //cerr << "finished shutdown" << endl;
00207             return args.This();
00208         } HANDLE_JS_EXCEPTIONS;
00209     }
00210 
00211     struct CallbackData {
00212         Slot onAuctionFinished;
00213         std::shared_ptr<Auction> auction;
00214         v8::Persistent<v8::Object> This;
00215 
00216         ~CallbackData()
00217         {
00218             if (!This.IsEmpty()) {
00219                 This.Dispose();
00220                 This.Clear();
00221             }
00222         }
00223     };
00224 
00225     static void doNothing(eio_req * req)
00226     {
00227         // TODO: don't do this; find how to use libeio properly
00228     }
00229 
00230     static int finishedCallback(eio_req * req)
00231     {
00232         HandleScope scope;
00233 
00234         auto_ptr<CallbackData> data((CallbackData *)req->data);
00235 
00236         TryCatch try_catch;
00237 
00238         try {
00239             const int argc = 1;
00240             v8::Handle<v8::Value> argv[argc] = { JS::toJS(data->auction) };
00241             data->onAuctionFinished.call(data->This, argc, argv);
00242         } catch (const JSPassException & exc) {
00243             // Corner case... but probably shouldn't happen
00244         } catch (...) {
00245             v8::Handle<v8::Value> result = translateCurrentException();
00246             v8::ThrowException(result);
00247         }
00248 
00249         if (try_catch.HasCaught())
00250             FatalException(try_catch);
00251 
00252         return 0;
00253     }
00254 
00255 
00256     static Handle<v8::Value>
00257     injectAuction(const Arguments & args)
00258     {
00259         try {
00260             Slot onAuctionFinished = getArg(args, 0, "onAuctionFinished");
00261             double startTime       = getArg(args, 2,
00262                                             Date::now().secondsSinceEpoch(),
00263                                             "startTime");
00264             double expiryTime      = getArg(args, 3, startTime + 0.03,
00265                                             "expiryTime");
00266             double lossTime        = getArg(args, 4,
00267                                             Date::positiveInfinity()
00268                                                 .secondsSinceEpoch(),
00269                                             "lossTime");
00270 
00271             std::shared_ptr<BidRequest> request;
00272             std::string requestStr;
00273 
00274             v8::Handle<v8::Value> requestArg = args[1];
00275             std::string requestFormat;
00276             if (requestArg->IsString()) {
00277                 requestStr = getArg<string>(args, 1, "requestStr");
00278                 requestFormat = getArg<string>(args, 5, "datacratic",
00279                                                "requestFormat");
00280                 request.reset(BidRequest::parse(requestFormat, requestStr));
00281             }
00282             else if (request = getBidRequestSharedPointer(requestArg)) {
00283                 requestStr = request->toJsonStr();
00284                 requestFormat = "datacratic";
00285             }
00286             else throw ML::Exception("don't know how to turn " + cstr(requestArg)
00287                                      + " into a bidRequest for injectAuction");
00288 
00289             CallbackData * data = new CallbackData();
00290             Call_Guard deleteDataGuard([&] () { delete data; });
00291 
00292             data->onAuctionFinished = onAuctionFinished;
00293             data->This = v8::Persistent<v8::Object>::New(args.This());
00294 
00295             /* Create an asynchronous, libeioified wrapper that will call
00296                back to the JS callback from the correct callback thread.
00297             */
00298             auto onAuctionFinished2 = [=] (std::shared_ptr<Auction> auction)
00299                 {
00300                     data->auction = auction;
00301 
00302                     eio_custom(doNothing, EIO_PRI_DEFAULT, finishedCallback,
00303                                data);
00304 
00305                     return true;
00306                 };
00307 
00308             getShared(args)
00309                 ->router.injectAuction(onAuctionFinished2,
00310                                        request, requestStr, requestFormat,
00311                                        startTime, expiryTime, lossTime);
00312 
00313             deleteDataGuard.clear();
00314 
00315             return args.This();
00316         } HANDLE_JS_EXCEPTIONS;
00317     }
00318 
00319     static Handle<v8::Value>
00320     injectWin(const Arguments & args)
00321     {
00322         try {
00323             Id auctionId = getArg(args, 0, "auctionId");
00324             Id adSpot = getArg(args, 1, "adspot");
00325             Amount winAmount = getArg(args, 2, "winPrice");
00326             Date timestamp
00327                 = Date::fromSecondsSinceEpoch
00328                     (getArg<double>(args, 3, "timestamp"));
00329             Json::Value meta = getArg(args, 4, Json::Value(), "meta");
00330             UserIds uids = getArg(args, 5, UserIds(), "userIds");
00331             AccountKey account = getArg(args, 6, AccountKey(), "account");
00332             Date bidTimestamp
00333                 = Date::fromSecondsSinceEpoch
00334                     (getArg<double>(args, 7, -1, "bidTimestamp"));
00335             getShared(args)->injectWin(auctionId, adSpot, winAmount,
00336                                        timestamp, meta, uids,
00337                                        account, bidTimestamp);
00338             return args.This();
00339         } HANDLE_JS_EXCEPTIONS;
00340     }
00341 
00342     static Handle<v8::Value>
00343     injectLoss(const Arguments & args)
00344     {
00345         try {
00346             Id auctionId = getArg(args, 0, "auctionId");
00347             Id adSpot = getArg(args, 1, "adspot");
00348             Date timestamp
00349                 = Date::fromSecondsSinceEpoch
00350                     (getArg<double>(args, 2, "timestamp"));
00351             Json::Value meta = getArg(args, 3, Json::Value(), "meta");
00352             AccountKey account = getArg(args, 4, AccountKey(), "account");
00353             Date bidTimestamp
00354                 = Date::fromSecondsSinceEpoch
00355                     (getArg<double>(args, 5, -1, "bidTimestamp"));
00356             getShared(args)->injectLoss(auctionId, adSpot, timestamp,
00357                                         meta, account, bidTimestamp);
00358             return args.This();
00359         } HANDLE_JS_EXCEPTIONS;
00360     }
00361 
00362     static Handle<v8::Value>
00363     injectCampaignEvent(const Arguments & args)
00364     {
00365         try {
00366             string label = getArg(args, 0, "label");
00367             Id auctionId = getArg(args, 1, "auctionId");
00368             Id adSpot = getArg(args, 2, "adspot");
00369             Date timestamp
00370                 = Date::fromSecondsSinceEpoch
00371                     (getArg<double>(args, 3, "timestamp"));
00372             Json::Value meta = getArg(args, 4, Json::Value(), "meta");
00373             UserIds uids = getArg(args, 5, UserIds(), "userIds");
00374             getShared(args)->injectCampaignEvent(label, auctionId, adSpot,
00375                                                  timestamp, meta, uids);
00376             return args.This();
00377         } HANDLE_JS_EXCEPTIONS;
00378     }
00379 
00380     static Handle<v8::Value>
00381     numAuctionsInProgress(const Arguments & args)
00382     {
00383         try {
00384             return JS::toJS(getShared(args)->numAuctionsInProgress());
00385         } HANDLE_JS_EXCEPTIONS;
00386     }
00387 
00388     static Handle<v8::Value>
00389     getStats(const Arguments & args)
00390     {
00391         try {
00392             return JS::toJS(getShared(args)->getStats());
00393         } HANDLE_JS_EXCEPTIONS;
00394     }
00395 
00396 #if 0
00397     static Handle<v8::Value>
00398     addBudget(const Arguments & args)
00399     {
00400         try {
00401             return JS::toJS(getShared(args)
00402                             ->addBudget(getArg(args, 0, "account"),
00403                                         getArg(args, 1, "amount")));
00404         } HANDLE_JS_EXCEPTIONS;
00405     }
00406 
00407     static Handle<v8::Value>
00408     setBudget(const Arguments & args)
00409     {
00410         try {
00411             return JS::toJS(getShared(args)
00412                             ->setBudget(getArg(args, 0, "account"),
00413                                         getArg(args, 1, "amount")));
00414         } HANDLE_JS_EXCEPTIONS;
00415     }
00416 #endif
00417 
00418 #if 0
00419     static v8::Handle<v8::Value>
00420     bankerGetter(v8::Local<v8::String> property,
00421                  const AccessorInfo & info)
00422     {
00423         try {
00424             return JS::toJS(getShared(info.This())->getBanker());
00425         } HANDLE_JS_EXCEPTIONS;
00426     }
00427 
00428     static void
00429     bankerSetter(v8::Local<v8::String> property,
00430                  v8::Local<v8::Value> value,
00431                  const AccessorInfo & info)
00432     {
00433         try {
00434             getShared(info.This())->setBanker(JS::fromJS(value));
00435         } HANDLE_JS_EXCEPTIONS_SETTER;
00436     }
00437 #endif
00438 
00439 };
00440 
00441 RegisterJsOps<void (std::shared_ptr<Auction>)> reg_handleAuction;
00442 
00443 } // namespace JS
00444 } // namespace Datacratic
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator