/* Copyright (c) 2011 Danish Maritime Authority. * * 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 net.maritimecloud.mms.server.connection.client; import static java.util.Objects.requireNonNull; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import net.maritimecloud.core.id.ServerId; import net.maritimecloud.internal.mms.messages.Close; import net.maritimecloud.internal.mms.messages.Connected; import net.maritimecloud.internal.mms.messages.Hello; import net.maritimecloud.internal.mms.messages.Welcome; import net.maritimecloud.internal.mms.messages.spi.MmsMessage; import net.maritimecloud.internal.mms.transport.AccessLogManager; import net.maritimecloud.message.Message; import net.maritimecloud.message.MessageFormatType; import net.maritimecloud.mms.server.connection.transport.ServerTransport; import net.maritimecloud.mms.server.connection.transport.ServerTransportListener; import net.maritimecloud.net.mms.MmsConnectionClosingCode; /** * * @author Kasper Nielsen */ public class DefaultTransportListener implements ServerTransportListener { static final String ATTACHMENT_CLIENT = "client"; /** The client manager responsible for creating a new client when a hello message is received. */ private final ClientManager clientManager; /** The id of the server. */ private final String serverId; /** The access log manager */ private AccessLogManager accessLogManager; /** * We keep track of clients that have not yet send a hello. This is done in order to be able to close those * connections at some point. Otherwise they will be lying around forever, unless the client closes the socket. */ final Set<ServerTransport> missingHellos = Collections.newSetFromMap(new ConcurrentHashMap<>()); public DefaultTransportListener(ClientManager clientManager, ServerId id, AccessLogManager accessLogManager) { this.clientManager = requireNonNull(clientManager); this.serverId = id.toString(); this.accessLogManager = requireNonNull(accessLogManager); } /** {@inheritDoc} */ @Override public void onClose(ServerTransport t, MmsConnectionClosingCode closingCode) { // if _succesfully_ connected the transport have already been removed. If connection failed. It is most likely // still there so we are just going to remove it to be sure. missingHellos.remove(t); Client client = t.getAttachment(ATTACHMENT_CLIENT, Client.class); if (client != null) { client.onClose(t, closingCode); t.setAttachment(ATTACHMENT_CLIENT, null); // clear attachment } } /** {@inheritDoc} */ @Override public void onMessageReceived(ServerTransport t, MmsMessage message) { updateAccessLog(t, message, true, t.getChannelFormatType()); Message m = message.getM(); // temporary fix if (m instanceof Close) { t.close(MmsConnectionClosingCode.NORMAL.withMessage("Closed normally")); return; } if (m instanceof Welcome) { t.close(MmsConnectionClosingCode.WRONG_MESSAGE.withMessage("A client must not send a Welcome message")); } else if (m instanceof Connected) { t.close(MmsConnectionClosingCode.WRONG_MESSAGE.withMessage("A client must not send a Connected message")); } else { Client client = t.getAttachment(ATTACHMENT_CLIENT, Client.class); if (client == null) { if (m instanceof Hello) { Hello hello = (Hello) m; missingHellos.remove(t); Client newClient = clientManager.onHello(hello, t); if (newClient != null) { t.setAttachment(ATTACHMENT_CLIENT, newClient); t.clientResolved(newClient); } } else { t.close(MmsConnectionClosingCode.WRONG_MESSAGE .withMessage("A Hello message must be sent to succesfully connect, but received a : " + m.getClass().getSimpleName() + " message")); } } else if (m instanceof Hello) { t.close(MmsConnectionClosingCode.WRONG_MESSAGE .withMessage("A client must not send a Hello message more than once")); } else { client.onMessage(t, message); } } } /** {@inheritDoc} */ @Override public void onMessageSent(ServerTransport t, MmsMessage message) { updateAccessLog(t, message, false, t.getChannelFormatType()); } /** * Updates the access log with the given message * * @param t * the server transport * @param msg * the message * @param inbound * inbound or outbound * @param type * the message type */ private void updateAccessLog(ServerTransport t, MmsMessage msg, boolean inbound, MessageFormatType type) { Client client = t.getAttachment(ATTACHMENT_CLIENT, Client.class); String id = client == null ? null : client.getId(); if (id == null && msg.getMessage() instanceof Hello) { // The ATTACHMENT_CLIENT has not been updated yet when 'Hello' is logged // Pending: Find solution for 'Connected' which has the same problem id = ((Hello)msg.getMessage()).getClientId(); } accessLogManager.logMessage(msg, id, inbound, type); } /** {@inheritDoc} */ @Override public void onOpen(ServerTransport t) { // send a Welcome message to the client as the first thing MmsMessage welcome = new MmsMessage(new Welcome().addProtocolVersion(1).setServerId(serverId) .putProperties("implementation", "mmsServer/0.2")); t.sendMessage(welcome); missingHellos.add(t); // add this transport to set of transports waiting for a Hello } }