/**
* 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.lang.reflect.InvocationTargetException;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.diqube.connection.integrity.IntegrityCheckingProtocol;
import org.diqube.connection.integrity.IntegritySecretHelper;
import org.diqube.thrift.base.services.DiqubeThriftServiceInfoManager.DiqubeThriftServiceInfo;
import org.diqube.thrift.base.thrift.RNodeAddress;
import org.diqube.thrift.util.RememberingTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation for {@link ConnectionFactory}.
*
* @author Bastian Gloeckle
*/
/* package */class DefaultConnectionFactory implements ConnectionFactory {
private static final Logger logger = LoggerFactory.getLogger(DefaultConnectionFactory.class);
private final ConnectionPool connectionPool;
private int socketTimeout;
private byte[][] macKeys;
/**
* @param connectionPool
* The pool this factory belongs to.
*/
/* package */ DefaultConnectionFactory(ConnectionPool connectionPool, IntegritySecretHelper integritySecretHelper,
int socketTimeout) {
this.connectionPool = connectionPool;
this.socketTimeout = socketTimeout;
macKeys = integritySecretHelper.provideMessageIntegritySecrets();
}
private TTransport openTransport(RNodeAddress addr, SocketListener socketListener) throws ConnectionException {
if (addr.isSetHttpAddr()) {
try {
// TODO #32: Integrate SocketListener into HTTP connections.
return new THttpClient(addr.getHttpAddr().getUrl());
} catch (TTransportException e) {
throw new ConnectionException("Could not open connection to " + addr, e);
}
}
TTransport transport = new DiqubeClientSocket(addr.getDefaultAddr().getHost(), addr.getDefaultAddr().getPort(),
socketTimeout, socketListener);
return new TFramedTransport(transport);
}
@Override
public <T> Connection<T> createConnection(DiqubeThriftServiceInfo<T> serviceInfo, RNodeAddress addr,
SocketListener socketListener) throws ConnectionException {
TTransport transport = openTransport(addr, socketListener);
T queryResultClient = createProtocolAndClient(serviceInfo, transport);
try {
transport.open();
} catch (TTransportException e) {
throw new ConnectionException("Could not open connection to " + addr, e);
}
return new Connection<>(connectionPool, serviceInfo, queryResultClient, transport, addr);
}
@Override
public <T, U> Connection<U> createConnection(Connection<T> oldConnection, DiqubeThriftServiceInfo<U> serviceInfo)
throws ConnectionException {
U client = createProtocolAndClient(serviceInfo, oldConnection.getTransport());
oldConnection.setWasReplaced(true);
Connection<U> res =
new Connection<>(connectionPool, serviceInfo, client, oldConnection.getTransport(), oldConnection.getAddress());
res.setTimeout(oldConnection.getTimeout());
res.setExecutionUuid(oldConnection.getExecutionUuid());
logger.trace("Connection {}: Replacing object {} with {}", System.identityHashCode(res.getTransport()),
System.identityHashCode(oldConnection), System.identityHashCode(res));
return res;
}
private <T> T createProtocolAndClient(DiqubeThriftServiceInfo<T> serviceInfo, TTransport transport)
throws ConnectionException {
TProtocol innerProtocol;
if (serviceInfo.isIntegrityChecked()) {
if (!(transport instanceof RememberingTransport))
transport = new RememberingTransport(transport);
innerProtocol = new IntegrityCheckingProtocol(new TCompactProtocol(transport), macKeys);
} else
innerProtocol = new TCompactProtocol(transport);
TProtocol queryProtocol = new TMultiplexedProtocol(innerProtocol, serviceInfo.getServiceName());
try {
@SuppressWarnings("unchecked")
T queryResultClient = (T) serviceInfo.getClientClass().getConstructor(TProtocol.class).newInstance(queryProtocol);
return queryResultClient;
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
ConnectionPool.logger.error("Error while constructing a client", e);
throw new ConnectionException("Error while constructing a client", e);
}
}
}