/* * Copyright 2014 Avanza Bank AB * * 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.avanza.astrix.remoting.client; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Optional; import com.avanza.astrix.core.AstrixRemoteResult; import com.avanza.astrix.core.CorrelationId; import com.avanza.astrix.core.RemoteServiceInvocationException; import com.avanza.astrix.core.ServiceInvocationException; import com.avanza.astrix.core.remoting.RoutingKey; import com.avanza.astrix.versioning.core.AstrixObjectSerializer; import rx.Observable; public final class RemotingEngine { // TODO: find suitable name for this abstraction private final RemotingTransport serviceTransport; private final AstrixObjectSerializer objectSerializer; private final int apiVersion; public RemotingEngine(RemotingTransport serviceTransport, AstrixObjectSerializer objectSerializer, int apiVersion) { this.serviceTransport = serviceTransport; this.objectSerializer = objectSerializer; this.apiVersion = apiVersion; } @SuppressWarnings("unchecked") protected final <T> AstrixRemoteResult<T> toRemoteResult(AstrixServiceInvocationResponse response, Type returnType) { if (response.isServiceUnavailable()) { return AstrixRemoteResult.unavailable(response.getExceptionMsg(), CorrelationId.valueOf(response.getCorrelationId())); } if (response.hasThrownException()) { CorrelationId correlationId = CorrelationId.valueOf(response.getCorrelationId()); return AstrixRemoteResult.failure(createClientSideException(response, apiVersion), correlationId); } if (returnType.equals(Void.TYPE) || returnType.equals(Void.class)) { return AstrixRemoteResult.voidResult(); } if (isOptionalType(returnType)) { return AstrixRemoteResult.successful(restoreOptional(response, returnType)); } T result = unmarshall(response, returnType, apiVersion); return AstrixRemoteResult.successful(result); } private <T> T restoreOptional(AstrixServiceInvocationResponse response, Type returnType) { if (isNullOptionalReturnValue(response)) { return null; } ParameterizedType optionalType = ParameterizedType.class.cast(returnType); Object result = unmarshall(response, optionalType.getActualTypeArguments()[0], apiVersion); return (T) Optional.ofNullable(result); } // Defines whether the service invocation returned the value 'null' (as opposed to Optional.empty()). private boolean isNullOptionalReturnValue(AstrixServiceInvocationResponse response) { return "true".equals(response.getHeader(AstrixServiceInvocationResponseHeaders.OPTIONAL_RETURN_VALUE_IS_NULL)); } private boolean isOptionalType(Type returnType) { if (!(returnType instanceof ParameterizedType)) { return false; } ParameterizedType parameterizedType = ParameterizedType.class.cast(returnType); Type rawType = parameterizedType.getRawType(); return rawType.equals(Optional.class); } protected final Object[] marshall(Object[] elements) { if (elements == null) { // No argument method return new Object[0]; } Object[] result = new Object[elements.length]; for (int i = 0; i < result.length; i++) { result[i] = this.objectSerializer.serialize(elements[i], apiVersion); } return result; } private <T> T unmarshall(AstrixServiceInvocationResponse response, Type returnType, int version) { return objectSerializer.deserialize(response.getResponseBody(), returnType, version); } protected final ServiceInvocationException createClientSideException(AstrixServiceInvocationResponse response, int version) { if (response.getException() != null) { ServiceInvocationException exception = objectSerializer.deserialize(response.getException(), ServiceInvocationException.class, version); return exception; } return new RemoteServiceInvocationException(response.getExceptionMsg(), response.getThrownExceptionType()); } final Observable<AstrixServiceInvocationResponse> submitRoutedRequest(AstrixServiceInvocationRequest request, RoutingKey routingKey) { return this.serviceTransport.submitRoutedRequest(request, routingKey); } final Observable<List<AstrixServiceInvocationResponse>> submitRoutedRequests(List<RoutedServiceInvocationRequest> requests) { return this.serviceTransport.submitRoutedRequests(requests); } final Observable<List<AstrixServiceInvocationResponse>> submitBroadcastRequest(AstrixServiceInvocationRequest request) { return this.serviceTransport.submitBroadcastRequest(request); } public int partitionCount() { return this.serviceTransport.partitionCount(); } }