All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.voltdb.SimpleClientResponseAdapter Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */

package org.voltdb;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;

import org.voltcore.network.Connection;
import org.voltcore.network.NIOReadStream;
import org.voltcore.network.WriteStream;
import org.voltcore.utils.DeferredSerialization;
import org.voltcore.utils.Pair;
import org.voltdb.client.ClientResponse;

import com.google_voltpatches.common.base.Supplier;
import com.google_voltpatches.common.util.concurrent.ListenableFuture;
import com.google_voltpatches.common.util.concurrent.SettableFuture;

/**
 * A very simple adapter that deserializes bytes into client responses. It calls
 * crashLocalVoltDB() if the deserialization fails, which should only happen if there's a bug.
 */
public class SimpleClientResponseAdapter implements Connection, WriteStream {
    public static interface Callback {
        public void handleResponse(ClientResponse response);
    }

    public static final Callback NULL_CALLBACK = new Callback() {
        @Override
        public void handleResponse(ClientResponse response)
        {}
    };

    public static final class SyncCallback implements Callback {
        private final SettableFuture m_responseFuture = SettableFuture.create();

        public ClientResponse getResponse(long timeoutMs) throws InterruptedException
        {
            try {
                return m_responseFuture.get(timeoutMs, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                return null;
            } catch (ExecutionException e) {
                VoltDB.crashLocalVoltDB("Should never happen", true, e);
                return null;
            }
        }

        @Override
        public void handleResponse(ClientResponse response)
        {
            m_responseFuture.set(response);
        }

        public ListenableFuture getResponseFuture() {
            return m_responseFuture;
        }
    }

    private final long m_connectionId;
    private final AtomicLong m_handles = new AtomicLong();
    private Map m_callbacks = Collections.synchronizedMap(new HashMap());
    private final String m_name;
    public static volatile AtomicLong m_testConnectionIdGenerator;
    private final boolean m_leaveCallback;
    private final SettableFuture m_retFuture;

    /**
     * @param connectionId    The connection ID for this adapter, needs to be unique for this
     *                        node.
     * @param name            Human readable name identifying the adapter, will stand in for hostname
     */
    public SimpleClientResponseAdapter(long connectionId, String name) {
        this(connectionId, name, false);
    }


    /**
     * @param connectionId    The connection ID for this adapter, needs to be unique for this
     *                        node.
     * @param name            Human readable name identifying the adapter, will stand in for hostname
     * @param leaveCallback   Don't remove callbacks when invoking them, they are reused
     */
    public SimpleClientResponseAdapter(long connectionId, String name, boolean leaveCallback) {
        if (m_testConnectionIdGenerator != null) {
            m_connectionId = m_testConnectionIdGenerator.incrementAndGet();
        } else {
            m_connectionId = connectionId;
        }
        m_name = name;
        m_leaveCallback = leaveCallback;
        m_retFuture = null;
    }

    //None of the values matter (other than the future) just using this as a shortcut to collect stats internally
    public SimpleClientResponseAdapter(SettableFuture fut) {
        m_retFuture = fut;
        m_leaveCallback = false;
        m_name = "";
        m_connectionId = 0;
    }

    public static
        Pair>  getAsListenableFuture() {
        final SettableFuture fut = SettableFuture.create();
        return Pair.of(new SimpleClientResponseAdapter(fut), (ListenableFuture)fut);
    }

    public void registerCallback(long handle, Callback c) {
        m_handles.set(handle + 1);//just in case make them not match
        m_callbacks.put(handle, c);
    }

    public long registerCallback(Callback c) {
        final long handle = m_handles.incrementAndGet();
        m_callbacks.put( handle, c);
        return handle;
    }

    public Supplier> getSyncCallbackSupplier() {
        return new Supplier>() {
            @Override
            public Pair get() {
                final SyncCallback callback = new SyncCallback();
                final long handle = registerCallback(callback);
                return Pair.of(handle, callback);
            }
        };
    }

    @Override
    public void queueTask(Runnable r) {
        // Called when node failure happens
        r.run();
    }

    @Override
    public boolean hadBackPressure() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void fastEnqueue(DeferredSerialization ds) {
        enqueue(ds);
    }

    @Override
    public void enqueue(DeferredSerialization ds) {
        try {
            // serialize() touches not-threadsafe state around Initiator
            // stats.  In the normal code path, this is protected by a lock
            // in NIOWriteStream.enqueue().
            ByteBuffer buf = null;
            synchronized(this) {
                final int serializedSize = ds.getSerializedSize();
                if (serializedSize <= 0) {
                    return;
                }

                buf = ByteBuffer.allocate(serializedSize);
                ds.serialize(buf);
            }
            if (buf == null) {
                throw new UnsupportedOperationException();
            }
            enqueue(buf);
        } catch (IOException e) {
            VoltDB.crashLocalVoltDB("enqueue() in SimpleClientResponseAdapter throw an exception", true, e);
        }
    }

    @Override
    public void enqueue(ByteBuffer b) {
        ClientResponseImpl resp = new ClientResponseImpl();
        try {
            b.position(4);
            resp.initFromBuffer(b);

            //Go for a different behavior if we are using this adapter as a gussied up future for
            //an internal request
            if (m_retFuture != null) {
                m_retFuture.set(resp);
                return;
            }

            Callback callback = null;
            if (m_leaveCallback) {
                callback = m_callbacks.get(resp.getClientHandle());
            } else {
                callback = m_callbacks.remove(resp.getClientHandle());
            }
            if (callback != null) {
                callback.handleResponse(resp);
            }
        }
        catch (IOException e)
        {
            throw new RuntimeException("Unable to deserialize ClientResponse in SimpleClientResponseAdapter", e);
        }
    }

    @Override
    public void enqueue(ByteBuffer[] b)
    {
        if (b.length != 1)
        {
            throw new RuntimeException("Can't use chained ByteBuffers to enqueue");
        }
        enqueue(b[0]);
    }

    @Override
    public int calculatePendingWriteDelta(long now) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isEmpty() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getOutstandingMessageCount() {
        throw new UnsupportedOperationException();
    }

    @Override
    public WriteStream writeStream() {
        return this;
    }

    @Override
    public NIOReadStream readStream() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void disableReadSelection() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void enableReadSelection() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void disableWriteSelection() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void enableWriteSelection() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getHostnameAndIPAndPort() {
        return m_name;
    }

    @Override
    public String getHostnameOrIP() {
        return m_name;
    }

    @Override
    public String getHostnameOrIP(long clientHandle) {
        return getHostnameOrIP();
    }

    @Override
    public int getRemotePort() {
        return -1;
    }

    @Override
    public InetSocketAddress getRemoteSocketAddress() {
        return null;
    }

    @Override
    public long connectionId() {
        return m_connectionId;
    }

    @Override
    public long connectionId(long clientHandle) {
        return connectionId();
    }

    @Override
    public Future unregister() {
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy