Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
httptextlog.C
Go to the documentation of this file.
1 /// \file
2 /// \ingroup tutorial_http
3 /// This macro demonstrates custom access and custom drawing for TMsgList class
4 /// Main motivation for this example - demonstrate how traffic between server and
5 /// client can be minimized and how one could build custom UI in the browser
6 ///
7 /// TMsgList in this tutorial keep last N messages, numbering each with unique identifier
8 /// There is TMsgList::Select() method which selects messages from the list
9 /// If one specifies identifier, only messages newer than this identifier are selected
10 /// In the selection list (TList object of TObjString) first item always identifier for
11 /// the latest message in the list
12 ///
13 /// In JavaScript code (httptextlog.js) one uses Select() method to receive latest
14 /// messages, which not yet been seen in the browser and display them as text
15 /// At maximum, 1000 elements are preserved in the browser.
16 ///
17 /// Macro should always be started in compiled mode, otherwise Select() method is not
18 /// accessible via TClass instance. One also requires comments after ClassDef to
19 /// correctly configure behavior of the JavaScript ROOT code
20 ///
21 /// After macro started, one could open in browser address
22 /// ~~~
23 /// http://localhost:8080?item=log
24 /// ~~~
25 /// One could either click item again or enable monitoring to always receive latest messages
26 /// Or one could open only this output and nothing else:
27 /// ~~~
28 /// http://localhost:8080/log/draw.htm?monitoring=2000
29 /// ~~~
30 /// In last case it could be used in iframe, also it requires less code to load on the page
31 ///
32 /// \macro_code
33 ///
34 /// \author Sergey Linev
35 
36 
37 #include <stdio.h>
38 #include <string.h>
39 
40 #include "TNamed.h"
41 #include "TList.h"
42 #include "TObjString.h"
43 #include "TH1.h"
44 #include "TH2.h"
45 #include "TRandom3.h"
46 #include "TSystem.h"
47 #include "THttpServer.h"
48 #include "TRootSniffer.h"
49 #include "TDatime.h"
50 #include "TClass.h"
51 
52 Bool_t bRun = kTRUE;
53 
54 class TMsgList : public TNamed {
55 
56  protected:
57 
58  TList fMsgs; // list messages, stored as TObjString
59  Int_t fLimit; // max number of stored messages
60  Long64_t fCounter; // current message id
61  TList fSelect; //! temporary list used for selection
62  TObjString fStrCounter; //! current id stored in the string
63 
64  public:
65 
66  TMsgList(const char* name = "log", Int_t limit = 1000) :
67  TNamed(name,"list of log messages"),
68  fMsgs(),
69  fLimit(limit),
70  fCounter(0),
71  fSelect(),
72  fStrCounter()
73  {
74  fMsgs.SetOwner(kTRUE);
75 
76  // counter initialized from current time
77  // if application restarted, id will be bigger and request from browser
78  // will not lead to messages lost. Of course, if more than 1000 messages
79  // per second are generated, one could have mismatch
80 
81  fCounter = ((Long64_t) TDatime().Get()) * 1000;
82  }
83 
84  virtual ~TMsgList() { fMsgs.Clear(); }
85 
86  void AddMsg(const char* msg)
87  {
88  // add message to the list
89  // if number of stored messages bigger than configured, old messages will be removed
90  // zero (msg==0) messages will not be add to the list
91 
92  while (fMsgs.GetSize() >= fLimit) {
93  TObject* last = fMsgs.Last();
94  fMsgs.RemoveLast();
95  delete last;
96  }
97  if (msg==0) return;
98 
99  fMsgs.AddFirst(new TObjString(msg));
100  fCounter++;
101  }
102 
103  TList* Select(Int_t max = 0, Long64_t id = 0)
104  {
105  // Central method to select new messages
106  // Current id stored as first item and used on the client to request new portion
107  // One could limit number of returned messages
108 
109  TIter iter(&fMsgs);
110  TObject* obj = 0;
111  Long64_t curr = fCounter;
112  fSelect.Clear();
113 
114  if (max == 0) max = fMsgs.GetLast()+1;
115 
116  // add current id as first string in the list
117  fStrCounter.SetString(TString::LLtoa(fCounter, 10));
118  fSelect.Add(&fStrCounter);
119 
120  while (((obj = iter()) != 0) && (--curr >= id) && (--max>=0)) fSelect.Add(obj);
121 
122  return &fSelect;
123  }
124 
125  ClassDef(TMsgList, 1); // Custom messages list
126 };
127 
128 void httptextlog()
129 {
130  // create logging instance
131  TMsgList* log = new TMsgList("log", 200);
132 
133  if ((TMsgList::Class()->GetMethodAllAny("Select") == 0) || (strcmp(log->ClassName(), "TMsgList")!=0)) {
134  printf("Most probably, macro runs in interpreter mode\n");
135  printf("To access new methods from TMsgList class,\n");
136  printf("one should run macro with ACLiC like:\n");
137  printf(" shell> root -b httpextlog.C+\n");
138  return;
139  }
140 
141  if (gSystem->AccessPathName("httptextlog.js")!=0) {
142  printf("Please start macro from directory where httptextlog.js is available\n");
143  printf("Only in this case web interface can work\n");
144  return;
145  }
146 
147  // create histograms, just for fun
148  TH1D *hpx = new TH1D("hpx","This is the px distribution",100,-4,4);
149  hpx->SetFillColor(48);
150  hpx->SetDirectory(0);
151  TH2F *hpxpy = new TH2F("hpxpy","py vs px",40,-4,4,40,-4,4);
152  hpxpy->SetDirectory(0);
153 
154  // start http server
155  THttpServer* serv = new THttpServer("http:8080");
156 
157  // One could specify location of newer version of JSROOT
158  // serv->SetJSROOT("https://root.cern.ch/js/latest/");
159  // serv->SetJSROOT("http://jsroot.gsi.de/latest/");
160 
161  // let always load httptextlog.js script in the browser
162  serv->GetSniffer()->SetAutoLoad("currentdir/httptextlog.js");
163 
164  // register histograms
165  serv->Register("/", hpx);
166  serv->Register("/", hpxpy);
167 
168  // register log instance
169  serv->Register("/", log);
170 
171  // while server runs in read-only mode, we should allow methods execution
172  serv->Restrict("/log", "allow_method=Select,GetTitle");
173 
174  // register exit command
175  serv->RegisterCommand("/Stop","bRun=kFALSE;", "rootsys/icons/ed_delete.png");
176  serv->RegisterCommand("/ExitRoot","gSystem->Exit(1);", "rootsys/icons/ed_delete.png");
177 
178  // Fill histograms randomly
179  TRandom3 random;
180  Float_t px, py;
181  const Long_t kUPDATE = 1000;
182  Long_t cnt = 0;
183  while (bRun) {
184  random.Rannor(px,py);
185  hpx->Fill(px);
186  hpxpy->Fill(px,py);
187 
188  // IMPORTANT: one should regularly call ProcessEvents
189  if (cnt++ % kUPDATE == 0) {
190  if (gSystem->ProcessEvents()) break;
191 
192  Long_t loop = cnt / kUPDATE;
193 
194  // make messages not very often
195  if (loop % 1000 == 0) {
196  loop = loop/1000;
197  int shift = loop % 40;
198  // make a 'stairs' with spaces
199  log->AddMsg(TString::Format("%*s Message %ld", shift, "", loop));
200  }
201  }
202  }
203 
204  delete serv; // delete http server
205 }