/*
* Copyright MapR Technologies, 2013
*
* 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 com.mapr.franz.hazel;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import com.hazelcast.core.DistributedTask;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.ISet;
import com.mapr.franz.ProtoSerializable;
import com.mapr.franz.Server;
import com.mapr.franz.catcher.wire.Catcher;
import com.mapr.franz.server.ProtoLogger;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/**
* The kinda broken server.
*/
public class CatcherImpl implements Catcher.CatcherService.BlockingInterface {
// TODO: configuration option
private String basePath;
private static ProtoLogger logger;
private static long serverId;
private Server us;
private HazelcastInstance instance;
// TODO implement some sort of statistics that records (a) number of
// clients, (b) transactions per topic, (c) bytes per topic
public CatcherImpl(Server us, HazelcastInstance instance, String basePath) throws FileNotFoundException {
// this is evil because it prevents running more than one server per JVM
CatcherImpl.serverId = us.getProto().getServerId();
this.us = us;
this.instance = instance;
this.basePath = basePath;
logger = new ProtoLogger(this.basePath);
}
@Override
public Catcher.HelloResponse hello(RpcController controller,
Catcher.Hello request) throws ServiceException {
Catcher.HelloResponse.Builder r = Catcher.HelloResponse.newBuilder()
.setServerId(serverId);
ISet<Server> servers = instance.getSet("servers");
for (Server server : servers) {
System.out.printf("server = %s\n", server);
r.addCluster(server.getProto());
}
r.addAllHost(us.getProto().getHostList());
return r.build();
}
@Override
public Catcher.LogMessageResponse log(RpcController controller,
Catcher.LogMessage request) throws ServiceException {
try {
// forward request, possibly to ourselves via HazelCast
String topic = request.getTopic();
DistributedTask<LogResponse> task = new DistributedTask<>(new LogMessage(request), topic);
instance.getExecutorService().execute(task);
return task.get().getProto();
} catch (InterruptedException | ExecutionException e) {
StringWriter s = new StringWriter();
PrintWriter pw = new PrintWriter(s);
new ClusterStateException("Can't handle request ... can't see rest of cluster", e).printStackTrace(pw);
new ClusterStateException("Can't handle request ... can't see rest of cluster", e).printStackTrace();
pw.close();
return Catcher.LogMessageResponse
.newBuilder()
.setServerId(serverId)
.setSuccessful(false)
.setBackTrace(s.toString())
.build();
}
}
public static class LogMessage extends ProtoSerializable<Catcher.LogMessage> implements Callable<LogResponse> {
public LogMessage(Catcher.LogMessage request) {
super(request);
}
// for serialization framework
public LogMessage() {
super();
}
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
*/
@Override
public LogResponse call() throws IOException {
ByteString payload = getProto().getPayload();
String topic = getProto().getTopic();
logger.write(topic, payload);
return new LogResponse(
Catcher.LogMessageResponse.newBuilder()
.setServerId(serverId)
.setSuccessful(true)
.build()
);
}
@Override
public void readData(DataInput in) throws IOException {
super.readData(in); //To change body of overridden methods use File | Settings | File Templates.
}
@Override
public void writeData(DataOutput out) throws IOException {
super.writeData(out); //To change body of overridden methods use File | Settings | File Templates.
}
@Override
protected Catcher.LogMessage parse(byte[] bytes) throws InvalidProtocolBufferException {
return Catcher.LogMessage.parseFrom(bytes);
}
}
public static class LogResponse extends ProtoSerializable<Catcher.LogMessageResponse> {
public LogResponse(Catcher.LogMessageResponse response) {
super(response);
}
public LogResponse() {
super();
}
@Override
protected Catcher.LogMessageResponse parse(byte[] bytes) throws InvalidProtocolBufferException {
return Catcher.LogMessageResponse.parseFrom(bytes);
}
}
@Override
public Catcher.CloseResponse close(RpcController controller,
Catcher.Close request) throws ServiceException {
return Catcher.CloseResponse.newBuilder().build();
}
private class ClusterStateException extends Exception {
private static final long serialVersionUID = -1473457816312999824L;
public ClusterStateException(String msg, Throwable cause) {
super(msg, cause);
}
}
}