/* * Copyright (c) 2013-2017 Cinchapi 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.cinchapi.concourse.server.plugin; import io.atomix.catalyst.buffer.Buffer; import java.nio.ByteBuffer; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import com.cinchapi.concourse.annotate.PackagePrivate; import com.cinchapi.concourse.thrift.AccessToken; import com.cinchapi.concourse.thrift.ComplexTObject; import com.google.common.collect.Maps; /** * A message that is sent from one process to another via a * {@link InterProcessCommunication} segment with the result of a * {@link RemoteMethodRequest}. * * @author Jeff Nelson */ @Immutable @PackagePrivate final class RemoteMethodResponse extends RemoteMessage { /** * The {@link AccessToken} of the session for which the response is routed. */ public AccessToken creds; /** * The error that was thrown. */ @Nullable public Exception error; /** * The response encapsulated as a thrift serializable object. */ @Nullable public ComplexTObject response; /** * Construct a new instance. * * @param creds * @param response */ public RemoteMethodResponse(AccessToken creds, ComplexTObject response) { this.creds = creds; this.response = response; this.error = null; } /** * Construct a new instance. * * @param creds * @param error */ public RemoteMethodResponse(AccessToken creds, Exception error) { this.creds = creds; this.response = null; this.error = error; } /** * DO NOT CALL. Only here for deserialization. */ RemoteMethodResponse() {/* no-op */} @Override public void deserialize(Buffer buffer) { boolean isError = buffer.readBoolean(); int credsLength = buffer.readInt(); byte[] creds = new byte[credsLength]; buffer.read(creds); this.creds = new AccessToken(ByteBuffer.wrap(creds)); if(isError) { this.error = new RuntimeException(buffer.readUTF8()); } else { byte[] response = new byte[(int) buffer.remaining()]; buffer.read(response); this.response = ComplexTObject .fromByteBuffer(ByteBuffer.wrap(response)); } } @Override public boolean equals(Object obj) { if(obj instanceof RemoteMethodResponse) { return Objects.equals(creds, ((RemoteMethodResponse) obj).creds) && error == null ? Objects.equals(response, ((RemoteMethodResponse) obj).response) : Objects.equals(error, ((RemoteMethodResponse) obj).error); } else { return false; } } @Override public int hashCode() { return Objects.hash(creds, error == null ? response : error); } /** * Return {@code true} if this {@link RemoteMethodResponse response} * indicates an error. * * @return {@code true} if this is an error response */ public boolean isError() { return error != null; } @Override public String toString() { Map<String, Object> data = Maps.newHashMap(); data.put("error", isError()); data.put("response", isError() ? error : response); data.put("creds", creds); return data.toString(); } @Override public Type type() { return Type.RESPONSE; } @Override protected void serialize(Buffer buffer) { buffer.writeBoolean(isError()); byte[] creds = this.creds.getData(); buffer.writeInt(creds.length); buffer.write(creds); if(isError()) { buffer.writeUTF8(error.getMessage()); } else { buffer.write(response.toByteBuffer().array()); } } }