/**
* 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.itest.util;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import org.apache.thrift.TException;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.diqube.connection.integrity.IntegrityCheckingProtocol;
import org.diqube.itest.control.ServerControl.ServerAddr;
import org.diqube.remote.query.IdentityCallbackServiceConstants;
import org.diqube.remote.query.KeepAliveServiceConstants;
import org.diqube.remote.query.thrift.IdentityCallbackService;
import org.diqube.remote.query.thrift.IdentityService;
import org.diqube.remote.query.thrift.KeepAliveService;
import org.diqube.remote.query.thrift.TicketInfo;
import org.diqube.thrift.util.RememberingTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Util class which opens a {@link IdentityCallbackService} in this process in order to receive results from calling
* another {@link IdentityService} on a diqube-server.
*
* @author Bastian Gloeckle
*/
public class IdentityCallbackServiceTestUtil {
private static final Logger logger = LoggerFactory.getLogger(IdentityCallbackServiceTestUtil.class);
public static TestIdentityCallbackService createIdentityCallbackService() {
short port = 5200; // TODO find port dynamically.
TMultiplexedProcessor multiProcessor = new TMultiplexedProcessor();
TestIdentityCallbackService res = new TestIdentityCallbackService(new ServerAddr("127.0.0.1", port));
IdentityCallbackServiceImpl serviceImpl = new IdentityCallbackServiceImpl(res);
multiProcessor.registerProcessor(IdentityCallbackServiceConstants.SERVICE_NAME,
new IdentityCallbackService.Processor<IdentityCallbackService.Iface>(serviceImpl));
multiProcessor.registerProcessor(KeepAliveServiceConstants.SERVICE_NAME,
// no integrity check for keep alives.
new IntegrityCheckingProtocol.IntegrityCheckDisablingProcessor(
new KeepAliveService.Processor<KeepAliveService.Iface>(new KeepAliveService.Iface() {
@Override
public void ping() throws TException {
// noop.
}
})));
TNonblockingServerSocket transport;
try {
transport = new TNonblockingServerSocket(new InetSocketAddress("127.0.0.1", port));
} catch (TTransportException e) {
throw new RuntimeException("Could not open transport for result service", e);
}
TNonblockingServer.Args args = new TNonblockingServer.Args(transport);
args.processor(multiProcessor);
args.transportFactory(new RememberingTransport.Factory(new TFramedTransport.Factory()));
args.protocolFactory(new TCompactProtocol.Factory());
TNonblockingServer thriftServer = new TNonblockingServer(args);
Thread serverThread = new Thread(() -> thriftServer.serve(), "Test-IdentityCallbackService-serverthread");
res.setThriftServer(thriftServer);
res.setServerThread(serverThread);
serverThread.start();
return res;
}
public static class TestIdentityCallbackService implements Closeable {
private TServer thriftServer;
private Thread serverThread;
private Deque<TicketInfo> invalidTickets = new ConcurrentLinkedDeque<>();
private ServerAddr thisServicesAddr;
/* package */ TestIdentityCallbackService(ServerAddr thisServicesAddr) {
this.thisServicesAddr = thisServicesAddr;
}
@Override
public void close() throws IOException {
thriftServer.stop();
try {
serverThread.join(1000);
} catch (InterruptedException e) {
throw new IOException("Interrupted while waiting for test thread to shut down.", e);
}
if (serverThread.isAlive())
throw new IOException("Could not shutdown test server thread.");
}
private void setThriftServer(TServer thriftServer) {
this.thriftServer = thriftServer;
}
private void setServerThread(Thread serverThread) {
this.serverThread = serverThread;
}
public ServerAddr getThisServicesAddr() {
return thisServicesAddr;
}
public Deque<TicketInfo> getInvalidTickets() {
return invalidTickets;
}
}
/**
* Internal implementation of {@link IdentityCallbackService} which forwards all data to
* {@link TestIdentityCallbackService}.
*/
private static class IdentityCallbackServiceImpl implements IdentityCallbackService.Iface {
private TestIdentityCallbackService res;
public IdentityCallbackServiceImpl(TestIdentityCallbackService res) {
this.res = res;
}
@Override
public void ticketBecameInvalid(TicketInfo ticketInfo) throws TException {
logger.info("Test IdentityCallbackService received info for invalid ticket: {}", ticketInfo);
res.invalidTickets.add(ticketInfo);
}
}
}