/*
* 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 org.rakam.kume;
import org.rakam.kume.service.Service;
import org.rakam.kume.transport.LocalOperationContext;
import org.rakam.kume.transport.Packet;
import org.rakam.kume.transport.Request;
import org.rakam.kume.util.ThrowableNioEventLoopGroup;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkState;
/**
* Instead of sending packets using network connections, it simply find the local instance and send it directly to the related service.
* It works only for Kume instances created on same JVM. Since it avoids network operations, it's mostly used for testing purposes.
*/
public class NoNetworkTransport implements Transport {
private final Map<Member, NoNetworkTransport> bus;
private ThrowableNioEventLoopGroup requestExecutor;
private List<Service> services;
private Member localMember;
private Map<Member, MemberChannel> channels;
public NoNetworkTransport(Member localMember) {
this.localMember = localMember;
bus = new ConcurrentHashMap<>();
bus.put(localMember, this);
}
public NoNetworkTransport setContext(ThrowableNioEventLoopGroup requestExecutor, List<Service> services, Member localMember) {
checkState(localMember.equals(this.localMember), "localMember doesn't match");
this.requestExecutor = requestExecutor;
this.services = services;
this.localMember = localMember;
return this;
}
public NoNetworkTransport addMember(NoNetworkTransport transport) {
bus.put(transport.localMember, transport);
return this;
}
public Member getLocalMember() {
return localMember;
}
@Override
public synchronized MemberChannel connect(Member member) throws InterruptedException {
if (channels == null) {
throw new IllegalStateException();
}
return channels.compute(member, (key, value) -> new NoNetworkChannel(member));
}
@Override
public void close() {
channels.clear();
}
@Override
public synchronized void initialize() {
channels = new ConcurrentHashMap<>();
}
public class NoNetworkChannel implements MemberChannel {
private final Member member;
public NoNetworkChannel(Member member) {
this.member = member;
}
@Override
public CompletableFuture ask(Packet message) {
CompletableFuture future = new CompletableFuture<>();
LocalOperationContext ctx1 = new LocalOperationContext(future, message.service, localMember);
if (message.data instanceof Request) {
if(!(message.data instanceof HeartbeatRequest)) {
int i = 0;
}
bus.get(member).services.get(message.service).handle(requestExecutor, ctx1, (Request) message.data);
} else {
bus.get(member).services.get(message.service).handle(requestExecutor, ctx1, message.data);
}
return future;
}
@Override
public void send(Packet message) {
LocalOperationContext ctx1 = new LocalOperationContext(null, message.service, localMember);
if (message.data instanceof Request) {
services.get(message.service).handle(requestExecutor, ctx1, (Request) message.data);
} else {
services.get(message.service).handle(requestExecutor, ctx1, message.data);
}
}
@Override
public void close() throws InterruptedException {
channels.remove(this);
}
}
}