Index: trunk/FACT++/src/InterpreterV8.cc
===================================================================
--- trunk/FACT++/src/InterpreterV8.cc	(revision 14546)
+++ trunk/FACT++/src/InterpreterV8.cc	(revision 14547)
@@ -21,4 +21,7 @@
 bool InterpreterV8::ReportException(TryCatch* try_catch)
 {
+    if (!try_catch->CanContinue())
+        return false;
+
     const HandleScope handle_scope;
 
@@ -27,6 +30,10 @@
     if (*exception && string(*exception)=="exit")
         return true;
+    if (*exception && string(*exception)=="null")
+        return false;
 
     const Handle<Message> message = try_catch->Message();
+    if (message.IsEmpty())
+        return false;
 
     // Print (filename):(line number): (message).
@@ -36,14 +43,10 @@
 
     if (*filename)
-        out << *filename;
-    if (!message.IsEmpty())
-        out << ": l." << message->GetLineNumber();
+        out << *filename << ": ";
+    out << "l." << message->GetLineNumber();
     if (*exception)
         out << ": " << *exception;
 
     JsException(out.str());
-
-    if (message.IsEmpty())
-        return false;
 
     // Print line of source code.
@@ -103,6 +106,7 @@
     const bool rc = ExecuteStringNT(code, file);
 
-    if (!exception.CanContinue())
-        return false;
+    // Check if this is a termination exception
+    //if (!exception.CanContinue())
+    //    return false;
 
     if (exception.HasCaught())
@@ -115,5 +119,5 @@
 {
     return ExecuteCode(String::New(code.c_str(), code.size()),
-                         String::New(file.c_str()));
+                       String::New(file.c_str()));
 }
 
@@ -148,10 +152,59 @@
         return ThrowException(String::New("Argument 1 not a string."));
 
-    if (!args[1]->IsInt32())
-        return ThrowException(String::New("Argument 2 not an int32."));
+    if (!args[1]->IsInt32() && !args[1]->IsString())
+        return ThrowException(String::New("Argument 2 not an int32 and not a string."));
 
     if (args.Length()==3 && !args[2]->IsUint32())
         return ThrowException(String::New("Argument 3 not an uint32."));
 
+    // Using a Javascript function has the advantage that it is fully
+    // interruptable without the need of C++ code
+
+    const string index   = args[1]->IsInt32() ? "s.index" : "s.name";
+    const bool   timeout = args.Length()==3;
+    const string arg0    = *String::Utf8Value(args[0]);
+    const string state   = args[1]->IsString() ? *String::Utf8Value(args[1]) : "";
+    const string arg1    = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
+
+    if (arg0.find_first_of("\"'")!=string::npos)
+        return ThrowException(String::New("Server name must not contain quotation marks."));
+
+    if (args[1]->IsString())
+        if (state.find_first_of("\"'")!=string::npos)
+            return ThrowException(String::New("State name must not contain quotation marks."));
+
+    string code =  "(function(name,state,ms)"
+                   "{";
+    if (timeout)
+        code +=       "var t = new Date();";
+    code +=           "while (1)"
+                      "{"
+                         "var s = dim.state(name);"
+                         "if(!"+index+")throw 'Waitig for state "+arg1+" of server "+arg0+" failed.';"
+                         "if(state=="+index+")return true;";
+    if (timeout)
+        code +=          "if((new Date()-t)>ms)return false;";
+
+    code +=              "dim.sleep();"
+                      "}"
+                   "})('"+arg0+"',"+arg1;
+    if (timeout)
+        code +=    "," + to_string(args[2]->Int32Value());
+    code +=        ");";
+
+    const HandleScope handle_scope;
+
+    // It is not strictly necessary to catch the exception, instead
+    // script->Run() could just be returned, but catching the
+    // exception allow to print the position in the file in
+    // the exception handler instead of just the posiiton in the script.
+    TryCatch exception;
+
+    const Handle<Script> script = Script::Compile(String::New(code.c_str()));
+    const Handle<Value>  result = script->Run();
+
+    return exception.HasCaught() ? exception.ReThrow() : result;
+
+    /*
     const string   server   = *String::Utf8Value(args[0]);
     const  int32_t state    = args[1]->Int32Value();
@@ -164,4 +217,5 @@
 
     return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
+        */
 }
 
@@ -193,5 +247,8 @@
     if (args.Length()==0)
     {
-        JsSleep(1);
+        // Theoretically, the CPU usage can be reduced by maybe a factor
+        // of four using a larger value, but this also means that the
+        // JavaScript is locked for a longer time.
+        usleep(1000);
         return Undefined();
     }
@@ -203,7 +260,20 @@
         return ThrowException(String::New("Argument 1 must be an uint32."));
 
-    JsSleep(args[0]->Int32Value());
-
-    return Undefined();
+    // Using a Javascript function has the advantage that it is fully
+    // interruptable without the need of C++ code
+
+    const string code =
+        "(function(){"
+        "var t=new Date();"
+        "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") dim.sleep();"
+        "})();";
+
+    const HandleScope handle_scope;
+
+    const Handle<Script> script = Script::Compile(String::New(code.c_str()));
+    return script->Run();
+
+    //JsSleep(args[0]->Int32Value());
+    //return Undefined();
 }
 
@@ -218,6 +288,4 @@
     // Return state.name/state.index
 
-    HandleScope handle_scope;
-
     const String::Utf8Value str(args[0]);
 
@@ -227,9 +295,20 @@
     //    return Undefined();
 
+    HandleScope handle_scope;
+
+    // It is important to catch the exception thrown
+    // by Date::New in case of thread termination!
+    Local<Value> date;
+    {
+        TryCatch exception;
+        date = Date::New(rc.time.JavaDate());
+        if (exception.HasCaught())
+            return exception.ReThrow();
+    }
+
     Handle<ObjectTemplate> obj = ObjectTemplate::New();
-
+    obj->Set(String::New("time"),  date);
     obj->Set(String::New("index"), rc.index<=-256?Undefined():Integer::New(rc.index),       ReadOnly);
     obj->Set(String::New("name"),  rc.index<=-256?Undefined():String::New(rc.name.c_str()), ReadOnly);
-    obj->Set(String::New("time"),  Date::New(rc.time.JavaDate()),                           ReadOnly);
     //obj->Set(String::New("toString"),  String::New(("[Object state "+string(*str)+":"+to_string(rc.index)+"]").c_str()));
 
@@ -239,5 +318,5 @@
 Handle<Value> InterpreterV8::FuncExit(const Arguments &)
 {
-    v8::V8::TerminateExecution(fThreadId);
+    V8::TerminateExecution(fThreadId);
     return ThrowException(String::New("exit"));
 /*
@@ -306,5 +385,5 @@
 Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
 {
-    for (int i = 0; i<args.Length(); i++)
+    for (int i=0; i<args.Length(); i++)
     {
         const HandleScope handle_scope;
@@ -329,5 +408,5 @@
     HandleScope handle_scope;
 
-    void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
+    void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
     if (!ptr)
         return Boolean::New(false);
@@ -354,5 +433,5 @@
     HandleScope handle_scope;
 
-    void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
+    void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
     if (!ptr)
         return Undefined();
@@ -452,22 +531,27 @@
                 }
 
+                time_t date = 0;
                 if (sql_type.find("TIMESTAMP")!=string::npos)
+                    date = mysqlpp::Time((*it)[i]);
+
+                if (sql_type.find("DATETIME")!=string::npos)
+                    date = mysqlpp::DateTime((*it)[i]);
+
+                if (sql_type.find(" DATE ")!=string::npos)
+                    date = mysqlpp::Date((*it)[i]);
+
+                if (date>0)
                 {
-                    row->Set(name, Date::New(time_t(mysqlpp::Time((*it)[i]))*1000), ReadOnly);
-                    continue;
+                    // It is important to catch the exception thrown
+                    // by Date::New in case of thread termination!
+                    TryCatch exception;
+                    Local<Value> val = Date::New(date*1000);
+                    if (exception.HasCaught())
+                        return exception.ReThrow();
+                    //if (V8::IsExecutionTerminating())
+                    //    return Undefined();
+
+                    row->Set(name, val, ReadOnly);
                 }
-
-                if (sql_type.find("DATETIME")!=string::npos)
-                {
-                    row->Set(name, Date::New(time_t(mysqlpp::DateTime((*it)[i]))*1000), ReadOnly);
-                    continue;
-                }
-
-                if (sql_type.find(" DATE ")!=string::npos)
-                {
-                    row->Set(name, Date::New(time_t((mysqlpp::Date)(*it)[i])*1000), ReadOnly);
-                    continue;
-                }
-
             }
 
@@ -562,36 +646,43 @@
 Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
 {
-    HandleScope handle_scope;
-
-    //const void *ptr = Local<External>::Cast(info.This()->GetInternalField(0))->Value();
+    const HandleScope handle_scope;
+
+    //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
 
     const String::Utf8Value str(args.Holder()->Get(String::New("name")));
+
+    const auto it = fReverseMap.find(*str);
+    if (it!=fReverseMap.end())
+    {
+        it->second.Dispose();
+        fReverseMap.erase(it);
+    }
+
     return Boolean::New(JsUnsubscribe(*str));
 }
 
-Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
-{
-    HandleScope handle_scope;
-
-    const String::Utf8Value str(args.Holder()->Get(String::New("name")));
-
-    const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
-
-    const EventImp *evt = p.second;
-    if (!evt)
-        return Undefined();
-
-    //if (counter==cnt)
-    //    return info.Holder();//Holder()->Get(String::New("data"));
-
-    const vector<Description> vec = JsDescription(*str);
+Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
+{
+    Local<Value> date;
+
+    // It is important to catch the exception thrown
+    // by Date::New in case of thread termination!
+    {
+        TryCatch exception;
+        date = Date::New(evt->GetJavaDate());
+        if (exception.HasCaught())
+            return exception.ReThrow();
+    }
+
+    const vector<Description> vec = JsDescription(str);
 
     Handle<Array> ret = Array::New();
+    ret->Set(String::New("name"),    String::New(str),             ReadOnly);
     ret->Set(String::New("format"),  String::New(evt->GetFormat().c_str()), ReadOnly);
-    ret->Set(String::New("named"),   Boolean::New(vec.size()>0), ReadOnly);
-    ret->Set(String::New("counter"), Integer::New(p.first), ReadOnly);
-    ret->Set(String::New("time"),    Date::New(evt->GetJavaDate()), ReadOnly);
+    ret->Set(String::New("named"),   Boolean::New(vec.size()>0),    ReadOnly);
     ret->Set(String::New("qos"),     Integer::New(evt->GetQoS()),   ReadOnly);
     ret->Set(String::New("size"),    Integer::New(evt->GetSize()),  ReadOnly);
+    ret->Set(String::New("counter"), Integer::New(counter),         ReadOnly);
+    ret->Set(String::New("time"),    date,                          ReadOnly);
 
     typedef boost::char_separator<char> separator;
@@ -609,5 +700,5 @@
         {
             if (ptr>=evt->GetText())
-                return handle_scope.Close(ret);
+                return ret;
 
             char type = (*it)[0];
@@ -618,5 +709,5 @@
 
             if (it==tok.end() && type!=':')
-                return ThrowException(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
+                return Exception::Error(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
 
             string name = pos<vec.size() ? vec[pos].name : "";
@@ -652,10 +743,90 @@
             ret->Set(String::New("data"), obj, ReadOnly);
 
-        return handle_scope.Close(ret);
+        return ret;
     }
     catch (...)
     {
-        return ThrowException(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
-    }
+        return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
+    }
+}
+
+Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
+{
+    HandleScope handle_scope;
+
+    const String::Utf8Value str(args.Holder()->Get(String::New("name")));
+
+    const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
+
+    const EventImp *evt = p.second;
+    if (!evt)
+        return Undefined();
+
+    //if (counter==cnt)
+    //    return info.Holder();//Holder()->Get(String::New("data"));
+
+    Handle<Value> ret = ConvertEvent(evt, p.first, *str);
+    return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
+}
+
+// This is a callback from the RemoteControl piping event handling
+// to the java script ---> in test phase!
+void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
+{
+    if (fThreadId<0)
+        return;
+
+    const auto it = fReverseMap.find(service);
+    if (it==fReverseMap.end())
+        return;
+
+    Locker locker;
+
+    const HandleScope handle_scope;
+
+    Handle<Object> obj = it->second;
+    if (obj.IsEmpty())
+        return;
+
+    const Handle<String> onchange = String::New("onchange");
+    if (!obj->Has(onchange))
+        return;
+
+    const Handle<Value> val = obj->Get(onchange);
+    if (!val->IsFunction())
+        return;
+
+    // -------------------------------------------------------------------
+    // We are not in a context... we need to get into one for Array::New
+
+    Persistent<Context> context = Context::New();
+    if (context.IsEmpty())
+        return;
+
+    const Context::Scope scope(context);
+
+    // -------------------------------------------------------------------
+
+    TryCatch exception;
+
+    Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
+    if (ret->IsArray())
+    {
+        Handle<Array> data = Handle<Array>::Cast(ret);
+        Handle<Value> args[] = { data };
+
+        Handle<Function>::Cast(val)->Call(obj, 1, args);
+    }
+
+    if (exception.HasCaught())
+        ReportException(&exception);
+
+    if (ret->IsNativeError())
+        JsException(service+".onchange callback - "+*String::Utf8Value(ret));
+
+    context.Dispose();
+
+    if (ret->IsUndefined() || ret->IsNativeError() || exception.HasCaught())
+        V8::TerminateExecution(fThreadId);
 }
 
@@ -695,4 +866,6 @@
     obj->SetInternalField(0, External::New(ptr));
 
+    fReverseMap[*str] = Persistent<Object>::New(obj);
+
     return handle_scope.Close(obj);
 
@@ -704,5 +877,5 @@
 bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
 {
-    v8::Locker locker;
+    Locker locker;
     fThreadId = V8::GetCurrentThreadId();
 
@@ -737,7 +910,7 @@
     }
 
-    v8::Context::Scope scope(context);
-
-    Local<Array> args = Array::New(map.size());
+    Context::Scope scope(context);
+
+    Handle<Array> args = Array::New(map.size());
     for (auto it=map.begin(); it!=map.end(); it++)
         args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
@@ -748,5 +921,5 @@
 
     //context->Enter();
-    v8::Locker::StartPreemption(10);
+    Locker::StartPreemption(10);
     const bool rc = ExecuteFile(filename);
 
@@ -763,6 +936,10 @@
     // }
 
-    v8::Locker::StopPreemption();
+    Locker::StopPreemption();
     //context->Exit();
+
+    for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
+        it->second.Dispose();
+    fReverseMap.clear();
 
     context.Dispose();
@@ -774,5 +951,4 @@
 #endif
 
-
     JsEnd(filename);
 
@@ -783,11 +959,30 @@
 {
     Locker locker;
+
     //cout << "Terminate " << fThreadId << endl;
     if (This->fThreadId>=0)
         V8::TerminateExecution(This->fThreadId);
     //cout << "Terminate " << fThreadId << endl;
-    Unlocker unlocker;
+
+    //Unlocker unlocker;
 }
 
 #endif
 
+/*
+#0  0x00007ffff5ae0e62 in ?? () from /usr/lib/libv8.so.3.7.12.22
+#1  0x00007ffff5ae1969 in ?? () from /usr/lib/libv8.so.3.7.12.22
+#2  0x00007ffff5af1976 in v8::V8::Dispose() () from /usr/lib/libv8.so.3.7.12.22
+#3  0x0000000000426459 in RemoteControl<Console>::~RemoteControl() ()
+#4  0x00007ffff51e9901 in __run_exit_handlers (status=3, listp=0x7ffff5566688, run_list_atexi
+#5  0x00007ffff51e9985 in __GI_exit (status=<optimized out>) at exit.c:100
+#6  0x00000000004374db in DimErrorRedirecter::exitHandler(int) ()
+#7  0x00007ffff66bce72 in exit_user_routine () from /home/fact/FACT++.in-run-fad-loss/.libs/l
+#8  0x00007ffff6477387 in recv_dns_dis_rout () from /home/fact/FACT++.in-run-fad-loss/.libs/l
+#9  0x00007ffff647b79f in ast_read_h () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.s
+#10 0x00007ffff647f388 in do_read () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.so
+#11 0x00007ffff647ff32 in tcpip_task () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.s
+#12 0x00007ffff6482c57 in dim_tcpip_thread () from /home/fact/FACT++.in-run-fad-loss/.libs/li
+#13 0x00007ffff7bc4e9a in start_thread (arg=0x7ffff13be700) at pthread_create.c:308
+#14 0x00007ffff52a1cbd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
+*/
Index: trunk/FACT++/src/InterpreterV8.h
===================================================================
--- trunk/FACT++/src/InterpreterV8.h	(revision 14546)
+++ trunk/FACT++/src/InterpreterV8.h	(revision 14547)
@@ -20,5 +20,13 @@
     static InterpreterV8 *This;
 
+    // The main thread id, needed to be able to terminate
+    // the thread forcefully from 'the outside'
     int fThreadId;
+
+    // A loookup table which allows to indentify the
+    // the JavaScript object corrsponding to the
+    // service name (for checking of an .onchange
+    // subscription exists for that object)
+    std::map<std::string, v8::Persistent<v8::Object>> fReverseMap;
 
 #ifdef HAVE_SQL
@@ -69,4 +77,5 @@
 
     static v8::Handle<v8::Value> Convert(char type, const char* &ptr);
+    v8::Handle<v8::Value> ConvertEvent(const EventImp *evt, uint64_t, const char *str);
 #endif
 
@@ -93,6 +102,6 @@
     virtual void  JsException(const std::string &) { }
     virtual bool  JsSend(const std::string &) { return true; }
-    virtual void  JsSleep(uint32_t) { }
-    virtual int   JsWait(const std::string &, int32_t, uint32_t) { return -1; };
+    //virtual void  JsSleep(uint32_t) { }
+    //virtual int   JsWait(const std::string &, int32_t, uint32_t) { return -1; };
     virtual State JsState(const std::string &) { return State(); };
     virtual void *JsSubscribe(const std::string &) { return 0; };
@@ -100,4 +109,6 @@
     virtual std::vector<Description> JsDescription(const std::string &) { return std::vector<Description>(); };
     virtual std::pair<uint64_t, EventImp *> JsGetEvent(const std::string &) { return std::make_pair(0, (EventImp*)0); };
+
+    void JsHandleEvent(const EventImp &, uint64_t, const std::string &);
 
     bool JsRun(const std::string &, const std::map<std::string,std::string> & = std::map<std::string,std::string>());
