Java tutorial
/* * Copyright (C) 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gapid.server; import static java.util.logging.Level.FINE; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.gapid.models.Strings; import com.google.gapid.proto.service.Service; import com.google.gapid.proto.service.Service.FollowRequest; import com.google.gapid.proto.service.Service.GetAvailableStringTablesRequest; import com.google.gapid.proto.service.Service.GetDevicesForReplayRequest; import com.google.gapid.proto.service.Service.GetDevicesRequest; import com.google.gapid.proto.service.Service.GetFramebufferAttachmentRequest; import com.google.gapid.proto.service.Service.GetRequest; import com.google.gapid.proto.service.Service.GetSchemaRequest; import com.google.gapid.proto.service.Service.GetServerInfoRequest; import com.google.gapid.proto.service.Service.GetStringTableRequest; import com.google.gapid.proto.service.Service.ImportCaptureRequest; import com.google.gapid.proto.service.Service.LoadCaptureRequest; import com.google.gapid.proto.service.Service.ServerInfo; import com.google.gapid.proto.service.Service.SetRequest; import com.google.gapid.proto.service.Service.Value; import com.google.gapid.proto.service.gfxapi.GfxAPI; import com.google.gapid.proto.service.path.Path; import com.google.gapid.proto.stringtable.Stringtable; import com.google.gapid.rpclib.binary.BinaryObject; import com.google.gapid.rpclib.binary.Decoder; import com.google.gapid.rpclib.binary.Encoder; import com.google.gapid.rpclib.rpccore.RpcException; import com.google.gapid.rpclib.schema.Message; import com.google.gapid.util.Paths; import com.google.protobuf.ByteString; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; import java.util.logging.Logger; /** * Client interface to the RPC server. */ public class Client { private static final Logger LOG = Logger.getLogger(Client.class.getName()); static { com.google.gapid.rpclib.any.Factory.register(); com.google.gapid.rpclib.schema.Factory.register(); com.google.gapid.service.atom.Factory.register(); com.google.gapid.service.memory.Factory.register(); com.google.gapid.service.snippets.Factory.register(); } private final GapidClient client; public Client(GapidClient client) { this.client = client; } public ListenableFuture<ServerInfo> getSeverInfo() { LOG.log(FINE, "RPC->getServerInfo()"); return Futures.transformAsync(client.getServerInfo(GetServerInfoRequest.newBuilder().build()), in -> Futures.immediateFuture(throwIfError(in.getInfo(), in.getError()))); } public ListenableFuture<Value> get(Path.Any path) { LOG.log(FINE, "RPC->get({0})", path); return Futures.transformAsync(client.get(GetRequest.newBuilder().setPath(path).build()), in -> Futures.immediateFuture(throwIfError(in.getValue(), in.getError()))); } public ListenableFuture<Path.Any> set(Path.Any path, Service.Value value) { LOG.log(FINE, "RPC->set({0}, {1})", new Object[] { path, value }); return Futures.transformAsync(client.set(SetRequest.newBuilder().setPath(path).setValue(value).build()), in -> Futures.immediateFuture(throwIfError(in.getPath(), in.getError()))); } public ListenableFuture<Path.Any> follow(Path.Any path) { LOG.log(FINE, "RPC->follow({0})", path); return Futures.transformAsync(client.follow(FollowRequest.newBuilder().setPath(path).setPath(path).build()), in -> Futures.immediateFuture(throwIfError(in.getPath(), in.getError()))); } public ListenableFuture<Message> getSchema() { LOG.log(FINE, "RPC->getSchema()"); return Futures.transformAsync(client.getSchema(GetSchemaRequest.newBuilder().build()), in -> Futures.immediateFuture(decode(throwIfError(in.getObject(), in.getError())))); } public ListenableFuture<List<Stringtable.Info>> getAvailableStringTables() { LOG.log(FINE, "RPC->getAvailableStringTables()"); return Futures.transformAsync( client.getAvailableStringTables(GetAvailableStringTablesRequest.newBuilder().build()), in -> Futures.immediateFuture(throwIfError(in.getTables(), in.getError()).getListList())); } public ListenableFuture<Stringtable.StringTable> getStringTable(Stringtable.Info info) { LOG.log(FINE, "RPC->getStringTable({0})", info); return Futures.transformAsync( client.getStringTable(GetStringTableRequest.newBuilder().setTable(info).build()), in -> Futures.immediateFuture(throwIfError(in.getTable(), in.getError()))); } public ListenableFuture<Path.Capture> importCapture(byte[] data) { LOG.log(FINE, "RPC->importCapture(<{0} bytes>)", data.length); return Futures.transformAsync( client.importCapture(ImportCaptureRequest.newBuilder().setData(ByteString.copyFrom(data)).build()), in -> Futures.immediateFuture(throwIfError(in.getCapture(), in.getError()))); } public ListenableFuture<Path.Capture> loadCapture(String path) { LOG.log(FINE, "RPC->loadCapture({0})", path); return Futures.transformAsync(client.loadCapture(LoadCaptureRequest.newBuilder().setPath(path).build()), in -> Futures.immediateFuture(throwIfError(in.getCapture(), in.getError()))); } public ListenableFuture<List<Path.Device>> getDevices() { LOG.log(FINE, "RPC->getDevices()"); return Futures.transformAsync(client.getDevices(GetDevicesRequest.newBuilder().build()), in -> Futures.immediateFuture(throwIfError(in.getDevices(), in.getError()).getListList())); } public ListenableFuture<List<Path.Device>> getDevicesForReplay(Path.Capture capture) { LOG.log(FINE, "RPC->getDevicesForReplay({0})", capture); return Futures.transformAsync( client.getDevicesForReplay(GetDevicesForReplayRequest.newBuilder().setCapture(capture).build()), in -> Futures.immediateFuture(throwIfError(in.getDevices(), in.getError()).getListList())); } public ListenableFuture<Path.ImageInfo> getFramebufferAttachment(Path.Device device, Path.Command after, GfxAPI.FramebufferAttachment attachment, Service.RenderSettings settings) { LOG.log(FINE, "RPC->getFramebufferAttachment({0}, {1}, {2}, {3})", new Object[] { device, after, attachment, settings }); return Futures.transformAsync( client.getFramebufferAttachment(GetFramebufferAttachmentRequest.newBuilder().setDevice(device) .setAfter(after).setAttachment(attachment).setSettings(settings).build()), in -> Futures.immediateFuture(throwIfError(in.getImage(), in.getError()))); } @SuppressWarnings("unchecked") public static <V> V decode(Service.Object object) throws IOException { Decoder d = new Decoder(new ByteArrayInputStream(object.getData().toByteArray())); return (V) d.object(); } public static Service.Object encode(BinaryObject object) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Encoder e = new Encoder(baos); e.object(object); return Service.Object.newBuilder().setData(ByteString.copyFrom(baos.toByteArray())).build(); } private static <V> V throwIfError(V value, Service.Error err) throws RpcException { switch (err.getErrCase()) { case ERR_NOT_SET: return value; case ERR_INTERNAL: { Service.ErrInternal e = err.getErrInternal(); throw new InternalServerErrorException(e.getMessage()); } case ERR_INVALID_ARGUMENT: { Service.ErrInvalidArgument e = err.getErrInvalidArgument(); throw new InvalidArgumentException(e.getReason()); } case ERR_INVALID_PATH: { Service.ErrInvalidPath e = err.getErrInvalidPath(); throw new InvalidPathException(e.getReason(), e.getPath()); } case ERR_DATA_UNAVAILABLE: { Service.ErrDataUnavailable e = err.getErrDataUnavailable(); throw new DataUnavailableException(e.getReason()); } case ERR_PATH_NOT_FOLLOWABLE: { Service.ErrPathNotFollowable e = err.getErrPathNotFollowable(); throw new PathNotFollowableException(e.getPath()); } default: throw new RuntimeException("Unknown error: " + err.getErrCase()); } } public static class InternalServerErrorException extends RpcException { public InternalServerErrorException(String message) { super(message); } } public static class DataUnavailableException extends RpcException { public DataUnavailableException(Stringtable.Msg reason) { super(Strings.getMessage(reason)); } } public static class InvalidArgumentException extends RpcException { public InvalidArgumentException(Stringtable.Msg reason) { super(Strings.getMessage(reason)); } } public static class InvalidPathException extends RpcException { public final Path.Any path; public InvalidPathException(Stringtable.Msg reason, Path.Any path) { super(Strings.getMessage(reason)); this.path = path; } @Override public String toString() { return super.toString() + " Path: " + Paths.toString(path); } } public static class PathNotFollowableException extends RpcException { public PathNotFollowableException(Path.Any path) { super("Path " + Paths.toString(path) + " not followable."); } } }