package Communication.Messages.Service;
import Communication.Messages.Handler.MessageHandler;
import Communication.Messages.Handler.Receive;
import Communication.Messages.Handler.Send;
import Communication.Messages.TCP.TCPSession;
import Communication.Messages.UDP.UDPSession;
import org.reflections.Reflections;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentHashMap;
public class MessageFactory
{
private final ConcurrentHashMap messageHandlersByData = new ConcurrentHashMap<>();
private final ConcurrentHashMap messageHandlersByName = new ConcurrentHashMap<>();
private final ConcurrentHashMap sessions = new ConcurrentHashMap<>();
private final ConcurrentHashMap sends = new ConcurrentHashMap<>();
private final ConcurrentHashMap receives = new ConcurrentHashMap<>();
private int maxSize = 0;
private int maxNameLength = 0;
// set default timeout to five minutes
private int sessionTimeoutMilli = 1000 * 60 * 5;
private static MessageFactory instance = null;
private MessageFactory()
{
Reflections reflections = new Reflections();
for (Class c : reflections.getTypesAnnotatedWith(MessageHandler.class))
{
MessageHandler messageHandler = getMessageHandler(c);
if (!this.messageHandlersByData.containsKey(messageHandler.data()))
{
this.messageHandlersByData.put(messageHandler.data(), c);
}
else
{
throw new RuntimeException(this.messageHandlersByData.get(messageHandler.data()).getName() + " handles the same data as " + c.getName());
}
if (!this.messageHandlersByName.containsKey(messageHandler.name()))
{
this.messageHandlersByName.put(messageHandler.name(), c);
}
else
{
throw new RuntimeException(this.messageHandlersByName.get(messageHandler.name()).getName() + " has the same name as " + c.getName());
}
this.maxSize = this.maxSize > messageHandler.maxSize() ? this.maxSize : messageHandler.maxSize();
this.maxNameLength = this.maxNameLength > messageHandler.name().length() ? this.maxNameLength : messageHandler.name().length();
}
}
public static MessageFactory getInstance()
{
if (instance == null)
{
instance = new MessageFactory();
}
return instance;
}
public Class getMessageHandlerClass(Class data)
{
return this.messageHandlersByData.get(data);
}
public Class getMessageHandlerClass(String name)
{
return this.messageHandlersByName.get(name);
}
public int getMaxSize()
{
return this.maxSize;
}
public int getMaxNameLength() {
return this.maxNameLength;
}
public static MessageHandler getMessageHandler(Class c)
{
return (MessageHandler) c.getAnnotation(MessageHandler.class);
}
public MessageSession getSession(Socket sock)
{
MessageSession session;
if ((session = this.sessions.get(sock.getRemoteSocketAddress())) == null)
{
session = new TCPSession(sock);
this.sessions.put(sock.getRemoteSocketAddress(), session);
}
return session;
}
public MessageSession getSession(DatagramSocket sock)
{
MessageSession session;
if ((session = this.sessions.get(sock.getRemoteSocketAddress())) == null)
{
session = new UDPSession(sock);
this.sessions.put(sock.getRemoteSocketAddress(), session);
}
return session;
}
public MessageSession getSession(SocketAddress address)
{
return this.sessions.get(address);
}
public Method getSendMethod(Class messageHandlerClass) throws NoSuchMethodException
{
Method method;
if ((method = this.sends.get(messageHandlerClass)) == null)
{
method = getAnnotatedMethod(messageHandlerClass, Send.class);
this.sends.put(messageHandlerClass, method);
}
return method;
}
public Method getReceiveMethod(Class messageHandlerClass) throws NoSuchMethodException
{
Method method;
if ((method = this.receives.get(messageHandlerClass)) == null)
{
method = getAnnotatedMethod(messageHandlerClass, Receive.class);
this.receives.put(messageHandlerClass, method);
}
return method;
}
private static Method getAnnotatedMethod(Class messageHandlerClass, Class extends Annotation> annotationClass) throws NoSuchMethodException {
Method foundMethod = null;
for (Method method : messageHandlerClass.getMethods())
{
if (method.isAnnotationPresent(annotationClass))
{
foundMethod = method;
break;
}
}
if (foundMethod == null)
{
throw new NoSuchMethodException(annotationClass.getName() + " not present for Any method in " + messageHandlerClass.getName());
}
return foundMethod;
}
}