/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diqube.connection;
import java.io.Closeable;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.thrift.transport.TTransport;
import org.diqube.thrift.base.services.DiqubeThriftServiceInfoManager.DiqubeThriftServiceInfo;
import org.diqube.thrift.base.thrift.RNodeAddress;
/**
* A connection to a server.
*
* This is {@link Closeable}: When closed, {@link ConnectionPool#releaseConnection(Connection)} will be called.
*
* @author Bastian Gloeckle
*/
public class Connection<T> implements Closeable, ServiceProvider<T> {
private T service;
private TTransport transport;
private RNodeAddress address;
private ConnectionPool parentPool;
private UUID executionUuid = null;
private boolean wasReplaced = false;
private AtomicBoolean isPooled = new AtomicBoolean(true);
private AtomicLong timeout = null;
private DiqubeThriftServiceInfo<T> serviceInfo;
/* package */ Connection(ConnectionPool parentPool, DiqubeThriftServiceInfo<T> serviceInfo, T service,
TTransport transport, RNodeAddress address) {
this.parentPool = parentPool;
this.serviceInfo = serviceInfo;
this.service = service;
this.transport = transport;
this.address = address;
}
/**
* @return Easy to use service bean - each method call on the returned object will actually trigger a remote call.
* @throws IllegalStateException
* if connection was replaced or connection is pooled.
*/
@Override
public T getService() throws IllegalStateException {
if (wasReplaced)
throw new IllegalStateException("Connection disabled (replaced): " + System.identityHashCode(getTransport()) + " "
+ System.identityHashCode(this));
if (isPooled.get())
throw new IllegalStateException(
"Connection not reserved: " + System.identityHashCode(getTransport()) + " " + System.identityHashCode(this));
return service;
}
/* package */TTransport getTransport() {
return transport;
}
/* package */ RNodeAddress getAddress() {
return address;
}
/* package */ DiqubeThriftServiceInfo<T> getServiceInfo() {
return serviceInfo;
}
/**
* @return The executionUuid this connection was working for. Can be <code>null</code>.
*/
/* package */UUID getExecutionUuid() {
return executionUuid;
}
/**
* @return <code>true</code> if the connection can be used, <code>false</code> if this connection cannot be used any
* more because it was used as "oldConnection" in a call to
* {@link ConnectionFactory#createConnection(Connection, Class, String)} as "old connection".
*/
public boolean wasReplaced() {
return wasReplaced;
}
/* package */ void setWasReplaced(boolean wasReplaced) {
this.wasReplaced = wasReplaced;
}
/**
* @return <code>true</code> if this connection is currently available in the ConnectionPool, <code>false</code> if it
* is reserved by someone. Default value is "true".
*/
public boolean isPooled() {
return isPooled.get();
}
/**
* comapre-and-set "pooled" value. Returns <code>true</code> if successful.
*/
/* package */ boolean pooledCAS(boolean expected, boolean newValue) {
return this.isPooled.compareAndSet(expected, newValue);
}
/**
* For {@link ConnectionPool} only: Set the executionUuid this connection is working either when this connection is
* being reserved or is being released.
*/
/* package */void setExecutionUuid(UUID executionUuid) {
this.executionUuid = executionUuid;
}
/* package */ Long getTimeout() {
return (timeout == null) ? null : timeout.get();
}
/* package */ void setTimeout(Long timeout) {
if (timeout == null)
this.timeout = null;
if (this.timeout == null) {
this.timeout = new AtomicLong(timeout);
return;
}
this.timeout.set(timeout);
}
@Override
public void close() throws IOException {
parentPool.releaseConnection(this);
}
@Override
public boolean isLocal() {
return false;
}
}