/* * Copyright 2016-present Facebook, 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.facebook.buck.message_ipc; import com.google.common.base.Preconditions; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Arrays; import javax.annotation.Nullable; /** * Connection is a wrapper around MessageTransport class that transforms function calls into * messages. * * <p>You may specify your own interface, and call methods of that interface on an object that * Connection exposes to you via getRemoteObjectProxy(). Call method calls on proxy object will be * translated into InvocationMessage objects and sent via assigned MessageTransport object. * * <p>Example: * * <p>private interface RemoteInterface { String doString(int arg1, boolean arg2); } * * <p>MessageSerializer messageSerializer = new MessageSerializer(new ObjectMapper()); // imagine * you have workerProcess ready to use MessageTransport messageTransport = new * MessageTransport(workerProcess, messageSerializer); * * <p>Connection<RemoteInterface> connection = new Connection<>(messageTransport); * connection.setRemoteInterface(RemoteInterface.class, RemoteInterface.class.getClassLoader()); * String result = connection.getRemoteObjectProxy().doString(42, true); // process result */ public class Connection<REMOTE> implements AutoCloseable { private final MessageTransport messageTransport; @Nullable private REMOTE remoteObjectProxy; private boolean isClosed = false; public Connection(MessageTransport messageTransport) { this.messageTransport = messageTransport; } @SuppressWarnings("unchecked") public void setRemoteInterface(Class<REMOTE> remoteInterface, ClassLoader classLoader) { checkNotClose(); InvocationHandler invocationHandler = (proxy, method, args) -> { InvocationMessage invocation = new InvocationMessage(method.getName(), Arrays.asList(args)); ReturnResultMessage response = messageTransport.sendMessageAndWaitForResponse(invocation); return response.getValue(); }; this.remoteObjectProxy = (REMOTE) Proxy.newProxyInstance(classLoader, new Class[] {remoteInterface}, invocationHandler); } public REMOTE getRemoteObjectProxy() { checkNotClose(); Preconditions.checkNotNull( remoteObjectProxy, "You must set remote interface before obtaining remote object proxy."); return remoteObjectProxy; } @Override public void close() throws Exception { checkNotClose(); isClosed = true; messageTransport.close(); remoteObjectProxy = null; } private void checkNotClose() { Preconditions.checkState( !isClosed, "%s <%d> is already closed", this.getClass().getSimpleName(), System.identityHashCode(this)); } }