Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion csharp/Glacier2/Session/Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
}

// Exiting, closing the connection, or calling destroyAsync on the session terminates both PokeSession and the
// internal session state maintained by the Glacier router.
// internal session state maintained by the Glacier2 router.
Console.WriteLine("Destroying the session...");
await pokeSession.destroyAsync();

Expand Down
2 changes: 1 addition & 1 deletion csharp/Glacier2/Session/Server/IUserIdResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Server;
internal interface IUserIdResolver
{
/// <summary>Gets the user ID associated with the specified session token.</summary>
/// <param name="token">The session token. </param>
/// <param name="token">The session token.</param>
/// <returns>The user ID associated with the specified session token, or null if not found.</returns>
string? GetUserId(string token);

Expand Down
2 changes: 1 addition & 1 deletion csharp/Glacier2/Session/slice/PokeBox.ice
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) ZeroC, Inc.

// Include the Glacier/Session.ice file included in the Glacier2 NuGet package.
// Include the Glacier2/Session.ice file included in the Glacier2 NuGet package.
#include <Glacier2/Session.ice>

module CatchThemAll
Expand Down
4 changes: 4 additions & 0 deletions java/Glacier2/session/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Gradle generated build files
bin
build
.gradle
60 changes: 60 additions & 0 deletions java/Glacier2/session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Glacier2 Session

This demo shows how to implement and configure a Session Manager with Glacier2. A Session Manager creates
application-level session objects that typically maintain some per-session state.

This demo also illustrates how to use a default servant that implements multiple objects (see the SharedPokeBox class).

In the demo, the client catches Pokémon and stores them in a PokeBox hosted by the server behind the Glacier2 router:

```mermaid
flowchart LR
c[Client] --tcp--> g[Glacier2 router:4063] --tcp--> s[Server:4061<br>hosts SessionManager, PokeSession, PokeBox]
```

In a typical Glacier2 deployment, the client can establish a connection to the Glacier2 router but cannot establish
a connection to the server, because the server is on a separate network.

## Ice prerequisites

- Install Glacier2. See [Ice service installation].

## Building the demo

The demo has two Gradle projects, **client** and **server**, both using the [application plugin].

To build the demo, run:

```shell
./gradlew build
```

## Running the demo

Start the Server program in its own terminal:

```shell
./gradlew :server:run --quiet
```

Next, start the Glacier2 router in its own terminal:

```shell
glacier2router --Ice.Config=config.glacier2
```

> [!TIP]
> You can also start the Glacier2 router before the server. The order does not matter.

Finally, in a separate terminal, run the client application several times:

```shell
./gradlew :client:run --quiet --args="ash"
./gradlew :client:run --quiet --args="ash"
./gradlew :client:run --quiet --args="ash"
```

If you don't specify a name, the client uses the current username.

[Application plugin]: https://docs.gradle.org/current/userguide/application_plugin.html
[Ice service installation]: https://github.com/zeroc-ice/ice/blob/main/NIGHTLY.md#ice-services
32 changes: 32 additions & 0 deletions java/Glacier2/session/client/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) ZeroC, Inc.

plugins {
// Apply the application plugin to tell gradle this is a runnable Java application.
id("application")

// Apply the Slice-tools plugin to enable Slice compilation.
id("com.zeroc.slice-tools") version "3.8.+"

// Pull in our local 'convention plugin' to enable linting.
id("zeroc-linting")
}

dependencies {
// Add Ice and Glacier2 as implementation dependencies.
implementation("com.zeroc:ice:3.8.+")
implementation("com.zeroc:glacier2:3.8.+")
}

sourceSets {
main {
// Add the PokeBox.ice file from the parent slice directory to the main source set.
slice {
srcDirs("../slice")
}
}
}

application {
// Specify the main entry point for the application.
mainClass.set("com.example.glacier2.session.client.Client")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) ZeroC, Inc.

package com.example.glacier2.session.client;

import com.example.catchthemall.PokeBoxPrx;
import com.example.catchthemall.PokeSessionPrx;

import com.zeroc.Glacier2.CannotCreateSessionException;
import com.zeroc.Glacier2.PermissionDeniedException;
import com.zeroc.Glacier2.RouterPrx;
import com.zeroc.Glacier2.SessionPrx;
import com.zeroc.Ice.Communicator;
import com.zeroc.Ice.ConnectionLostException;
import com.zeroc.Ice.DispatchException;
import com.zeroc.Ice.Util;

import java.util.List;

class Client {
/** All the Pokémon we know about. */
static final String[] ALL_POKEMON = {
"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"
};

public static void main(String[] args) {

// Retrieve the user ID for this run.
final String userId = args.length > 0 ? args[0] : System.getProperty("user.name");

// Create an Ice communicator. We'll use this communicator to create proxies and manage outgoing connections.
try (Communicator communicator = Util.initialize(args)) {

// 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.
RouterPrx router = RouterPrx.createProxy(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.
SessionPrx session;
try {
session = router.createSession(userId, "password");
} catch (PermissionDeniedException | CannotCreateSessionException e) {
System.out.println("Could not create session: " + e.getMessage());
return;
}

// We configured a SessionManager on the Glacier2 router, so 'session' is a non-null 'PokeSession'.
PokeSessionPrx pokeSession = PokeSessionPrx.uncheckedCast(session);
assert pokeSession != null;

// Retrieve the PokeBox proxy from the session.
PokeBoxPrx pokeBox = pokeSession.getPokeBox();
assert pokeBox != null;

int currentCount = pokeBox.getInventory().size();
System.out.println(userId + "'s PokeBox contains " + currentCount + " Pokemon.");

// Catch a few Pokémon.
var randomSource = new java.util.Random();
int addCount = randomSource.nextInt(6) + 1;
System.out.println("Catching " + addCount + " Pokemon... ");
List<String> newPokemon = randomSource.ints(addCount, 0, ALL_POKEMON.length)
.mapToObj(i -> ALL_POKEMON[i])
.toList();
pokeBox.caught(newPokemon);

// Display the contents of the PokeBox.
List<String> inventory = pokeBox.getInventory();
System.out.println(userId + "'s PokeBox now holds " + inventory.size() + " Pokemon:");
for (String pokemon : inventory) {
System.out.println("\t" + pokemon);
}

if (inventory.size() > 10) {
System.out.println("Oh no! All the Pokemon escaped!");
pokeBox.releaseAll();
}

// Exiting, closing the connection, or calling 'destroy' on the session terminates both
// PokeSession and the internal session state maintained by the Glacier2 router.
System.out.println("Destroying the session...");
pokeSession.destroy();

// Verify the proxy no longer works.
try {
pokeBox.getInventory();
System.out.println("Error: the PokeBox proxy should not work without a session!");
} catch (ConnectionLostException expected) {
// We get a ConnectionLostException because the Glacier2 router aborts the request on the
// (new) connection without an associated session.
System.out.println("The PokeBox proxy is no longer valid, as expected.");
}

// Create a new session. This allows us to reach the PokeBox object again.
System.out.println("Creating a new session...");
try {
session = router.createSession(userId, "password");
} catch (PermissionDeniedException | CannotCreateSessionException e) {
System.out.println("Could not create new session: " + e.getMessage());
return;
}

try {
// The pokeBox proxy no longer works as it was created with the token of an old session.
pokeBox.getInventory();
System.out.println("Error: the PokeBox proxy should not work with a new session!");
} catch (DispatchException ex) {
if (ex.replyStatus != com.zeroc.Ice.ReplyStatus.Unauthorized.value()) {
throw ex;
}

// See code in 'SharedPokeBox.getUserId'.
System.out.println("The PokeBox proxy remains unusable, as expected.");
}
}
}
}
18 changes: 18 additions & 0 deletions java/Glacier2/session/config.glacier2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Config file for glacier2router

# The client-visible endpoint of the Glacier2 router.
Glacier2.Client.Endpoints=tcp -p 4063

# Configure the Server endpoints to get SessionControl proxies.
Glacier2.Server.Endpoints=tcp

# This Glacier router accepts any username/password combination.
Glacier2.PermissionsVerifier=Glacier2/NullPermissionsVerifier

# Configure the SessionManager proxy.
Glacier2.SessionManager=SessionManager:tcp -h localhost -p 4061

# Turn on tracing.
Glacier2.Client.Trace.Request=1
Glacier2.Trace.Session=1
Glacier2.Trace.RoutingTable=1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading