-
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
80923dd
C++ Glacier2/session demo
pepone e6eccd8
Checkpoint
pepone c028810
Review fixes
pepone 90a191f
Review fixes
pepone e610d48
Review fixes
pepone bc4698e
Fix Windows build failures
pepone 197b213
Apply suggestion from @Copilot
pepone 7346cea
clang-tidy fixes
pepone c635f11
Review fixes
pepone ddc94b7
Review fixes
pepone 555457f
clang tidy fixes
pepone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Default cmake build directory | ||
| build |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| PokeBox.ice | ||
| PokeStore.h | ||
| Server.cpp | ||
| SessionManager.cpp | ||
| SessionManager.h | ||
| SharedPokeBox.cpp | ||
| SharedPokeBox.h | ||
| UserIdResolver.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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, | ||
| UserIdResolverPtr userIdResolver) | ||
| : _adapter(std::move(adapter)), | ||
| _sessionControl(std::move(sessionControl)), | ||
| _userIdResolver(std::move(userIdResolver)) | ||
| { | ||
| } | ||
|
|
||
| void | ||
| DefaultPokeSession::destroy(const Ice::Current& current) | ||
| { | ||
| cout << "Destroying session #" << current.id.name << 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"}); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 "PokeBox.h" | ||
| #include "UserIdResolver.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, | ||
| UserIdResolverPtr 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; | ||
|
|
||
| UserIdResolverPtr _userIdResolver; | ||
| }; | ||
| } | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
|
|
||
| #include "InMemoryPokeStore.h" | ||
|
|
||
| using namespace Server; | ||
| using namespace std; | ||
|
|
||
| void | ||
| InMemoryPokeStore::saveCollection(const string& userId, CatchThemAll::PokemonList pokemon) | ||
| { | ||
| if (pokemon.empty()) | ||
| { | ||
| _store.erase(userId); | ||
| } | ||
| else | ||
| { | ||
| _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; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #ifndef IN_MEMORY_POKE_STORE_H | ||
| #define IN_MEMORY_POKE_STORE_H | ||
|
|
||
| #include "PokeStore.h" | ||
| #include <map> | ||
| #include <string> | ||
|
|
||
| namespace Server | ||
| { | ||
| /// An in-memory implementation of PokeStore. | ||
| /// @remark This mock implementation is not thread-safe. A real implementation should support concurrent calls. | ||
| class InMemoryPokeStore : public PokeStore | ||
| { | ||
| public: | ||
| void saveCollection(const std::string& userId, CatchThemAll::PokemonList pokemon) final; | ||
| [[nodiscard]] CatchThemAll::PokemonList retrieveCollection(const std::string& userId) const final; | ||
|
|
||
| private: | ||
| std::map<std::string, CatchThemAll::PokemonList> _store; | ||
| }; | ||
| } | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| // Include the Glacier2/Session.ice file included in the Glacier2 NuGet package. | ||
| #include <Glacier2/Session.ice> | ||
|
|
||
| module CatchThemAll | ||
| { | ||
| /// Represents a list of Pokémon. | ||
| sequence<string> PokemonList; | ||
|
|
||
| /// Represents the Pokémon collected by a user. | ||
| interface PokeBox | ||
| { | ||
| /// Lists all the Pokémon in this box. | ||
| /// @return The list of Pokémon. | ||
| PokemonList getInventory(); | ||
|
|
||
| /// Adds one or more Pokémon to this box. | ||
| /// @param pokemon The Pokémon to add. | ||
| void caught(PokemonList pokemon); | ||
|
|
||
| /// Releases all the Pokémon. | ||
| void releaseAll(); | ||
| } | ||
|
|
||
| /// Represents a specialized session for our Pokémon application. | ||
| interface PokeSession : Glacier2::Session | ||
| { | ||
| /// Retrieves the PokeBox proxy associated with this session. | ||
| /// @return The PokeBox proxy. | ||
| PokeBox* getPokeBox(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // Copyright (c) ZeroC, Inc. | ||
|
|
||
| #ifndef POKE_STORE_H | ||
| #define POKE_STORE_H | ||
|
|
||
| #include "PokeBox.h" | ||
|
|
||
| namespace Server | ||
| { | ||
| /// Represents a Pokémon storage system. | ||
| class PokeStore | ||
| { | ||
| public: | ||
| virtual ~PokeStore() = 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. | ||
| [[nodiscard]] virtual CatchThemAll::PokemonList retrieveCollection(const std::string& userId) const = 0; | ||
| }; | ||
|
|
||
| using PokeStorePtr = std::shared_ptr<PokeStore>; | ||
| } | ||
|
|
||
| #endif | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.