Skip to content

Commit ff3f5d0

Browse files
New Ice/callback demo (C++) (#293)
1 parent 7692900 commit ff3f5d0

15 files changed

+263
-316
lines changed

cpp/Ice/callback/AlarmClock.ice

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
#pragma once
4+
5+
module EarlyRiser
6+
{
7+
/// Indicates the button pressed when the alarm rang.
8+
enum ButtonPressed { Snooze, Stop }
9+
10+
/// Represents a simple alarm clock. It's the "callback" in this demo, and it's implemented by the client.
11+
interface AlarmClock
12+
{
13+
/// Rings the alarm clock.
14+
/// @param message The message to display.
15+
/// @return The button pressed by the user.
16+
ButtonPressed ring(string message);
17+
}
18+
19+
/// Represents the wake up service provided by the server.
20+
interface WakeUpService
21+
{
22+
/// Schedules a call to {@link AlarmClock::ring} at the specified time.
23+
/// @param alarmClock A proxy to the alarm clock to ring.
24+
/// @param timeStamp The time to ring the alarm clock. It's encoded as the number of ticks (100 nanoseconds)
25+
/// since January 1, 0001 00:00:00 UTC in the Gregorian calendar.
26+
void wakeMeUp(AlarmClock* alarmClock, long timeStamp);
27+
}
28+
}

cpp/Ice/callback/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ project(callback CXX)
44

55
include(../../cmake/common.cmake)
66

7-
add_executable(client Client.cpp Callback.ice)
7+
add_executable(client Client.cpp MockAlarmClock.h MockAlarmClock.cpp AlarmClock.ice)
88
slice2cpp_generate(client)
99
target_link_libraries(client Ice::Ice)
1010

11-
add_executable(server Server.cpp CallbackSenderI.cpp CallbackSenderI.h Callback.ice)
11+
add_executable(server Server.cpp SimpleWakeUpService.h SimpleWakeUpService.cpp AlarmClock.ice)
1212
slice2cpp_generate(server)
1313
target_link_libraries(server Ice::Ice)

cpp/Ice/callback/Callback.ice

Lines changed: 0 additions & 17 deletions
This file was deleted.

cpp/Ice/callback/CallbackSenderI.cpp

Lines changed: 0 additions & 29 deletions
This file was deleted.

cpp/Ice/callback/CallbackSenderI.h

Lines changed: 0 additions & 15 deletions
This file was deleted.

cpp/Ice/callback/Client.cpp

Lines changed: 35 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,57 @@
11
// Copyright (c) ZeroC, Inc.
22

3-
#include "Callback.h"
4-
#include <Ice/Ice.h>
3+
#include "MockAlarmClock.h"
4+
55
#include <iostream>
66

7+
using namespace EarlyRiser;
78
using namespace std;
8-
using namespace Demo;
9-
10-
class CallbackReceiverI final : public CallbackReceiver
11-
{
12-
public:
13-
void callback(const Ice::Current&) final { cout << "received callback" << endl; }
14-
};
159

16-
int run(const shared_ptr<Ice::Communicator>&);
17-
18-
int
19-
main(int argc, char* argv[])
10+
namespace
2011
{
21-
int status = 0;
22-
23-
try
12+
// Converts a time point to a time stamp.
13+
int64_t toTimeStamp(const chrono::system_clock::time_point& timePoint)
2414
{
25-
//
26-
// CommunicatorHolder's ctor initializes an Ice communicator,
27-
// and its dtor destroys this communicator.
28-
//
29-
const Ice::CommunicatorHolder ich(argc, argv, "config.client");
15+
const int daysBeforeEpoch = 719162;
3016

31-
//
32-
// The communicator initialization removes all Ice-related arguments from argc/argv
33-
//
34-
if (argc > 1)
35-
{
36-
cerr << argv[0] << ": too many arguments" << endl;
37-
status = 1;
38-
}
39-
else
40-
{
41-
status = run(ich.communicator());
42-
}
43-
}
44-
catch (const std::exception& ex)
45-
{
46-
cerr << argv[0] << ": " << ex.what() << endl;
47-
status = 1;
48-
}
17+
int64_t timeStampMicro =
18+
chrono::duration_cast<chrono::microseconds>(timePoint.time_since_epoch() + daysBeforeEpoch * 24h).count();
4919

50-
return status;
20+
// The time stamp is in ticks, where each tick is 100 nanoseconds = 0.1 microsecond.
21+
return timeStampMicro * 10;
22+
}
5123
}
5224

53-
void menu();
54-
5525
int
56-
run(const shared_ptr<Ice::Communicator>& communicator)
26+
main(int argc, char* argv[])
5727
{
58-
auto sender = Ice::checkedCast<CallbackSenderPrx>(
59-
communicator->propertyToProxy("CallbackSender.Proxy")->ice_twoway()->ice_secure(false));
60-
if (!sender)
61-
{
62-
cerr << "invalid proxy" << endl;
63-
return 1;
64-
}
28+
// Create an Ice communicator to initialize the Ice runtime.
29+
const Ice::CommunicatorHolder communicatorHolder{argc, argv};
30+
const Ice::CommunicatorPtr& communicator = communicatorHolder.communicator();
31+
32+
// Create an object adapter that listens for incoming requests and dispatches them to servants.
33+
// Since we don't specify a port, the OS will choose an ephemeral port. This allows multiple client applications to
34+
// run concurrently on the same host.
35+
Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapterWithEndpoints("AlarmClockAdapter", "tcp");
6536

66-
auto adapter = communicator->createObjectAdapter("Callback.Client");
67-
adapter->add(make_shared<CallbackReceiverI>(), Ice::stringToIdentity("callbackReceiver"));
37+
// Register the MockAlarmClock servant with the adapter, and get an alarm clock proxy.
38+
auto mockAlarmClock = make_shared<Client::MockAlarmClock>();
39+
auto alarmClock = adapter->add<AlarmClockPrx>(mockAlarmClock, Ice::stringToIdentity("alarmClock"));
40+
41+
// Start dispatching requests.
6842
adapter->activate();
43+
cout << "Listening on ephemeral port..." << endl;
6944

70-
auto receiver =
71-
Ice::uncheckedCast<CallbackReceiverPrx>(adapter->createProxy(Ice::stringToIdentity("callbackReceiver")));
45+
// Create a proxy to the wake-up service.
46+
WakeUpServicePrx wakeUpService{communicator, "wakeUpService:tcp -h localhost -p 4061"};
7247

73-
menu();
48+
// Schedule a wake-up call in 5 seconds.
49+
wakeUpService->wakeMeUp(alarmClock, toTimeStamp(chrono::system_clock::now() + chrono::seconds{5}));
50+
cout << "Wake-up call scheduled, falling asleep..." << endl;
7451

75-
char c = 'x';
76-
do
77-
{
78-
try
79-
{
80-
cout << "==> ";
81-
cin >> c;
82-
if (c == 't')
83-
{
84-
sender->initiateCallback(receiver);
85-
}
86-
else if (c == 's')
87-
{
88-
sender->shutdown();
89-
}
90-
else if (c == 'x')
91-
{
92-
// Nothing to do
93-
}
94-
else if (c == '?')
95-
{
96-
menu();
97-
}
98-
else
99-
{
100-
cout << "unknown command `" << c << "'" << endl;
101-
menu();
102-
}
103-
}
104-
catch (const Ice::Exception& ex)
105-
{
106-
cerr << ex << endl;
107-
}
108-
} while (cin.good() && c != 'x');
52+
// Wait until the "stop" button is pressed on the mock alarm clock.
53+
mockAlarmClock->wait();
54+
cout << "Stop button pressed, exiting..." << endl;
10955

11056
return 0;
11157
}
112-
113-
void
114-
menu()
115-
{
116-
cout << "usage:\n"
117-
"t: send callback\n"
118-
"s: shutdown server\n"
119-
"x: exit\n"
120-
"?: help\n";
121-
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
#include "MockAlarmClock.h"
4+
5+
#include <iostream>
6+
7+
using namespace EarlyRiser;
8+
using namespace std;
9+
10+
ButtonPressed
11+
Client::MockAlarmClock::ring(string message, const Ice::Current&)
12+
{
13+
cout << "Dispatching ring request { message = '" << message << "' }" << endl;
14+
15+
if (_needMoreTime)
16+
{
17+
cout << "Returning " << ButtonPressed::Snooze << " to request more time." << endl;
18+
_needMoreTime = false; // we only snooze one time
19+
return ButtonPressed::Snooze;
20+
}
21+
else
22+
{
23+
_stopPromise.set_value();
24+
cout << "Returning " << ButtonPressed::Stop << " to stop the alarm." << endl;
25+
return ButtonPressed::Stop;
26+
}
27+
}
28+
29+
void
30+
Client::MockAlarmClock::wait()
31+
{
32+
_stopPromise.get_future().wait();
33+
}

cpp/Ice/callback/MockAlarmClock.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
#ifndef MOCK_ALARM_CLOCK_H
4+
#define MOCK_ALARM_CLOCK_H
5+
6+
#include "AlarmClock.h"
7+
8+
#include <future>
9+
10+
namespace Client
11+
{
12+
/// MockAlarmClock is an Ice servant that implements Slice interface AlarmClock.
13+
class MockAlarmClock : public EarlyRiser::AlarmClock
14+
{
15+
public:
16+
// Implements the pure virtual function in the base class (AlarmClock) generated by the Slice compiler.
17+
EarlyRiser::ButtonPressed ring(std::string message, const Ice::Current&) override;
18+
19+
/// Waits until ring returns ButtonPressed::Stop. Can only be called once.
20+
void wait();
21+
22+
private:
23+
bool _needMoreTime{true};
24+
std::promise<void> _stopPromise;
25+
};
26+
}
27+
28+
#endif

cpp/Ice/callback/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
# Callback
22

3-
A simple callback demo that illustrates how a client can pass a proxy
4-
to a server, invoke an operation in the server, and the [server call
5-
back][1] into an object provided by the client as part of that invocation.
3+
The Callback demo illustrates how to implement callbacks in a client application.
64

7-
To run the demo, first start the server:
5+
In this demo, the client hosts an alarm clock (an Ice object), and asks the server's wake up service to call this
6+
object at a later time. The server opens a TCP connection to the client when making this call.
7+
8+
```mermaid
9+
flowchart LR
10+
c[Client<br>hosts AlarmClock] --connection1--> s[Server:4061<br>hosts WakeUpService]
11+
s --connection2--> c
12+
```
813

914
To build the demo run:
1015

@@ -40,5 +45,3 @@ In a separate window, start the client:
4045
```shell
4146
build\Release\client
4247
```
43-
44-
[1]: https://doc.zeroc.com/ice/3.7/client-server-features/the-ice-threading-model/nested-invocations

0 commit comments

Comments
 (0)