/** * Decode message. * * @param message the original message (with {@link ByteBuf} data) * @param dataType the type of the data. * @return a new Service message that upon {@link ServiceMessage#data()} returns the actual data * (of type data type) * @throws MessageCodecException when decode fails */ public static ServiceMessage decodeData(ServiceMessage message, Class<?> dataType) throws MessageCodecException { if (!message.hasData(ByteBuf.class) || dataType == null) { return message; } Object data; Class<?> targetType = message.isError() ? ErrorData.class : dataType; ByteBuf dataBuffer = message.data(); try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer, true)) { DataCodec dataCodec = DataCodec.getInstance(message.dataFormatOrDefault()); data = dataCodec.decode(inputStream, targetType); } catch (Throwable ex) { throw new MessageCodecException( "Failed to decode data on message q=" + message.qualifier(), ex); } return ServiceMessage.from(message).data(data).build(); } }
/** * Instantiates new message with the same data and headers as at given message. * * @param message the message to be copied * @return a new message, with the same data and headers */ public static Builder from(ServiceMessage message) { return ServiceMessage.builder().data(message.data()).headers(message.headers()); }
/** * Returns message's qualifier. * * @return qualifier string */ public String qualifier() { return header(HEADER_QUALIFIER); }
@Override public List<ServiceReference> lookupService(ServiceMessage request) { List<ServiceReference> result = referencesByQualifier.get(request.qualifier()); if (result == null || result.isEmpty()) { return Collections.emptyList(); } String contentType = request.dataFormatOrDefault(); return result .stream() .filter(ref -> ref.contentTypes().contains(contentType)) .collect(Collectors.toList()); }
private Mono<Void> error(HttpServerResponse httpResponse, ServiceMessage response) { int code = Integer.parseInt(Qualifier.getQualifierAction(response.qualifier())); HttpResponseStatus status = HttpResponseStatus.valueOf(code); ByteBuf content = response.hasData(ErrorData.class) ? encodeData(response.data(), response.dataFormatOrDefault()) : response.data(); return httpResponse.status(status).sendObject(content).then(); }
private Mono<Void> error(HttpServerResponse httpResponse, ServiceMessage response) { int code = response.errorType(); HttpResponseStatus status = HttpResponseStatus.valueOf(code); ByteBuf content = response.hasData(ErrorData.class) ? encodeData(response.data(), response.dataFormatOrDefault()) : response.data(); return httpResponse.status(status).sendObject(content).then(); }
/** * Performs basic validation on incoming message: qualifier must be present, method invoker must * be present in the method registry by incoming qualifier. May throw exception * * @param message incoming message * @throws ServiceException in case qualfier is missing or method invoker is missing by given * qualifier */ private void validateRequest(ServiceMessage message) throws ServiceException { if (message.qualifier() == null) { Optional.ofNullable(message.data()) .ifPresent(ReferenceCountUtil::safestRelease); // release message data if any LOGGER.error("Failed to invoke service with msg={}: qualifier is null", message); throw new BadRequestException("Qualifier is null in service msg request: " + message); } if (!methodRegistry.containsInvoker(message.qualifier())) { Optional.ofNullable(message.data()) .ifPresent(ReferenceCountUtil::safestRelease); // release message data if any LOGGER.error( "Failed to invoke service with msg={}: no service invoker found by qualifier={}", message, message.qualifier()); throw new ServiceUnavailableException( "No service invoker found by qualifier=" + message.qualifier()); } } }
private Mono<Void> ok(HttpServerResponse httpResponse, ServiceMessage response) { ByteBuf content = response.hasData(ByteBuf.class) ? response.data() : encodeData(response.data(), response.dataFormatOrDefault()); return httpResponse.status(OK).sendObject(content).then(); }
/** * Describes whether the message is an error. * * @return <code>true</code> if error, otherwise <code>false</code>. */ public boolean isError() { String qualifier = qualifier(); return qualifier != null && qualifier.contains(Qualifier.ERROR_NAMESPACE); }
private Mono<Address> addressLookup(ServiceMessage request) { Callable<Address> callable = () -> router .route(serviceRegistry, request) .map(ServiceReference::address) .orElseThrow(() -> noReachableMemberException(request)); return Mono.fromCallable(callable) .doOnError( t -> { Optional<Object> dataOptional = Optional.ofNullable(request.data()); dataOptional.ifPresent(ReferenceCountUtil::safestRelease); }); }
private static ServiceMessage enrichRequest() { return ServiceMessage.builder() .qualifier(QUALIFIER) .header(CLIENT_SEND_TIME, System.currentTimeMillis()) .build(); } }
/** * Get a builder by pattern form given {@link ServiceMessage}. * * @param message ServiceMessage form where to copy field values. * @return builder with fields copied from given {@link ServiceMessage} */ public static Builder from(ServiceMessage message) { return new Builder().headers(message.headers()).data(message.data()); }
private ServiceMessage toServiceMessage(MethodInfo methodInfo, Object... params) { if (methodInfo.parameterCount() != 0 && params[0] instanceof ServiceMessage) { return ServiceMessage.from((ServiceMessage) params[0]) .qualifier(methodInfo.serviceName(), methodInfo.methodName()) .build(); } return ServiceMessage.builder() .qualifier(methodInfo.serviceName(), methodInfo.methodName()) .data(methodInfo.parameterCount() != 0 ? params[0] : null) .build(); }
private Mono<Void> handleRequest( ByteBuf content, HttpServerRequest httpRequest, HttpServerResponse httpResponse) { String qualifier = httpRequest.uri(); Builder builder = ServiceMessage.builder().qualifier(qualifier).data(content); enrichRequest(httpRequest.requestHeaders(), builder); return serviceCall .requestOne(builder.build()) .doOnNext(message -> metrics.markServiceResponse()) .switchIfEmpty( Mono.defer(() -> Mono.just(ServiceMessage.builder().qualifier(qualifier).build()))) .flatMap( response -> { enrichResponse(httpResponse, response); return Mono.defer( () -> response.isError() // check error ? error(httpResponse, response) : response.hasData() // check data ? ok(httpResponse, response) : noContent(httpResponse)); }); }
private static ServiceMessage enichResponse(ServiceMessage msg) { return ServiceMessage.from(msg).header(CLIENT_RECV_TIME, System.currentTimeMillis()).build(); } }
@Override public Throwable toError(ServiceMessage message) { ErrorData data = message.data(); int errorType = message.errorType(); int errorCode = data.getErrorCode(); String errorMessage = data.getErrorMessage(); switch (errorType) { case BadRequestException.ERROR_TYPE: return new BadRequestException(errorCode, errorMessage); case UnauthorizedException.ERROR_TYPE: return new UnauthorizedException(errorCode, errorMessage); case ServiceUnavailableException.ERROR_TYPE: return new ServiceUnavailableException(errorCode, errorMessage); case InternalServiceException.ERROR_TYPE: return new InternalServiceException(errorCode, errorMessage); // Handle other types of Service Exceptions here default: return new InternalServiceException(errorCode, errorMessage); } }
/** * Returns error type. Error type is an identifier of a group of errors. * * @return error type. */ public int errorType() { if (!isError()) { throw new IllegalStateException("Message is not an error"); } try { return Integer.parseInt(Qualifier.getQualifierAction(qualifier())); } catch (NumberFormatException e) { throw new IllegalStateException("Error type must be a number"); } }
private Mono<Void> handleRequest( ByteBuf content, HttpServerRequest httpRequest, HttpServerResponse httpResponse) { String qualifier = httpRequest.uri(); Builder builder = ServiceMessage.builder().qualifier(qualifier).data(content); enrichRequest(httpRequest.requestHeaders(), builder); return serviceCall .requestOne(builder.build()) .doOnNext(message -> metrics.markServiceResponse()) .switchIfEmpty( Mono.defer(() -> Mono.just(ServiceMessage.builder().qualifier(qualifier).build()))) .flatMap( response -> { enrichResponse(httpResponse, response); return Mono.defer( () -> ExceptionProcessor.isError(response) // check error ? error(httpResponse, response) : response.hasData() // check data ? ok(httpResponse, response) : noContent(httpResponse)); }); }