use of net.morimekta.providence.PServiceCall in project providence by morimekta.
the class FastBinarySerializer method deserialize.
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <Message extends PMessage<Message, Field>, Field extends PField> PServiceCall<Message, Field> deserialize(@Nonnull InputStream is, @Nonnull PService service) throws SerializerException {
String methodName = null;
int sequence = 0;
PServiceCallType type = null;
try {
LittleEndianBinaryReader in = new LittleEndianBinaryReader(is);
// Max method name length: 255 chars.
int tag = in.readIntVarint();
int len = tag >>> 3;
int typeKey = tag & 0x07;
methodName = new String(in.expectBytes(len), UTF_8);
sequence = in.readIntVarint();
type = PServiceCallType.findById(typeKey);
if (type == null) {
throw new SerializerException("Invalid call type " + typeKey).setExceptionType(PApplicationExceptionType.INVALID_MESSAGE_TYPE);
} else if (type == PServiceCallType.EXCEPTION) {
PApplicationException ex = readMessage(in, PApplicationException.kDescriptor);
return (PServiceCall<Message, Field>) new PServiceCall<>(methodName, type, sequence, ex);
}
PServiceMethod method = service.getMethod(methodName);
if (method == null) {
throw new SerializerException("No such method %s on %s", methodName, service.getQualifiedName()).setExceptionType(PApplicationExceptionType.UNKNOWN_METHOD);
}
@SuppressWarnings("unchecked") PMessageDescriptor<Message, Field> descriptor = isRequestCallType(type) ? method.getRequestType() : method.getResponseType();
if (descriptor == null) {
throw new SerializerException("No such %s descriptor for %s", isRequestCallType(type) ? "request" : "response", service.getQualifiedName()).setExceptionType(PApplicationExceptionType.UNKNOWN_METHOD);
}
Message message = readMessage(in, descriptor);
return new PServiceCall<>(methodName, type, sequence, message);
} catch (SerializerException e) {
throw new SerializerException(e).setCallType(type).setMethodName(methodName).setSequenceNo(sequence);
} catch (IOException e) {
throw new SerializerException(e, e.getMessage()).setCallType(type).setMethodName(methodName).setSequenceNo(sequence);
}
}
use of net.morimekta.providence.PServiceCall in project providence by morimekta.
the class JsonSerializer method parseServiceCall.
@SuppressWarnings("unchecked")
private <T extends PMessage<T, F>, F extends PField> PServiceCall<T, F> parseServiceCall(JsonTokenizer tokenizer, PService service) throws IOException {
PServiceCallType type = null;
String methodName = null;
int sequence = 0;
try {
tokenizer.expectSymbol("service call start", JsonToken.kListStart);
methodName = tokenizer.expectString("method name").rawJsonLiteral();
tokenizer.expectSymbol("entry sep", JsonToken.kListSep);
JsonToken callTypeToken = tokenizer.expect("call type");
if (callTypeToken.isInteger()) {
int typeKey = callTypeToken.byteValue();
type = PServiceCallType.findById(typeKey);
if (type == null) {
throw new SerializerException("Service call type " + typeKey + " is not valid").setExceptionType(PApplicationExceptionType.INVALID_MESSAGE_TYPE);
}
} else if (callTypeToken.isLiteral()) {
String typeName = callTypeToken.rawJsonLiteral();
type = PServiceCallType.findByName(typeName.toUpperCase(Locale.US));
if (type == null) {
throw new SerializerException("Service call type \"" + Strings.escape(typeName) + "\" is not valid").setExceptionType(PApplicationExceptionType.INVALID_MESSAGE_TYPE);
}
} else {
throw new SerializerException("Invalid service call type token " + callTypeToken.asString()).setExceptionType(PApplicationExceptionType.INVALID_MESSAGE_TYPE);
}
tokenizer.expectSymbol("entry sep", JsonToken.kListSep);
sequence = tokenizer.expectNumber("Service call sequence").intValue();
tokenizer.expectSymbol("entry sep", JsonToken.kListSep);
if (type == PServiceCallType.EXCEPTION) {
PApplicationException ex = (PApplicationException) parseTypedValue(tokenizer.expect("Message start"), tokenizer, PApplicationException.kDescriptor, false);
tokenizer.expectSymbol("service call end", JsonToken.kListEnd);
return (PServiceCall<T, F>) new PServiceCall<>(methodName, type, sequence, ex);
}
PServiceMethod method = service.getMethod(methodName);
if (method == null) {
throw new SerializerException("No such method " + methodName + " on " + service.getQualifiedName()).setExceptionType(PApplicationExceptionType.UNKNOWN_METHOD);
}
@SuppressWarnings("unchecked") PMessageDescriptor<T, F> descriptor = isRequestCallType(type) ? method.getRequestType() : method.getResponseType();
if (descriptor == null) {
throw new SerializerException("No %s type for %s.%s()", isRequestCallType(type) ? "request" : "response", service.getQualifiedName(), methodName).setExceptionType(PApplicationExceptionType.UNKNOWN_METHOD);
}
T message = (T) parseTypedValue(tokenizer.expect("message start"), tokenizer, descriptor, false);
tokenizer.expectSymbol("service call end", JsonToken.kListEnd);
return new PServiceCall<>(methodName, type, sequence, message);
} catch (SerializerException se) {
throw new SerializerException(se).setMethodName(methodName).setCallType(type).setSequenceNo(sequence);
} catch (JsonException je) {
throw new JsonSerializerException(je).setMethodName(methodName).setCallType(type).setSequenceNo(sequence);
}
}
use of net.morimekta.providence.PServiceCall in project providence by morimekta.
the class SerializerTest method setUpData.
@BeforeClass
public static void setUpData() throws IOException, ExceptionFields {
MessageGenerator gen = new MessageGenerator().addFactory(f -> {
if (f.equals(Operand._Field.OPERATION)) {
return () -> Operation.builder().setOperator(Operator.ADD).addToOperands(Operand.withNumber(123)).addToOperands(Operand.withNumber(321)).build();
}
return null;
});
if (operation == null) {
operation = gen.generate(Operation.kDescriptor);
}
if (containers == null) {
containers = new ArrayList<>();
for (int i = 0; i < 1; ++i) {
containers.add(gen.generate(Containers.kDescriptor));
}
}
serviceCalls = new ArrayList<>();
/**
* Temporary setup needed to generate
*/
ContainerService.Iface impl = pC -> {
if (pC == null) {
throw new PApplicationException("", PApplicationExceptionType.INTERNAL_ERROR);
}
if (pC.mutate().presentFields().isEmpty()) {
throw gen.generate(ExceptionFields.kDescriptor);
}
return CompactFields.builder().setName("" + pC.hashCode()).setId(pC.hashCode()).build();
};
PProcessor processor = new ContainerService.Processor(impl);
PServiceCallHandler handler = new PServiceCallHandler() {
@Nullable
@Override
@SuppressWarnings("unchecked")
public <Request extends PMessage<Request, RequestField>, Response extends PMessage<Response, ResponseField>, RequestField extends PField, ResponseField extends PField> PServiceCall<Response, ResponseField> handleCall(PServiceCall<Request, RequestField> call, PService service) throws IOException {
serviceCalls.add(call);
try {
PServiceCall response = processor.handleCall(call, service);
serviceCalls.add(response);
return response;
} catch (PApplicationException e) {
PServiceCall ex = new PServiceCall(call.getMethod(), PServiceCallType.EXCEPTION, call.getSequence(), e);
serviceCalls.add(ex);
return ex;
}
}
};
ContainerService.Client client = new ContainerService.Client(handler);
client.load(gen.generate(Containers.kDescriptor));
try {
client.load(Containers.builder().build());
} catch (ExceptionFields e) {
// ignore.
}
try {
// NPE -> PApplicationException
client.load(null);
} catch (PApplicationException e) {
// ignore.
}
}
use of net.morimekta.providence.PServiceCall in project providence by morimekta.
the class NonblockingSocketClientHandler method handleReadResponses.
private void handleReadResponses(SocketChannel channel, PService service) {
while (this.channel == channel && channel.isOpen()) {
FramedBufferInputStream in = new FramedBufferInputStream(channel);
try {
in.nextFrame();
PServiceCall reply = serializer.deserialize(in, service);
if (reply.getType() == PServiceCallType.CALL || reply.getType() == PServiceCallType.ONEWAY) {
throw new PApplicationException("Reply with invalid call type: " + reply.getType(), PApplicationExceptionType.INVALID_MESSAGE_TYPE);
}
CompletableFuture<PServiceCall> future = responseFutures.get(reply.getSequence());
if (future == null) {
// The item response timed out.
LOGGER.debug("No future for sequence ID " + reply.getSequence());
continue;
}
responseFutures.remove(reply.getSequence());
future.complete(reply);
} catch (Exception e) {
if (!channel.isOpen()) {
// If the channel is closed. Should not trigger on disconnected.
break;
}
LOGGER.error("Exception in channel response reading", e);
}
}
if (responseFutures.size() > 0) {
LOGGER.warn("Channel closed with {} unfinished calls", responseFutures.size());
responseFutures.forEach((s, f) -> f.completeExceptionally(new IOException("Channel closed")));
responseFutures.clear();
}
}
use of net.morimekta.providence.PServiceCall in project providence by morimekta.
the class NonblockingSocketServer method handleRead.
@SuppressWarnings("unchecked")
private void handleRead(SelectionKey key, Context context) throws IOException {
long startTime = System.nanoTime();
// part a: read into the readBuffer.
if (context.currentFrameSize == 0) {
// read frame size.
try {
if (context.channel.read(context.sizeBuffer) < 0) {
context.close();
key.cancel();
return;
}
if (context.sizeBuffer.position() < 4) {
return;
}
} catch (IOException e) {
// LOGGER.error(e.getMessage(), e);
context.close();
key.cancel();
return;
}
context.sizeBuffer.flip();
try (ByteBufferInputStream in = new ByteBufferInputStream(context.sizeBuffer);
BigEndianBinaryReader reader = new BigEndianBinaryReader(in)) {
context.currentFrameSize = reader.expectInt();
}
context.sizeBuffer.rewind();
if (context.currentFrameSize > maxFrameSizeInBytes) {
LOGGER.warn("Attempting message of " + context.currentFrameSize + " > " + maxFrameSizeInBytes);
context.close();
key.cancel();
return;
}
if (context.currentFrameSize < 1) {
LOGGER.warn("Attempting message of " + context.currentFrameSize);
context.close();
key.cancel();
return;
}
context.readBuffer.rewind();
context.readBuffer.limit(context.currentFrameSize);
}
try {
if (context.channel.read(context.readBuffer) < 0) {
LOGGER.warn("Closed connection while reading frame");
context.close();
key.cancel();
return;
}
} catch (IOException e) {
LOGGER.warn("Exception reading frame: {}", e.getMessage(), e);
context.close();
key.cancel();
return;
}
if (context.readBuffer.position() < context.readBuffer.limit()) {
// wait until next read, and see if remaining of frame has arrived.
return;
}
// part b: if the read buffer is complete, handle the content.
PServiceCall call;
try {
context.currentFrameSize = 0;
context.readBuffer.flip();
call = serializer.deserialize(new ByteBufferInputStream(context.readBuffer), processor.getDescriptor());
context.readBuffer.clear();
workerExecutor.submit(() -> {
PServiceCall reply;
try {
reply = processor.handleCall(call);
} catch (Exception e) {
reply = new PServiceCall<>(call.getMethod(), PServiceCallType.EXCEPTION, call.getSequence(), PApplicationException.builder().setMessage(e.getMessage()).setId(PApplicationExceptionType.INTERNAL_ERROR).initCause(e).build());
}
synchronized (context.writeQueue) {
context.writeQueue.offer(new WriteEntry(startTime, call, reply));
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
selector.wakeup();
}
});
} catch (IOException e) {
double duration = ((double) System.nanoTime() - startTime) / NS_IN_MILLIS;
instrumentation.onTransportException(e, duration, null, null);
}
}
Aggregations