-
Notifications
You must be signed in to change notification settings - Fork 216
C++ Glacier2/session demo #655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
80923dd
e6eccd8
c028810
90a191f
e610d48
bc4698e
197b213
7346cea
c635f11
ddc94b7
555457f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Default cmake build directory | ||
| build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| cmake_minimum_required(VERSION 3.21) | ||
|
|
||
| project(glacier2_session CXX) | ||
|
|
||
| include(../../cmake/common.cmake) | ||
|
|
||
| add_executable(client PokeBox.ice Client.cpp) | ||
|
|
||
| slice2cpp_generate(client) | ||
| target_link_libraries(client PRIVATE Ice::Ice Ice::Glacier2) | ||
| if(WIN32) | ||
| add_custom_command(TARGET client POST_BUILD | ||
| COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:client> | ||
| $<TARGET_RUNTIME_DLLS:client> | ||
| $<GENEX_EVAL:$<TARGET_PROPERTY:Ice::Ice,ICE_RUNTIME_DLLS>> | ||
| COMMAND_EXPAND_LISTS | ||
| ) | ||
| endif() | ||
|
|
||
| add_executable( | ||
| server | ||
| DefaultPokeSession.cpp | ||
| DefaultPokeSession.h | ||
| InMemoryPokeStore.cpp | ||
| InMemoryPokeStore.h | ||
| IPokeStore.h | ||
| IUserIdResolver.h | ||
| PokeBox.ice | ||
| Server.cpp | ||
| SessionManager.cpp | ||
| SessionManager.h | ||
| SharedPokeBox.cpp | ||
| SharedPokeBox.h) | ||
|
|
||
| slice2cpp_generate(server) | ||
| target_link_libraries(server PRIVATE Ice::Ice Ice::Glacier2) | ||
| if(WIN32) | ||
| add_custom_command(TARGET server POST_BUILD | ||
| COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:server> | ||
| $<TARGET_RUNTIME_DLLS:server> | ||
| $<GENEX_EVAL:$<TARGET_PROPERTY:Ice::Ice,ICE_RUNTIME_DLLS>> | ||
| COMMAND_EXPAND_LISTS | ||
| ) | ||
| endif() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #include "../../common/Env.h" | ||
| #include "PokeBox.h" | ||
|
|
||
| #include <Glacier2/Glacier2.h> | ||
| #include <Ice/Ice.h> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <iostream> | ||
| #include <random> | ||
|
|
||
| using namespace std; | ||
| using namespace CatchThemAll; | ||
|
|
||
| // All the Pokemon we know about. | ||
| std::array<const char*, 57> allPokemon = { | ||
| "Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmeleon", "Charizard", "Squirtle", "Wartortle", | ||
| "Blastoise", "Caterpie", "Metapod", "Butterfree", "Weedle", "Kakuna", "Beedrill", "Pidgey", | ||
| "Pidgeotto", "Pidgeot", "Rattata", "Raticate", "Spearow", "Fearow", "Ekans", "Arbok", | ||
| "Pikachu", "Raichu", "Sandshrew", "Sandslash", "Nidoran♀", "Nidorina", "Nidoqueen", "Nidoran♂", | ||
| "Nidorino", "Nidoking", "Clefairy", "Clefable", "Vulpix", "Ninetales", "Jigglypuff", "Wigglytuff", | ||
| "Zubat", "Golbat", "Oddish", "Gloom", "Vileplume", "Paras", "Parasect", "Venonat", | ||
| "Venomoth", "Diglett", "Dugtrio", "Meowth", "Persian", "Psyduck", "Golduck", "Mankey", | ||
| "Primeape"}; | ||
|
|
||
| int | ||
| main(int argc, char* argv[]) | ||
| { | ||
| // Retrieve the user ID for this run. | ||
| string userId = argc > 1 ? argv[1] : Env::getUsername(); | ||
|
|
||
| // Create an Ice communicator. We'll use this communicator to create proxies and manage outgoing connections. | ||
| Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv); | ||
|
|
||
| // Make sure the communicator is destroyed at the end of this scope. | ||
| Ice::CommunicatorHolder communicatorHolder{communicator}; | ||
|
|
||
| // Create a proxy to the Glacier2 router. The addressing information (transport, host, and port number) is derived | ||
| // from the value of Glacier2.Client.Endpoints in the glacier2 router configuration file. | ||
| Glacier2::RouterPrx router{communicator, "Glacier2/router:tcp -h localhost -p 4063"}; | ||
|
|
||
| // Set this proxy as the default router for all future proxies created from this communicator. | ||
| communicator->setDefaultRouter(router); | ||
|
|
||
| // Create a session with the Glacier2 router. In this demo, the Glacier2 router is configured to accept any | ||
| // username/password combination. This call establishes a network connection to the Glacier2 router; the lifetime | ||
| // of the session is the same as the lifetime of the connection. | ||
| optional<Glacier2::SessionPrx> session = router->createSession(userId, "password"); | ||
|
|
||
| // We configured a SessionManager on the Glacier2 router, so session is a non-null PokeSession. | ||
| assert(session); | ||
| auto pokeSession = Ice::uncheckedCast<PokeSessionPrx>(*session); | ||
|
|
||
| // Retrieve the PokeBox proxy from the session. | ||
| optional<PokeBoxPrx> pokeBox = pokeSession->getPokeBox(); | ||
| assert(pokeBox); | ||
|
|
||
| size_t currentCount = pokeBox->getInventory().size(); | ||
| cout << userId << "'s PokeBox contains " << currentCount << " Pokémon." << endl; | ||
|
|
||
| // Catch a few Pokémon. | ||
| // Initialize random number generators. | ||
| std::mt19937 gen{std::random_device{}()}; | ||
| std::uniform_int_distribution<size_t> addDist{1, 6}; | ||
| std::uniform_int_distribution<size_t> pokeDist{0, allPokemon.size() - 1}; | ||
| size_t addCount = addDist(gen); | ||
| cout << "Catching " << addCount << " Pokémon... " << endl; | ||
| vector<string> newPokemon; | ||
| newPokemon.reserve(addCount); | ||
| for (size_t i = 0; i < addCount; ++i) | ||
| { | ||
| newPokemon.emplace_back(allPokemon[pokeDist(gen)]); | ||
| } | ||
| pokeBox->caught(newPokemon); | ||
|
|
||
| // Display the contents of the PokeBox. | ||
| PokemonList inventory = pokeBox->getInventory(); | ||
| cout << userId << "'s PokeBox now holds " << inventory.size() << " Pokémon:" << endl; | ||
| for (const string& pokemon : inventory) | ||
| { | ||
| cout << "\t" << pokemon << endl; | ||
| } | ||
|
|
||
| if (inventory.size() > 10) | ||
| { | ||
| cout << "Oh no! All the Pokémon escaped!" << endl; | ||
| pokeBox->releaseAll(); | ||
| } | ||
|
|
||
| // Exiting, closing the connection, or calling `destroy(Async)` on the session terminates both PokeSession and the | ||
| // internal session state maintained by the Glacier2 router. | ||
| cout << "Destroying the session..." << endl; | ||
| pokeSession->destroy(); | ||
|
|
||
| // Verify the proxy no longer works. | ||
| try | ||
| { | ||
| pokeBox->getInventory(); | ||
| cout << "Error: the PokeBox proxy should not work without a session!" << endl; | ||
| } | ||
| catch (const Ice::ConnectionLostException&) | ||
| { | ||
| // We get a ConnectionLostException because the Glacier2 router aborts the request on the (new) connection | ||
| // without an associated session. | ||
| cout << "The PokeBox proxy is no longer valid, as expected." << endl; | ||
| } | ||
|
|
||
| // Create a new session. This allows us to reach the PokeBox object again. | ||
| cout << "Creating a new session..." << endl; | ||
| session = router->createSession(userId, "password"); | ||
|
|
||
| try | ||
| { | ||
| // The pokeBox proxy no longer works as it was created with the token of an old session. | ||
| pokeBox->getInventory(); | ||
| cout << "Error: the PokeBox proxy should not work with a new session!" << endl; | ||
| } | ||
| catch (const Ice::DispatchException& dispatchException) | ||
| { | ||
| if (dispatchException.replyStatus() == Ice::ReplyStatus::Unauthorized) | ||
| { | ||
| // See code in SharedPokeBox::getUserId. | ||
| cout << "The PokeBox proxy remains unusable, as expected." << endl; | ||
| } | ||
| else | ||
| { | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #include "DefaultPokeSession.h" | ||
|
|
||
| #include <iostream> | ||
|
|
||
| using namespace Server; | ||
| using namespace std; | ||
|
|
||
| DefaultPokeSession::DefaultPokeSession( | ||
| Ice::ObjectAdapterPtr adapter, | ||
| Glacier2::SessionControlPrx sessionControl, | ||
| IUserIdResolverPtr userIdResolver) | ||
| : _adapter(std::move(adapter)), | ||
| _sessionControl(std::move(sessionControl)), | ||
| _userIdResolver(std::move(userIdResolver)) | ||
| { | ||
| } | ||
|
|
||
| void | ||
| DefaultPokeSession::destroy(const Ice::Current& current) | ||
| { | ||
| std::cout << "Destroying session #" << current.id.name << std::endl; | ||
|
|
||
| // Remove the token from the user ID resolver, since the token is no longer valid. | ||
| _userIdResolver->removeToken(current.id.name); | ||
|
|
||
| // Remove this servant from the object adapter. A new call to this session object will fail with | ||
| // ObjectNotExistException. | ||
| _adapter->remove(current.id); | ||
|
|
||
| // Destroy the session in the Glacier2 router. | ||
| _sessionControl->destroy(); | ||
| } | ||
|
|
||
| optional<CatchThemAll::PokeBoxPrx> | ||
| DefaultPokeSession::getPokeBox(const Ice::Current& current) | ||
| { | ||
| // The session token is the name component of the session identity; we use it for the identity of the PokeBox | ||
| // object as well. | ||
| return _adapter->createProxy<CatchThemAll::PokeBoxPrx>(Ice::Identity{current.id.name, "PokeBox"}); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #ifndef DEFAULT_POKE_SESSION_H | ||
| #define DEFAULT_POKE_SESSION_H | ||
|
|
||
| #include "IUserIdResolver.h" | ||
| #include "PokeBox.h" | ||
|
|
||
| namespace Server | ||
| { | ||
| /// DefaultPokeSession is an Ice servant that implements Slice interface PokeSession. We create a | ||
| /// DefaultPokeSession for each PokeSession object. | ||
| class DefaultPokeSession : public CatchThemAll::PokeSession | ||
| { | ||
| public: | ||
| /// Constructs a DefaultPokeSession servant. | ||
| /// @param adapter The object adapter that hosts this servant and the PokeBox objects. | ||
| /// @param sessionControl The session control proxy. | ||
| /// @param userIdResolver The user ID resolver. | ||
| DefaultPokeSession( | ||
| Ice::ObjectAdapterPtr adapter, | ||
| Glacier2::SessionControlPrx sessionControl, | ||
| IUserIdResolverPtr userIdResolver); | ||
|
|
||
| void destroy(const Ice::Current& current) final; | ||
|
|
||
| // Implements the pure virtual function in the base class (CatchThemAll::PokeSession) generated by the Slice | ||
| // compiler. | ||
| std::optional<CatchThemAll::PokeBoxPrx> getPokeBox(const Ice::Current&) final; | ||
|
|
||
| private: | ||
| // The object adapter that hosts this servant and the PokeBox objects. | ||
| Ice::ObjectAdapterPtr _adapter; | ||
|
|
||
| // A proxy to the SessionControl object hosted by the Glacier2 router; this proxy allows us to control the | ||
| // Glacier2 session, in particular to destroy it. In this demo, that's the only per-session state maintained by | ||
| // DefaultPokeSession. | ||
| Glacier2::SessionControlPrx _sessionControl; | ||
|
|
||
| IUserIdResolverPtr _userIdResolver; | ||
| }; | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #ifndef IPOKE_STORE_H | ||
| #define IPOKE_STORE_H | ||
|
|
||
| #include "PokeBox.h" | ||
| #include <list> | ||
pepone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| namespace Server | ||
| { | ||
| /// Represents a Pokémon storage system. | ||
| class IPokeStore | ||
pepone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| public: | ||
| virtual ~IPokeStore() = default; | ||
|
|
||
| /// Saves the Pokémon collection for a specific user. | ||
| /// @param userId The user ID. | ||
| /// @param pokemon The Pokémon collection to save. | ||
| virtual void saveCollection(const std::string& userId, CatchThemAll::PokemonList pokemon) = 0; | ||
|
|
||
| /// Retrieves the Pokémon collection for a specific user. | ||
| /// @param userId The user ID. | ||
| /// @returns The saved Pokémon collection, or an empty list if no collection was saved for @p userId. | ||
| virtual CatchThemAll::PokemonList retrieveCollection(const std::string& userId) const = 0; | ||
| }; | ||
|
|
||
| using IPokeStorePtr = std::shared_ptr<IPokeStore>; | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #ifndef IUSER_ID_RESOLVER_H | ||
| #define IUSER_ID_RESOLVER_H | ||
|
|
||
| #include <memory> | ||
| #include <optional> | ||
| #include <string> | ||
|
|
||
| namespace Server | ||
| { | ||
| /// Resolves a user ID from a session token. | ||
| class IUserIdResolver | ||
pepone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| public: | ||
| /// Gets the user ID associated with the specified session token. | ||
| /// @param token The session token. | ||
| /// @returns The user ID associated with the specified session token, or null if not found. | ||
pepone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| virtual std::optional<std::string> getUserId(const std::string& token) = 0; | ||
|
|
||
| /// Removes the specified session token. | ||
| /// @param token The session token. | ||
| virtual void removeToken(const std::string& token) = 0; | ||
| }; | ||
|
|
||
| using IUserIdResolverPtr = std::shared_ptr<IUserIdResolver>; | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
|
|
||
| #include "InMemoryPokeStore.h" | ||
|
|
||
| using namespace Server; | ||
| using namespace std; | ||
|
|
||
| void | ||
| InMemoryPokeStore::saveCollection(const string& userId, CatchThemAll::PokemonList pokemon) | ||
| { | ||
| _store[userId] = std::move(pokemon); | ||
|
||
| } | ||
|
|
||
| CatchThemAll::PokemonList | ||
| InMemoryPokeStore::retrieveCollection(const string& userId) const | ||
| { | ||
| auto it = _store.find(userId); | ||
| return it == _store.end() ? CatchThemAll::PokemonList{} : it->second; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #include "IPokeStore.h" | ||
| #include <list> | ||
pepone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #include <map> | ||
| #include <string> | ||
|
|
||
| namespace Server | ||
| { | ||
| /// An in-memory implementation of IPokeStore. | ||
| /// @remark This mock implementation is not thread-safe. A real implementation should support concurrent calls. | ||
| class InMemoryPokeStore : public IPokeStore | ||
| { | ||
| public: | ||
| void saveCollection(const std::string& userId, CatchThemAll::PokemonList pokemon) final; | ||
| CatchThemAll::PokemonList retrieveCollection(const std::string& userId) const final; | ||
|
|
||
| private: | ||
| std::map<std::string, CatchThemAll::PokemonList> _store; | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.