GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GVOClient.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GVOClient.cpp - VO client class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2013-2020 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GVOClient.cpp
23  * @brief VO client class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdlib> // std::getenv() function
32 #include <cstdio> // std::fopen(), etc. functions
33 #include <cstring> // std::memset() function
34 #include <unistd.h> // close() function
35 #include <netdb.h> // getaddrinfo() function
36 #include <sys/socket.h> // socket(), connect() functions
37 #include <fstream>
38 #include "GTools.hpp"
39 #include "GException.hpp"
40 #include "GXml.hpp"
41 #include "GXmlNode.hpp"
42 #include "GFits.hpp"
43 #include "GVOClient.hpp"
44 #include "GVOHub.hpp"
45 #include "GVOTable.hpp"
46 
47 /* __ Method name definitions ____________________________________________ */
48 #define G_FIND_HUB "GVOClient::find_hub()"
49 #define G_REQUIRE_HUB "GVOClient::require_hub()"
50 #define G_REGISTER_TO_HUB "GVOClient::register_to_hub()"
51 
52 /* __ Macros _____________________________________________________________ */
53 
54 /* __ Coding definitions _________________________________________________ */
55 
56 /* __ Debug definitions __________________________________________________ */
57 //#define G_SHOW_MESSAGE //!< Show posted and received messages
58 
59 
60 /*==========================================================================
61  = =
62  = Constructors/destructors =
63  = =
64  ==========================================================================*/
65 
66 /***********************************************************************//**
67  * @brief Void constructor
68  ***************************************************************************/
70 {
71  // Initialise members
72  init_members();
73 
74  // Find Hub
75  find_hub();
76 
77  // Return
78  return;
79 }
80 
81 
82 /***********************************************************************//**
83  * @brief Copy constructor
84  *
85  * @param[in] client VO client.
86  ***************************************************************************/
88 {
89  // Initialise members
90  init_members();
91 
92  // Copy members
93  copy_members(client);
94 
95  // Return
96  return;
97 }
98 
99 
100 /***********************************************************************//**
101  * @brief Destructor
102  ***************************************************************************/
104 {
105  // Free members
106  free_members();
107 
108  // Return
109  return;
110 }
111 
112 
113 /*==========================================================================
114  = =
115  = Operators =
116  = =
117  ==========================================================================*/
118 
119 /***********************************************************************//**
120  * @brief Assignment operator
121  *
122  * @param[in] client VO client.
123  * @return VO client.
124  ***************************************************************************/
126 {
127  // Execute only if object is not identical
128  if (this != &client) {
129 
130  // Free members
131  free_members();
132 
133  // Initialise members
134  init_members();
135 
136  // Copy members
137  copy_members(client);
138 
139  } // endif: object was not identical
140 
141  // Return
142  return *this;
143 }
144 
145 
146 /*==========================================================================
147  = =
148  = Public methods =
149  = =
150  ==========================================================================*/
151 
152 /***********************************************************************//**
153  * @brief Clear object.
154  *
155  * Reset object to a clean initial state.
156  ***************************************************************************/
158 {
159  // Free memory
160  free_members();
161 
162  // Initialise members
163  init_members();
164 
165  // Return
166  return;
167 }
168 
169 
170 /***********************************************************************//**
171  * @brief Clone object
172  ***************************************************************************/
174 {
175  // Clone client
176  return new GVOClient(*this);
177 }
178 
179 
180 /***********************************************************************//**
181  * @brief Register client at VO Hub
182  *
183  * Registers a client at the VO Hub. The method will search for a VO Hub,
184  * launch it's own VO Hub if no Hub was found, and the register the client
185  * at the Hub, including sending of metadata.
186  ***************************************************************************/
188 {
189  // Require a Hub
190  require_hub();
191 
192  // Continue only if we have a Hub
193  if (has_hub()) {
194 
195  // Register to Hub
196  register_to_hub();
197 
198  // Send meta data
199  send_metadata();
200 
201  } // endif: we had a Hub
202 
203  // Return
204  return;
205 }
206 
207 
208 /***********************************************************************//**
209  * @brief Unregister client from VO Hub
210  *
211  * Disconnects the VO client from the VO Hub. The client is unregistered.
212  ***************************************************************************/
214 {
215  // Continue only if we have a Hub
216  if (has_hub()) {
217 
218  // Unregister from Hub
220 
221  // Close socket
222  if (m_socket != -1) {
223  close(m_socket);
224  m_socket = -1;
225  }
226 
227  } // endif: we had a Hub
228 
229  // Return
230  return;
231 }
232 
233 
234 /***********************************************************************//**
235  * @brief Signals if client has VO Hub information
236  *
237  * @return True if client has VO Hub information, false otherwise.
238  *
239  * Checks if all mandatory VO Hub tokens are non-empty.
240  ***************************************************************************/
241 bool GVOClient::has_hub(void) const
242 {
243  // Return Hub information status
244  return (!m_secret.empty() && !m_hub_url.empty() && !m_version.empty());
245 }
246 
247 
248 /***********************************************************************//**
249  * @brief Signals if client is connected to VO Hub
250  *
251  * @return True if client is connected to VO Hub, false otherwise.
252  *
253  * Checks if the clients has non-empty client key, Hub ID and client ID.
254  ***************************************************************************/
255 bool GVOClient::is_connected(void) const
256 {
257  // Return Hub information status
258  return (!m_client_key.empty() && !m_hub_id.empty() && !m_client_id.empty());
259 }
260 
261 
262 /***********************************************************************//**
263  * @brief Ping VO Hub
264  *
265  * @return True if VO Hub is alive, false otherwise.
266  ***************************************************************************/
267 bool GVOClient::ping_hub(void) const
268 {
269  // Initialise ping result
270  bool result = false;
271 
272  // Declare request
273  std::string request = "";
274 
275  // Set request
276  request.append("<?xml version=\"1.0\"?>\n");
277  request.append("<methodCall>\n");
278  request.append(" <methodName>samp.hub.ping</methodName>\n");
279  request.append(" <params>\n");
280  request.append(" </params>\n");
281  request.append("</methodCall>\n");
282 
283  // Execute request
284  GXml xml = execute(request);
285 
286  // If we got a valid response then set ping result flag to true
287  if (response_is_valid(xml)) {
288  result = true;
289  }
290 
291  // Return ping result
292  return result;
293 }
294 
295 
296 /***********************************************************************//**
297  * @brief Shutdown VO Hub
298  ***************************************************************************/
299 void GVOClient::shutdown_hub(void) const
300 {
301  // Declare request
302  std::string request = "";
303 
304  // Set request
305  request.append("<?xml version=\"1.0\"?>\n");
306  request.append("<methodCall>\n");
307  request.append(" <methodName>samp.hub.shutdown</methodName>\n");
308  request.append(" <params>\n");
309  request.append(" <param><value/></param>\n");
310  request.append(" </params>\n");
311  request.append("</methodCall>\n");
312 
313  // Execute request
314  execute(request);
315 
316  // Return
317  return;
318 }
319 
320 
321 /***********************************************************************//**
322  * @brief Execute function on server
323  *
324  * @param[in] request XML request string
325  * @return XML response
326  ***************************************************************************/
327 GXml GVOClient::execute(const std::string& request) const
328 {
329  // Initialise response
330  GXml xml;
331 
332  // Connect to hub
333  connect_to_hub();
334 
335  // Continue only if connection has been established
336  if (m_socket != -1) {
337 
338  // Post request
339  post_string(request);
340 
341  // Receive response
342  std::string response = receive_string();
343 
344  // Find start of XML text
345  size_t start = response.find("<?xml");
346 
347  // If found then convert text into XML document
348  if (start != std::string::npos) {
349  xml = GXml(response.substr(start, std::string::npos));
350  }
351 
352  // Close socket
353  if (m_socket != -1) {
354  close(m_socket);
355  m_socket = -1;
356  }
357 
358  } // endif: connection has been established
359 
360  // Return response
361  return xml;
362 }
363 
364 
365 /***********************************************************************//**
366  * @brief Publish FITS HDU
367  *
368  * @param[in] hdu FITS HDU
369  *
370  * Publishes a FITS HDU.
371  ***************************************************************************/
372 void GVOClient::publish(const GFitsHDU& hdu)
373 {
374  // Signal that client should be disconnected after sending the image
375  // to Hub
376  bool disconnected = !is_connected();
377 
378  // Make sure that the client is connected to a Hub
379  if (disconnected) {
380  connect();
381  }
382 
383  // Save FITS HDU into a temporary file
384  std::string samp_share = gammalib::tmpnam();
385  GFits fits;
386  fits.append(hdu);
387  fits.saveto(samp_share, true);
388 
389  // Get FITS extension name
390  std::string extname = hdu.extname();
391  if (extname.empty()) {
392  extname = "FITS Image";
393  }
394 
395  // Compose notification to be passed to the Hub
396  std::string hub_command = "";
397  hub_command.append("<?xml version=\"1.0\"?>\n");
398  hub_command.append("<methodCall>\n");
399  hub_command.append(" <methodName>samp.hub.notifyAll</methodName>\n");
400  hub_command.append(" <params>\n");
401  hub_command.append(" <param><value>"+m_client_key+"</value></param>\n");
402  hub_command.append(" <param><value><struct>\n");
403  hub_command.append(" <member><name>samp.params</name><value><struct>\n");
404  hub_command.append(" <member>\n");
405  hub_command.append(" <name>name</name>\n");
406  hub_command.append(" <value>"+extname+"</value>\n");
407  hub_command.append(" </member>\n");
408  hub_command.append(" <member>\n");
409  hub_command.append(" <name>url</name>\n");
410  hub_command.append(" <value>file://localhost"+samp_share+"</value>\n");
411  hub_command.append(" </member>\n");
412  hub_command.append(" <member>\n");
413  hub_command.append(" <name>image-id</name>\n");
414  hub_command.append(" <value>Gammalib Data</value>\n");
415  hub_command.append(" </member>\n");
416  hub_command.append(" </struct></value></member>\n");
417  hub_command.append(" <member>\n");
418  hub_command.append(" <name>samp.mtype</name>\n");
419  hub_command.append(" <value>image.load.fits</value>\n");
420  hub_command.append(" </member>\n");
421  hub_command.append(" </struct></value></param>\n");
422  hub_command.append(" </params>\n");
423  hub_command.append("</methodCall>\n");
424 
425  // Send notification
426  execute(hub_command);
427 
428  // Disconnect client from Hub
429  if (disconnected) {
430  disconnect();
431  }
432 
433  // Return
434  return;
435 }
436 
437 
438 /***********************************************************************//**
439  * @brief Publish VO table
440  *
441  * @param[in] votable VO table
442  *
443  * Publishes a VO table.
444  ***************************************************************************/
445 void GVOClient::publish(const GVOTable& votable)
446 {
447  // Signal that client should be disconnected after sending the table
448  // to Hub
449  bool disconnected = !is_connected();
450 
451  // Make sure that the client is connected to a Hub
452  if (disconnected) {
453  connect();
454  }
455 
456  // Save FITS HDU into a temporary file
457  std::string samp_share = gammalib::tmpnam();
458  votable.xml().save(samp_share);
459 
460  // Compose notification to be passed to the Hub
461  std::string hub_command = "";
462  hub_command.append("<?xml version=\"1.0\"?>\n");
463  hub_command.append("<methodCall>");
464  hub_command.append(" <methodName>samp.hub.notifyAll</methodName>\n");
465  hub_command.append(" <params>\n");
466  hub_command.append(" <param><value>"+m_client_key+"</value></param>\n");
467  hub_command.append(" <param><value><struct>\n");
468  hub_command.append(" <member><name>samp.params</name><value><struct>\n");
469  hub_command.append(" <member>\n");
470  hub_command.append(" <name>name</name>\n");
471  hub_command.append(" <value>"+votable.name()+"</value>\n");
472  hub_command.append(" </member>\n");
473  hub_command.append(" <member>\n");
474  hub_command.append(" <name>url</name>\n");
475  hub_command.append(" <value>\n");
476  hub_command.append(" file://localhost"+samp_share+"\n");
477  hub_command.append(" </value>\n");
478  hub_command.append(" </member>\n");
479  hub_command.append(" <member>\n");
480  hub_command.append(" <name>table-id</name>\n");
481  hub_command.append(" <value>\n");
482  hub_command.append(" file://localhost"+samp_share+"\n");
483  hub_command.append(" </value>\n");
484  hub_command.append(" </member>\n");
485  hub_command.append(" </struct></value></member>\n");
486  hub_command.append(" <member>\n");
487  hub_command.append(" <name>samp.mtype</name>\n");
488  hub_command.append(" <value>table.load.votable</value>\n");
489  hub_command.append(" </member>\n");
490  hub_command.append(" </struct></value></param>\n");
491  hub_command.append(" </params>\n");
492  hub_command.append("</methodCall>\n");
493 
494  // Send notification
495  execute(hub_command);
496 
497  // Disconnect client from Hub
498  if (disconnected) {
499  disconnect();
500  }
501 
502  // Return
503  return;
504 }
505 
506 
507 /***********************************************************************//**
508  * @brief Print VO client information
509  *
510  * @param[in] chatter Chattiness.
511  * @return String containing VO client information
512  ***************************************************************************/
513 std::string GVOClient::print(const GChatter& chatter) const
514 {
515  // Initialise result string
516  std::string result;
517 
518  // Continue only if chatter is not silent
519  if (chatter != SILENT) {
520 
521  // Append header
522  result.append("=== GVOClient ===");
523 
524  // Append client information
525  result.append("\n"+gammalib::parformat("Name")+m_name);
526 
527  // Append Hub information
528  if (has_hub()) {
529  result.append("\n"+gammalib::parformat("Hub key")+m_secret);
530  result.append("\n"+gammalib::parformat("Hub URL")+m_hub_url);
531  result.append("\n"+gammalib::parformat("Hub host")+m_hub_host);
532  result.append("\n"+gammalib::parformat("Hub port")+m_hub_port);
533  result.append("\n"+gammalib::parformat("Hub path")+m_hub_path);
534  result.append("\n"+gammalib::parformat("SAMP protocol version"));
535  result.append(m_version);
536  result.append("\n"+gammalib::parformat("Hub connection"));
537  if (is_connected()) {
538  if (!m_client_key.empty()) {
539  result.append("registered as \""+m_client_id);
540  result.append("\" on Hub \""+m_hub_id);
541  result.append("\".");
542  }
543  else {
544  result.append("established.");
545  }
546  }
547  else {
548  result.append("no");
549  }
550  }
551  else {
552  result.append("\n"+gammalib::parformat("Hub key"));
553  result.append("No Hub has been found");
554  }
555 
556  } // endif: chatter was not silent
557 
558  // Return result
559  return result;
560 }
561 
562 
563 /*==========================================================================
564  = =
565  = Private methods =
566  = =
567  ==========================================================================*/
568 
569 /***********************************************************************//**
570  * @brief Initialise class members
571  ***************************************************************************/
573 {
574  // Initialise members
575  m_name = "GammaLib";
576  m_secret.clear();
577  m_hub_url.clear();
578  m_hub_host.clear();
579  m_hub_port.clear();
580  m_hub_path.clear();
581  m_version.clear();
582  m_client_key.clear();
583  m_hub_id.clear();
584  m_client_id.clear();
585  m_socket = -1; // Signals no socket
586 
587  // Return
588  return;
589 }
590 
591 
592 /***********************************************************************//**
593  * @brief Copy class members
594  *
595  * @param[in] client VO client.
596  ***************************************************************************/
598 {
599  // Copy members
600  m_name = client.m_name;
601  m_secret = client.m_secret;
602  m_hub_url = client.m_hub_url;
603  m_hub_host = client.m_hub_host;
604  m_hub_port = client.m_hub_port;
605  m_hub_path = client.m_hub_path;
606  m_version = client.m_version;
607  m_client_key = client.m_client_key;
608  m_hub_id = client.m_hub_id;
609  m_client_id = client.m_client_id;
610  m_socket = client.m_socket;
611 
612  // Return
613  return;
614 }
615 
616 
617 /***********************************************************************//**
618  * @brief Delete class members
619  ***************************************************************************/
621 {
622  // Unregister from Hub
624 
625  // Close socket
626  if (m_socket != -1) {
627  close(m_socket);
628  m_socket = -1;
629  }
630 
631  // Return
632  return;
633 }
634 
635 
636 /***********************************************************************//**
637  * @brief Find VO Hub
638  *
639  * @return True if VO Hub has been found, false otherwise.
640  *
641  * Search a valid VO Hub and retrieve all mandatory token for this Hub.
642  * The mandatory tokens are
643  *
644  * samp.secret Opaque text string required for Hub registration
645  * samp.hub.xmlrpc.url XML-RPC endpoint for communication
646  * samp.profile.version Version of SAMP profile
647  *
648  * Implements IVOA standard REC-SAMP-1.3-20120411.
649  ***************************************************************************/
651 {
652  // Initialise find flag to false
653  bool found = false;
654 
655  // Get lockfile URL
656  std::string lockurl = get_hub_lockfile();
657 
658  // Continue only if a URL has been found
659  if (!lockurl.empty()) {
660 
661  // If we have a file:// prefix then strip it now. This is a kluge
662  // and should be remplaced by a method that allows opening any kind
663  // of URL
664  if (lockurl.compare(0, 7, "file://") == 0) {
665  lockurl = lockurl.substr(7, std::string::npos);
666  }
667 
668  // Open SAMP lockfile. Continue only if opening was successful
669  FILE* fptr = fopen(lockurl.c_str(), "r");
670  if (fptr != NULL) {
671 
672  // Allocate line buffer
673  const int n = 1000;
674  char line[n];
675 
676  // Parse lockfile and search for mandatory tokens
677  while (fgets(line, n, fptr) != NULL) {
678 
679  // Convert line to C++ string
680  std::string cline = std::string(line);
681 
682  // Check for secret key
683  if (cline.compare(0, 12, "samp.secret=") == 0) {
684  m_secret = gammalib::strip_chars(cline.substr(12, std::string::npos), "\r\n");
685  }
686 
687  // Check for Hub URL
688  else if (cline.compare(0, 20, "samp.hub.xmlrpc.url=") == 0) {
689  m_hub_url = gammalib::strip_chars(cline.substr(20, std::string::npos), "\r\n");
690  }
691 
692  // Check for profile version
693  else if (cline.compare(0, 21, "samp.profile.version=") == 0) {
694  m_version = gammalib::strip_chars(cline.substr(21, std::string::npos), "\r\n");
695  }
696 
697  }
698 
699  // Close SAMP lockfile
700  fclose(fptr);
701 
702  // Extract host, port and path from Hub URL
703  if (m_hub_url.compare(0, 7, "http://") == 0) {
704  size_t length;
705  size_t start = 7;
706  size_t stop = m_hub_url.find(":", start);
707  size_t end = std::string::npos;
708  if (stop != std::string::npos) {
709  length = stop - start;
710  }
711  else {
712  length = std::string::npos;
713  }
714  m_hub_host = m_hub_url.substr(start, length);
715  if (stop != std::string::npos) {
716  stop++;
717  end = m_hub_url.find("/", stop);
718  if (end != std::string::npos) {
719  length = end - stop;
720  }
721  else {
722  length = std::string::npos;
723  }
724  m_hub_port = m_hub_url.substr(stop, length);
725  }
726  if (end != std::string::npos) {
727  end++;
728  length = m_hub_url.length() - end;
729  m_hub_path = m_hub_url.substr(end, length);
730  }
731  }
732 
733  // Check for existence of mandatory tokens
734  found = has_hub();
735 
736  } // endif: SAMP lockfile opened
737 
738  } // endif: URL has been found
739 
740  // Return find flag
741  return found;
742 }
743 
744 
745 /***********************************************************************//**
746  * @brief Require VO Hub
747  *
748  * @return True if VO Hub has been found, false otherwise.
749  *
750  * Make sure that a VO Hub is available and alive. If no VO Hub is found
751  * or alive the method will start a GammaLib internal Hub.
752  ***************************************************************************/
754 {
755  // First get Hub information
756  bool found = find_hub();
757 
758  // If we have Hub information, check if the Hub is alive
759  if (found) {
760  found = ping_hub();
761  }
762 
763  // If the Hub is not alive then create a GammaLib own Hub
764  if (!found) {
765 
766  // Create child process to start the Hub
767  int pid = fork();
768  if (pid < 0) {
769  std::string msg = "Unable to create child process for VO Hub.";
771  }
772 
773  // If we have a PID of 0 we are in the child process. In this case
774  // we create and start a VO Hub ...
775  if (pid == 0) {
776  GVOHub hub;
777  hub.start();
778  exit(0);
779  }
780 
781  // ... otherwise we are in the parent process. Check if the Hub
782  // is alive.
783  else {
784 
785  // Check that Hub is alive
786  for (int i = 0; i < 5; ++i) {
787  found = find_hub() && ping_hub();
788  if (found) {
789  break;
790  }
791  sleep(1);
792  }
793 
794  }
795 
796  } // endif: created own VO Hub
797 
798  // If no Hub has been found, clear all Hub related members
799  if (!found) {
800  m_secret.clear();
801  m_hub_url.clear();
802  m_version.clear();
803  }
804 
805  // Return find flag
806  return found;
807 }
808 
809 
810 /***********************************************************************//**
811  * @brief Register client at SAMP Hub
812  *
813  * @exception GException::invalid_value
814  * Unable to connect to Hub.
815  ***************************************************************************/
817 {
818  // Declare request
819  std::string request;
820 
821  // Set metadata header
822  request.append("<?xml version=\"1.0\"?>\n");
823  request.append("<methodCall>\n");
824  request.append(" <methodName>samp.hub.register</methodName>\n");
825  request.append(" <params>\n");
826  request.append(" <param><value>"+m_secret+"</value></param>\n");
827  request.append(" </params>\n");
828  request.append("</methodCall>\n");
829 
830  // Execute request
831  GXml xml = execute(request);
832 
833  // If no error occured, extract hub and client identifiers
834  if (response_is_valid(xml)) {
835  m_client_key = get_response_value(xml, "samp.private-key");
836  m_hub_id = get_response_value(xml, "samp.hub-id");
837  m_client_id = get_response_value(xml, "samp.self-id");
838  }
839 
840  // ... otherwise signal an error
841  else {
842  std::string msg = "Unable to connect to Hub (" +
843  response_error_message(xml) + ", code=" +
844  gammalib::str(response_error_code(xml)) + ").";
846  }
847 
848  // Return
849  return;
850 }
851 
852 
853 /***********************************************************************//**
854  * @brief Unregister client from SAMP Hub
855  ***************************************************************************/
857 {
858  // Unregister only if we were registered
859  if (is_connected()) {
860 
861  // Declare request
862  std::string request;
863 
864  // Set metadata header
865  request.append("<?xml version=\"1.0\"?>\n");
866  request.append("<methodCall>\n");
867  request.append(" <methodName>samp.hub.unregister</methodName>\n");
868  request.append(" <params>\n");
869  request.append(" <param><value>"+m_client_key+"</value></param>\n");
870  request.append(" </params>\n");
871  request.append("</methodCall>\n");
872 
873  // Execute request
874  execute(request);
875 
876  } // endif: we were registered
877 
878  // Reset Hub and client identifiers
879  m_client_key.clear();
880  m_hub_id.clear();
881  m_client_id.clear();
882 
883  // Return
884  return;
885 }
886 
887 
888 /***********************************************************************//**
889  * @brief Send client metadata to SAMP Hub
890  ***************************************************************************/
891 void GVOClient::send_metadata(void) const
892 {
893  // Declare request
894  std::string request;
895 
896  // Set metadata header
897  request.append("<?xml version=\"1.0\"?>\n");
898  request.append("<methodCall>\n");
899  request.append("<methodName>samp.hub.declareMetadata</methodName>\n");
900  request.append("<params>\n");
901  request.append(" <param><value>"+m_client_key+"</value></param>\n");
902  request.append(" <param><value><struct>\n");
903 
904  // Set SAMP name
905  request.append(" <member>\n");
906  request.append(" <name>samp.name</name>\n");
907  request.append(" <value>"+m_name+"</value>\n");
908  request.append(" </member>\n");
909 
910  // Set SAMP description text
911  request.append(" <member>\n");
912  request.append(" <name>samp.description.text</name>\n");
913  request.append(" <value>GammaLib client</value>\n");
914  request.append(" </member>\n");
915 
916  // Set SAMP icon URL
917  request.append(" <member>\n");
918  request.append(" <name>samp.icon.url</name>\n");
919  request.append(" <value>http://a.fsdn.com/allura/p/gammalib/icon</value>\n");
920  request.append(" </member>\n");
921 
922  // Set author affiliation
923  request.append(" <member>\n");
924  request.append(" <name>author.affiliation</name>\n");
925  request.append(" <value>IRAP, Toulouse, France</value>\n");
926  request.append(" </member>\n");
927 
928  // Set author e-mail
929  request.append(" <member>\n");
930  request.append(" <name>author.email</name>\n");
931  request.append(" <value>jurgen.knodlseder@irap.omp.eu</value>\n");
932  request.append(" </member>\n");
933 
934  // Set author name
935  request.append(" <member>\n");
936  request.append(" <name>author.name</name>\n");
937  request.append(" <value>Juergen Knoedlseder</value>\n");
938  request.append(" </member>\n");
939 
940  // Set metadata trailer
941  request.append(" </struct></value></param>\n");
942  request.append("</params>\n");
943  request.append("</methodCall>\n");
944 
945  // Execute request
946  execute(request);
947 
948  // Return
949  return;
950 }
951 
952 
953 /*==========================================================================
954  = =
955  = Low-level private methods =
956  = =
957  ==========================================================================*/
958 
959 /***********************************************************************//**
960  * @brief Connect to SAMP Hub
961  *
962  * Connects to Hub by creating a socket and connecting to this socket. The
963  * method expects that a Hub has already been found. If no Hub has been found
964  * (i.e. has_hub() returns false), the method does nothing.
965  *
966  * The method extracts the hostname and the port from the Hub endpoint URL.
967  * If Hub connection fails, any created socket is closed.
968  *
969  * If the method is successful, m_socket will contain on exit a non-negative
970  * number. If any failure occurs, m_socket will be set to -1.
971  ***************************************************************************/
973 {
974  // Close any existing socket
975  if (m_socket != -1) {
976  close(m_socket);
977  m_socket = -1;
978  }
979 
980  // Continue only if host and port information was found
981  if (!m_hub_host.empty() && !m_hub_port.empty()) {
982 
983  // Set hints
984  struct addrinfo hints;
985  std::memset(&hints, 0, sizeof(hints));
986  hints.ai_family = AF_INET;
987  hints.ai_socktype = SOCK_STREAM;
988 
989  // Get server information
990  struct addrinfo* servinfo;
991  if (getaddrinfo(m_hub_host.c_str(), m_hub_port.c_str(),
992  &hints, &servinfo) == 0) {
993 
994  // Loop through all the results and connect to the first
995  // we can
996  for (struct addrinfo* ptr = servinfo; ptr != NULL; ptr = ptr->ai_next) {
997 
998  // Create socket
999  m_socket = socket(ptr->ai_family,
1000  ptr->ai_socktype,
1001  ptr->ai_protocol);
1002 
1003  // Connect to socket if socket is valid
1004  if (m_socket != -1) {
1005  if (::connect(m_socket,
1006  ptr->ai_addr,
1007  ptr->ai_addrlen) == -1) {
1008  close(m_socket);
1009  m_socket = -1;
1010  }
1011  else {
1012  // Socket connection successful, break now
1013  break;
1014  }
1015  }
1016 
1017  } // endfor: looped through all results
1018 
1019  } // endif: server information was valid
1020 
1021  } // endif: host and port information found
1022 
1023  // Return
1024  return;
1025 }
1026 
1027 
1028 /***********************************************************************//**
1029  * @brief Post string content to Hub
1030  *
1031  * @param[in] content String content to post
1032  *
1033  * Posts the content of a string to the Hub.
1034  *
1035  * The method does nothing if no Hub connection has been established.
1036  ***************************************************************************/
1037 void GVOClient::post_string(const std::string& content) const
1038 {
1039  // Continue only if Hub connection has been established
1040  if (m_socket != -1) {
1041 
1042  // Determine content length
1043  int length = content.length();
1044 
1045  // Set prefix
1046  std::string prefix = "POST /"+m_hub_path+" HTTP/1.0\n"
1047  "User-Agent: GammaLib\n"
1048  "Content-Type: text/xml\n"
1049  "Content-Length: "+gammalib::str(length)+"\n\n";
1050 
1051  // Build post string
1052  std::string post = prefix + content;
1053 
1054  // Debug option: show posted message
1055  #if defined(G_SHOW_MESSAGE)
1056  std::cout << post << std::endl;
1057  #endif
1058 
1059  // Send content to socket
1060  bool done = false;
1061  do {
1062  int length = post.length();
1063  int sent_length = send(m_socket, post.c_str(), length, 0);
1064  if (sent_length < length) {
1065  post = post.substr(sent_length, std::string::npos);
1066  }
1067  else {
1068  done = true;
1069  }
1070  } while (!done);
1071 
1072  } // endif: Hub connection had been established
1073 
1074  // Return
1075  return;
1076 }
1077 
1078 
1079 /***********************************************************************//**
1080  * @brief Receive string content from Hub
1081  *
1082  * @return String received from Hub.
1083  *
1084  * Reads information sent by Hub into a string.
1085  *
1086  * The method does nothing if no Hub connection has been established.
1087  ***************************************************************************/
1088 std::string GVOClient::receive_string(void) const
1089 {
1090  // Initialise empty string
1091  std::string result = "";
1092 
1093  // Continue only if Hub connection has been established
1094  if (m_socket != -1) {
1095 
1096  // Define buffer
1097  char buffer[1001];
1098 
1099  // Read buffer until it is empty
1100  int timeout = 2000; // Initial timeout is 2 sec
1101  int n = 0;
1102  do {
1103  n = gammalib::recv(m_socket, buffer, 1000, 0, timeout);
1104  if (n > 0) {
1105  buffer[n] = '\0';
1106  result.append(std::string(buffer));
1107  }
1108  timeout = 10; // The timeout now is 0.01 sec
1109  } while (n > 0);
1110 
1111  // Debug option: show received message
1112  #if defined(G_SHOW_MESSAGE)
1113  std::cout << result << std::endl;
1114  #endif
1115 
1116  } // endif: Hub connection had been established
1117 
1118  // Return result
1119  return result;
1120 }
1121 
1122 
1123 /***********************************************************************//**
1124  * @brief Returns value for a SAMP Hub response parameter
1125  *
1126  * @param[in] xml Hub response XML document.
1127  * @param[in] name Parameter name.
1128  * @return Parameter value.
1129  *
1130  * Returns value for a SAMP Hub response parameter. If the specified
1131  * parameter was not found or if the response structure is not compliant,
1132  * an empty string is returned.
1133  ***************************************************************************/
1134 std::string GVOClient::get_response_value(const GXml& xml,
1135  const std::string& name) const
1136 {
1137  // Declare value
1138  std::string value = "";
1139 
1140  // Search for value of specified member
1141  const GXmlNode* node = xml.element("methodResponse > params > param > value > struct");
1142  if (node != NULL) {
1143  int num = node->elements("member");
1144  for (int i = 0; i < num; ++i) {
1145  const GXmlNode* member = node->element("member", i);
1146  std::string one_name;
1147  std::string one_value;
1148  gammalib::xml_get_name_value_pair(member, one_name, one_value);
1149  if (one_name == name) {
1150  value = one_value;
1151  break;
1152  }
1153  }
1154  }
1155 
1156  // Return value
1157  return value;
1158 }
1159 
1160 
1161 /***********************************************************************//**
1162  * @brief Returns SAMP Hub lockfile URL
1163  *
1164  * @return SAMP Hub lockfile URL (empty if no lockfile was found).
1165  *
1166  * Implements IVOA standard REC-SAMP-1.3-20120411.
1167  ***************************************************************************/
1168 std::string GVOClient::get_hub_lockfile(void) const
1169 {
1170  // Initialise result
1171  std::string url = "";
1172 
1173  // Check for existence of the SAMP_HUB environment variable first
1174  char* hub_ptr = std::getenv("SAMP_HUB");
1175  if (hub_ptr != NULL) {
1176 
1177  // Check for mandatory std-lockurl: prefix (no other prefixe is
1178  // supported so far)
1179  std::string lockurl = std::string(hub_ptr);
1180  if (lockurl.compare(0, 12, "std-lockurl:") == 0) {
1181 
1182  // Extract URL
1183  url = lockurl.substr(12, std::string::npos);
1184 
1185  } // endif: std-lockurl: prefix found
1186 
1187  }
1188 
1189  // ... otherwise the lockfile should be $HOME/.samp
1190  else {
1191 
1192  // Get user's HOME directory path as the prefix of the full
1193  // path. If the HOME environment variable is not set we
1194  // expect that .samp is in the local directory. This is non
1195  // standard, but prevents for creating an exception here.
1196  std::string prefix = "";
1197  char* home_ptr = std::getenv("HOME");
1198  if (home_ptr != NULL) {
1199  prefix = std::string(home_ptr) + "/";
1200  }
1201 
1202  // Set filename
1203  url = prefix + ".samp";
1204 
1205  } // endelse: no SAMP_HUB environment variable found
1206 
1207  // Return URL
1208  return url;
1209 }
1210 
1211 
1212 /***********************************************************************//**
1213  * @brief Checks if response is valid
1214  *
1215  * @return True if response is valid, false otherwise.
1216  ***************************************************************************/
1217 bool GVOClient::response_is_valid(const GXml& xml) const
1218 {
1219  // Initialise validity flag
1220  bool valid = true;
1221 
1222  // Check for data
1223  const GXmlNode* node = xml.element("methodResponse > params > param > value");
1224  if (node == NULL) {
1225  valid = false;
1226  }
1227 
1228  // Return validity
1229  return valid;
1230 }
1231 
1232 
1233 /***********************************************************************//**
1234  * @brief Return response error code
1235  *
1236  * @return Response error core (0 if no error occured).
1237  ***************************************************************************/
1239 {
1240  // Initialise error code
1241  int error_code = 0;
1242 
1243  // Extract error code
1244  const GXmlNode* node = xml.element("methodResponse > fault > value > struct");
1245  if (node != NULL) {
1246  const GXmlNode* code = node->element("member[0] > value > int");
1247  if (code != NULL) {
1248  error_code = gammalib::toint(static_cast<const GXmlText*>((*code)[0])->text());
1249  }
1250  }
1251 
1252  // Return error code
1253  return error_code;
1254 }
1255 
1256 
1257 /***********************************************************************//**
1258  * @brief Return response error message
1259  *
1260  * @return Response error message (blank if no error occured).
1261  ***************************************************************************/
1262 std::string GVOClient::response_error_message(const GXml& xml) const
1263 {
1264  // Initialise error message
1265  std::string error_message;
1266 
1267  // Extract error message
1268  const GXmlNode* node = xml.element("methodResponse > fault > value > struct");
1269  if (node != NULL) {
1270  const GXmlNode* msg = node->element("member[1] > value");
1271  if (msg != NULL) {
1272  error_message = static_cast<const GXmlText*>((*msg)[0])->text();
1273  }
1274  }
1275 
1276  // Return error message
1277  return error_message;
1278 }
Abstract XML node base class.
Definition: GXmlNode.hpp:57
bool ping_hub(void) const
Ping VO Hub.
Definition: GVOClient.cpp:267
void xml_get_name_value_pair(const GXmlNode *node, std::string &name, std::string &value)
Extract name / value pair from XML node.
Definition: GTools.cpp:1988
VO client class.
Definition: GVOClient.hpp:59
void connect_to_hub(void) const
Connect to SAMP Hub.
Definition: GVOClient.cpp:972
std::string m_name
Client name.
Definition: GVOClient.hpp:107
GXml execute(const std::string &request) const
Execute function on server.
Definition: GVOClient.cpp:327
std::string getenv(const std::string &arg)
Return value of environment variable.
Definition: GTools.cpp:465
void clear(void)
Clear object.
Definition: GVOClient.cpp:157
void save(const GFilename &filename) const
Save XML document into file.
Definition: GXml.cpp:581
GVOClient * clone(void) const
Clone object.
Definition: GVOClient.cpp:173
void publish(const GFitsHDU &hdu)
Publish FITS HDU.
Definition: GVOClient.cpp:372
XML class interface definition.
Abstract FITS extension base class.
Definition: GFitsHDU.hpp:51
std::string receive_string(void) const
Receive string content from Hub.
Definition: GVOClient.cpp:1088
int recv(int fd, char *buffer, int len, int flags, int timeout)
Checks whether a parameter has occured once.
Definition: GTools.cpp:2036
std::string get_response_value(const GXml &xml, const std::string &name) const
Returns value for a SAMP Hub response parameter.
Definition: GVOClient.cpp:1134
std::string strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition: GTools.cpp:94
void shutdown_hub(void) const
Shutdown VO Hub.
Definition: GVOClient.cpp:299
std::string m_version
The version of the SAMP Standard Profile implemented by the hub.
Definition: GVOClient.hpp:113
void disconnect(void)
Unregister client from VO Hub.
Definition: GVOClient.cpp:213
virtual int elements(void) const
Return number of GXMLElement children of node.
Definition: GXmlNode.cpp:586
Gammalib tools definition.
std::string m_hub_url
The XML-RPC endpoint for communication with the hub.
Definition: GVOClient.hpp:109
FITS file class.
Definition: GFits.hpp:63
std::string m_hub_path
Hub path (extracted from XML-RPC endpoint)
Definition: GVOClient.hpp:112
FITS file class interface definition.
std::string print(const GChatter &chatter=NORMAL) const
Print VO client information.
Definition: GVOClient.cpp:513
bool find_hub(void)
Find VO Hub.
Definition: GVOClient.cpp:650
std::string m_secret
Secret Hub key.
Definition: GVOClient.hpp:108
void unregister_from_hub(void)
Unregister client from SAMP Hub.
Definition: GVOClient.cpp:856
#define G_REQUIRE_HUB
Definition: GVOClient.cpp:49
int m_socket
Hub socket.
Definition: GVOClient.hpp:117
virtual ~GVOClient(void)
Destructor.
Definition: GVOClient.cpp:103
VO client class interface definition.
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition: GFits.cpp:1293
bool require_hub(void)
Require VO Hub.
Definition: GVOClient.cpp:753
std::string m_hub_host
Hub host (extracted from XML-RPC endpoint)
Definition: GVOClient.hpp:110
std::string m_client_id
Client identifier.
Definition: GVOClient.hpp:116
GXmlElement * element(const int &index)
Return pointer to child element.
Definition: GXml.cpp:419
XML class.
Definition: GXml.hpp:172
XML text node class.
Definition: GXmlText.hpp:43
SAMP hub class interface definition.
void start(void)
Start Hub.
Definition: GVOHub.cpp:184
std::string m_client_key
Private client key.
Definition: GVOClient.hpp:114
GChatter
Definition: GTypemaps.hpp:33
VO SAMP Hub class.
Definition: GVOHub.hpp:51
bool is_connected(void) const
Signals if client is connected to VO Hub.
Definition: GVOClient.cpp:255
const std::string & extname(void) const
Return extension name.
Definition: GFitsHDU.hpp:162
#define G_REGISTER_TO_HUB
Definition: GVOClient.cpp:50
int response_error_code(const GXml &xml) const
Return response error code.
Definition: GVOClient.cpp:1238
bool response_is_valid(const GXml &xml) const
Checks if response is valid.
Definition: GVOClient.cpp:1217
virtual GXmlElement * element(const int &index)
Return pointer to GXMLElement child.
Definition: GXmlNode.cpp:640
const std::string & name(void) const
Return VO table name.
Definition: GVOTable.hpp:122
std::string tmpnam(void)
Return temporary file name.
Definition: GTools.cpp:421
bool has_hub(void) const
Signals if client has VO Hub information.
Definition: GVOClient.cpp:241
Exception handler interface definition.
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition: GFits.cpp:678
VO table class definition.
const GXml & xml(void) const
Return VO table XML file.
Definition: GVOTable.hpp:110
std::string m_hub_port
Hub port (extracted from XML-RPC endpoint)
Definition: GVOClient.hpp:111
int toint(const std::string &arg)
Convert string into integer value.
Definition: GTools.cpp:821
void copy_members(const GVOClient &client)
Copy class members.
Definition: GVOClient.cpp:597
std::string get_hub_lockfile(void) const
Returns SAMP Hub lockfile URL.
Definition: GVOClient.cpp:1168
void send_metadata(void) const
Send client metadata to SAMP Hub.
Definition: GVOClient.cpp:891
GVOClient(void)
Void constructor.
Definition: GVOClient.cpp:69
void connect(void)
Register client at VO Hub.
Definition: GVOClient.cpp:187
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
GVOClient & operator=(const GVOClient &client)
Assignment operator.
Definition: GVOClient.cpp:125
VOTable class.
Definition: GVOTable.hpp:55
std::string response_error_message(const GXml &xml) const
Return response error message.
Definition: GVOClient.cpp:1262
Abstract XML node base class interface definition.
void init_members(void)
Initialise class members.
Definition: GVOClient.cpp:572
void free_members(void)
Delete class members.
Definition: GVOClient.cpp:620
void register_to_hub(void)
Register client at SAMP Hub.
Definition: GVOClient.cpp:816
void post_string(const std::string &string) const
Post string content to Hub.
Definition: GVOClient.cpp:1037
std::string m_hub_id
Hub identifier.
Definition: GVOClient.hpp:115
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489