<commit_before>/*
 * ClusterDiscoveryService.cpp
 *
 * Copyright (C) 2006 -2007 by Universitaet Stuttgart (VIS).
 * Alle Rechte vorbehalten.
 */

#include "vislib/ClusterDiscoveryService.h"

#include "vislib/assert.h"
#include "vislib/SocketException.h"
#include "vislib/Trace.h"

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_USER
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_USER = 16;

/*
 * vislib::net::ClusterDiscoveryService::ClusterDiscoveryService
 */
vislib::net::ClusterDiscoveryService::ClusterDiscoveryService(
        const StringA& name, const SocketAddress& bindAddr,
        const IPAddress& bcastAddr, const SocketAddress& responseAddr)
        : bcastAddr(SocketAddress::FAMILY_INET, bcastAddr, bindAddr.GetPort()),
        bindAddr(bindAddr),
        expectedResponseCnt(2),      // TODO
        responseAddr(responseAddr),
        requestInterval(3 * 1000),  // TODO
        name(name),
        requester(NULL),
        responder(NULL),
        requestThread(NULL),
        responseThread(NULL),
        timeoutReceive(2 * 1000),   // TODO
        timeoutSend(5 * 1000) {     // TODO
    this->name.Truncate(MAX_USER_DATA);

    this->peerNodes.Resize(1);  // TODO: Remove alloc crowbar!
}

/*
 * vislib::net::ClusterDiscoveryService::~ClusterDiscoveryService
 */
vislib::net::ClusterDiscoveryService::~ClusterDiscoveryService(void) {
    if (this->requestThread != NULL) {
        try {
            this->requestThread->Terminate(false);
            SAFE_DELETE(this->requester);
            SAFE_DELETE(this->requestThread);
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "The discovery requester thread could "
                "not be successfully terminated.\n");
        }
    }

    if (this->responseThread != NULL) {
        try {
            this->responseThread->Terminate(false);
            SAFE_DELETE(this->responder);
            SAFE_DELETE(this->responseThread);
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "The discovery responder thread could "
                "not be successfully terminated.\n");
        }
    }
}

/*
 * vislib::net::ClusterDiscoveryService::AddListener
 */
void vislib::net::ClusterDiscoveryService::AddListener(
        ClusterDiscoveryListener *listener) {
    ASSERT(listener != NULL);

    this->critSect.Lock();
    if ((listener != NULL) && !this->listeners.Contains(listener)) {
        this->listeners.Append(listener);
    }
    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::RemoveListener
 */
void vislib::net::ClusterDiscoveryService::RemoveListener(
        ClusterDiscoveryListener *listener) {
    ASSERT(listener != NULL);

    this->critSect.Lock();
    this->listeners.Remove(listener);
    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::Start
 */
bool vislib::net::ClusterDiscoveryService::Start(void) {
    /* Prepare and start the thread generating the discovery requests. */
    this->requester = new Requester(*this);
    this->requestThread = new vislib::sys::Thread(this->requester);
    this->requestThread->Start();

    /* Prepare the response thread. */
    this->responder = new Responder(*this);
    this->responseThread = new vislib::sys::Thread(this->responder);
    this->responseThread->Start();

    // TODO: Remove debug crowbaring
    //this->requestThread->Join();
    //sys::Thread::Sleep(5 * 1000);
    //this->requestThread->Terminate(false);
    //sys::Thread::Sleep(5 * 1000);
    //this->requestThread->Join();
    return true;
}

/*
 * vislib::net::ClusterDiscoveryService::Stop
 */
bool vislib::net::ClusterDiscoveryService::Stop(void) {
    try {
        this->requestThread->Terminate(false);
        this->responseThread->Terminate(false);
        return true;
    } catch (sys::SystemException e) {
        TRACE(Trace::LEVEL_ERROR, "Stopping discovery threads failed. The "
            "error code is %d (\"%s\").\n", e.GetErrorCode(), e.GetMsgA());
        return false;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Begin of nested class Requester

/*
 * vislib::net::ClusterDiscoveryService::Requester::Requester
 */
vislib::net::ClusterDiscoveryService::Requester::Requester(
        ClusterDiscoveryService& cds) : cds(cds), isRunning(true) {
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::~Requester
 */
vislib::net::ClusterDiscoveryService::Requester::~Requester(void) {
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::Run
 */
DWORD vislib::net::ClusterDiscoveryService::Requester::Run(
        const void *reserved) {
    SocketAddress peerAddr;
    PeerNode peerNode;
    Message request;
    Message response;
    Socket socket;

    // Assert expected memory layout of messages.
    ASSERT(sizeof(request) == MAX_USER_DATA + 4);
    ASSERT(reinterpret_cast<BYTE *>(&(request.name))
       == reinterpret_cast<BYTE *>(&request) + 4);

    /* Prepare the socket. */
    try {
        Socket::Startup();
        socket.Create(Socket::FAMILY_INET, Socket::TYPE_DGRAM,
            Socket::PROTOCOL_UDP);
        socket.SetBroadcast(true);
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Discovery requester thread could not "
            "create its. The error code is %d (\"%s\").\n", e.GetErrorCode(),
            e.GetMsgA());
        return 1;
    }

    /* Prepare our request for multiple broadcasting. */
    request.magicNumber = MAGIC_NUMBER;
    request.msgType = MSG_TYPE_DISCOVERY_REQUEST;
#if (_MSC_VER >= 1400)
    ::strncpy_s(request.name, MAX_USER_DATA, this->cds.name, MAX_USER_DATA);
#else /* (_MSC_VER >= 1400) */
    ::strncpy(request.name, this->cds.name.PeekBuffer(), MAX_USER_DATA);
#endif /* (_MSC_VER >= 1400) */

    while (this->isRunning) {
        try {

            /* Broadcast request. */
            socket.Send(&request, sizeof(Message), this->cds.bcastAddr);
            TRACE(Trace::LEVEL_INFO, "Request thread sent "
                "MSG_TYPE_DISCOVERY_REQUEST to %s.\n",
                this->cds.bcastAddr.ToStringA().PeekBuffer());

            for (UINT i = 0; i < this->cds.expectedResponseCnt; i++) {
                socket.Receive(&response, sizeof(Message),
                    this->cds.timeoutReceive, peerAddr);
                if (response.magicNumber == MAGIC_NUMBER) {
                    /* Message OK, look for its content. */

                    if (response.msgType == MSG_TYPE_DISCOVERY_RESPONSE) {
                        TRACE(Trace::LEVEL_INFO, "Request thread received "
                            "MSG_TYPE_DISCOVERY_RESPONSE from from %s.\n",
                            peerAddr.ToStringA().PeekBuffer());

                        /* Add peer to local list, if not yet known. */
                        peerNode.address = response.responseAddr;
                        this->cds.addPeerNode(peerNode);
                    }

                } /* end if (response.magicNumber == MAGIC_NUMBER) */
            } /* end for (UINT i = 0; i < ... */

            sys::Thread::Sleep(this->cds.requestInterval);

        } catch (SocketException e) {
            TRACE(Trace::LEVEL_WARN, "A socket error occurred in the "
                "discovery request thread. The error code is %d (\"%s\").\n",
                e.GetErrorCode(), e.GetMsgA());
        } catch (Exception e) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "dispatched due to an unexpected exception (\"%s\").\n",
                e.GetMsgA());
            return 2;
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "dispatched due to an unexpected exception.\n");
            return 3;
        }
    } /* end while (this->isRunning) */

    /* Clean up. */
    try {
        socket.Shutdown(Socket::BOTH);
        socket.Close();
        Socket::Cleanup();
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Socket cleanup failed in the discovery "
            "request thread. The error code is %d (\"%s\").\n",
            e.GetErrorCode(), e.GetMsgA());
    }

    return 0;
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::Terminate
 */
bool vislib::net::ClusterDiscoveryService::Requester::Terminate(void) {
    // TODO: Should perhaps be protected by crit sect.
    this->isRunning = false;
    return true;
}

// End of nested class Requester
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Begin of nested class Responder

/*
 * vislib::net::ClusterDiscoveryService::Responder::Responder
 */
vislib::net::ClusterDiscoveryService::Responder::Responder(
        ClusterDiscoveryService& cds) : cds(cds), isRunning(true) {
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::~Responder
 */
vislib::net::ClusterDiscoveryService::Responder::~Responder(void) {
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::Run
 */
DWORD vislib::net::ClusterDiscoveryService::Responder::Run(
        const void *reserved) {
    SocketAddress peerAddr;     // Receives address of UDP communication peer.
    PeerNode peerNode;          // The peer node to register in our list.
    Message request;            // Receives the request messages.
    Message discoveryResponse;  // The discovery response we send.
    Socket socket;              // The datagram socket that is listening.

    // Assert expected message memory layout.
    ASSERT(sizeof(request) == MAX_USER_DATA + 4);
    ASSERT(reinterpret_cast<BYTE *>(&(request.name))
       == reinterpret_cast<BYTE *>(&request) + 4);

    /*
     * Prepare a datagram socket listening for requests on the specified
     * adapter and port.
     */
    try {
        Socket::Startup();
        socket.Create(Socket::FAMILY_INET, Socket::TYPE_DGRAM,
            Socket::PROTOCOL_UDP);
        socket.Bind(this->cds.bindAddr);
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Discovery responder thread could not "
            "create its socket and bind it to the requested address. The "
            "error code is %d (\"%s\").\n", e.GetErrorCode(), e.GetMsgA());
        return 1;
    }

    /* Prepare a discovery response for multiple use. */
    discoveryResponse.magicNumber = MAGIC_NUMBER;
    discoveryResponse.msgType = MSG_TYPE_DISCOVERY_RESPONSE;
    discoveryResponse.responseAddr = this->cds.responseAddr;

    while (this->isRunning) {
        try {

            /* Wait for next message. */
            // Note: This receive operation must be timeouted in order to allow
            // the thread to be terminated in a controlled manner.
            socket.Receive(&request, sizeof(Message)/*, this->cds.timeoutReceive*/,
                peerAddr);

            if (request.magicNumber == MAGIC_NUMBER) {
                /* Message OK, look for its content. */

                if ((request.msgType == MSG_TYPE_DISCOVERY_REQUEST)
                        && (this->cds.name.Compare(request.name))) {
                    /* Got a discovery request for own cluster. */
                    TRACE(Trace::LEVEL_INFO, "Response thread received "
                        "MSG_TYPE_DISCOVERY_REQUEST from %s.\n",
                        peerAddr.ToStringA().PeekBuffer());

                    socket.Send(&discoveryResponse, sizeof(Message),
                        this->cds.timeoutSend, peerAddr);
                    TRACE(Trace::LEVEL_INFO, "Response thread sent "
                        "MSG_TYPE_DISCOVERY_RESPONSE to %s.\n",
                        peerAddr.ToStringA().PeekBuffer());

                    /* Add peer to local list, if not yet known. */
                    peerNode.address = request.responseAddr;
                    this->cds.addPeerNode(peerNode);
                }
            } /* end if (response.magicNumber == MAGIC_NUMBER) */

        } catch (SocketException e) {
            TRACE(Trace::LEVEL_WARN, "A socket error occurred in the "
                "discovery response thread. The error code is %d (\"%s\").\n",
                e.GetErrorCode(), e.GetMsgA());
        } catch (Exception e) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "answered due to an unexpected exception (\"%s\").\n",
                e.GetMsgA());
            return 2;
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "answered due to an unexpected exception.\n");
            return 3;
        }

    } /* end while (this->isRunning) */

    try {
        socket.Shutdown(Socket::BOTH);
        socket.Close();
        Socket::Cleanup();
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Socket cleanup failed in the discovery "
            "response thread. The error code is %d (\"%s\").\n",
            e.GetErrorCode(), e.GetMsgA());
    }

    return 0;
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::Terminate
 */
bool vislib::net::ClusterDiscoveryService::Responder::Terminate(void) {
    // TODO: Should perhaps be protected by crit sect.
    this->isRunning = false;
    return true;
}

// End of nested class Responder
////////////////////////////////////////////////////////////////////////////////

/*
 * vislib::net::ClusterDiscoveryService::addPeerNode
 */
void vislib::net::ClusterDiscoveryService::addPeerNode(const PeerNode& node) {
    this->critSect.Lock();

    if (!this->peerNodes.Contains(node)) {
        this->peerNodes.Append(node);

        /* Fire event. */
        // TODO: We need a const iterator.
        ListenerList::Iterator it = const_cast<ClusterDiscoveryService *>(
            this)->listeners.GetIterator();
        while (it.HasNext()) {
            it.Next()->OnNodeFound(*this, node.address);
        }
    }

    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::fireNodeFound
 */
//void vislib::net::ClusterDiscoveryService::fireNodeFound(
//        const PeerNode& node) const {
//    this->critSect.Lock();
//
//    // TODO: We need a const iterator.
//    ListenerList::Iterator it = const_cast<ClusterDiscoveryService *>(
//        this)->listeners.GetIterator();
//    while (it.HasNext()) {
//        it.Next()->OnNodeFound(*this, node.address);
//    }
//
//    this->critSect.Unlock();
//}

/*
 * vislib::net::ClusterDiscoveryService::MAGIC_NUMBER
 */
const UINT16 vislib::net::ClusterDiscoveryService::MAGIC_NUMBER
    = static_cast<UINT16>('v') << 8 | static_cast<UINT16>('l');

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_REQUEST
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_REQUEST
    = 1;

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_RESPONSE
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_RESPONSE
    = 2;
<commit_msg>Discovery of nonsense IPs removed. Consider change of discovery request, however.<commit_after>/*
 * ClusterDiscoveryService.cpp
 *
 * Copyright (C) 2006 -2007 by Universitaet Stuttgart (VIS).
 * Alle Rechte vorbehalten.
 */

#include "vislib/ClusterDiscoveryService.h"

#include "vislib/assert.h"
#include "vislib/SocketException.h"
#include "vislib/Trace.h"

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_USER
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_USER = 16;

/*
 * vislib::net::ClusterDiscoveryService::ClusterDiscoveryService
 */
vislib::net::ClusterDiscoveryService::ClusterDiscoveryService(
        const StringA& name, const SocketAddress& bindAddr,
        const IPAddress& bcastAddr, const SocketAddress& responseAddr)
        : bcastAddr(SocketAddress::FAMILY_INET, bcastAddr, bindAddr.GetPort()),
        bindAddr(bindAddr),
        expectedResponseCnt(2),      // TODO
        responseAddr(responseAddr),
        requestInterval(3 * 1000),  // TODO
        name(name),
        requester(NULL),
        responder(NULL),
        requestThread(NULL),
        responseThread(NULL),
        timeoutReceive(2 * 1000),   // TODO
        timeoutSend(5 * 1000) {     // TODO
    this->name.Truncate(MAX_USER_DATA);

    this->peerNodes.Resize(0);  // TODO: Remove alloc crowbar!
}

/*
 * vislib::net::ClusterDiscoveryService::~ClusterDiscoveryService
 */
vislib::net::ClusterDiscoveryService::~ClusterDiscoveryService(void) {
    if (this->requestThread != NULL) {
        try {
            this->requestThread->Terminate(false);
            SAFE_DELETE(this->requester);
            SAFE_DELETE(this->requestThread);
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "The discovery requester thread could "
                "not be successfully terminated.\n");
        }
    }

    if (this->responseThread != NULL) {
        try {
            this->responseThread->Terminate(false);
            SAFE_DELETE(this->responder);
            SAFE_DELETE(this->responseThread);
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "The discovery responder thread could "
                "not be successfully terminated.\n");
        }
    }
}

/*
 * vislib::net::ClusterDiscoveryService::AddListener
 */
void vislib::net::ClusterDiscoveryService::AddListener(
        ClusterDiscoveryListener *listener) {
    ASSERT(listener != NULL);

    this->critSect.Lock();
    if ((listener != NULL) && !this->listeners.Contains(listener)) {
        this->listeners.Append(listener);
    }
    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::RemoveListener
 */
void vislib::net::ClusterDiscoveryService::RemoveListener(
        ClusterDiscoveryListener *listener) {
    ASSERT(listener != NULL);

    this->critSect.Lock();
    this->listeners.Remove(listener);
    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::Start
 */
bool vislib::net::ClusterDiscoveryService::Start(void) {
    /* Prepare and start the thread generating the discovery requests. */
    this->requester = new Requester(*this);
    this->requestThread = new vislib::sys::Thread(this->requester);
    this->requestThread->Start();

    /* Prepare the response thread. */
    this->responder = new Responder(*this);
    this->responseThread = new vislib::sys::Thread(this->responder);
    this->responseThread->Start();

    // TODO: Remove debug crowbaring
    //this->requestThread->Join();
    //sys::Thread::Sleep(5 * 1000);
    //this->requestThread->Terminate(false);
    //sys::Thread::Sleep(5 * 1000);
    //this->requestThread->Join();
    return true;
}

/*
 * vislib::net::ClusterDiscoveryService::Stop
 */
bool vislib::net::ClusterDiscoveryService::Stop(void) {
    try {
        this->requestThread->Terminate(false);
        this->responseThread->Terminate(false);
        return true;
    } catch (sys::SystemException e) {
        TRACE(Trace::LEVEL_ERROR, "Stopping discovery threads failed. The "
            "error code is %d (\"%s\").\n", e.GetErrorCode(), e.GetMsgA());
        return false;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Begin of nested class Requester

/*
 * vislib::net::ClusterDiscoveryService::Requester::Requester
 */
vislib::net::ClusterDiscoveryService::Requester::Requester(
        ClusterDiscoveryService& cds) : cds(cds), isRunning(true) {
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::~Requester
 */
vislib::net::ClusterDiscoveryService::Requester::~Requester(void) {
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::Run
 */
DWORD vislib::net::ClusterDiscoveryService::Requester::Run(
        const void *reserved) {
    SocketAddress peerAddr;
    PeerNode peerNode;
    Message request;
    Message response;
    Socket socket;

    // Assert expected memory layout of messages.
    ASSERT(sizeof(request) == MAX_USER_DATA + 4);
    ASSERT(reinterpret_cast<BYTE *>(&(request.name))
       == reinterpret_cast<BYTE *>(&request) + 4);

    /* Prepare the socket. */
    try {
        Socket::Startup();
        socket.Create(Socket::FAMILY_INET, Socket::TYPE_DGRAM,
            Socket::PROTOCOL_UDP);
        socket.SetBroadcast(true);
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Discovery requester thread could not "
            "create its. The error code is %d (\"%s\").\n", e.GetErrorCode(),
            e.GetMsgA());
        return 1;
    }

    /* Prepare our request for multiple broadcasting. */
    request.magicNumber = MAGIC_NUMBER;
    request.msgType = MSG_TYPE_DISCOVERY_REQUEST;
#if (_MSC_VER >= 1400)
    ::strncpy_s(request.name, MAX_USER_DATA, this->cds.name, MAX_USER_DATA);
#else /* (_MSC_VER >= 1400) */
    ::strncpy(request.name, this->cds.name.PeekBuffer(), MAX_USER_DATA);
#endif /* (_MSC_VER >= 1400) */

    while (this->isRunning) {
        try {

            /* Broadcast request. */
            socket.Send(&request, sizeof(Message), this->cds.bcastAddr);
            TRACE(Trace::LEVEL_INFO, "Request thread sent "
                "MSG_TYPE_DISCOVERY_REQUEST to %s.\n",
                this->cds.bcastAddr.ToStringA().PeekBuffer());

            for (UINT i = 0; i < this->cds.expectedResponseCnt; i++) {
                socket.Receive(&response, sizeof(Message),
                    this->cds.timeoutReceive, peerAddr);
                if (response.magicNumber == MAGIC_NUMBER) {
                    /* Message OK, look for its content. */

                    if (response.msgType == MSG_TYPE_DISCOVERY_RESPONSE) {
                        TRACE(Trace::LEVEL_INFO, "Request thread received "
                            "MSG_TYPE_DISCOVERY_RESPONSE from from %s.\n",
                            peerAddr.ToStringA().PeekBuffer());

                        /* Add peer to local list, if not yet known. */
                        peerNode.address = response.responseAddr;
                        this->cds.addPeerNode(peerNode);
                    }

                } /* end if (response.magicNumber == MAGIC_NUMBER) */
            } /* end for (UINT i = 0; i < ... */

            sys::Thread::Sleep(this->cds.requestInterval);

        } catch (SocketException e) {
            TRACE(Trace::LEVEL_WARN, "A socket error occurred in the "
                "discovery request thread. The error code is %d (\"%s\").\n",
                e.GetErrorCode(), e.GetMsgA());
        } catch (Exception e) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "dispatched due to an unexpected exception (\"%s\").\n",
                e.GetMsgA());
            return 2;
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "dispatched due to an unexpected exception.\n");
            return 3;
        }
    } /* end while (this->isRunning) */

    /* Clean up. */
    try {
        socket.Shutdown(Socket::BOTH);
        socket.Close();
        Socket::Cleanup();
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Socket cleanup failed in the discovery "
            "request thread. The error code is %d (\"%s\").\n",
            e.GetErrorCode(), e.GetMsgA());
    }

    return 0;
}

/*
 * vislib::net::ClusterDiscoveryService::Requester::Terminate
 */
bool vislib::net::ClusterDiscoveryService::Requester::Terminate(void) {
    // TODO: Should perhaps be protected by crit sect.
    this->isRunning = false;
    return true;
}

// End of nested class Requester
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Begin of nested class Responder

/*
 * vislib::net::ClusterDiscoveryService::Responder::Responder
 */
vislib::net::ClusterDiscoveryService::Responder::Responder(
        ClusterDiscoveryService& cds) : cds(cds), isRunning(true) {
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::~Responder
 */
vislib::net::ClusterDiscoveryService::Responder::~Responder(void) {
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::Run
 */
DWORD vislib::net::ClusterDiscoveryService::Responder::Run(
        const void *reserved) {
    SocketAddress peerAddr;     // Receives address of UDP communication peer.
    PeerNode peerNode;          // The peer node to register in our list.
    Message request;            // Receives the request messages.
    Message discoveryResponse;  // The discovery response we send.
    Socket socket;              // The datagram socket that is listening.

    // Assert expected message memory layout.
    ASSERT(sizeof(request) == MAX_USER_DATA + 4);
    ASSERT(reinterpret_cast<BYTE *>(&(request.name))
       == reinterpret_cast<BYTE *>(&request) + 4);

    /*
     * Prepare a datagram socket listening for requests on the specified
     * adapter and port.
     */
    try {
        Socket::Startup();
        socket.Create(Socket::FAMILY_INET, Socket::TYPE_DGRAM,
            Socket::PROTOCOL_UDP);
        socket.Bind(this->cds.bindAddr);
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Discovery responder thread could not "
            "create its socket and bind it to the requested address. The "
            "error code is %d (\"%s\").\n", e.GetErrorCode(), e.GetMsgA());
        return 1;
    }

    /* Prepare a discovery response for multiple use. */
    discoveryResponse.magicNumber = MAGIC_NUMBER;
    discoveryResponse.msgType = MSG_TYPE_DISCOVERY_RESPONSE;
    discoveryResponse.responseAddr = this->cds.responseAddr;

    while (this->isRunning) {
        try {

            /* Wait for next message. */
            // Note: This receive operation must be timeouted in order to allow
            // the thread to be terminated in a controlled manner.
            socket.Receive(&request, sizeof(Message)/*, this->cds.timeoutReceive*/,
                peerAddr);

            if (request.magicNumber == MAGIC_NUMBER) {
                /* Message OK, look for its content. */

                if ((request.msgType == MSG_TYPE_DISCOVERY_REQUEST)
                        && (this->cds.name.Compare(request.name))) {
                    /* Got a discovery request for own cluster. */
                    TRACE(Trace::LEVEL_INFO, "Response thread received "
                        "MSG_TYPE_DISCOVERY_REQUEST from %s.\n",
                        peerAddr.ToStringA().PeekBuffer());

                    socket.Send(&discoveryResponse, sizeof(Message),
                        this->cds.timeoutSend, peerAddr);
                    TRACE(Trace::LEVEL_INFO, "Response thread sent "
                        "MSG_TYPE_DISCOVERY_RESPONSE to %s.\n",
                        peerAddr.ToStringA().PeekBuffer());

                    /* Add peer to local list, if not yet known. */
                    // TODO: This is nonsense. The message currently does not
                    // contain a response address. Consider inserting one.
                    //peerNode.address = request.responseAddr;
                    //this->cds.addPeerNode(peerNode);
                }
            } /* end if (response.magicNumber == MAGIC_NUMBER) */

        } catch (SocketException e) {
            TRACE(Trace::LEVEL_WARN, "A socket error occurred in the "
                "discovery response thread. The error code is %d (\"%s\").\n",
                e.GetErrorCode(), e.GetMsgA());
        } catch (Exception e) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "answered due to an unexpected exception (\"%s\").\n",
                e.GetMsgA());
            return 2;
        } catch (...) {
            TRACE(Trace::LEVEL_WARN, "A discovery request could not be "
                "answered due to an unexpected exception.\n");
            return 3;
        }

    } /* end while (this->isRunning) */

    try {
        socket.Shutdown(Socket::BOTH);
        socket.Close();
        Socket::Cleanup();
    } catch (SocketException e) {
        TRACE(Trace::LEVEL_ERROR, "Socket cleanup failed in the discovery "
            "response thread. The error code is %d (\"%s\").\n",
            e.GetErrorCode(), e.GetMsgA());
    }

    return 0;
}

/*
 * vislib::net::ClusterDiscoveryService::Responder::Terminate
 */
bool vislib::net::ClusterDiscoveryService::Responder::Terminate(void) {
    // TODO: Should perhaps be protected by crit sect.
    this->isRunning = false;
    return true;
}

// End of nested class Responder
////////////////////////////////////////////////////////////////////////////////

/*
 * vislib::net::ClusterDiscoveryService::addPeerNode
 */
void vislib::net::ClusterDiscoveryService::addPeerNode(const PeerNode& node) {
    this->critSect.Lock();

    if (!this->peerNodes.Contains(node)) {
        this->peerNodes.Append(node);

        /* Fire event. */
        // TODO: We need a const iterator.
        ListenerList::Iterator it = const_cast<ClusterDiscoveryService *>(
            this)->listeners.GetIterator();
        while (it.HasNext()) {
            it.Next()->OnNodeFound(*this, node.address);
        }
    }

    this->critSect.Unlock();
}

/*
 * vislib::net::ClusterDiscoveryService::fireNodeFound
 */
//void vislib::net::ClusterDiscoveryService::fireNodeFound(
//        const PeerNode& node) const {
//    this->critSect.Lock();
//
//    // TODO: We need a const iterator.
//    ListenerList::Iterator it = const_cast<ClusterDiscoveryService *>(
//        this)->listeners.GetIterator();
//    while (it.HasNext()) {
//        it.Next()->OnNodeFound(*this, node.address);
//    }
//
//    this->critSect.Unlock();
//}

/*
 * vislib::net::ClusterDiscoveryService::MAGIC_NUMBER
 */
const UINT16 vislib::net::ClusterDiscoveryService::MAGIC_NUMBER
    = static_cast<UINT16>('v') << 8 | static_cast<UINT16>('l');

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_REQUEST
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_REQUEST
    = 1;

/*
 * vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_RESPONSE
 */
const UINT16 vislib::net::ClusterDiscoveryService::MSG_TYPE_DISCOVERY_RESPONSE
    = 2;
<|endoftext|>


<commit_before>#include <libdariadb/utils/exception.h>
#include <cassert>
#include <common/async_connection.h>
#include <functional>

using namespace std::placeholders;

using namespace boost::asio;

using namespace dariadb;
using namespace dariadb::net;

AsyncConnection::AsyncConnection(NetData_Pool *pool, onDataRecvHandler onRecv,
                                 onNetworkErrorHandler onErr) {
  _pool = pool;
  _async_con_id = 0;
  _messages_to_send = 0;
  _is_stoped = true;
  _on_recv_hadler = onRecv;
  _on_error_handler = onErr;
}

AsyncConnection::~AsyncConnection() noexcept(false) {
  full_stop();
}

void AsyncConnection::set_pool(NetData_Pool *pool) {
  _pool = pool;
}

void AsyncConnection::start(const socket_ptr &sock) {
  if (!_is_stoped) {
    return;
  }
  _sock = sock;
  _is_stoped = false;
  _begin_stoping_flag = false;
  readNextAsync();
}

void AsyncConnection::mark_stoped() {
  _begin_stoping_flag = true;
}

void AsyncConnection::full_stop() {
  mark_stoped();
  try {
    if (auto spt = _sock.lock()) {
      if (spt->is_open()) {
        spt->close();
      }
    }
  } catch (...) {
  }
}

void AsyncConnection::send(const NetData_ptr &d) {
  if (!_begin_stoping_flag) {
    auto ptr = shared_from_this();

    auto ds = d->as_buffer();
    auto send_buffer = std::get<1>(ds);
    auto send_buffer_size = std::get<0>(ds);

    if (auto spt = _sock.lock()) {
      _messages_to_send++;
      auto buf = buffer(send_buffer, send_buffer_size);
      async_write(*spt.get(), buf, [ptr, d](auto err, auto read_bytes) {
        // logger("AsyncConnection::onDataSended #", _async_con_id, " readed ",
        // read_bytes);
        ptr->_messages_to_send--;
        assert(ptr->_messages_to_send >= 0);
        if (err) {
          ptr->_on_error_handler(err);
        }
        ptr->_pool->free(d);
      });
    }
  }
}

void AsyncConnection::readNextAsync() {
  using boost::system::error_code;
  if (auto spt = _sock.lock()) {
    auto ptr = shared_from_this();
    NetData_ptr d = this->_pool->construct();
    async_read(*spt.get(), buffer((uint8_t *)(&d->size), MARKER_SIZE),
               [ptr, d, spt](auto err, auto read_bytes) {
                 if (err) {
                   ptr->_pool->free(d);
                   if (err == boost::asio::error::operation_aborted) {
                     return;
                   }
                   ptr->_on_error_handler(err);
                 } else {
                   if (read_bytes != MARKER_SIZE) {
                     THROW_EXCEPTION("exception on async readMarker. #",
                                     ptr->_async_con_id,
                                     " - wrong marker size: expected ", MARKER_SIZE,
                                     " readed ", read_bytes);
                   }
                   auto buf = buffer((uint8_t *)(&d->data), d->size);
                   async_read(*spt.get(), buf, [ptr, d](auto err, auto read_bytes) {
                     // logger("AsyncConnection::onReadData #", _async_con_id,
                     // "
                     // readed ", read_bytes);
                     if (err) {
                       ptr->_on_error_handler(err);
                     } else {
                       bool cancel_flag = false;
                       bool dont_free_mem = false;
                       try {
                         ptr->_on_recv_hadler(d, cancel_flag, dont_free_mem);
                       } catch (std::exception &ex) {
                         THROW_EXCEPTION("exception on async readData. #",
                                         ptr->_async_con_id, " - ", ex.what());
                       }

                       if (!dont_free_mem) {
                         ptr->_pool->free(d);
                       }

                       if (!cancel_flag) {
                         ptr->readNextAsync();
                       }
                     }
                   });
                 }
               });
  }
}
<commit_msg>AsyncConnection: ignore error connection_reset.<commit_after>#include <libdariadb/utils/exception.h>
#include <cassert>
#include <common/async_connection.h>
#include <functional>

using namespace std::placeholders;

using namespace boost::asio;

using namespace dariadb;
using namespace dariadb::net;

AsyncConnection::AsyncConnection(NetData_Pool *pool, onDataRecvHandler onRecv,
                                 onNetworkErrorHandler onErr) {
  _pool = pool;
  _async_con_id = 0;
  _messages_to_send = 0;
  _is_stoped = true;
  _on_recv_hadler = onRecv;
  _on_error_handler = onErr;
}

AsyncConnection::~AsyncConnection() noexcept(false) {
  full_stop();
}

void AsyncConnection::set_pool(NetData_Pool *pool) {
  _pool = pool;
}

void AsyncConnection::start(const socket_ptr &sock) {
  if (!_is_stoped) {
    return;
  }
  _sock = sock;
  _is_stoped = false;
  _begin_stoping_flag = false;
  readNextAsync();
}

void AsyncConnection::mark_stoped() {
  _begin_stoping_flag = true;
}

void AsyncConnection::full_stop() {
  mark_stoped();
  try {
    if (auto spt = _sock.lock()) {
      if (spt->is_open()) {
        spt->close();
      }
    }
  } catch (...) {
  }
}

void AsyncConnection::send(const NetData_ptr &d) {
  if (!_begin_stoping_flag) {
    auto ptr = shared_from_this();

    auto ds = d->as_buffer();
    auto send_buffer = std::get<1>(ds);
    auto send_buffer_size = std::get<0>(ds);

    if (auto spt = _sock.lock()) {
      _messages_to_send++;
      auto buf = buffer(send_buffer, send_buffer_size);
      async_write(*spt.get(), buf, [ptr, d](auto err, auto read_bytes) {
        // logger("AsyncConnection::onDataSended #", _async_con_id, " readed ",
        // read_bytes);
        ptr->_messages_to_send--;
        assert(ptr->_messages_to_send >= 0);
        if (err) {
          ptr->_on_error_handler(err);
        }
        ptr->_pool->free(d);
      });
    }
  }
}

void AsyncConnection::readNextAsync() {
  using error_code = boost::system::error_code;
  if (auto spt = _sock.lock()) {
    auto ptr = shared_from_this();
    NetData_ptr d = this->_pool->construct();
    async_read(*spt.get(), buffer((uint8_t *)(&d->size), MARKER_SIZE),
               [ptr, d, spt](auto err, auto read_bytes) {
                 if (err) {
                   ptr->_pool->free(d);

                   if (err == boost::asio::error::operation_aborted ||
                       err == boost::asio::error::connection_reset ||
                       err == boost::asio::error::eof) {
                     return;
                   }
                   ptr->_on_error_handler(err);
                 } else {
                   if (read_bytes != MARKER_SIZE) {
                     THROW_EXCEPTION("exception on async readMarker. #",
                                     ptr->_async_con_id,
                                     " - wrong marker size: expected ", MARKER_SIZE,
                                     " readed ", read_bytes);
                   }
                   auto buf = buffer((uint8_t *)(&d->data), d->size);
                   async_read(*spt.get(), buf, [ptr, d](auto err, auto read_bytes) {
                     // logger("AsyncConnection::onReadData #", _async_con_id,
                     // "
                     // readed ", read_bytes);
                     if (err) {
                       ptr->_on_error_handler(err);
                     } else {
                       bool cancel_flag = false;
                       bool dont_free_mem = false;
                       try {
                         ptr->_on_recv_hadler(d, cancel_flag, dont_free_mem);
                       } catch (std::exception &ex) {
                         THROW_EXCEPTION("exception on async readData. #",
                                         ptr->_async_con_id, " - ", ex.what());
                       }

                       if (!dont_free_mem) {
                         ptr->_pool->free(d);
                       }

                       if (!cancel_flag) {
                         ptr->readNextAsync();
                       }
                     }
                   });
                 }
               });
  }
}
<|endoftext|>


<commit_before>/*
 * Win32Window.cpp
 *
 *  Created on: Oct 23, 2014
 *      Author: Carsten Uphoff (uphoff@mytum.de)
 */
#include "Win32Window.hpp"

#include <GL/glld.h>
#include <windowsx.h>

#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList);

#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);

namespace hpl {
LRESULT CALLBACK Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	Win32Window* win32 = reinterpret_cast<Win32Window*>(GetWindowLongPtr(hWnd, GWL_USERDATA));
    switch (message) {
        case WM_CREATE: {
            LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lParam);
            SetWindowLongPtr(hWnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
            break;
        }
        case WM_SIZE: {
            HDC hdc = GetDC(hWnd);
			wglMakeCurrent(hdc, win32->glcontext);
            glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
            win32->win->width = LOWORD(lParam);
            win32->win->height = HIWORD(lParam);
            break;
		}
        case WM_PAINT: {
            HDC hdc = GetDC(hWnd);
			PAINTSTRUCT ps;
			hdc = BeginPaint(hWnd, &ps);
			wglMakeCurrent(hdc, win32->glcontext);
            win32->win->draw();
            SwapBuffers(GetDC(hWnd));
			EndPaint(hWnd, &ps);
            break;
        }
        case WM_DESTROY: {
            HDC hdc = GetDC(hWnd);
			wglMakeCurrent(hdc, win32->glcontext);
			win32->win->close();
            wglMakeCurrent(NULL, NULL);
            wglDeleteContext(win32->glcontext);
            ReleaseDC(hWnd, GetDC(hWnd));
            PostQuitMessage(0);
            break;
        }
        case WM_KEYDOWN: {
            switch (wParam) {
                case VK_ESCAPE:
                    PostMessage(hWnd, WM_DESTROY, NULL, NULL);
                    break;
				case 0x52: // R
					win32->win->resetEvent();
					win32->win->update();
					break;
				default:
					break;
            }
            break;
		}
		case WM_LBUTTONDOWN: {
			win32->win->lastx = GET_X_LPARAM(lParam);
			win32->win->lasty = GET_Y_LPARAM(lParam);
			break;
		}
		case WM_MOUSEMOVE: {
			if (wParam == MK_LBUTTON) {
				double x = GET_X_LPARAM(lParam);
				double y = GET_Y_LPARAM(lParam);
				win32->win->moveEvent(x - win32->win->lastx, y - win32->win->lasty);
				win32->win->lastx = x;
				win32->win->lasty = y;
				win32->win->update();
			}
			break;
		}
		case WM_MOUSEWHEEL: {
            POINT p;
            p.x = GET_X_LPARAM(lParam);
            p.y = GET_Y_LPARAM(lParam);
            ScreenToClient(hWnd, &p);
			win32->win->mouseWheelEvent(
				p.x,
				p.y,
				GET_WHEEL_DELTA_WPARAM(wParam) / static_cast<double>(WHEEL_DELTA)
			);
			win32->win->update();
			break;
		}
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
    }

    return 0;
}

bool Win32Window::show(Window* windowBase)
{
	win = windowBase;

    char const windowClass[] = "canvas";

    HINSTANCE hInstance = GetModuleHandle(NULL);
    WNDCLASSEX wc;
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = sizeof(Win32Window*);
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, IDI_WINLOGO);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = windowClass;
    wc.hIconSm       = LoadIcon(hInstance, IDI_WINLOGO);

    RegisterClassEx(&wc);

    HWND hWndTmp = CreateWindow(windowClass, "Canvas", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);

    // Create a basic OpenGL context
	PIXELFORMATDESCRIPTOR  pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,
		0, 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0,
		24,
		0, 0,
		PFD_MAIN_PLANE,
		0, 0, 0, 0
	};

	HDC hdcTmp = GetDC(hWndTmp);
	int pf = ChoosePixelFormat(hdcTmp, &pfd);
	SetPixelFormat(hdcTmp, pf, &pfd);
	glcontext = wglCreateContext(hdcTmp);
	wglMakeCurrent(hdcTmp, glcontext);

	auto wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB");
	auto wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
	int iAttribs[] = {
		WGL_SAMPLES_ARB, 4,
		WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
		WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
		WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
		WGL_COLOR_BITS_ARB, 24,
		WGL_ALPHA_BITS_ARB, 8,
		WGL_DEPTH_BITS_ARB, 24,
		WGL_STENCIL_BITS_ARB, 0,
		WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
		WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
		0
	};
	float fAttribs[] = {0};
	int pixelFormat;
	UINT numFormats;

	GLint attribs[] = {
		WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
		WGL_CONTEXT_MINOR_VERSION_ARB, 1,
		WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
		0
	};

	DestroyWindow(hWndTmp);
    hWnd = CreateWindow(windowClass, "Canvas", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, this);
    HDC hdc = GetDC(hWnd);
	int msaa = 16;
	while (msaa >= 0 && !wglChoosePixelFormatARB(hdc, iAttribs, fAttribs, 1, &pixelFormat, &numFormats)) {
		--msaa;
	}
	SetPixelFormat(hdc, pixelFormat, &pfd);
	glcontext = wglCreateContextAttribsARB(hdc, 0, attribs);
	wglMakeCurrent(hdc, glcontext);

	if (!win->loadOpenGL()) {
		return false;
	}

	win->init();

    ShowWindow(hWnd, SW_MAXIMIZE);
    UpdateWindow(hWnd);

    return true;
}

void Win32Window::poll()
{
	MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void Win32Window::update()
{
	InvalidateRect(hWnd, NULL, TRUE);
	UpdateWindow(hWnd);
}
}
<commit_msg>Fixes #4.<commit_after>/*
 * Win32Window.cpp
 *
 *  Created on: Oct 23, 2014
 *      Author: Carsten Uphoff (uphoff@mytum.de)
 */
#include "Win32Window.hpp"

#include <GL/glld.h>
#include <windowsx.h>

#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList);

#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);

namespace hpl {
LRESULT CALLBACK Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	Win32Window* win32 = reinterpret_cast<Win32Window*>(GetWindowLongPtr(hWnd, GWL_USERDATA));
    switch (message) {
        case WM_CREATE: {
            LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lParam);
            SetWindowLongPtr(hWnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
            break;
        }
        case WM_SIZE: {
            HDC hdc = GetDC(hWnd);
			wglMakeCurrent(hdc, win32->glcontext);
            glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
            win32->win->width = LOWORD(lParam);
            win32->win->height = HIWORD(lParam);
            break;
		}
        case WM_PAINT: {
            HDC hdc = GetDC(hWnd);
			PAINTSTRUCT ps;
			hdc = BeginPaint(hWnd, &ps);
			wglMakeCurrent(hdc, win32->glcontext);
            win32->win->draw();
            SwapBuffers(GetDC(hWnd));
			EndPaint(hWnd, &ps);
            break;
        }
        case WM_DESTROY: {
            HDC hdc = GetDC(hWnd);
			if (win32 != nullptr) {
				wglMakeCurrent(hdc, win32->glcontext);
				win32->win->close();
				wglMakeCurrent(NULL, NULL);
				wglDeleteContext(win32->glcontext);
			}
            ReleaseDC(hWnd, GetDC(hWnd));
            PostQuitMessage(0);
            break;
        }
        case WM_KEYDOWN: {
            switch (wParam) {
                case VK_ESCAPE:
                    PostMessage(hWnd, WM_DESTROY, NULL, NULL);
                    break;
				case 0x52: // R
					win32->win->resetEvent();
					win32->win->update();
					break;
				default:
					break;
            }
            break;
		}
		case WM_LBUTTONDOWN: {
			win32->win->lastx = GET_X_LPARAM(lParam);
			win32->win->lasty = GET_Y_LPARAM(lParam);
			break;
		}
		case WM_MOUSEMOVE: {
			if (wParam == MK_LBUTTON) {
				double x = GET_X_LPARAM(lParam);
				double y = GET_Y_LPARAM(lParam);
				win32->win->moveEvent(x - win32->win->lastx, y - win32->win->lasty);
				win32->win->lastx = x;
				win32->win->lasty = y;
				win32->win->update();
			}
			break;
		}
		case WM_MOUSEWHEEL: {
            POINT p;
            p.x = GET_X_LPARAM(lParam);
            p.y = GET_Y_LPARAM(lParam);
            ScreenToClient(hWnd, &p);
			win32->win->mouseWheelEvent(
				p.x,
				p.y,
				GET_WHEEL_DELTA_WPARAM(wParam) / static_cast<double>(WHEEL_DELTA)
			);
			win32->win->update();
			break;
		}
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
    }

    return 0;
}

bool Win32Window::show(Window* windowBase)
{
	win = windowBase;

    char const windowClass[] = "canvas";

    HINSTANCE hInstance = GetModuleHandle(NULL);
    WNDCLASSEX wc;
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = sizeof(Win32Window*);
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, IDI_WINLOGO);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = windowClass;
    wc.hIconSm       = LoadIcon(hInstance, IDI_WINLOGO);

    RegisterClassEx(&wc);

    HWND hWndTmp = CreateWindow(windowClass, "Canvas", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, nullptr);

    // Create a basic OpenGL context
	PIXELFORMATDESCRIPTOR  pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,
		0, 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0,
		24,
		0, 0,
		PFD_MAIN_PLANE,
		0, 0, 0, 0
	};

	HDC hdcTmp = GetDC(hWndTmp);
	int pf = ChoosePixelFormat(hdcTmp, &pfd);
	SetPixelFormat(hdcTmp, pf, &pfd);
	glcontext = wglCreateContext(hdcTmp);
	wglMakeCurrent(hdcTmp, glcontext);

	auto wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB");
	auto wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
	int iAttribs[] = {
		WGL_SAMPLES_ARB, 4,
		WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
		WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
		WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
		WGL_COLOR_BITS_ARB, 24,
		WGL_ALPHA_BITS_ARB, 8,
		WGL_DEPTH_BITS_ARB, 24,
		WGL_STENCIL_BITS_ARB, 0,
		WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
		WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
		0
	};
	float fAttribs[] = {0};
	int pixelFormat;
	UINT numFormats;

	GLint attribs[] = {
		WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
		WGL_CONTEXT_MINOR_VERSION_ARB, 1,
		WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
		0
	};

	wglMakeCurrent(NULL, NULL);
	wglDeleteContext(glcontext);

	DestroyWindow(hWndTmp);
    hWnd = CreateWindow(windowClass, "Canvas", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, this);
    HDC hdc = GetDC(hWnd);
	int msaa = 16;
	while (msaa >= 0 && !wglChoosePixelFormatARB(hdc, iAttribs, fAttribs, 1, &pixelFormat, &numFormats)) {
		--msaa;
	}
	SetPixelFormat(hdc, pixelFormat, &pfd);
	glcontext = wglCreateContextAttribsARB(hdc, 0, attribs);
	wglMakeCurrent(hdc, glcontext);

	if (!win->loadOpenGL()) {
		return false;
	}

	win->init();

    ShowWindow(hWnd, SW_MAXIMIZE);
    UpdateWindow(hWnd);

    return true;
}

void Win32Window::poll()
{
	MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void Win32Window::update()
{
	InvalidateRect(hWnd, NULL, TRUE);
	UpdateWindow(hWnd);
}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: datasourceconnector.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: rt $ $Date: 2007-07-24 12:09:40 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _DBAUI_DATASOURCECONNECTOR_HXX_
#define _DBAUI_DATASOURCECONNECTOR_HXX_

#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATASOURCE_HPP_
#include <com/sun/star/sdbc/XDataSource.hpp>
#endif

class Window;
//.........................................................................
namespace dbaui
{
//.........................................................................

    //=====================================================================
    //= ODatasourceConnector
    //=====================================================================
    class ODatasourceConnector
    {
    protected:
        Window*         m_pErrorMessageParent;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >
                        m_xORB;
        ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >
                        m_xDatabaseContext;
        ::rtl::OUString m_sContextInformation;

    public:
        ODatasourceConnector(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB,
            Window* _pMessageParent
        );
        ODatasourceConnector(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB,
            Window* _pMessageParent,
            const ::rtl::OUString& _rContextInformation
        );

        /// returns <TRUE/> if the object is able to create data source connections
        sal_Bool    isValid() const { return m_xDatabaseContext.is(); }

        /// create a data source connection
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >
                    connect(const ::rtl::OUString& _rDataSourceName, sal_Bool _bShowError = sal_True) const;

        /// create a data source connection
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >
                    connect(const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDataSource>& _xDataSource
                            , sal_Bool _bShowError = sal_True) const;

    private:
        void implConstruct();
    };

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // _DBAUI_DATASOURCECONNECTOR_HXX_

<commit_msg>INTEGRATION: CWS odbmacros2 (1.6.82); FILE MERGED 2007/12/19 13:26:09 fs 1.6.82.1: m_xDatabaseContext is not needed<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: datasourceconnector.hxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: kz $ $Date: 2008-03-06 18:24:32 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _DBAUI_DATASOURCECONNECTOR_HXX_
#define _DBAUI_DATASOURCECONNECTOR_HXX_

#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATASOURCE_HPP_
#include <com/sun/star/sdbc/XDataSource.hpp>
#endif

class Window;
//.........................................................................
namespace dbaui
{
//.........................................................................

    //=====================================================================
    //= ODatasourceConnector
    //=====================================================================
    class ODatasourceConnector
    {
    protected:
        Window*         m_pErrorMessageParent;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >
                        m_xORB;
        ::rtl::OUString m_sContextInformation;

    public:
        ODatasourceConnector(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB,
            Window* _pMessageParent
        );
        ODatasourceConnector(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB,
            Window* _pMessageParent,
            const ::rtl::OUString& _rContextInformation
        );

        /// returns <TRUE/> if the object is able to create data source connections
        sal_Bool    isValid() const { return m_xORB.is(); }

        /// create a data source connection
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >
                    connect(const ::rtl::OUString& _rDataSourceName, sal_Bool _bShowError = sal_True) const;

        /// create a data source connection
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >
                    connect(const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDataSource>& _xDataSource
                            , sal_Bool _bShowError = sal_True) const;
    };

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // _DBAUI_DATASOURCECONNECTOR_HXX_

<|endoftext|>


<commit_before>#include "src/agent/agent_endpoint.h"
#include "src/agent/modules/echo.h"
#include "src/agent/modules/inventory.h"
#include "src/agent/modules/ping.h"
#include "src/agent/modules/status.h"
#include "src/agent/external_module.h"
#include "src/agent/schemas.h"
#include "src/agent/errors.h"
#include "src/common/log.h"
#include "src/common/uuid.h"
#include "src/common/string_utils.h"

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

#include <string>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

LOG_DECLARE_NAMESPACE("agent.agent_endpoint");

namespace Cthun {
namespace Agent {

static const uint BACKOFF_MULTIPLIER { 2 };
static const uint BACKOFF_LIMIT { 30 };
static const uint CONNECTION_STATE_CHECK_INTERVAL { 15 };
static const int DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS { 10 };

AgentEndpoint::AgentEndpoint() {
    // declare internal modules
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);

    // load external modules
    // TODO(ale): fix; this breaks if cthun-agent is not invoked from root dir
    boost::filesystem::path module_path { "modules" };
    boost::filesystem::directory_iterator end;

    for (auto file = boost::filesystem::directory_iterator(module_path);
            file != end; ++file) {
        if (!boost::filesystem::is_directory(file->status())) {
            LOG_INFO(file->path().string());

            try {
                ExternalModule* external = new ExternalModule(file->path().string());
                modules_[external->module_name] = std::shared_ptr<Module>(external);
            } catch (...) {
                LOG_ERROR("failed to load: %1%", file->path().string());
            }
        }
    }
}

AgentEndpoint::~AgentEndpoint() {
    if (connection_ptr_ != nullptr) {
        // reset callbacks to avoid breaking the WebSocket Endpoint
        // with invalid references

        LOG_INFO("resetting the WebSocket event callbacks");
        connection_ptr_->resetCallbacks();

        // NB: the heartbeat task will be destructed by RIIA
    }
}

void AgentEndpoint::startAgent(std::string url,
                               std::string ca_crt_path,
                               std::string client_crt_path,
                               std::string client_key_path) {
    listModules();

    // Configure the secure WebSocket endpoint

    try {
        Cthun::WebSocket::CONNECTION_MANAGER.configureSecureEndpoint(ca_crt_path,
                                                                     client_crt_path,
                                                                     client_key_path);
    } catch (Cthun::WebSocket::endpoint_error& e) {
        LOG_WARNING("failed to configure the WebSocket endpoint: %1%", e.what());
        throw fatal_error("failed to configure the WebSocket endpoint");
    }

    // Configure the connection

    connection_ptr_ = Cthun::WebSocket::CONNECTION_MANAGER.createConnection(url);
    setConnectionCallbacks();

    // Connect and wait for open connection event

    try {
        Cthun::WebSocket::CONNECTION_MANAGER.open(connection_ptr_);
        connection_ptr_->waitForOpen();
    } catch (Cthun::WebSocket::connection_error& e) {
        LOG_WARNING("failed to connect; %1%", e.what());
        throw fatal_error { "failed to connect" };
    }

    // Start heartbeat task

    // HeartbeatTask heartbeat_task { connection_ptr_ };
    // heartbeat_task.start();

    // Keep monitoring the connection

    monitorConnectionState();
}

//
// AgentEndpoint - private
//

void AgentEndpoint::listModules() {
    LOG_INFO("loaded modules:");
    for (auto module : modules_) {
        LOG_INFO("   %1%", module.first);
        for (auto action : module.second->actions) {
            LOG_INFO("       %1%", action.first);
        }
    }
}

void AgentEndpoint::sendLogin(Cthun::WebSocket::Client_Type* client_ptr) {
    Json::Value login {};
    login["id"] = 1;
    login["version"] = "1";
    login["expires"] = Common::StringUtils::getISO8601Time(DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS);
    login["sender"] = "cth://localhost/agent";
    login["endpoints"] = Json::Value { Json::arrayValue };
    login["endpoints"][0] = "cth://server";
    login["hops"] = Json::Value { Json::arrayValue };
    login["data_schema"] = "http://puppetlabs.com/loginschema";
    login["data"]["type"] = "agent";

    LOG_INFO("login message:\n%1%", login.toStyledString());

    valijson::Schema message_schema = Schemas::network_message();
    std::vector<std::string> errors;

    if (!Schemas::validate(login, message_schema, errors)) {
        LOG_WARNING("validation failed");
        for (auto error : errors) {
            LOG_WARNING("    %1%", error);
        }
        // This is unexpected
        throw fatal_error { "invalid login message schema" };
    }

    auto handle = connection_ptr_->getConnectionHandle();
    try {
        client_ptr->send(handle, login.toStyledString(),
                         Cthun::WebSocket::Frame_Opcode_Values::text);
    }  catch(Cthun::WebSocket::message_error& e) {
        LOG_WARNING("failed to send: %1%", e.what());
        // Fatal; we can't login...
        throw fatal_error { "failed to send login message" };
    }
}

Json::Value AgentEndpoint::parseAndValidateMessage(std::string message) {
    Json::Value doc;
    Json::Reader reader;

    if (!reader.parse(message, doc)) {
        throw validation_error { "json decode of message failed" };
    }

    valijson::Schema message_schema = Schemas::network_message();
    std::vector<std::string> errors;

    if (!Schemas::validate(doc, message_schema, errors)) {
        throw validation_error { "message schema validation failed" };
    }

    if (std::string("http://puppetlabs.com/cncschema").compare(
            doc["data_schema"].asString()) != 0) {
        throw validation_error { "message is not of cnc schema" };
    }

    valijson::Schema data_schema { Schemas::cnc_data() };
    if (!Schemas::validate(doc["data"], data_schema, errors)) {
        // TODO(ale): refactor error logging
        LOG_WARNING("data schema validation failed; logging the errors");
        for (auto error : errors) {
            LOG_WARNING("    %1%", error);
        }
        throw validation_error { "data schema validation failed" };
    }

    return doc;
}

void AgentEndpoint::delayedActionThread(std::shared_ptr<Module> module,
                 std::string action_name,
                 Json::Value doc,
                 Json::Value output,
                 std::string uuid) {
    module->validate_and_call_action(action_name,
                                     doc["data"]["params"],
                                     output,
                                     uuid);
}

void AgentEndpoint::handleMessage(Cthun::WebSocket::Client_Type* client_ptr,
                                   std::string message) {
    LOG_INFO("received message:\n%1%", message);

    Json::Value doc;

    try {
        doc = parseAndValidateMessage(message);
    } catch (validation_error& e) {
        LOG_ERROR("invalid message: %1%", e.what());
        return;
    }

    Json::Value output;
    std::string module_name = doc["data"]["module"].asString();
    std::string action_name = doc["data"]["action"].asString();
    std::string sender = doc["sender"].asString();

    try {
        if (modules_.find(module_name) != modules_.end()) {
            std::shared_ptr<Module> module = modules_[module_name];

            if (module->actions.find(action_name) == module->actions.end()) {
                LOG_ERROR("Invalid request: unknown action %1% for module %2%",
                          module_name, action_name);
                throw validation_error { "Invalid request: unknown action " +
                                         action_name +
                                         " for module " +
                                         module_name };
            }
            Agent::Action action = module->actions[action_name];

            if (action.behaviour.compare("delayed") == 0) {
                std::string uuid { Common::getUUID() };
                LOG_DEBUG("Delayed action execution requested. Creating job with ID %1%",
                          uuid);
                Json::Value output {};
                output["status"] = "Requested excution of action: " + action_name;
                output["id"] = uuid;
                sendResponseMessage(sender, output, client_ptr);
                thread_queue_.push_back(std::thread(&AgentEndpoint::delayedActionThread,
                                                    this,
                                                    module,
                                                    action_name,
                                                    doc,
                                                    output,
                                                    uuid));
            } else {
                module->validate_and_call_action(action_name,
                                                 doc["data"]["params"],
                                                 output);
                LOG_DEBUG("%1% %2% output: %3%",
                          module_name, action_name, output.toStyledString());
                sendResponseMessage(sender, output, client_ptr);
            }
        } else {
            LOG_ERROR("Invalid request: unknown module %1%", module_name);
            throw validation_error { "Invalid request: unknown module " +
                                     module_name };
        }
    } catch (validation_error& e) {
        LOG_ERROR("failed to perform '%1% %2%': %3%",
                  module_name, action_name, e.what());
        Json::Value err_result;
        err_result["error"] = e.what();
        sendResponseMessage(sender, err_result, client_ptr);
    }
}

void AgentEndpoint::sendResponseMessage(std::string sender,
                                        Json::Value output,
                                        Cthun::WebSocket::Client_Type* client_ptr) {
    Json::Value body {};
    body["id"] = 2;
    body["version"] = "1";
    body["expires"] = Common::StringUtils::getISO8601Time(DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS);
    body["sender"] = "cth://localhost/agent";
    body["endpoints"] = Json::Value { Json::arrayValue };
    body["endpoints"][0] = sender;
    body["hops"] = Json::Value { Json::arrayValue };
    body["data_schema"] = "http://puppetlabs.com/cncresponseschema";
    body["data"]["response"] = output;

    try {
        std::string response_txt = body.toStyledString();
        LOG_INFO("sending response of size %1%", response_txt.size());
        LOG_DEBUG("response:\n%1%", response_txt);

        auto handle = connection_ptr_->getConnectionHandle();
        client_ptr->send(handle,
                         response_txt,
                         Cthun::WebSocket::Frame_Opcode_Values::text);
    }  catch(Cthun::WebSocket::message_error& e) {
        LOG_ERROR("failed to send: %1%", e.what());
        // we don't want to throw anything here
    } catch (std::exception&  e) {
        LOG_ERROR("unexpected exception: %1%", e.what());
    } catch (...) {
        LOG_ERROR("badness occured");
    }
}

void AgentEndpoint::setConnectionCallbacks() {
    connection_ptr_->setOnOpenCallback(
        [this](Cthun::WebSocket::Client_Type* client_ptr,
               Cthun::WebSocket::Connection::Ptr connection_ptr_c) {
            assert(connection_ptr_ == connection_ptr_c);
            sendLogin(client_ptr);
        });

    connection_ptr_->setOnMessageCallback(
        [this](Cthun::WebSocket::Client_Type* client_ptr,
               Cthun::WebSocket::Connection::Ptr connection_ptr_c,
               std::string message) {
            assert(connection_ptr_ == connection_ptr_c);
            handleMessage(client_ptr, message);
        });

    std::atomic<uint> consecutive_pong_timeouts { 0 };

    connection_ptr_->setOnPongCallback(
        [&consecutive_pong_timeouts](Cthun::WebSocket::Client_Type* client_ptr,
                                     Cthun::WebSocket::Connection::Ptr connection_ptr_c,
                                     std::string binary_payload) {
            LOG_DEBUG("received pong - payload: '%1%'", binary_payload);
            if (consecutive_pong_timeouts > 0){
                consecutive_pong_timeouts = 0;
            }
        });

    connection_ptr_->setOnPongTimeoutCallback(
        [&consecutive_pong_timeouts](Cthun::WebSocket::Client_Type* client_ptr,
                                     Cthun::WebSocket::Connection::Ptr connection_ptr_c,
                                     std::string binary_payload) {
            LOG_WARNING("pong timeout (%1% consecutive) - payload: '%2 %'",
                        std::to_string(consecutive_pong_timeouts), binary_payload);
            ++consecutive_pong_timeouts;
        });
}

void AgentEndpoint::monitorConnectionState() {
    uint backoff_seconds = 2;

    for (;;) {
        sleep(CONNECTION_STATE_CHECK_INTERVAL);

        if (connection_ptr_->getState()
                != Cthun::WebSocket::Connection_State_Values::open) {
            LOG_WARNING("Connection to Cthun server lost. Retrying.");
            // Attempt to re-establish connection
            while (connection_ptr_->getState()
                != Cthun::WebSocket::Connection_State_Values::open) {
                try {
                    Cthun::WebSocket::CONNECTION_MANAGER.open(connection_ptr_);
                    connection_ptr_->waitForOpen();
                    LOG_INFO("Successfully re-established connection to Cthun server.");
                } catch (Cthun::WebSocket::connection_error& e) {
                    LOG_WARNING("Failed to re-establish connection to Cthun server");
                    LOG_INFO("Attemping reconnect in %1% seconds.", backoff_seconds);
                    sleep(backoff_seconds);
                    if ((backoff_seconds *= BACKOFF_MULTIPLIER) >= BACKOFF_LIMIT) {
                        // Limit backoff to 30 seconds.
                        backoff_seconds = 30;
                    }
                }
            }
        } else {
            LOG_INFO("Sending heartbeat ping");
            Cthun::WebSocket::CONNECTION_MANAGER.ping(connection_ptr_,
                                                          binary_payload_);
        }
    }
}

}  // namespace Agent
}  // namespace Cthun
<commit_msg>maint - log sending of heartbeats at debug<commit_after>#include "src/agent/agent_endpoint.h"
#include "src/agent/modules/echo.h"
#include "src/agent/modules/inventory.h"
#include "src/agent/modules/ping.h"
#include "src/agent/modules/status.h"
#include "src/agent/external_module.h"
#include "src/agent/schemas.h"
#include "src/agent/errors.h"
#include "src/common/log.h"
#include "src/common/uuid.h"
#include "src/common/string_utils.h"

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

#include <string>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

LOG_DECLARE_NAMESPACE("agent.agent_endpoint");

namespace Cthun {
namespace Agent {

static const uint BACKOFF_MULTIPLIER { 2 };
static const uint BACKOFF_LIMIT { 30 };
static const uint CONNECTION_STATE_CHECK_INTERVAL { 15 };
static const int DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS { 10 };

AgentEndpoint::AgentEndpoint() {
    // declare internal modules
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);

    // load external modules
    // TODO(ale): fix; this breaks if cthun-agent is not invoked from root dir
    boost::filesystem::path module_path { "modules" };
    boost::filesystem::directory_iterator end;

    for (auto file = boost::filesystem::directory_iterator(module_path);
            file != end; ++file) {
        if (!boost::filesystem::is_directory(file->status())) {
            LOG_INFO(file->path().string());

            try {
                ExternalModule* external = new ExternalModule(file->path().string());
                modules_[external->module_name] = std::shared_ptr<Module>(external);
            } catch (...) {
                LOG_ERROR("failed to load: %1%", file->path().string());
            }
        }
    }
}

AgentEndpoint::~AgentEndpoint() {
    if (connection_ptr_ != nullptr) {
        // reset callbacks to avoid breaking the WebSocket Endpoint
        // with invalid references

        LOG_INFO("resetting the WebSocket event callbacks");
        connection_ptr_->resetCallbacks();

        // NB: the heartbeat task will be destructed by RIIA
    }
}

void AgentEndpoint::startAgent(std::string url,
                               std::string ca_crt_path,
                               std::string client_crt_path,
                               std::string client_key_path) {
    listModules();

    // Configure the secure WebSocket endpoint

    try {
        Cthun::WebSocket::CONNECTION_MANAGER.configureSecureEndpoint(ca_crt_path,
                                                                     client_crt_path,
                                                                     client_key_path);
    } catch (Cthun::WebSocket::endpoint_error& e) {
        LOG_WARNING("failed to configure the WebSocket endpoint: %1%", e.what());
        throw fatal_error("failed to configure the WebSocket endpoint");
    }

    // Configure the connection

    connection_ptr_ = Cthun::WebSocket::CONNECTION_MANAGER.createConnection(url);
    setConnectionCallbacks();

    // Connect and wait for open connection event

    try {
        Cthun::WebSocket::CONNECTION_MANAGER.open(connection_ptr_);
        connection_ptr_->waitForOpen();
    } catch (Cthun::WebSocket::connection_error& e) {
        LOG_WARNING("failed to connect; %1%", e.what());
        throw fatal_error { "failed to connect" };
    }

    // Start heartbeat task

    // HeartbeatTask heartbeat_task { connection_ptr_ };
    // heartbeat_task.start();

    // Keep monitoring the connection

    monitorConnectionState();
}

//
// AgentEndpoint - private
//

void AgentEndpoint::listModules() {
    LOG_INFO("loaded modules:");
    for (auto module : modules_) {
        LOG_INFO("   %1%", module.first);
        for (auto action : module.second->actions) {
            LOG_INFO("       %1%", action.first);
        }
    }
}

void AgentEndpoint::sendLogin(Cthun::WebSocket::Client_Type* client_ptr) {
    Json::Value login {};
    login["id"] = 1;
    login["version"] = "1";
    login["expires"] = Common::StringUtils::getISO8601Time(DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS);
    login["sender"] = "cth://localhost/agent";
    login["endpoints"] = Json::Value { Json::arrayValue };
    login["endpoints"][0] = "cth://server";
    login["hops"] = Json::Value { Json::arrayValue };
    login["data_schema"] = "http://puppetlabs.com/loginschema";
    login["data"]["type"] = "agent";

    LOG_INFO("login message:\n%1%", login.toStyledString());

    valijson::Schema message_schema = Schemas::network_message();
    std::vector<std::string> errors;

    if (!Schemas::validate(login, message_schema, errors)) {
        LOG_WARNING("validation failed");
        for (auto error : errors) {
            LOG_WARNING("    %1%", error);
        }
        // This is unexpected
        throw fatal_error { "invalid login message schema" };
    }

    auto handle = connection_ptr_->getConnectionHandle();
    try {
        client_ptr->send(handle, login.toStyledString(),
                         Cthun::WebSocket::Frame_Opcode_Values::text);
    }  catch(Cthun::WebSocket::message_error& e) {
        LOG_WARNING("failed to send: %1%", e.what());
        // Fatal; we can't login...
        throw fatal_error { "failed to send login message" };
    }
}

Json::Value AgentEndpoint::parseAndValidateMessage(std::string message) {
    Json::Value doc;
    Json::Reader reader;

    if (!reader.parse(message, doc)) {
        throw validation_error { "json decode of message failed" };
    }

    valijson::Schema message_schema = Schemas::network_message();
    std::vector<std::string> errors;

    if (!Schemas::validate(doc, message_schema, errors)) {
        throw validation_error { "message schema validation failed" };
    }

    if (std::string("http://puppetlabs.com/cncschema").compare(
            doc["data_schema"].asString()) != 0) {
        throw validation_error { "message is not of cnc schema" };
    }

    valijson::Schema data_schema { Schemas::cnc_data() };
    if (!Schemas::validate(doc["data"], data_schema, errors)) {
        // TODO(ale): refactor error logging
        LOG_WARNING("data schema validation failed; logging the errors");
        for (auto error : errors) {
            LOG_WARNING("    %1%", error);
        }
        throw validation_error { "data schema validation failed" };
    }

    return doc;
}

void AgentEndpoint::delayedActionThread(std::shared_ptr<Module> module,
                 std::string action_name,
                 Json::Value doc,
                 Json::Value output,
                 std::string uuid) {
    module->validate_and_call_action(action_name,
                                     doc["data"]["params"],
                                     output,
                                     uuid);
}

void AgentEndpoint::handleMessage(Cthun::WebSocket::Client_Type* client_ptr,
                                   std::string message) {
    LOG_INFO("received message:\n%1%", message);

    Json::Value doc;

    try {
        doc = parseAndValidateMessage(message);
    } catch (validation_error& e) {
        LOG_ERROR("invalid message: %1%", e.what());
        return;
    }

    Json::Value output;
    std::string module_name = doc["data"]["module"].asString();
    std::string action_name = doc["data"]["action"].asString();
    std::string sender = doc["sender"].asString();

    try {
        if (modules_.find(module_name) != modules_.end()) {
            std::shared_ptr<Module> module = modules_[module_name];

            if (module->actions.find(action_name) == module->actions.end()) {
                LOG_ERROR("Invalid request: unknown action %1% for module %2%",
                          module_name, action_name);
                throw validation_error { "Invalid request: unknown action " +
                                         action_name +
                                         " for module " +
                                         module_name };
            }
            Agent::Action action = module->actions[action_name];

            if (action.behaviour.compare("delayed") == 0) {
                std::string uuid { Common::getUUID() };
                LOG_DEBUG("Delayed action execution requested. Creating job with ID %1%",
                          uuid);
                Json::Value output {};
                output["status"] = "Requested excution of action: " + action_name;
                output["id"] = uuid;
                sendResponseMessage(sender, output, client_ptr);
                thread_queue_.push_back(std::thread(&AgentEndpoint::delayedActionThread,
                                                    this,
                                                    module,
                                                    action_name,
                                                    doc,
                                                    output,
                                                    uuid));
            } else {
                module->validate_and_call_action(action_name,
                                                 doc["data"]["params"],
                                                 output);
                LOG_DEBUG("%1% %2% output: %3%",
                          module_name, action_name, output.toStyledString());
                sendResponseMessage(sender, output, client_ptr);
            }
        } else {
            LOG_ERROR("Invalid request: unknown module %1%", module_name);
            throw validation_error { "Invalid request: unknown module " +
                                     module_name };
        }
    } catch (validation_error& e) {
        LOG_ERROR("failed to perform '%1% %2%': %3%",
                  module_name, action_name, e.what());
        Json::Value err_result;
        err_result["error"] = e.what();
        sendResponseMessage(sender, err_result, client_ptr);
    }
}

void AgentEndpoint::sendResponseMessage(std::string sender,
                                        Json::Value output,
                                        Cthun::WebSocket::Client_Type* client_ptr) {
    Json::Value body {};
    body["id"] = 2;
    body["version"] = "1";
    body["expires"] = Common::StringUtils::getISO8601Time(DEFAULT_MESSAGE_TIMEOUT_IN_SECONDS);
    body["sender"] = "cth://localhost/agent";
    body["endpoints"] = Json::Value { Json::arrayValue };
    body["endpoints"][0] = sender;
    body["hops"] = Json::Value { Json::arrayValue };
    body["data_schema"] = "http://puppetlabs.com/cncresponseschema";
    body["data"]["response"] = output;

    try {
        std::string response_txt = body.toStyledString();
        LOG_INFO("sending response of size %1%", response_txt.size());
        LOG_DEBUG("response:\n%1%", response_txt);

        auto handle = connection_ptr_->getConnectionHandle();
        client_ptr->send(handle,
                         response_txt,
                         Cthun::WebSocket::Frame_Opcode_Values::text);
    }  catch(Cthun::WebSocket::message_error& e) {
        LOG_ERROR("failed to send: %1%", e.what());
        // we don't want to throw anything here
    } catch (std::exception&  e) {
        LOG_ERROR("unexpected exception: %1%", e.what());
    } catch (...) {
        LOG_ERROR("badness occured");
    }
}

void AgentEndpoint::setConnectionCallbacks() {
    connection_ptr_->setOnOpenCallback(
        [this](Cthun::WebSocket::Client_Type* client_ptr,
               Cthun::WebSocket::Connection::Ptr connection_ptr_c) {
            assert(connection_ptr_ == connection_ptr_c);
            sendLogin(client_ptr);
        });

    connection_ptr_->setOnMessageCallback(
        [this](Cthun::WebSocket::Client_Type* client_ptr,
               Cthun::WebSocket::Connection::Ptr connection_ptr_c,
               std::string message) {
            assert(connection_ptr_ == connection_ptr_c);
            handleMessage(client_ptr, message);
        });

    std::atomic<uint> consecutive_pong_timeouts { 0 };

    connection_ptr_->setOnPongCallback(
        [&consecutive_pong_timeouts](Cthun::WebSocket::Client_Type* client_ptr,
                                     Cthun::WebSocket::Connection::Ptr connection_ptr_c,
                                     std::string binary_payload) {
            LOG_DEBUG("received pong - payload: '%1%'", binary_payload);
            if (consecutive_pong_timeouts > 0){
                consecutive_pong_timeouts = 0;
            }
        });

    connection_ptr_->setOnPongTimeoutCallback(
        [&consecutive_pong_timeouts](Cthun::WebSocket::Client_Type* client_ptr,
                                     Cthun::WebSocket::Connection::Ptr connection_ptr_c,
                                     std::string binary_payload) {
            LOG_WARNING("pong timeout (%1% consecutive) - payload: '%2 %'",
                        std::to_string(consecutive_pong_timeouts), binary_payload);
            ++consecutive_pong_timeouts;
        });
}

void AgentEndpoint::monitorConnectionState() {
    uint backoff_seconds = 2;

    for (;;) {
        sleep(CONNECTION_STATE_CHECK_INTERVAL);

        if (connection_ptr_->getState()
                != Cthun::WebSocket::Connection_State_Values::open) {
            LOG_WARNING("Connection to Cthun server lost. Retrying.");
            // Attempt to re-establish connection
            while (connection_ptr_->getState()
                != Cthun::WebSocket::Connection_State_Values::open) {
                try {
                    Cthun::WebSocket::CONNECTION_MANAGER.open(connection_ptr_);
                    connection_ptr_->waitForOpen();
                    LOG_INFO("Successfully re-established connection to Cthun server.");
                } catch (Cthun::WebSocket::connection_error& e) {
                    LOG_WARNING("Failed to re-establish connection to Cthun server");
                    LOG_INFO("Attemping reconnect in %1% seconds.", backoff_seconds);
                    sleep(backoff_seconds);
                    if ((backoff_seconds *= BACKOFF_MULTIPLIER) >= BACKOFF_LIMIT) {
                        // Limit backoff to 30 seconds.
                        backoff_seconds = 30;
                    }
                }
            }
        } else {
            LOG_DEBUG("Sending heartbeat ping");
            Cthun::WebSocket::CONNECTION_MANAGER.ping(connection_ptr_,
                                                          binary_payload_);
        }
    }
}

}  // namespace Agent
}  // namespace Cthun
<|endoftext|>


<commit_before>#ifndef DUNE_STUFF_RESTRICT_PROLONG_HH
#define DUNE_STUFF_RESTRICT_PROLONG_HH

#include <set>
#include <dune/fem/space/common/restrictprolonginterface.hh>

namespace Dune {
/** \brief an arbitrary size set of RestrictProlong operators of same type
  * This class is useful if you have more than a Pair of discrete functions that
	need to be restricted/prolonged via some AdaptionManger.\n
	All interface mandated functions are simply sequentially forwarded to each element in the set.
	\note This set does not take ownership of added operators.
  */
template < class RestrictProlongOperatorPointerType >
class RestrictProlongOperatorSet :public RestrictProlongInterface< RestrictProlongTraits<
                                                                    RestrictProlongOperatorSet<RestrictProlongOperatorPointerType>,double > >
{
public:
    typedef typename RestrictProlongOperatorPointerType::element_type::DomainFieldType
        DomainFieldType;
	RestrictProlongOperatorSet()
	    : pair_set_( SetType() )
	{}

    void setFatherChildWeight (const DomainFieldType& val) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->setFatherChildWeight(val);
		}
	}
	//! restrict data to father
	template <class EntityType>
	void restrictLocal ( EntityType &father,
						 EntityType &son,
						 bool initialize ) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->restrictLocal(father,son,initialize);
		}
	}
	//! prolong data to children
	template <class EntityType>
	void prolongLocal ( EntityType &father,
						EntityType &son,
						bool initialize ) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->prolongLocal(father,son,initialize);
		}
	}

	//! prolong data to children
	template <class CommunicatorImp>
	void addToList(CommunicatorImp& comm)
	{
		for (	typename SetType::iterator it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
            RestrictProlongOperatorPointerType k = (*it);

            k->addToList(comm);
		}
	}

    void add( RestrictProlongOperatorPointerType rpair )
	{
		pair_set_.insert( rpair );
	}

    bool remove( RestrictProlongOperatorPointerType rpair )
	{
		// we use this erase signature so that only one of possibly multiple instances gets remvod
		SetIteratorType it = pair_set_.find( rpair );
		bool found = it != pair_set_.end();
		pair_set_.erase( it );
		return found;
	}

protected:
    typedef std::set<RestrictProlongOperatorPointerType>
		SetType;
	typedef typename SetType::iterator
		SetIteratorType;
	typedef typename SetType::const_iterator
		SetConstIteratorType;
	SetType pair_set_;
};

} //end namespace Dune
#endif // DUNE_STUFF_RESTRICT_PROLONG_HH

<commit_msg>not deleting non-exisiting elements in a set does not cause segfaults >_><commit_after>#ifndef DUNE_STUFF_RESTRICT_PROLONG_HH
#define DUNE_STUFF_RESTRICT_PROLONG_HH

#include <set>
#include <dune/fem/space/common/restrictprolonginterface.hh>

namespace Dune {
/** \brief an arbitrary size set of RestrictProlong operators of same type
  * This class is useful if you have more than a Pair of discrete functions that
	need to be restricted/prolonged via some AdaptionManger.\n
	All interface mandated functions are simply sequentially forwarded to each element in the set.
	\note This set does not take ownership of added operators.
  */
template < class RestrictProlongOperatorPointerType >
class RestrictProlongOperatorSet :public RestrictProlongInterface< RestrictProlongTraits<
                                                                    RestrictProlongOperatorSet<RestrictProlongOperatorPointerType>,double > >
{
public:
    typedef typename RestrictProlongOperatorPointerType::element_type::DomainFieldType
        DomainFieldType;
	RestrictProlongOperatorSet()
	    : pair_set_( SetType() )
	{}

    void setFatherChildWeight (const DomainFieldType& val) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->setFatherChildWeight(val);
		}
	}
	//! restrict data to father
	template <class EntityType>
	void restrictLocal ( EntityType &father,
						 EntityType &son,
						 bool initialize ) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->restrictLocal(father,son,initialize);
		}
	}
	//! prolong data to children
	template <class EntityType>
	void prolongLocal ( EntityType &father,
						EntityType &son,
						bool initialize ) const
	{
		for (	SetConstIteratorType it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
			(*it)->prolongLocal(father,son,initialize);
		}
	}

	//! prolong data to children
	template <class CommunicatorImp>
	void addToList(CommunicatorImp& comm)
	{
		for (	typename SetType::iterator it = pair_set_.begin();
				it != pair_set_.end();
				++it )
		{
            RestrictProlongOperatorPointerType k = (*it);

            k->addToList(comm);
		}
	}

    void add( RestrictProlongOperatorPointerType rpair )
	{
		pair_set_.insert( rpair );
	}

    bool remove( RestrictProlongOperatorPointerType rpair )
	{
		// we use this erase signature so that only one of possibly multiple instances gets remvod
		SetIteratorType it = pair_set_.find( rpair );
		bool found = it != pair_set_.end();
        if ( found )
            pair_set_.erase( it );
		return found;
	}

protected:
    typedef std::set<RestrictProlongOperatorPointerType>
		SetType;
	typedef typename SetType::iterator
		SetIteratorType;
	typedef typename SetType::const_iterator
		SetConstIteratorType;
	SetType pair_set_;
};

} //end namespace Dune
#endif // DUNE_STUFF_RESTRICT_PROLONG_HH

<|endoftext|>