xref: /aosp_15_r20/external/flatbuffers/grpc/tests/JavaGrpcTest.java (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Worker /*
2*890232f2SAndroid Build Coastguard Worker  * Copyright 2014 Google Inc. All rights reserved.
3*890232f2SAndroid Build Coastguard Worker  *
4*890232f2SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*890232f2SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*890232f2SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*890232f2SAndroid Build Coastguard Worker  *
8*890232f2SAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*890232f2SAndroid Build Coastguard Worker  *
10*890232f2SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*890232f2SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*890232f2SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*890232f2SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*890232f2SAndroid Build Coastguard Worker  * limitations under the License.
15*890232f2SAndroid Build Coastguard Worker  */
16*890232f2SAndroid Build Coastguard Worker 
17*890232f2SAndroid Build Coastguard Worker import MyGame.Example.Monster;
18*890232f2SAndroid Build Coastguard Worker import MyGame.Example.MonsterStorageGrpc;
19*890232f2SAndroid Build Coastguard Worker import MyGame.Example.Stat;
20*890232f2SAndroid Build Coastguard Worker import com.google.flatbuffers.FlatBufferBuilder;
21*890232f2SAndroid Build Coastguard Worker import io.grpc.ManagedChannel;
22*890232f2SAndroid Build Coastguard Worker import io.grpc.ManagedChannelBuilder;
23*890232f2SAndroid Build Coastguard Worker import io.grpc.Server;
24*890232f2SAndroid Build Coastguard Worker import io.grpc.ServerBuilder;
25*890232f2SAndroid Build Coastguard Worker import io.grpc.stub.StreamObserver;
26*890232f2SAndroid Build Coastguard Worker import org.junit.Assert;
27*890232f2SAndroid Build Coastguard Worker 
28*890232f2SAndroid Build Coastguard Worker import java.io.IOException;
29*890232f2SAndroid Build Coastguard Worker import java.lang.InterruptedException;
30*890232f2SAndroid Build Coastguard Worker import java.nio.ByteBuffer;
31*890232f2SAndroid Build Coastguard Worker import java.util.Iterator;
32*890232f2SAndroid Build Coastguard Worker import java.util.concurrent.TimeUnit;
33*890232f2SAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicReference;
34*890232f2SAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicInteger;
35*890232f2SAndroid Build Coastguard Worker import java.util.concurrent.CountDownLatch;
36*890232f2SAndroid Build Coastguard Worker 
37*890232f2SAndroid Build Coastguard Worker 
38*890232f2SAndroid Build Coastguard Worker /**
39*890232f2SAndroid Build Coastguard Worker  * Demonstrates basic client-server interaction using grpc-java over netty.
40*890232f2SAndroid Build Coastguard Worker  */
41*890232f2SAndroid Build Coastguard Worker public class JavaGrpcTest {
42*890232f2SAndroid Build Coastguard Worker     static final String BIG_MONSTER_NAME = "Cyberdemon";
43*890232f2SAndroid Build Coastguard Worker     static final short nestedMonsterHp = 600;
44*890232f2SAndroid Build Coastguard Worker     static final short nestedMonsterMana = 1024;
45*890232f2SAndroid Build Coastguard Worker     static final int numStreamedMsgs = 10;
46*890232f2SAndroid Build Coastguard Worker     static final int timeoutMs = 3000;
47*890232f2SAndroid Build Coastguard Worker     static Server server;
48*890232f2SAndroid Build Coastguard Worker     static ManagedChannel channel;
49*890232f2SAndroid Build Coastguard Worker     static MonsterStorageGrpc.MonsterStorageBlockingStub blockingStub;
50*890232f2SAndroid Build Coastguard Worker     static MonsterStorageGrpc.MonsterStorageStub asyncStub;
51*890232f2SAndroid Build Coastguard Worker 
52*890232f2SAndroid Build Coastguard Worker     static class MyService extends MonsterStorageGrpc.MonsterStorageImplBase {
53*890232f2SAndroid Build Coastguard Worker         @Override
store(Monster request, io.grpc.stub.StreamObserver<Stat> responseObserver)54*890232f2SAndroid Build Coastguard Worker         public void store(Monster request, io.grpc.stub.StreamObserver<Stat> responseObserver) {
55*890232f2SAndroid Build Coastguard Worker             Assert.assertEquals(request.name(), BIG_MONSTER_NAME);
56*890232f2SAndroid Build Coastguard Worker             Assert.assertEquals(request.hp(), nestedMonsterHp);
57*890232f2SAndroid Build Coastguard Worker             Assert.assertEquals(request.mana(), nestedMonsterMana);
58*890232f2SAndroid Build Coastguard Worker             System.out.println("Received store request from " + request.name());
59*890232f2SAndroid Build Coastguard Worker             // Create a response from the incoming request name.
60*890232f2SAndroid Build Coastguard Worker             Stat stat = GameFactory.createStat("Hello " + request.name(), 100, 10);
61*890232f2SAndroid Build Coastguard Worker             responseObserver.onNext(stat);
62*890232f2SAndroid Build Coastguard Worker             responseObserver.onCompleted();
63*890232f2SAndroid Build Coastguard Worker         }
64*890232f2SAndroid Build Coastguard Worker 
65*890232f2SAndroid Build Coastguard Worker         @Override
retrieve(Stat request, io.grpc.stub.StreamObserver<Monster> responseObserver)66*890232f2SAndroid Build Coastguard Worker         public void retrieve(Stat request, io.grpc.stub.StreamObserver<Monster> responseObserver) {
67*890232f2SAndroid Build Coastguard Worker             // Create 10 monsters for streaming response.
68*890232f2SAndroid Build Coastguard Worker             for (int i=0; i<numStreamedMsgs; i++) {
69*890232f2SAndroid Build Coastguard Worker                 Monster monster = GameFactory.createMonsterFromStat(request, i);
70*890232f2SAndroid Build Coastguard Worker                 responseObserver.onNext(monster);
71*890232f2SAndroid Build Coastguard Worker             }
72*890232f2SAndroid Build Coastguard Worker             responseObserver.onCompleted();
73*890232f2SAndroid Build Coastguard Worker         }
74*890232f2SAndroid Build Coastguard Worker 
75*890232f2SAndroid Build Coastguard Worker         @Override
getMaxHitPoint(final StreamObserver<Stat> responseObserver)76*890232f2SAndroid Build Coastguard Worker         public StreamObserver<Monster> getMaxHitPoint(final StreamObserver<Stat> responseObserver) {
77*890232f2SAndroid Build Coastguard Worker           return computeMinMax(responseObserver, false);
78*890232f2SAndroid Build Coastguard Worker         }
79*890232f2SAndroid Build Coastguard Worker 
80*890232f2SAndroid Build Coastguard Worker         @Override
getMinMaxHitPoints(final StreamObserver<Stat> responseObserver)81*890232f2SAndroid Build Coastguard Worker         public StreamObserver<Monster> getMinMaxHitPoints(final StreamObserver<Stat> responseObserver) {
82*890232f2SAndroid Build Coastguard Worker           return computeMinMax(responseObserver, true);
83*890232f2SAndroid Build Coastguard Worker         }
84*890232f2SAndroid Build Coastguard Worker 
computeMinMax(final StreamObserver<Stat> responseObserver, final boolean includeMin)85*890232f2SAndroid Build Coastguard Worker         private StreamObserver<Monster> computeMinMax(final StreamObserver<Stat> responseObserver, final boolean includeMin) {
86*890232f2SAndroid Build Coastguard Worker           final AtomicInteger maxHp = new AtomicInteger(Integer.MIN_VALUE);
87*890232f2SAndroid Build Coastguard Worker           final AtomicReference<String> maxHpMonsterName = new AtomicReference<String>();
88*890232f2SAndroid Build Coastguard Worker           final AtomicInteger maxHpCount = new AtomicInteger();
89*890232f2SAndroid Build Coastguard Worker 
90*890232f2SAndroid Build Coastguard Worker           final AtomicInteger minHp = new AtomicInteger(Integer.MAX_VALUE);
91*890232f2SAndroid Build Coastguard Worker           final AtomicReference<String> minHpMonsterName = new AtomicReference<String>();
92*890232f2SAndroid Build Coastguard Worker           final AtomicInteger minHpCount = new AtomicInteger();
93*890232f2SAndroid Build Coastguard Worker 
94*890232f2SAndroid Build Coastguard Worker           return new StreamObserver<Monster>() {
95*890232f2SAndroid Build Coastguard Worker             public void onNext(Monster monster) {
96*890232f2SAndroid Build Coastguard Worker               if (monster.hp() > maxHp.get()) {
97*890232f2SAndroid Build Coastguard Worker                 // Found a monster of higher hit points.
98*890232f2SAndroid Build Coastguard Worker                 maxHp.set(monster.hp());
99*890232f2SAndroid Build Coastguard Worker                 maxHpMonsterName.set(monster.name());
100*890232f2SAndroid Build Coastguard Worker                 maxHpCount.set(1);
101*890232f2SAndroid Build Coastguard Worker               }
102*890232f2SAndroid Build Coastguard Worker               else if (monster.hp() == maxHp.get()) {
103*890232f2SAndroid Build Coastguard Worker                 // Count how many times we saw a monster of current max hit points.
104*890232f2SAndroid Build Coastguard Worker                 maxHpCount.getAndIncrement();
105*890232f2SAndroid Build Coastguard Worker               }
106*890232f2SAndroid Build Coastguard Worker 
107*890232f2SAndroid Build Coastguard Worker               if (monster.hp() < minHp.get()) {
108*890232f2SAndroid Build Coastguard Worker                 // Found a monster of a lower hit points.
109*890232f2SAndroid Build Coastguard Worker                 minHp.set(monster.hp());
110*890232f2SAndroid Build Coastguard Worker                 minHpMonsterName.set(monster.name());
111*890232f2SAndroid Build Coastguard Worker                 minHpCount.set(1);
112*890232f2SAndroid Build Coastguard Worker               }
113*890232f2SAndroid Build Coastguard Worker               else if (monster.hp() == minHp.get()) {
114*890232f2SAndroid Build Coastguard Worker                 // Count how many times we saw a monster of current min hit points.
115*890232f2SAndroid Build Coastguard Worker                 minHpCount.getAndIncrement();
116*890232f2SAndroid Build Coastguard Worker               }
117*890232f2SAndroid Build Coastguard Worker             }
118*890232f2SAndroid Build Coastguard Worker             public void onCompleted() {
119*890232f2SAndroid Build Coastguard Worker               Stat maxHpStat = GameFactory.createStat(maxHpMonsterName.get(), maxHp.get(), maxHpCount.get());
120*890232f2SAndroid Build Coastguard Worker               // Send max hit points first.
121*890232f2SAndroid Build Coastguard Worker               responseObserver.onNext(maxHpStat);
122*890232f2SAndroid Build Coastguard Worker               if (includeMin) {
123*890232f2SAndroid Build Coastguard Worker                 // Send min hit points.
124*890232f2SAndroid Build Coastguard Worker                 Stat minHpStat = GameFactory.createStat(minHpMonsterName.get(), minHp.get(), minHpCount.get());
125*890232f2SAndroid Build Coastguard Worker                 responseObserver.onNext(minHpStat);
126*890232f2SAndroid Build Coastguard Worker               }
127*890232f2SAndroid Build Coastguard Worker               responseObserver.onCompleted();
128*890232f2SAndroid Build Coastguard Worker             }
129*890232f2SAndroid Build Coastguard Worker             public void onError(Throwable t) {
130*890232f2SAndroid Build Coastguard Worker               // Not expected
131*890232f2SAndroid Build Coastguard Worker               Assert.fail();
132*890232f2SAndroid Build Coastguard Worker             };
133*890232f2SAndroid Build Coastguard Worker           };
134*890232f2SAndroid Build Coastguard Worker         }
135*890232f2SAndroid Build Coastguard Worker     }
136*890232f2SAndroid Build Coastguard Worker 
137*890232f2SAndroid Build Coastguard Worker     @org.junit.BeforeClass
138*890232f2SAndroid Build Coastguard Worker     public static void startServer() throws IOException {
139*890232f2SAndroid Build Coastguard Worker         server = ServerBuilder.forPort(0).addService(new MyService()).build().start();
140*890232f2SAndroid Build Coastguard Worker         int port = server.getPort();
141*890232f2SAndroid Build Coastguard Worker         channel = ManagedChannelBuilder.forAddress("localhost", port)
142*890232f2SAndroid Build Coastguard Worker                 // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
143*890232f2SAndroid Build Coastguard Worker                 // needing certificates.
144*890232f2SAndroid Build Coastguard Worker                 .usePlaintext()
145*890232f2SAndroid Build Coastguard Worker                 .directExecutor()
146*890232f2SAndroid Build Coastguard Worker                 .build();
147*890232f2SAndroid Build Coastguard Worker         blockingStub = MonsterStorageGrpc.newBlockingStub(channel);
148*890232f2SAndroid Build Coastguard Worker         asyncStub = MonsterStorageGrpc.newStub(channel);
149*890232f2SAndroid Build Coastguard Worker     }
150*890232f2SAndroid Build Coastguard Worker 
151*890232f2SAndroid Build Coastguard Worker     @org.junit.Test
152*890232f2SAndroid Build Coastguard Worker     public void testUnary() throws IOException {
153*890232f2SAndroid Build Coastguard Worker         Monster monsterRequest = GameFactory.createMonster(BIG_MONSTER_NAME, nestedMonsterHp, nestedMonsterMana);
154*890232f2SAndroid Build Coastguard Worker         Stat stat = blockingStub.store(monsterRequest);
155*890232f2SAndroid Build Coastguard Worker         Assert.assertEquals(stat.id(), "Hello " + BIG_MONSTER_NAME);
156*890232f2SAndroid Build Coastguard Worker         System.out.println("Received stat response from service: " + stat.id());
157*890232f2SAndroid Build Coastguard Worker     }
158*890232f2SAndroid Build Coastguard Worker 
159*890232f2SAndroid Build Coastguard Worker     @org.junit.Test
160*890232f2SAndroid Build Coastguard Worker     public void testServerStreaming() throws IOException {
161*890232f2SAndroid Build Coastguard Worker         Monster monsterRequest = GameFactory.createMonster(BIG_MONSTER_NAME, nestedMonsterHp, nestedMonsterMana);
162*890232f2SAndroid Build Coastguard Worker         Stat stat = blockingStub.store(monsterRequest);
163*890232f2SAndroid Build Coastguard Worker         Iterator<Monster> iterator = blockingStub.retrieve(stat);
164*890232f2SAndroid Build Coastguard Worker         int counter = 0;
165*890232f2SAndroid Build Coastguard Worker         while(iterator.hasNext()) {
166*890232f2SAndroid Build Coastguard Worker             Monster m = iterator.next();
167*890232f2SAndroid Build Coastguard Worker             System.out.println("Received monster " + m.name());
168*890232f2SAndroid Build Coastguard Worker             counter ++;
169*890232f2SAndroid Build Coastguard Worker         }
170*890232f2SAndroid Build Coastguard Worker         Assert.assertEquals(counter, numStreamedMsgs);
171*890232f2SAndroid Build Coastguard Worker         System.out.println("FlatBuffers GRPC client/server test: completed successfully");
172*890232f2SAndroid Build Coastguard Worker     }
173*890232f2SAndroid Build Coastguard Worker 
174*890232f2SAndroid Build Coastguard Worker     @org.junit.Test
175*890232f2SAndroid Build Coastguard Worker     public void testClientStreaming() throws IOException, InterruptedException {
176*890232f2SAndroid Build Coastguard Worker       final AtomicReference<Stat> maxHitStat = new AtomicReference<Stat>();
177*890232f2SAndroid Build Coastguard Worker       final CountDownLatch streamAlive = new CountDownLatch(1);
178*890232f2SAndroid Build Coastguard Worker 
179*890232f2SAndroid Build Coastguard Worker       StreamObserver<Stat> statObserver = new StreamObserver<Stat>() {
180*890232f2SAndroid Build Coastguard Worker         public void onCompleted() {
181*890232f2SAndroid Build Coastguard Worker           streamAlive.countDown();
182*890232f2SAndroid Build Coastguard Worker         }
183*890232f2SAndroid Build Coastguard Worker         public void onError(Throwable ex) { }
184*890232f2SAndroid Build Coastguard Worker         public void onNext(Stat stat) {
185*890232f2SAndroid Build Coastguard Worker           maxHitStat.set(stat);
186*890232f2SAndroid Build Coastguard Worker         }
187*890232f2SAndroid Build Coastguard Worker       };
188*890232f2SAndroid Build Coastguard Worker       StreamObserver<Monster> monsterStream = asyncStub.getMaxHitPoint(statObserver);
189*890232f2SAndroid Build Coastguard Worker       short count = 10;
190*890232f2SAndroid Build Coastguard Worker       for (short i = 0;i < count; ++i) {
191*890232f2SAndroid Build Coastguard Worker         Monster monster = GameFactory.createMonster(BIG_MONSTER_NAME + i, (short) (nestedMonsterHp * i), nestedMonsterMana);
192*890232f2SAndroid Build Coastguard Worker         monsterStream.onNext(monster);
193*890232f2SAndroid Build Coastguard Worker       }
194*890232f2SAndroid Build Coastguard Worker       monsterStream.onCompleted();
195*890232f2SAndroid Build Coastguard Worker       // Wait a little bit for the server to send the stats of the monster with the max hit-points.
196*890232f2SAndroid Build Coastguard Worker       streamAlive.await(timeoutMs, TimeUnit.MILLISECONDS);
197*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().id(), BIG_MONSTER_NAME + (count - 1));
198*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().val(), nestedMonsterHp * (count - 1));
199*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().count(), 1);
200*890232f2SAndroid Build Coastguard Worker     }
201*890232f2SAndroid Build Coastguard Worker 
202*890232f2SAndroid Build Coastguard Worker     @org.junit.Test
203*890232f2SAndroid Build Coastguard Worker     public void testBiDiStreaming() throws IOException, InterruptedException {
204*890232f2SAndroid Build Coastguard Worker       final AtomicReference<Stat> maxHitStat = new AtomicReference<Stat>();
205*890232f2SAndroid Build Coastguard Worker       final AtomicReference<Stat> minHitStat = new AtomicReference<Stat>();
206*890232f2SAndroid Build Coastguard Worker       final CountDownLatch streamAlive = new CountDownLatch(1);
207*890232f2SAndroid Build Coastguard Worker 
208*890232f2SAndroid Build Coastguard Worker       StreamObserver<Stat> statObserver = new StreamObserver<Stat>() {
209*890232f2SAndroid Build Coastguard Worker         public void onCompleted() {
210*890232f2SAndroid Build Coastguard Worker           streamAlive.countDown();
211*890232f2SAndroid Build Coastguard Worker         }
212*890232f2SAndroid Build Coastguard Worker         public void onError(Throwable ex) { }
213*890232f2SAndroid Build Coastguard Worker         public void onNext(Stat stat) {
214*890232f2SAndroid Build Coastguard Worker           // We expect the server to send the max stat first and then the min stat.
215*890232f2SAndroid Build Coastguard Worker           if (maxHitStat.get() == null) {
216*890232f2SAndroid Build Coastguard Worker             maxHitStat.set(stat);
217*890232f2SAndroid Build Coastguard Worker           }
218*890232f2SAndroid Build Coastguard Worker           else {
219*890232f2SAndroid Build Coastguard Worker             minHitStat.set(stat);
220*890232f2SAndroid Build Coastguard Worker           }
221*890232f2SAndroid Build Coastguard Worker         }
222*890232f2SAndroid Build Coastguard Worker       };
223*890232f2SAndroid Build Coastguard Worker       StreamObserver<Monster> monsterStream = asyncStub.getMinMaxHitPoints(statObserver);
224*890232f2SAndroid Build Coastguard Worker       short count = 10;
225*890232f2SAndroid Build Coastguard Worker       for (short i = 0;i < count; ++i) {
226*890232f2SAndroid Build Coastguard Worker         Monster monster = GameFactory.createMonster(BIG_MONSTER_NAME + i, (short) (nestedMonsterHp * i), nestedMonsterMana);
227*890232f2SAndroid Build Coastguard Worker         monsterStream.onNext(monster);
228*890232f2SAndroid Build Coastguard Worker       }
229*890232f2SAndroid Build Coastguard Worker       monsterStream.onCompleted();
230*890232f2SAndroid Build Coastguard Worker 
231*890232f2SAndroid Build Coastguard Worker       // Wait a little bit for the server to send the stats of the monster with the max and min hit-points.
232*890232f2SAndroid Build Coastguard Worker       streamAlive.await(timeoutMs, TimeUnit.MILLISECONDS);
233*890232f2SAndroid Build Coastguard Worker 
234*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().id(), BIG_MONSTER_NAME + (count - 1));
235*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().val(), nestedMonsterHp * (count - 1));
236*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(maxHitStat.get().count(), 1);
237*890232f2SAndroid Build Coastguard Worker 
238*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(minHitStat.get().id(), BIG_MONSTER_NAME + 0);
239*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(minHitStat.get().val(), nestedMonsterHp * 0);
240*890232f2SAndroid Build Coastguard Worker       Assert.assertEquals(minHitStat.get().count(), 1);
241*890232f2SAndroid Build Coastguard Worker     }
242*890232f2SAndroid Build Coastguard Worker }
243