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