GammaLib 2.0.0
Loading...
Searching...
No Matches
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
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
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
197
198 // Send meta data
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 ***************************************************************************/
241bool 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 ***************************************************************************/
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 ***************************************************************************/
267bool 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 ***************************************************************************/
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 ***************************************************************************/
327GXml GVOClient::execute(const std::string& request) const
328{
329 // Initialise response
330 GXml xml;
331
332 // 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 ***************************************************************************/
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 ***************************************************************************/
445void 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 ***************************************************************************/
513std::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=" +
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 ***************************************************************************/
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 ***************************************************************************/
1037void 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 ***************************************************************************/
1088std::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 ***************************************************************************/
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 ***************************************************************************/
1168std::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 ***************************************************************************/
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 ***************************************************************************/
1262std::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}
Exception handler interface definition.
FITS file class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
#define G_REQUIRE_HUB
Definition GVOClient.cpp:49
#define G_REGISTER_TO_HUB
Definition GVOClient.cpp:50
VO client class interface definition.
SAMP hub class interface definition.
VO table class definition.
Abstract XML node base class interface definition.
XML class interface definition.
Abstract FITS extension base class.
Definition GFitsHDU.hpp:51
const std::string & extname(void) const
Return extension name.
Definition GFitsHDU.hpp:162
FITS file class.
Definition GFits.hpp:63
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition GFits.cpp:1293
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition GFits.cpp:678
VO client class.
Definition GVOClient.hpp:59
std::string m_hub_host
Hub host (extracted from XML-RPC endpoint)
GVOClient * clone(void) const
Clone object.
int response_error_code(const GXml &xml) const
Return response error code.
std::string m_version
The version of the SAMP Standard Profile implemented by the hub.
std::string m_secret
Secret Hub key.
std::string response_error_message(const GXml &xml) const
Return response error message.
bool has_hub(void) const
Signals if client has VO Hub information.
std::string print(const GChatter &chatter=NORMAL) const
Print VO client information.
std::string m_hub_id
Hub identifier.
void free_members(void)
Delete class members.
bool response_is_valid(const GXml &xml) const
Checks if response is valid.
std::string m_hub_url
The XML-RPC endpoint for communication with the hub.
void post_string(const std::string &string) const
Post string content to Hub.
std::string m_name
Client name.
virtual ~GVOClient(void)
Destructor.
void publish(const GFitsHDU &hdu)
Publish FITS HDU.
void copy_members(const GVOClient &client)
Copy class members.
std::string get_hub_lockfile(void) const
Returns SAMP Hub lockfile URL.
void unregister_from_hub(void)
Unregister client from SAMP Hub.
void shutdown_hub(void) const
Shutdown VO Hub.
void connect(void)
Register client at VO Hub.
void send_metadata(void) const
Send client metadata to SAMP Hub.
void register_to_hub(void)
Register client at SAMP Hub.
void clear(void)
Clear object.
GXml execute(const std::string &request) const
Execute function on server.
std::string m_client_key
Private client key.
std::string m_client_id
Client identifier.
std::string m_hub_port
Hub port (extracted from XML-RPC endpoint)
GVOClient & operator=(const GVOClient &client)
Assignment operator.
void disconnect(void)
Unregister client from VO Hub.
GVOClient(void)
Void constructor.
Definition GVOClient.cpp:69
void init_members(void)
Initialise class members.
int m_socket
Hub socket.
void connect_to_hub(void) const
Connect to SAMP Hub.
std::string m_hub_path
Hub path (extracted from XML-RPC endpoint)
std::string get_response_value(const GXml &xml, const std::string &name) const
Returns value for a SAMP Hub response parameter.
bool find_hub(void)
Find VO Hub.
bool require_hub(void)
Require VO Hub.
std::string receive_string(void) const
Receive string content from Hub.
bool ping_hub(void) const
Ping VO Hub.
bool is_connected(void) const
Signals if client is connected to VO Hub.
VO SAMP Hub class.
Definition GVOHub.hpp:51
void start(void)
Start Hub.
Definition GVOHub.cpp:184
VOTable class.
Definition GVOTable.hpp:55
const GXml & xml(void) const
Return VO table XML file.
Definition GVOTable.hpp:110
const std::string & name(void) const
Return VO table name.
Definition GVOTable.hpp:122
Abstract XML node base class.
Definition GXmlNode.hpp:57
virtual GXmlElement * element(const int &index)
Return pointer to GXMLElement child.
Definition GXmlNode.cpp:640
virtual int elements(void) const
Return number of GXMLElement children of node.
Definition GXmlNode.cpp:586
XML text node class.
Definition GXmlText.hpp:43
XML class.
Definition GXml.hpp:172
void save(const GFilename &filename) const
Save XML document into file.
Definition GXml.cpp:581
GXmlElement * element(const int &index)
Return pointer to child element.
Definition GXml.cpp:419
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
int toint(const std::string &arg)
Convert string into integer value.
Definition GTools.cpp:821
std::string tmpnam(void)
Return temporary file name.
Definition GTools.cpp:421
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
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 strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition GTools.cpp:94