diff --git a/application/src/main/java/javasabr/mqtt/application/config/MqttBrokerConfig.java b/application/src/main/java/javasabr/mqtt/application/config/MqttBrokerConfig.java deleted file mode 100644 index 4e49cf4f..00000000 --- a/application/src/main/java/javasabr/mqtt/application/config/MqttBrokerConfig.java +++ /dev/null @@ -1,138 +0,0 @@ -package javasabr.mqtt.application.config; - -import javasabr.mqtt.network.handler.MqttClientReleaseHandler; -import javasabr.mqtt.network.handler.PacketInHandler; -import javasabr.mqtt.network.handler.PublishInHandler; -import javasabr.mqtt.network.packet.PacketType; -import javasabr.mqtt.service.AuthenticationService; -import javasabr.mqtt.service.ClientIdRegistry; -import javasabr.mqtt.service.CredentialSource; -import javasabr.mqtt.service.MqttSessionService; -import javasabr.mqtt.service.PublishingService; -import javasabr.mqtt.service.SubscriptionService; -import javasabr.mqtt.service.handler.client.DefaultMqttClientReleaseHandler; -import javasabr.mqtt.service.handler.in.ConnectInPacketHandler; -import javasabr.mqtt.service.handler.in.DisconnetInPacketHandler; -import javasabr.mqtt.service.handler.in.PublishAckInPacketHandler; -import javasabr.mqtt.service.handler.in.PublishCompleteInPacketHandler; -import javasabr.mqtt.service.handler.in.PublishInPacketHandler; -import javasabr.mqtt.service.handler.in.PublishReceiveInPacketHandler; -import javasabr.mqtt.service.handler.in.PublishReleaseInPacketHandler; -import javasabr.mqtt.service.handler.in.SubscribeInPacketHandler; -import javasabr.mqtt.service.handler.in.UnsubscribeInPacketHandler; -import javasabr.mqtt.service.handler.publish.in.Qos0PublishInHandler; -import javasabr.mqtt.service.handler.publish.in.Qos1PublishInHandler; -import javasabr.mqtt.service.handler.publish.in.Qos2PublishInHandler; -import javasabr.mqtt.service.handler.publish.out.PublishOutHandler; -import javasabr.mqtt.service.handler.publish.out.Qos0PublishOutHandler; -import javasabr.mqtt.service.handler.publish.out.Qos1PublishOutHandler; -import javasabr.mqtt.service.handler.publish.out.Qos2PublishOutHandler; -import javasabr.mqtt.service.impl.DefaultPublishingService; -import javasabr.mqtt.service.impl.FileCredentialsSource; -import javasabr.mqtt.service.impl.InMemoryClientIdRegistry; -import javasabr.mqtt.service.impl.InMemoryMqttSessionService; -import javasabr.mqtt.service.impl.SimpleAuthenticationService; -import javasabr.mqtt.service.impl.SimpleSubscriptionService; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; - -@Log4j2 -@Configuration -@RequiredArgsConstructor -public class MqttBrokerConfig { - - private final Environment env; - - @Bean - ClientIdRegistry clientIdRegistry() { - return new InMemoryClientIdRegistry( - env.getProperty( - "client.id.available.chars", - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"), - env.getProperty("client.id.max.length", int.class, 36)); - } - - @Bean - MqttSessionService mqttSessionService() { - return new InMemoryMqttSessionService(env.getProperty("sessions.clean.thread.interval", int.class, 60000)); - } - - @Bean - CredentialSource credentialSource() { - return new FileCredentialsSource(env.getProperty("credentials.source.file.name", "credentials")); - } - - @Bean - AuthenticationService authenticationService(CredentialSource credentialSource) { - return new SimpleAuthenticationService( - credentialSource, - env.getProperty("authentication.allow.anonymous", boolean.class, false)); - } - - @Bean - PacketInHandler[] packetHandlers( - AuthenticationService authenticationService, - ClientIdRegistry clientIdRegistry, - SubscriptionService subscriptionService, - PublishingService publishingService, - MqttSessionService mqttSessionService) { - - var handlers = new PacketInHandler[PacketType.INVALID.ordinal()]; - handlers[PacketType.CONNECT.ordinal()] = new ConnectInPacketHandler( - clientIdRegistry, - authenticationService, - mqttSessionService, - subscriptionService); - handlers[PacketType.SUBSCRIBE.ordinal()] = new SubscribeInPacketHandler(subscriptionService); - handlers[PacketType.UNSUBSCRIBE.ordinal()] = new UnsubscribeInPacketHandler(subscriptionService); - handlers[PacketType.PUBLISH.ordinal()] = new PublishInPacketHandler(publishingService); - handlers[PacketType.DISCONNECT.ordinal()] = new DisconnetInPacketHandler(); - handlers[PacketType.PUBLISH_ACK.ordinal()] = new PublishAckInPacketHandler(); - handlers[PacketType.PUBLISH_RECEIVED.ordinal()] = new PublishReceiveInPacketHandler(); - handlers[PacketType.PUBLISH_RELEASED.ordinal()] = new PublishReleaseInPacketHandler(); - handlers[PacketType.PUBLISH_COMPLETED.ordinal()] = new PublishCompleteInPacketHandler(); - - return handlers; - } - - @Bean - MqttClientReleaseHandler mqttClientReleaseHandler( - ClientIdRegistry clientIdRegistry, - MqttSessionService mqttSessionService, - SubscriptionService subscriptionService) { - return new DefaultMqttClientReleaseHandler(clientIdRegistry, mqttSessionService, subscriptionService); - } - - @Bean - SubscriptionService subscriptionService() { - return new SimpleSubscriptionService(); - } - - @Bean - PublishOutHandler[] publishOutHandlers() { - return new PublishOutHandler[]{ - new Qos0PublishOutHandler(), - new Qos1PublishOutHandler(), - new Qos2PublishOutHandler() - }; - } - - @Bean - PublishInHandler[] publishInHandlers( - SubscriptionService subscriptionService, - PublishOutHandler[] publishOutHandlers) { - return new PublishInHandler[]{ - new Qos0PublishInHandler(subscriptionService, publishOutHandlers), - new Qos1PublishInHandler(subscriptionService, publishOutHandlers), - new Qos2PublishInHandler(subscriptionService, publishOutHandlers) - }; - } - - @Bean - PublishingService publishingService(PublishInHandler[] publishInHandlers) { - return new DefaultPublishingService(publishInHandlers); - } -} diff --git a/application/src/main/java/javasabr/mqtt/application/config/MqttNetworkConfig.java b/application/src/main/java/javasabr/mqtt/application/config/MqttNetworkConfig.java deleted file mode 100644 index b81f0710..00000000 --- a/application/src/main/java/javasabr/mqtt/application/config/MqttNetworkConfig.java +++ /dev/null @@ -1,311 +0,0 @@ -package javasabr.mqtt.application.config; - -import java.net.InetSocketAddress; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import javasabr.mqtt.model.MqttProperties; -import javasabr.mqtt.model.MqttServerConnectionConfig; -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.client.ExternalMqttClient; -import javasabr.mqtt.network.client.InternalMqttClient; -import javasabr.mqtt.network.handler.MqttClientReleaseHandler; -import javasabr.mqtt.network.handler.PacketInHandler; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Network; -import javasabr.rlib.network.NetworkFactory; -import javasabr.rlib.network.ServerNetworkConfig; -import javasabr.rlib.network.ServerNetworkConfig.SimpleServerNetworkConfig; -import javasabr.rlib.network.impl.DefaultBufferAllocator; -import javasabr.rlib.network.server.ServerNetwork; -import lombok.CustomLog; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; - -@CustomLog -@Configuration -@RequiredArgsConstructor -public class MqttNetworkConfig { - - private interface ChannelFactory extends - BiFunction, AsynchronousSocketChannel, MqttConnection> {} - - private final Environment env; - - @Bean - ServerNetworkConfig internalNetworkConfig() { - return SimpleServerNetworkConfig - .builder() - .readBufferSize(env.getProperty("mqtt.internal.network.read.buffer.size", int.class, 2048)) - .pendingBufferSize(env.getProperty("mqtt.internal.network.pending.buffer.size", int.class, 4096)) - .writeBufferSize(env.getProperty("mqtt.internal.network.write.buffer.size", int.class, 2048)) - .threadGroupName("InternalNetwork") - .threadGroupMaxSize(env.getProperty("mqtt.internal.network.thread.count", int.class, 1)) - .build(); - } - - @Bean - ServerNetworkConfig externalNetworkConfig() { - return SimpleServerNetworkConfig - .builder() - .readBufferSize(env.getProperty("mqtt.external.network.read.buffer.size", int.class, 100)) - .pendingBufferSize(env.getProperty("mqtt.external.network.pending.buffer.size", int.class, 200)) - .writeBufferSize(env.getProperty("mqtt.external.network.write.buffer.size", int.class, 100)) - .threadGroupName("ExternalNetwork") - .threadGroupMaxSize(env.getProperty("mqtt.external.network.thread.count", int.class, 1)) - .build(); - } - - @Bean - BufferAllocator internalBufferAllocator(ServerNetworkConfig internalNetworkConfig) { - return new DefaultBufferAllocator(internalNetworkConfig); - } - - @Bean - BufferAllocator externalBufferAllocator(ServerNetworkConfig externalNetworkConfig) { - return new DefaultBufferAllocator(externalNetworkConfig); - } - - @Bean - ServerNetwork externalNetwork( - ServerNetworkConfig externalNetworkConfig, - BufferAllocator externalBufferAllocator, - MqttServerConnectionConfig externalConnectionConfig, - PacketInHandler[] packetHandlers, - MqttClientReleaseHandler mqttClientReleaseHandler) { - return NetworkFactory.serverNetwork( - externalNetworkConfig, - externalConnectionFactory( - externalBufferAllocator, - externalConnectionConfig, - packetHandlers, - mqttClientReleaseHandler)); - } - - @Bean - ServerNetwork internalNetwork( - ServerNetworkConfig internalNetworkConfig, - BufferAllocator internalBufferAllocator, - MqttServerConnectionConfig internalConnectionConfig, - PacketInHandler[] packetHandlers, - MqttClientReleaseHandler mqttClientReleaseHandler) { - return NetworkFactory.serverNetwork( - internalNetworkConfig, - internalConnectionFactory( - internalBufferAllocator, - internalConnectionConfig, - packetHandlers, - mqttClientReleaseHandler)); - } - - @Bean - InetSocketAddress externalNetworkAddress( - ServerNetwork externalNetwork, - Consumer externalConnectionConsumer) { - - var address = new InetSocketAddress( - env.getProperty("mqtt.external.network.host", "localhost"), - env.getProperty("mqtt.external.network.port", int.class, 1883)); - - externalNetwork.start(address); - externalNetwork.onAccept(externalConnectionConsumer); - - return address; - } - - @Bean - InetSocketAddress internalNetworkAddress( - ServerNetwork internalNetwork, - Consumer internalConnectionConsumer) { - - var address = new InetSocketAddress( - env.getProperty("mqtt.internal.network.host", "localhost"), - env.getProperty("mqtt.internal.network.port", int.class, 11883)); - - internalNetwork.start(address); - internalNetwork.onAccept(internalConnectionConsumer); - - return address; - } - - @Bean - Consumer externalConnectionConsumer() { - return mqttConnection -> { - log.info(mqttConnection.remoteAddress(), "[%s] Accepted external connection"::formatted); - var client = (UnsafeMqttClient) mqttConnection.client(); - mqttConnection.onReceive((conn, packet) -> client.handle((MqttReadablePacket) packet)); - }; - } - - @Bean - Consumer internalConnectionConsumer() { - return mqttConnection -> { - log.info(mqttConnection.remoteAddress(), "[%s] Accepted internal connection"::formatted); - var client = (UnsafeMqttClient) mqttConnection.client(); - mqttConnection.onReceive((conn, packet) -> client.handle((MqttReadablePacket) packet)); - }; - } - - @Bean - MqttServerConnectionConfig externalConnectionConfig() { - return new MqttServerConnectionConfig( - QoS.of(env.getProperty("mqtt.connection.max.qos", int.class, 2)), - env.getProperty( - "mqtt.external.connection.max.packet.size", - int.class, - MqttProperties.MAXIMUM_PACKET_SIZE_DEFAULT), - env.getProperty( - "mqtt.external.connection.max.string.length", - int.class, - MqttProperties.MAXIMUM_STRING_LENGTH), - env.getProperty( - "mqtt.external.connection.max.binary.size", - int.class, - MqttProperties.MAXIMUM_BINARY_SIZE), - env.getProperty( - "mqtt.external.connection.min.keep.alive", - int.class, - MqttProperties.SERVER_KEEP_ALIVE_DEFAULT), - env.getProperty( - "mqtt.external.connection.receive.maximum", - int.class, - MqttProperties.RECEIVE_MAXIMUM_DEFAULT), - env.getProperty( - "mqtt.external.connection.topic.alias.maximum", - int.class, - MqttProperties.TOPIC_ALIAS_MAXIMUM_DISABLED), - env.getProperty( - "mqtt.external.connection.default.session.expiration.time", - long.class, - MqttProperties.SESSION_EXPIRY_INTERVAL_DEFAULT), - env.getProperty( - "mqtt.external.connection.keep.alive.enabled", - boolean.class, - MqttProperties.KEEP_ALIVE_ENABLED_DEFAULT), - env.getProperty( - "mqtt.external.connection.sessions.enabled", - boolean.class, - MqttProperties.SESSIONS_ENABLED_DEFAULT), - env.getProperty( - "mqtt.external.connection.retain.available", - boolean.class, - MqttProperties.RETAIN_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.external.connection.wildcard.subscription.available", - boolean.class, - MqttProperties.WILDCARD_SUBSCRIPTION_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.external.connection.subscription.id.available", - boolean.class, - MqttProperties.SUBSCRIPTION_IDENTIFIER_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.external.connection.shared.subscription.available", - boolean.class, - MqttProperties.SHARED_SUBSCRIPTION_AVAILABLE_DEFAULT)); - } - - @Bean - MqttServerConnectionConfig internalConnectionConfig() { - return new MqttServerConnectionConfig( - QoS.of(env.getProperty("mqtt.internal.connection.max.qos", int.class, 2)), - env.getProperty( - "mqtt.internal.connection.max.packet.size", - int.class, - MqttProperties.MAXIMUM_PACKET_SIZE_DEFAULT), - env.getProperty( - "mqtt.internal.connection.max.string.length", - int.class, - MqttProperties.MAXIMUM_STRING_LENGTH), - env.getProperty( - "mqtt.internal.connection.max.binary.size", - int.class, - MqttProperties.MAXIMUM_BINARY_SIZE), - env.getProperty( - "mqtt.internal.connection.min.keep.alive", - int.class, - MqttProperties.SERVER_KEEP_ALIVE_DEFAULT), - env.getProperty( - "mqtt.internal.connection.receive.maximum", - int.class, - MqttProperties.RECEIVE_MAXIMUM_DEFAULT), - env.getProperty( - "mqtt.internal.connection.topic.alias.maximum", - int.class, - MqttProperties.TOPIC_ALIAS_MAXIMUM_DISABLED), - env.getProperty( - "mqtt.internal.connection.default.session.expiration.time", - long.class, - MqttProperties.SESSION_EXPIRY_INTERVAL_DEFAULT), - env.getProperty( - "mqtt.internal.connection.keep.alive.enabled", - boolean.class, - MqttProperties.KEEP_ALIVE_ENABLED_DEFAULT), - env.getProperty( - "mqtt.internal.connection.sessions.enabled", - boolean.class, - MqttProperties.SESSIONS_ENABLED_DEFAULT), - env.getProperty( - "mqtt.internal.connection.retain.available", - boolean.class, - MqttProperties.RETAIN_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.internal.connection.wildcard.subscription.available", - boolean.class, - MqttProperties.WILDCARD_SUBSCRIPTION_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.internal.connection.subscription.id.available", - boolean.class, - MqttProperties.SUBSCRIPTION_IDENTIFIER_AVAILABLE_DEFAULT), - env.getProperty( - "mqtt.internal.connection.shared.subscription.available", - boolean.class, - MqttProperties.SHARED_SUBSCRIPTION_AVAILABLE_DEFAULT)); - } - - private ChannelFactory externalConnectionFactory( - BufferAllocator bufferAllocator, - MqttServerConnectionConfig connectionConfig, - PacketInHandler[] packetHandlers, - MqttClientReleaseHandler releaseHandler) { - return connectionFactory( - bufferAllocator, - connectionConfig, - packetHandlers, - releaseHandler, - ExternalMqttClient::new); - } - - private ChannelFactory internalConnectionFactory( - BufferAllocator bufferAllocator, - MqttServerConnectionConfig connectionConfig, - PacketInHandler[] packetHandlers, - MqttClientReleaseHandler releaseHandler) { - return connectionFactory( - bufferAllocator, - connectionConfig, - packetHandlers, - releaseHandler, - InternalMqttClient::new); - } - - private ChannelFactory connectionFactory( - BufferAllocator bufferAllocator, - MqttServerConnectionConfig connectionConfig, - PacketInHandler[] packetHandlers, - MqttClientReleaseHandler releaseHandler, - BiFunction clientFactory) { - return (network, channel) -> new MqttConnection( - network, - channel, - bufferAllocator, - 100, - packetHandlers, - connectionConfig, - mqttConnection -> clientFactory.apply(mqttConnection, releaseHandler)); - } -} diff --git a/application/src/main/java/javasabr/mqtt/application/MqttBrokerApplication.java b/application/src/main/java/javasabr/mqtt/broker/application/MqttBrokerApplication.java similarity index 53% rename from application/src/main/java/javasabr/mqtt/application/MqttBrokerApplication.java rename to application/src/main/java/javasabr/mqtt/broker/application/MqttBrokerApplication.java index 3ffd9e45..f5500602 100644 --- a/application/src/main/java/javasabr/mqtt/application/MqttBrokerApplication.java +++ b/application/src/main/java/javasabr/mqtt/broker/application/MqttBrokerApplication.java @@ -1,18 +1,14 @@ -package javasabr.mqtt.application; +package javasabr.mqtt.broker.application; -import javasabr.mqtt.application.config.MqttBrokerConfig; -import javasabr.mqtt.application.config.MqttNetworkConfig; +import javasabr.mqtt.broker.application.config.MqttBrokerSpringConfig; import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; -import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -@Configuration -@RequiredArgsConstructor @Import({ - MqttBrokerConfig.class, - MqttNetworkConfig.class + MqttBrokerSpringConfig.class }) +@RequiredArgsConstructor public class MqttBrokerApplication { static void main(String[] args) { SpringApplication.run(MqttBrokerApplication.class, args); diff --git a/application/src/main/java/javasabr/mqtt/broker/application/config/MqttBrokerSpringConfig.java b/application/src/main/java/javasabr/mqtt/broker/application/config/MqttBrokerSpringConfig.java new file mode 100644 index 00000000..a087aa1c --- /dev/null +++ b/application/src/main/java/javasabr/mqtt/broker/application/config/MqttBrokerSpringConfig.java @@ -0,0 +1,365 @@ +package javasabr.mqtt.broker.application.config; + +import java.net.InetSocketAddress; +import java.util.Collection; +import javasabr.mqtt.model.MqttProperties; +import javasabr.mqtt.model.MqttServerConnectionConfig; +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.MqttClientFactory; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.MqttConnectionFactory; +import javasabr.mqtt.network.handler.MqttClientReleaseHandler; +import javasabr.mqtt.service.AuthenticationService; +import javasabr.mqtt.service.ClientIdRegistry; +import javasabr.mqtt.service.ConnectionService; +import javasabr.mqtt.service.CredentialSource; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.PublishReceivingService; +import javasabr.mqtt.service.SessionService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.handler.client.ExternalMqttClientReleaseHandler; +import javasabr.mqtt.service.impl.DefaultConnectionService; +import javasabr.mqtt.service.impl.DefaultMessageOutFactoryService; +import javasabr.mqtt.service.impl.DefaultMqttConnectionFactory; +import javasabr.mqtt.service.impl.DefaultPublishDeliveringService; +import javasabr.mqtt.service.impl.DefaultPublishReceivingService; +import javasabr.mqtt.service.impl.ExternalMqttClientFactory; +import javasabr.mqtt.service.impl.FileCredentialsSource; +import javasabr.mqtt.service.impl.InMemoryClientIdRegistry; +import javasabr.mqtt.service.impl.InMemorySessionService; +import javasabr.mqtt.service.impl.SimpleAuthenticationService; +import javasabr.mqtt.service.impl.SimpleSubscriptionService; +import javasabr.mqtt.service.message.handler.MqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.ConnectInMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.DisconnectMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.PublishAckMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.PublishCompleteMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.PublishMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.PublishReceiveMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.PublishReleaseMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.SubscribeMqttInMessageHandler; +import javasabr.mqtt.service.message.handler.impl.UnsubscribeMqttInMessageHandler; +import javasabr.mqtt.service.message.out.factory.Mqtt311MessageOutFactory; +import javasabr.mqtt.service.message.out.factory.Mqtt5MessageOutFactory; +import javasabr.mqtt.service.message.out.factory.MqttMessageOutFactory; +import javasabr.mqtt.service.publish.handler.MqttPublishInMessageHandler; +import javasabr.mqtt.service.publish.handler.MqttPublishOutMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos0MqttPublishInMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos0MqttPublishOutMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos1MqttPublishInMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos1MqttPublishOutMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos2MqttPublishInMessageHandler; +import javasabr.mqtt.service.publish.handler.impl.Qos2MqttPublishOutMessageHandler; +import javasabr.rlib.network.NetworkFactory; +import javasabr.rlib.network.ServerNetworkConfig; +import javasabr.rlib.network.server.ServerNetwork; +import lombok.CustomLog; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@CustomLog +@Configuration(proxyBeanMethods = false) +public class MqttBrokerSpringConfig { + + @Bean + ClientIdRegistry clientIdRegistry(Environment env) { + return new InMemoryClientIdRegistry( + env.getProperty( + "client.id.available.chars", + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"), + env.getProperty("client.id.max.length", int.class, 36)); + } + + @Bean + SessionService mqttSessionService( + @Value("${sessions.clean.thread.interval:60000}") int cleanInterval) { + return new InMemorySessionService(cleanInterval); + } + + @Bean + CredentialSource credentialSource( + @Value("${credentials.source.file.name:credentials}") String fileName) { + return new FileCredentialsSource(fileName); + } + + @Bean + AuthenticationService authenticationService( + CredentialSource credentialSource, + @Value("${authentication.allow.anonymous:false}") boolean allowAnonymousAuth) { + return new SimpleAuthenticationService(credentialSource, allowAnonymousAuth); + } + + @Bean + SubscriptionService subscriptionService() { + return new SimpleSubscriptionService(); + } + + @Bean + MqttMessageOutFactory mqtt311MessageOutFactory() { + return new Mqtt311MessageOutFactory(); + } + + @Bean + MqttMessageOutFactory mqtt5MessageOutFactory() { + return new Mqtt5MessageOutFactory(); + } + + @Bean + MessageOutFactoryService mqttMessageOutFactoryService( + Collection knownFactories) { + return new DefaultMessageOutFactoryService(knownFactories); + } + + @Bean + MqttInMessageHandler connectInMqttInMessageHandler( + ClientIdRegistry clientIdRegistry, + AuthenticationService authenticationService, + SessionService sessionService, + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new ConnectInMqttInMessageHandler( + clientIdRegistry, + authenticationService, + sessionService, + subscriptionService, + messageOutFactoryService); + } + + @Bean + MqttInMessageHandler publishAckMqttInMessageHandler() { + return new PublishAckMqttInMessageHandler(); + } + + @Bean + MqttInMessageHandler publishCompleteMqttInMessageHandler() { + return new PublishCompleteMqttInMessageHandler(); + } + + @Bean + MqttInMessageHandler publishMqttInMessageHandler(PublishReceivingService publishReceivingService) { + return new PublishMqttInMessageHandler(publishReceivingService); + } + + @Bean + MqttInMessageHandler publishReceiveMqttInMessageHandler() { + return new PublishReceiveMqttInMessageHandler(); + } + + @Bean + MqttInMessageHandler publishReleaseMqttInMessageHandler() { + return new PublishReleaseMqttInMessageHandler(); + } + + @Bean + MqttInMessageHandler disconnectMqttInMessageHandler() { + return new DisconnectMqttInMessageHandler(); + } + + @Bean + MqttInMessageHandler subscribeMqttInMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new SubscribeMqttInMessageHandler(subscriptionService, messageOutFactoryService); + } + + @Bean + MqttInMessageHandler unsubscribeMqttInMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new UnsubscribeMqttInMessageHandler(subscriptionService, messageOutFactoryService); + } + + @Bean + ConnectionService mqttConnectionService(Collection inMessageHandlers) { + return new DefaultConnectionService(inMessageHandlers); + } + + @Bean + MqttPublishOutMessageHandler qos0MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new Qos0MqttPublishOutMessageHandler(subscriptionService, messageOutFactoryService); + } + + @Bean + MqttPublishOutMessageHandler qos1MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new Qos1MqttPublishOutMessageHandler(subscriptionService, messageOutFactoryService); + } + + @Bean + MqttPublishOutMessageHandler qos2MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + return new Qos2MqttPublishOutMessageHandler(subscriptionService, messageOutFactoryService); + } + + @Bean + PublishDeliveringService publishDeliveringService( + Collection knownPublishOutHandlers) { + return new DefaultPublishDeliveringService(knownPublishOutHandlers); + } + + @Bean + MqttPublishInMessageHandler qos0MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService) { + return new Qos0MqttPublishInMessageHandler(subscriptionService, publishDeliveringService); + } + + @Bean + MqttPublishInMessageHandler qos1MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService, + MessageOutFactoryService messageOutFactoryService) { + return new Qos1MqttPublishInMessageHandler( + subscriptionService, + publishDeliveringService, + messageOutFactoryService); + } + + @Bean + MqttPublishInMessageHandler qos2MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService, + MessageOutFactoryService messageOutFactoryService) { + return new Qos2MqttPublishInMessageHandler( + subscriptionService, + publishDeliveringService, + messageOutFactoryService); + } + + @Bean + PublishReceivingService publishReceivingService( + Collection knownPublishInHandlers) { + return new DefaultPublishReceivingService(knownPublishInHandlers); + } + + @Bean + MqttClientReleaseHandler externalMqttClientReleaseHandler( + ClientIdRegistry clientIdRegistry, + SessionService sessionService, + SubscriptionService subscriptionService) { + return new ExternalMqttClientReleaseHandler(clientIdRegistry, sessionService, subscriptionService); + } + + @Bean + MqttServerConnectionConfig externalConnectionConfig(Environment env) { + return new MqttServerConnectionConfig( + QoS.of(env.getProperty("mqtt.connection.max.qos", int.class, 2)), + env.getProperty( + "mqtt.external.connection.max.packet.size", + int.class, + MqttProperties.MAXIMUM_PACKET_SIZE_DEFAULT), + env.getProperty( + "mqtt.external.connection.max.string.length", + int.class, + MqttProperties.MAXIMUM_STRING_LENGTH), + env.getProperty( + "mqtt.external.connection.max.binary.size", + int.class, + MqttProperties.MAXIMUM_BINARY_SIZE), + env.getProperty( + "mqtt.external.connection.min.keep.alive", + int.class, + MqttProperties.SERVER_KEEP_ALIVE_DEFAULT), + env.getProperty( + "mqtt.external.connection.receive.maximum", + int.class, + MqttProperties.RECEIVE_MAXIMUM_DEFAULT), + env.getProperty( + "mqtt.external.connection.topic.alias.maximum", + int.class, + MqttProperties.TOPIC_ALIAS_MAXIMUM_DISABLED), + env.getProperty( + "mqtt.external.connection.default.session.expiration.time", + long.class, + MqttProperties.SESSION_EXPIRY_INTERVAL_DEFAULT), + env.getProperty( + "mqtt.external.connection.keep.alive.enabled", + boolean.class, + MqttProperties.KEEP_ALIVE_ENABLED_DEFAULT), + env.getProperty( + "mqtt.external.connection.sessions.enabled", + boolean.class, + MqttProperties.SESSIONS_ENABLED_DEFAULT), + env.getProperty( + "mqtt.external.connection.retain.available", + boolean.class, + MqttProperties.RETAIN_AVAILABLE_DEFAULT), + env.getProperty( + "mqtt.external.connection.wildcard.subscription.available", + boolean.class, + MqttProperties.WILDCARD_SUBSCRIPTION_AVAILABLE_DEFAULT), + env.getProperty( + "mqtt.external.connection.subscription.id.available", + boolean.class, + MqttProperties.SUBSCRIPTION_IDENTIFIER_AVAILABLE_DEFAULT), + env.getProperty( + "mqtt.external.connection.shared.subscription.available", + boolean.class, + MqttProperties.SHARED_SUBSCRIPTION_AVAILABLE_DEFAULT)); + } + + @Bean + ServerNetworkConfig externalNetworkConfig( + @Value("${mqtt.external.network.read.buffer.size:512}") int readBufferSize, + @Value("${mqtt.external.network.pending.buffer.size:1024}") int pendingBufferSize, + @Value("${mqtt.external.network.write.buffer.size:512}") int writeBufferSize, + @Value("${mqtt.external.network.thread.group.name:ExternalNetwork}") String threadGroupName, + @Value("${mqtt.external.network.thread.count:1}") int threadGroupMaxSize) { + return ServerNetworkConfig.SimpleServerNetworkConfig + .builder() + .readBufferSize(readBufferSize) + .pendingBufferSize(pendingBufferSize) + .writeBufferSize(writeBufferSize) + .threadGroupName(threadGroupName) + .threadGroupMaxSize(threadGroupMaxSize) + .build(); + } + + @Bean + MqttClientFactory externalClientFactory(MqttClientReleaseHandler externalMqttClientReleaseHandler) { + return new ExternalMqttClientFactory(externalMqttClientReleaseHandler); + } + + @Bean + MqttConnectionFactory externalConnectionFactory( + MqttServerConnectionConfig externalServerConnectionConfig, + MqttClientFactory externalClientFactory, + @Value("${mqtt.external.connection.max.packets.by.read:100}") int maxPacketsByRead) { + return new DefaultMqttConnectionFactory(externalServerConnectionConfig, externalClientFactory, maxPacketsByRead); + } + + @Bean + InetSocketAddress externalNetworkAddress( + @Value("${mqtt.external.network.host:localhost}") String host, + @Value("${mqtt.external.network.port:1883}") int port) { + return new InetSocketAddress(host, port); + } + + @Bean + ServerNetwork externalNetwork( + ServerNetworkConfig externalNetworkConfig, + MqttConnectionFactory externalConnectionFactory) { + return NetworkFactory.serverNetwork(externalNetworkConfig, externalConnectionFactory::newConnection); + } + + @Bean + ApplicationListener externalNetworkStarter( + ServerNetwork externalNetwork, + ConnectionService connectionService, + InetSocketAddress externalNetworkAddress) { + return _ -> { + externalNetwork.start(externalNetworkAddress); + externalNetwork.onAccept(connectionService::processAcceptedConnection); + log.info(externalNetworkAddress, "Started external MQTT network by address:[%s]"::formatted); + }; + } +} diff --git a/network/src/main/java/javasabr/mqtt/network/handler/packet/in/package-info.java b/application/src/main/java/javasabr/mqtt/broker/application/config/package-info.java similarity index 53% rename from network/src/main/java/javasabr/mqtt/network/handler/packet/in/package-info.java rename to application/src/main/java/javasabr/mqtt/broker/application/config/package-info.java index b5042af1..5eb34fed 100644 --- a/network/src/main/java/javasabr/mqtt/network/handler/packet/in/package-info.java +++ b/application/src/main/java/javasabr/mqtt/broker/application/config/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package javasabr.mqtt.network.handler.packet.in; +package javasabr.mqtt.broker.application.config; import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/application/src/test/groovy/javasabr/mqtt/application/integration/PublishRetryTest.groovy b/application/src/test/groovy/javasabr/mqtt/application/integration/PublishRetryTest.groovy index 15d3ecae..e2f5486d 100644 --- a/application/src/test/groovy/javasabr/mqtt/application/integration/PublishRetryTest.groovy +++ b/application/src/test/groovy/javasabr/mqtt/application/integration/PublishRetryTest.groovy @@ -12,14 +12,14 @@ import javasabr.mqtt.network.packet.in.PublishInPacket import javasabr.mqtt.network.packet.in.PublishReleaseInPacket import javasabr.mqtt.network.packet.in.SubscribeAckInPacket import javasabr.mqtt.network.packet.out.* -import javasabr.mqtt.service.MqttSessionService +import javasabr.mqtt.service.SessionService import javasabr.rlib.collections.array.Array import org.springframework.beans.factory.annotation.Autowired class PublishRetryTest extends IntegrationSpecification { @Autowired - MqttSessionService mqttSessionService + SessionService mqttSessionService def "mqtt 3.1.1 client should be generate session with one pending QoS 1 packet"() { given: diff --git a/application/src/test/groovy/javasabr/mqtt/application/integration/config/MqttBrokerTestConfig.groovy b/application/src/test/groovy/javasabr/mqtt/application/integration/config/MqttBrokerTestConfig.groovy index e7a9dddf..69cee195 100644 --- a/application/src/test/groovy/javasabr/mqtt/application/integration/config/MqttBrokerTestConfig.groovy +++ b/application/src/test/groovy/javasabr/mqtt/application/integration/config/MqttBrokerTestConfig.groovy @@ -1,40 +1,49 @@ package javasabr.mqtt.application.integration.config -import javasabr.mqtt.application.config.MqttBrokerConfig -import javasabr.mqtt.application.config.MqttNetworkConfig +import javasabr.mqtt.broker.application.config.MqttBrokerSpringConfig import javasabr.mqtt.network.MqttConnection +import javasabr.mqtt.service.ConnectionService import javasabr.rlib.network.server.ServerNetwork -import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.boot.context.event.ApplicationStartedEvent +import org.springframework.context.ApplicationListener import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.PropertySource -import java.util.function.Consumer +import java.util.concurrent.ThreadLocalRandom @Import([ - MqttBrokerConfig, - MqttNetworkConfig + MqttBrokerSpringConfig, ]) @Configuration(proxyBeanMethods = false) @PropertySource("classpath:application-test.properties") class MqttBrokerTestConfig { @Bean - InetSocketAddress externalNetworkAddress( - @Qualifier("externalNetwork") ServerNetwork externalNetwork, - @Qualifier("externalConnectionConsumer") Consumer externalConnectionConsumer) { - def address = externalNetwork.start() - externalNetwork.onAccept(externalConnectionConsumer) - return address + InetSocketAddress externalNetworkAddress(ServerNetwork externalNetwork) { + def random = ThreadLocalRandom.current() + for (int i = 0; i < 100; i++) { + def address = new InetSocketAddress("localhost", random.nextInt(800, 45000)) + try { + externalNetwork.start(address) + return address; + } catch (RuntimeException e) { + } + } + throw new RuntimeException() } @Bean - InetSocketAddress internalNetworkAddress( - @Qualifier("internalNetwork") ServerNetwork internalNetwork, - @Qualifier("internalConnectionConsumer") Consumer internalConnectionConsumer) { - def address = internalNetwork.start() - internalNetwork.onAccept(internalConnectionConsumer) - return address + ApplicationListener externalNetworkStarter() { + return (event) -> { }; + } + + @Bean + Void startExternalNetwork(ServerNetwork externalNetwork, + ConnectionService connectionService, + InetSocketAddress externalNetworkAddress) { + externalNetwork.onAccept(connectionService::processAcceptedConnection); + return null; } } diff --git a/application/src/test/groovy/javasabr/mqtt/application/integration/service/MqttSessionServiceTest.groovy b/application/src/test/groovy/javasabr/mqtt/application/integration/service/SessionServiceTest.groovy similarity index 90% rename from application/src/test/groovy/javasabr/mqtt/application/integration/service/MqttSessionServiceTest.groovy rename to application/src/test/groovy/javasabr/mqtt/application/integration/service/SessionServiceTest.groovy index 061704eb..831bd6dd 100644 --- a/application/src/test/groovy/javasabr/mqtt/application/integration/service/MqttSessionServiceTest.groovy +++ b/application/src/test/groovy/javasabr/mqtt/application/integration/service/SessionServiceTest.groovy @@ -3,16 +3,16 @@ package javasabr.mqtt.application.integration.service import com.hivemq.client.mqtt.mqtt5.message.connect.connack.Mqtt5ConnAckReasonCode import javasabr.mqtt.application.integration.IntegrationSpecification import javasabr.mqtt.service.ClientIdRegistry -import javasabr.mqtt.service.MqttSessionService +import javasabr.mqtt.service.SessionService import org.springframework.beans.factory.annotation.Autowired -class MqttSessionServiceTest extends IntegrationSpecification { +class SessionServiceTest extends IntegrationSpecification { @Autowired ClientIdRegistry clientIdRegistry @Autowired - MqttSessionService mqttSessionService + SessionService mqttSessionService def "subscriber should create and re-use mqtt session"() { given: diff --git a/application/src/test/groovy/javasabr/mqtt/application/mock/MqttMockClient.groovy b/application/src/test/groovy/javasabr/mqtt/application/mock/MqttMockClient.groovy index bb330a6d..bef6c6b0 100644 --- a/application/src/test/groovy/javasabr/mqtt/application/mock/MqttMockClient.groovy +++ b/application/src/test/groovy/javasabr/mqtt/application/mock/MqttMockClient.groovy @@ -1,7 +1,7 @@ package javasabr.mqtt.application.mock import javasabr.mqtt.network.MqttConnection -import javasabr.mqtt.network.packet.PacketType +import javasabr.mqtt.network.packet.MqttPacketType import javasabr.mqtt.network.packet.in.* import javasabr.mqtt.network.packet.out.Disconnect311OutPacket import javasabr.mqtt.network.packet.out.MqttWritablePacket @@ -75,17 +75,17 @@ class MqttMockClient { MqttReadablePacket packet - switch (PacketType.fromByte(type)) { - case PacketType.CONNECT_ACK: + switch (MqttPacketType.fromByte(type)) { + case MqttPacketType.CONNECT_ACK: packet = new ConnectAckInPacket(info) break - case PacketType.SUBSCRIBE_ACK: + case MqttPacketType.SUBSCRIBE_ACK: packet = new SubscribeAckInPacket(info) break - case PacketType.PUBLISH: + case MqttPacketType.PUBLISH: packet = new PublishInPacket(info) break - case PacketType.PUBLISH_RELEASED: + case MqttPacketType.PUBLISH_RELEASED: packet = new PublishReleaseInPacket(info) break default: diff --git a/application/src/test/resources/log4j2.xml b/application/src/test/resources/log4j2.xml index 66ea614f..0f78067a 100644 --- a/application/src/test/resources/log4j2.xml +++ b/application/src/test/resources/log4j2.xml @@ -9,6 +9,9 @@ + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1b5b6302..e51cef81 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # https://gitlab.com/JavaSaBr/maven-repo/-/packages -rlib = "10.0.alpha4" +rlib = "10.0.alpha5" # https://mvnrepository.com/artifact/org.jetbrains/annotations jetbrains-annotations = "26.0.2" # https://mvnrepository.com/artifact/org.projectlombok/lombok diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 00000000..15999264 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,17 @@ +plugins { + id("java-library") + id("configure-java") +} + +description = "The module to launch MQTT Broker as a part of another application" + +dependencies { + implementation projects.service + implementation libs.rlib.logger.slf4j + + testImplementation projects.testSupport +} + +tasks.withType(GroovyCompile).configureEach { + options.forkOptions.jvmArgs += "--enable-preview" +} diff --git a/library/src/main/java/javasabr/mqtt/broker/library/MqttBrokerConfiguration.java b/library/src/main/java/javasabr/mqtt/broker/library/MqttBrokerConfiguration.java new file mode 100644 index 00000000..f4f4cbb7 --- /dev/null +++ b/library/src/main/java/javasabr/mqtt/broker/library/MqttBrokerConfiguration.java @@ -0,0 +1,3 @@ +package javasabr.mqtt.broker.library; + +public class MqttBrokerConfiguration {} diff --git a/model/src/main/java/javasabr/mqtt/model/QoS.java b/model/src/main/java/javasabr/mqtt/model/QoS.java index 9b0d7367..e02e9858 100644 --- a/model/src/main/java/javasabr/mqtt/model/QoS.java +++ b/model/src/main/java/javasabr/mqtt/model/QoS.java @@ -1,16 +1,21 @@ package javasabr.mqtt.model; import javasabr.mqtt.model.reason.code.SubscribeAckReasonCode; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; @Getter @RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum QoS { - AT_MOST_ONCE(SubscribeAckReasonCode.GRANTED_QOS_0), - AT_LEAST_ONCE(SubscribeAckReasonCode.GRANTED_QOS_1), - EXACTLY_ONCE(SubscribeAckReasonCode.GRANTED_QOS_2), - INVALID(SubscribeAckReasonCode.IMPLEMENTATION_SPECIFIC_ERROR); + AT_MOST_ONCE(0, SubscribeAckReasonCode.GRANTED_QOS_0), + AT_LEAST_ONCE(1, SubscribeAckReasonCode.GRANTED_QOS_1), + EXACTLY_ONCE(2, SubscribeAckReasonCode.GRANTED_QOS_2), + INVALID(3, SubscribeAckReasonCode.IMPLEMENTATION_SPECIFIC_ERROR); private static final QoS[] VALUES = values(); @@ -22,5 +27,6 @@ public static QoS of(int level) { } } - private final SubscribeAckReasonCode subscribeAckReasonCode; + int index; + SubscribeAckReasonCode subscribeAckReasonCode; } diff --git a/model/src/main/java/javasabr/mqtt/model/reason/code/PublishAckReasonCode.java b/model/src/main/java/javasabr/mqtt/model/reason/code/PublishAckReasonCode.java index aade08c8..33684715 100644 --- a/model/src/main/java/javasabr/mqtt/model/reason/code/PublishAckReasonCode.java +++ b/model/src/main/java/javasabr/mqtt/model/reason/code/PublishAckReasonCode.java @@ -2,10 +2,16 @@ import java.util.stream.Stream; import javasabr.rlib.common.util.ObjectUtils; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +@Getter @RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum PublishAckReasonCode { /** * The message is accepted. Publication of the QoS 1 message proceeds. @@ -54,14 +60,14 @@ public enum PublishAckReasonCode { static { - var maxId = Stream + int maxValue = Stream .of(values()) - .mapToInt(PublishAckReasonCode::getValue) + .mapToInt(PublishAckReasonCode::value) .map(value -> Byte.toUnsignedInt((byte) value)) .max() .orElse(0); - var values = new PublishAckReasonCode[maxId + 1]; + var values = new PublishAckReasonCode[maxValue + 1]; for (var value : values()) { values[Byte.toUnsignedInt(value.value)] = value; @@ -70,13 +76,12 @@ public enum PublishAckReasonCode { VALUES = values; } - public static PublishAckReasonCode of(int index) { + public static PublishAckReasonCode ofValue(int value) { return ObjectUtils.notNull( - VALUES[index], - index, + VALUES[value], + value, arg -> new IndexOutOfBoundsException("Doesn't support reason code: " + arg)); } - @Getter - private final byte value; + byte value; } diff --git a/model/src/main/java/javasabr/mqtt/model/reason/code/PublishReceivedReasonCode.java b/model/src/main/java/javasabr/mqtt/model/reason/code/PublishReceivedReasonCode.java index fb9e9f73..4f2e9343 100644 --- a/model/src/main/java/javasabr/mqtt/model/reason/code/PublishReceivedReasonCode.java +++ b/model/src/main/java/javasabr/mqtt/model/reason/code/PublishReceivedReasonCode.java @@ -2,14 +2,20 @@ import java.util.stream.Stream; import javasabr.rlib.common.util.ObjectUtils; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +@Getter @RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum PublishReceivedReasonCode { /** - * The message is accepted. Publication of the QoS 2 message proceeds.. + * The message is accepted. Publication of the QoS 2 message proceeds... */ SUCCESS((byte) 0x00), /** @@ -52,14 +58,14 @@ public enum PublishReceivedReasonCode { static { - var maxId = Stream + int maxValue = Stream .of(values()) - .mapToInt(PublishReceivedReasonCode::getValue) + .mapToInt(PublishReceivedReasonCode::value) .map(value -> Byte.toUnsignedInt((byte) value)) .max() .orElse(0); - var values = new PublishReceivedReasonCode[maxId + 1]; + var values = new PublishReceivedReasonCode[maxValue + 1]; for (var value : values()) { values[Byte.toUnsignedInt(value.value)] = value; @@ -68,13 +74,12 @@ public enum PublishReceivedReasonCode { VALUES = values; } - public static PublishReceivedReasonCode of(int index) { + public static PublishReceivedReasonCode ofValue(int index) { return ObjectUtils.notNull( VALUES[index], index, arg -> new IndexOutOfBoundsException("Doesn't support reason code: " + arg)); } - @Getter - private final byte value; + byte value; } diff --git a/network/src/main/java/javasabr/mqtt/network/MqttClient.java b/network/src/main/java/javasabr/mqtt/network/MqttClient.java index 696f76b5..6901ddd6 100644 --- a/network/src/main/java/javasabr/mqtt/network/MqttClient.java +++ b/network/src/main/java/javasabr/mqtt/network/MqttClient.java @@ -3,9 +3,7 @@ import java.util.concurrent.CompletableFuture; import javasabr.mqtt.model.MqttClientConnectionConfig; import javasabr.mqtt.model.MqttUser; -import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; -import javasabr.mqtt.network.out.MqttPacketOutFactory; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; +import javasabr.mqtt.network.packet.out.ConnectAck311OutPacket; import javasabr.mqtt.network.packet.out.MqttWritablePacket; import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; @@ -16,19 +14,15 @@ interface UnsafeMqttClient extends MqttClient { MqttConnection connection(); - void handle(MqttReadablePacket packet); - void clientId(String clientId); void session(@Nullable MqttSession session); - void reject(ConnectAckReasonCode reasonCode); + void reject(ConnectAck311OutPacket connectAsk); Mono release(); } - MqttPacketOutFactory packetOutFactory(); - String clientId(); @Nullable diff --git a/network/src/main/java/javasabr/mqtt/network/MqttClientFactory.java b/network/src/main/java/javasabr/mqtt/network/MqttClientFactory.java new file mode 100644 index 00000000..1f44c450 --- /dev/null +++ b/network/src/main/java/javasabr/mqtt/network/MqttClientFactory.java @@ -0,0 +1,8 @@ +package javasabr.mqtt.network; + +import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; + +public interface MqttClientFactory { + + UnsafeMqttClient newClient(MqttConnection connection); +} diff --git a/network/src/main/java/javasabr/mqtt/network/MqttConnection.java b/network/src/main/java/javasabr/mqtt/network/MqttConnection.java index 2aa26367..c22d92c9 100644 --- a/network/src/main/java/javasabr/mqtt/network/MqttConnection.java +++ b/network/src/main/java/javasabr/mqtt/network/MqttConnection.java @@ -1,12 +1,10 @@ package javasabr.mqtt.network; import java.nio.channels.AsynchronousSocketChannel; -import java.util.function.Function; import javasabr.mqtt.model.MqttClientConnectionConfig; import javasabr.mqtt.model.MqttServerConnectionConfig; import javasabr.mqtt.model.MqttVersion; import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.handler.PacketInHandler; import javasabr.mqtt.network.packet.MqttPacketReader; import javasabr.mqtt.network.packet.MqttPacketWriter; import javasabr.rlib.network.BufferAllocator; @@ -31,10 +29,6 @@ public class MqttConnection extends AbstractConnection { @Getter(AccessLevel.PROTECTED) final NetworkPacketWriter packetWriter; - @Getter - final PacketInHandler[] packetHandlers; - - @Getter final UnsafeMqttClient client; @Getter final MqttServerConnectionConfig serverConnectionConfig; @@ -47,15 +41,13 @@ public MqttConnection( AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, int maxPacketsByRead, - PacketInHandler[] packetHandlers, - MqttServerConnectionConfig config, - Function clientFactory) { + MqttServerConnectionConfig serverConnectionConfig, + MqttClientFactory clientFactory) { super(network, channel, bufferAllocator, maxPacketsByRead); - this.packetHandlers = packetHandlers; - this.serverConnectionConfig = config; + this.serverConnectionConfig = serverConnectionConfig; this.packetReader = createPacketReader(); this.packetWriter = createPacketWriter(); - this.client = clientFactory.apply(this); + this.client = clientFactory.newClient(this); } public boolean isSupported(MqttVersion mqttVersion) { @@ -83,6 +75,10 @@ public MqttClientConnectionConfig clientConnectionConfig() { return config; } + public MqttClient client() { + return client; + } + private NetworkPacketReader createPacketReader() { return new MqttPacketReader( this, diff --git a/network/src/main/java/javasabr/mqtt/network/MqttConnectionFactory.java b/network/src/main/java/javasabr/mqtt/network/MqttConnectionFactory.java new file mode 100644 index 00000000..16fb7cf9 --- /dev/null +++ b/network/src/main/java/javasabr/mqtt/network/MqttConnectionFactory.java @@ -0,0 +1,9 @@ +package javasabr.mqtt.network; + +import java.nio.channels.AsynchronousSocketChannel; +import javasabr.rlib.network.Network; + +public interface MqttConnectionFactory { + + MqttConnection newConnection(Network network, AsynchronousSocketChannel channel); +} diff --git a/network/src/main/java/javasabr/mqtt/network/MqttSession.java b/network/src/main/java/javasabr/mqtt/network/MqttSession.java index 93934ac7..15a71192 100644 --- a/network/src/main/java/javasabr/mqtt/network/MqttSession.java +++ b/network/src/main/java/javasabr/mqtt/network/MqttSession.java @@ -19,7 +19,7 @@ interface UnsafeMqttSession extends MqttSession { void onRestored(); } - interface PendingPacketHandler { + interface PendingMessageHandler { /** * @return true if pending packet can be removed. @@ -48,9 +48,9 @@ default void resend(MqttClient client, PublishInPacket packet, int packetId) {} boolean hasOutPending(int packetId); - void registerOutPublish(PublishInPacket publish, PendingPacketHandler handler, int packetId); + void registerOutPublish(PublishInPacket publish, PendingMessageHandler handler, int packetId); - void registerInPublish(PublishInPacket publish, PendingPacketHandler handler, int packetId); + void registerInPublish(PublishInPacket publish, PendingMessageHandler handler, int packetId); void updateOutPendingPacket(MqttClient client, HasPacketId response); diff --git a/network/src/main/java/javasabr/mqtt/network/client/AbstractMqttClient.java b/network/src/main/java/javasabr/mqtt/network/client/AbstractMqttClient.java index b454ae88..954070c7 100644 --- a/network/src/main/java/javasabr/mqtt/network/client/AbstractMqttClient.java +++ b/network/src/main/java/javasabr/mqtt/network/client/AbstractMqttClient.java @@ -4,15 +4,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import javasabr.mqtt.base.utils.DebugUtils; import javasabr.mqtt.model.MqttClientConnectionConfig; -import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.MqttSession; import javasabr.mqtt.network.handler.MqttClientReleaseHandler; -import javasabr.mqtt.network.handler.PacketInHandler; -import javasabr.mqtt.network.out.MqttPacketOutFactories; -import javasabr.mqtt.network.out.MqttPacketOutFactory; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; +import javasabr.mqtt.network.packet.out.ConnectAck311OutPacket; import javasabr.mqtt.network.packet.out.MqttWritablePacket; import lombok.AccessLevel; import lombok.CustomLog; @@ -52,17 +48,6 @@ public AbstractMqttClient(MqttConnection connection, MqttClientReleaseHandler re this.clientId = connection.remoteAddress(); } - @Override - public void handle(MqttReadablePacket packet) { - log.debug(clientId, packet.name(), packet, "[%s] Received packet:[%s] %s"::formatted); - PacketInHandler packetHandler = connection.packetHandlers()[packet.packetType()]; - if (packetHandler != null) { - packetHandler.handle(this, packet); - } else { - log.warning(clientId, packet.name(), packet, "[%s] No packet handler for packet:[%s] %s"::formatted); - } - } - @Override public void send(MqttWritablePacket packet) { log.debug(clientId, packet.name(), packet, "[%s] Send to client packet:[%s] %s"::formatted); @@ -75,19 +60,13 @@ public CompletableFuture sendWithFeedback(MqttWritablePacket packet) { return connection.sendWithFeedback(packet); } - public void reject(ConnectAckReasonCode reasonCode) { + @Override + public void reject(ConnectAck311OutPacket connectAsk) { connection - .sendWithFeedback(packetOutFactory().newConnectAck(this, reasonCode)) + .sendWithFeedback(connectAsk) .thenAccept(_ -> connection.close()); } - @Override - public MqttPacketOutFactory packetOutFactory() { - return MqttPacketOutFactories.of(connection - .clientConnectionConfig() - .mqttVersion()); - } - @Override public Mono release() { if (released.compareAndSet(false, true)) { diff --git a/network/src/main/java/javasabr/mqtt/network/handler/PacketInHandler.java b/network/src/main/java/javasabr/mqtt/network/handler/PacketInHandler.java deleted file mode 100644 index 76142d15..00000000 --- a/network/src/main/java/javasabr/mqtt/network/handler/PacketInHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package javasabr.mqtt.network.handler; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; - -public interface PacketInHandler { - - PacketInHandler EMPTY = (client, packet) -> {}; - - void handle(UnsafeMqttClient client, MqttReadablePacket packet); -} diff --git a/network/src/main/java/javasabr/mqtt/network/DefaultMqttSession.java b/network/src/main/java/javasabr/mqtt/network/impl/DefaultMqttSession.java similarity index 94% rename from network/src/main/java/javasabr/mqtt/network/DefaultMqttSession.java rename to network/src/main/java/javasabr/mqtt/network/impl/DefaultMqttSession.java index 1c2aaf03..e7fd08d0 100644 --- a/network/src/main/java/javasabr/mqtt/network/DefaultMqttSession.java +++ b/network/src/main/java/javasabr/mqtt/network/impl/DefaultMqttSession.java @@ -1,10 +1,11 @@ -package javasabr.mqtt.network; +package javasabr.mqtt.network.impl; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import javasabr.mqtt.model.MqttProperties; import javasabr.mqtt.model.subscriber.SubscribeTopicFilter; import javasabr.mqtt.model.topic.TopicFilter; +import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.MqttSession.UnsafeMqttSession; import javasabr.mqtt.network.packet.HasPacketId; import javasabr.mqtt.network.packet.in.PublishInPacket; @@ -29,13 +30,13 @@ public class DefaultMqttSession implements UnsafeMqttSession { @AllArgsConstructor private static class PendingPublish { private final PublishInPacket publish; - private final PendingPacketHandler handler; + private final PendingMessageHandler handler; private final int packetId; } private static void registerPublish( PublishInPacket publish, - PendingPacketHandler handler, + PendingMessageHandler handler, int packetId, LockableArray pendingPublishes) { PendingPublish pendingPublish = new PendingPublish(publish, handler, packetId); @@ -63,7 +64,7 @@ private static void updatePendingPacket( } if (pendingPublish == null) { - log.warning(clientId , response, "Not found pending publish for client:[%s] by received packet:[%]"::formatted); + log.warning(clientId , response, "Not found pending publish for client:[%s] by received packet:[%s]"::formatted); return; } @@ -113,12 +114,12 @@ public String clientId() { } @Override - public void registerOutPublish(PublishInPacket publish, PendingPacketHandler handler, int packetId) { + public void registerOutPublish(PublishInPacket publish, PendingMessageHandler handler, int packetId) { registerPublish(publish, handler, packetId, pendingOutPublishes); } @Override - public void registerInPublish(PublishInPacket publish, PendingPacketHandler handler, int packetId) { + public void registerInPublish(PublishInPacket publish, PendingMessageHandler handler, int packetId) { registerPublish(publish, handler, packetId, pendingInPublishes); } @@ -164,7 +165,7 @@ public void resendPendingPackets(MqttClient mqttClient) { .iterations() .forEach( mqttClient, (pendingPublish, client) -> { - PendingPacketHandler handler = pendingPublish.handler; + PendingMessageHandler handler = pendingPublish.handler; handler.resend(client, pendingPublish.publish, pendingPublish.packetId); }); } finally { diff --git a/network/src/main/java/javasabr/mqtt/network/out/package-info.java b/network/src/main/java/javasabr/mqtt/network/impl/package-info.java similarity index 60% rename from network/src/main/java/javasabr/mqtt/network/out/package-info.java rename to network/src/main/java/javasabr/mqtt/network/impl/package-info.java index c7253a6a..cac005fa 100644 --- a/network/src/main/java/javasabr/mqtt/network/out/package-info.java +++ b/network/src/main/java/javasabr/mqtt/network/impl/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package javasabr.mqtt.network.out; +package javasabr.mqtt.network.impl; import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactories.java b/network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactories.java deleted file mode 100644 index 754fa874..00000000 --- a/network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactories.java +++ /dev/null @@ -1,17 +0,0 @@ -package javasabr.mqtt.network.out; - -import javasabr.mqtt.model.MqttVersion; -import javasabr.rlib.common.util.ArrayUtils; - -public class MqttPacketOutFactories { - - private static final MqttPacketOutFactory[] FACTORIES = ArrayUtils - .array( - new Mqtt311PacketOutFactory(), - new Mqtt311PacketOutFactory(), - new Mqtt5PacketOutFactory()); - - public static MqttPacketOutFactory of(MqttVersion version) { - return FACTORIES[version.ordinal()]; - } -} diff --git a/network/src/main/java/javasabr/mqtt/network/packet/PacketType.java b/network/src/main/java/javasabr/mqtt/network/packet/MqttPacketType.java similarity index 78% rename from network/src/main/java/javasabr/mqtt/network/packet/PacketType.java rename to network/src/main/java/javasabr/mqtt/network/packet/MqttPacketType.java index 9999556f..dd5f3af9 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/PacketType.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/MqttPacketType.java @@ -1,40 +1,50 @@ package javasabr.mqtt.network.packet; -public enum PacketType { - RESERVED, +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; + +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public enum MqttPacketType { + RESERVED(0), /** * After a Network Connection is established by a Client to a Server, the first Packet sent from the Client to the * Server MUST be a CONNECT Packet */ - CONNECT, + CONNECT(1), /** * The CONNACK Packet is the packet sent by the Server in response to a CONNECT Packet received from a Client. The * first packet sent from the Server to the Client MUST be a CONNACK Packet [MQTT-3.2.0-1]. */ - CONNECT_ACK, + CONNECT_ACK(2), /** * A PUBLISH Control Packet is sent from a Client to a Server or from Server to a Client to transport an Application * Message. */ - PUBLISH, + PUBLISH(3), /** * A PUBACK Packet is the response to a PUBLISH Packet with QoS level 1. */ - PUBLISH_ACK, + PUBLISH_ACK(4), /** * A PUBREC Packet is the response to a PUBLISH Packet with QoS 2. It is the second packet of the QoS 2 protocol * exchange. */ - PUBLISH_RECEIVED, + PUBLISH_RECEIVED(5), /** * A PUBREL Packet is the response to a PUBREC Packet. It is the third packet of the QoS 2 protocol exchange. */ - PUBLISH_RELEASED, + PUBLISH_RELEASED(6), /** * The PUBCOMP packet is the response to a PUBREL packet. It is the fourth and final packet of the QoS 2 protocol * exchange. */ - PUBLISH_COMPLETED, + PUBLISH_COMPLETED(7), /** * The SUBSCRIBE Packet is sent from the Client to the Server to create one or more Subscriptions. Each Subscription * registers a Client’s interest in one or more Topics. The Server sends PUBLISH Packets to the Client in order to @@ -42,19 +52,19 @@ public enum PacketType { * also specifies (for each Subscription) the maximum QoS with which the Server can send Application Messages to the * Client. */ - SUBSCRIBE, + SUBSCRIBE(8), /** * A SUBACK Packet is sent by the Server to the Client to confirm receipt and processing of a SUBSCRIBE Packet. */ - SUBSCRIBE_ACK, + SUBSCRIBE_ACK(9), /** * An UNSUBSCRIBE Packet is sent by the Client to the Server, to unsubscribe from topics. */ - UNSUBSCRIBE, + UNSUBSCRIBE(10), /** * The UNSUBACK Packet is sent by the Server to the Client to confirm receipt of an UNSUBSCRIBE Packet. */ - UNSUBSCRIBE_ACK, + UNSUBSCRIBE_ACK(11), /** * The PINGREQ packet is sent from a Client to the Server. It can be used to: *

@@ -67,39 +77,41 @@ public enum PacketType { *

* This packet is used in Keep Alive processing. Refer to section 3.1.2.10 for more details */ - PING_REQUEST, + PING_REQUEST(12), /** * A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ packet. It indicates that the Server * is alive. */ - PING_RESPONSE, + PING_RESPONSE(13), /** - * The DISCONNECT packet is the final MQTT Control Packet sent from the Client or the Server. It indicates the reason + * The DISCONNECT packet PacketTypeis the final MQTT Control Packet sent from the Client or the Server. It indicates the reason * why the Network Connection is being closed. The Client or Server MAY send a DISCONNECT packet before closing the * Network Connection. If the Network Connection is closed without the Client first sending a DISCONNECT packet with * Reason Code 0x00 (Normal disconnection) and the Connection has a Will Message, the Will Message is published. Refer * to section 3.1.2.5 for further details. */ - DISCONNECT, + DISCONNECT(14), /** * An AUTH packet is sent from Client to Server or Server to Client as part of an extended authentication exchange, * such as challenge / response authentication. It is a Protocol Error for the Client or Server to send an AUTH packet * if the CONNECT packet did not contain the same Authentication Method. */ - AUTHENTICATE, + AUTHENTICATE(15), /** * Not supported */ - INVALID; + INVALID(16); - private static final PacketType[] VALUES = values(); + private static final MqttPacketType[] VALUES = values(); - public static PacketType fromByte(byte packetType) { - if (packetType < 0 || packetType > AUTHENTICATE.ordinal()) { + public static MqttPacketType fromByte(byte packetType) { + if (packetType < 0 || packetType > AUTHENTICATE.typeIndex()) { return INVALID; } else { return VALUES[packetType]; } } + + int typeIndex; } diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/AuthenticationInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/AuthenticationInPacket.java index effc7efb..38886277 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/AuthenticationInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/AuthenticationInPacket.java @@ -6,7 +6,7 @@ import javasabr.mqtt.model.PacketProperty; import javasabr.mqtt.model.reason.code.AuthenticateReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.StringUtils; import lombok.Getter; @@ -17,7 +17,7 @@ @Getter public class AuthenticationInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.AUTHENTICATE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.AUTHENTICATE.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectAckInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectAckInPacket.java index 79ca1492..f727a255 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectAckInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectAckInPacket.java @@ -10,7 +10,7 @@ import javasabr.mqtt.model.data.type.StringPair; import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.NumberUtils; @@ -23,7 +23,7 @@ @Getter public class ConnectAckInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.CONNECT_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.CONNECT_ACK.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectInPacket.java index 793ed6a2..5fb4e1b4 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/ConnectInPacket.java @@ -10,7 +10,7 @@ import javasabr.mqtt.model.exception.ConnectionRejectException; import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.NumberUtils; import javasabr.rlib.common.util.StringUtils; @@ -27,7 +27,7 @@ @FieldDefaults(level = AccessLevel.PRIVATE) public class ConnectInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.CONNECT.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.CONNECT.ordinal(); static { DebugUtils.registerIncludedFields("clientId", "keepAlive", "cleanStart", "mqttVersion"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/DisconnectInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/DisconnectInPacket.java index ae82806d..b38308e0 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/DisconnectInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/DisconnectInPacket.java @@ -9,7 +9,7 @@ import javasabr.mqtt.model.PacketProperty; import javasabr.mqtt.model.reason.code.DisconnectReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.common.util.StringUtils; import lombok.Getter; @@ -19,7 +19,7 @@ @Getter public class DisconnectInPacket extends MqttReadablePacket { - public static final byte PACKET_TYPE = (byte) PacketType.DISCONNECT.ordinal(); + public static final byte PACKET_TYPE = (byte) MqttPacketType.DISCONNECT.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PingRequestInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PingRequestInPacket.java index 6afc8214..c5aeaece 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PingRequestInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PingRequestInPacket.java @@ -1,13 +1,13 @@ package javasabr.mqtt.network.packet.in; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; /** * PING request. */ public class PingRequestInPacket extends MqttReadablePacket { - public static final byte PACKET_TYPE = (byte) PacketType.PING_REQUEST.ordinal(); + public static final byte PACKET_TYPE = (byte) MqttPacketType.PING_REQUEST.ordinal(); public PingRequestInPacket(byte info) { super(info); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PingResponseInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PingResponseInPacket.java index bacd6b87..516d50ac 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PingResponseInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PingResponseInPacket.java @@ -1,13 +1,13 @@ package javasabr.mqtt.network.packet.in; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; /** * PING response. */ public class PingResponseInPacket extends MqttReadablePacket { - public static final byte PACKET_TYPE = (byte) PacketType.PING_RESPONSE.ordinal(); + public static final byte PACKET_TYPE = (byte) MqttPacketType.PING_RESPONSE.ordinal(); public PingResponseInPacket(byte info) { super(info); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishAckInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishAckInPacket.java index 7b2c1190..417f08e9 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishAckInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishAckInPacket.java @@ -9,7 +9,7 @@ import javasabr.mqtt.model.reason.code.PublishAckReasonCode; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.Getter; import lombok.experimental.Accessors; @@ -20,7 +20,7 @@ @Accessors(fluent = true, chain = false) public class PublishAckInPacket extends MqttReadablePacket implements HasPacketId { - private static final int PACKET_TYPE = PacketType.PUBLISH_ACK.ordinal(); + private static final int PACKET_TYPE = MqttPacketType.PUBLISH_ACK.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode", "packetId"); @@ -72,7 +72,7 @@ protected void readVariableHeader(MqttConnection connection, ByteBuffer buffer) // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901123 if (connection.isSupported(MqttVersion.MQTT_5) && buffer.hasRemaining()) { - reasonCode = PublishAckReasonCode.of(readByteUnsigned(buffer)); + reasonCode = PublishAckReasonCode.ofValue(readByteUnsigned(buffer)); } } diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishCompleteInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishCompleteInPacket.java index 351fe8d1..1a4fc477 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishCompleteInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishCompleteInPacket.java @@ -9,7 +9,7 @@ import javasabr.mqtt.model.reason.code.PublishCompletedReasonCode; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.Getter; import lombok.experimental.Accessors; @@ -20,7 +20,7 @@ @Accessors(fluent = true, chain = false) public class PublishCompleteInPacket extends MqttReadablePacket implements HasPacketId { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_COMPLETED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_COMPLETED.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode", "packetId"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishInPacket.java index 9a7aff54..5fadbba8 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishInPacket.java @@ -12,7 +12,7 @@ import javasabr.mqtt.model.QoS; import javasabr.mqtt.model.topic.TopicName; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.IntArray; import javasabr.rlib.collections.array.MutableIntArray; @@ -27,7 +27,7 @@ @Getter public class PublishInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH.ordinal(); static { DebugUtils.registerIncludedFields("topicName", "qos", "duplicate", "packetId"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReceivedInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReceivedInPacket.java index 2e8ca042..561956f2 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReceivedInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReceivedInPacket.java @@ -9,7 +9,7 @@ import javasabr.mqtt.model.reason.code.PublishReceivedReasonCode; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.common.util.StringUtils; import lombok.Getter; import lombok.experimental.Accessors; @@ -21,7 +21,7 @@ @Accessors(fluent = true, chain = false) public class PublishReceivedInPacket extends MqttReadablePacket implements HasPacketId { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_RECEIVED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_RECEIVED.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode", "packetId"); @@ -69,7 +69,7 @@ protected void readVariableHeader(MqttConnection connection, ByteBuffer buffer) // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901143 if (connection.isSupported(MqttVersion.MQTT_5) && buffer.hasRemaining()) { - reasonCode = PublishReceivedReasonCode.of(readByteUnsigned(buffer)); + reasonCode = PublishReceivedReasonCode.ofValue(readByteUnsigned(buffer)); } } diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReleaseInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReleaseInPacket.java index 8d600d77..2a6ec02e 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReleaseInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/PublishReleaseInPacket.java @@ -9,7 +9,7 @@ import javasabr.mqtt.model.reason.code.PublishReleaseReasonCode; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.Getter; import lombok.experimental.Accessors; @@ -20,7 +20,7 @@ @Accessors(fluent = true, chain = false) public class PublishReleaseInPacket extends MqttReadablePacket implements HasPacketId { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_RELEASED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_RELEASED.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode", "packetId"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeAckInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeAckInPacket.java index 73ddfc68..208a6c2e 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeAckInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeAckInPacket.java @@ -6,7 +6,7 @@ import javasabr.mqtt.model.PacketProperty; import javasabr.mqtt.model.reason.code.SubscribeAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.StringUtils; @@ -18,7 +18,7 @@ @Getter public class SubscribeAckInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.SUBSCRIBE_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.SUBSCRIBE_ACK.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeInPacket.java index 2f60b048..508de457 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/SubscribeInPacket.java @@ -13,7 +13,7 @@ import javasabr.mqtt.model.SubscribeRetainHandling; import javasabr.mqtt.model.subscriber.SubscribeTopicFilter; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.NumberUtils; @@ -25,7 +25,7 @@ @Getter public class SubscribeInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.SUBSCRIBE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.SUBSCRIBE.ordinal(); static { DebugUtils.registerIncludedFields("packetId", "topicFilters"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeAckInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeAckInPacket.java index 3eb61f34..8bb82936 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeAckInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeAckInPacket.java @@ -7,7 +7,7 @@ import javasabr.mqtt.model.PacketProperty; import javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.Array; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; @@ -26,7 +26,7 @@ @FieldDefaults(level = AccessLevel.PRIVATE) public class UnsubscribeAckInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.UNSUBSCRIBE_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.UNSUBSCRIBE_ACK.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeInPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeInPacket.java index ef081c6c..455b0603 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeInPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/in/UnsubscribeInPacket.java @@ -8,7 +8,7 @@ import javasabr.mqtt.model.PacketProperty; import javasabr.mqtt.model.topic.TopicFilter; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; import lombok.Getter; @@ -19,7 +19,7 @@ @Getter public class UnsubscribeInPacket extends MqttReadablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.UNSUBSCRIBE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.UNSUBSCRIBE.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/Authentication5OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/Authentication5OutPacket.java index f608e93a..4429803b 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/Authentication5OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/Authentication5OutPacket.java @@ -7,7 +7,7 @@ import javasabr.mqtt.model.data.type.StringPair; import javasabr.mqtt.model.reason.code.AuthenticateReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.Array; import lombok.RequiredArgsConstructor; @@ -17,7 +17,7 @@ @RequiredArgsConstructor public class Authentication5OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.AUTHENTICATE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.AUTHENTICATE.ordinal(); private static final Set AVAILABLE_PROPERTIES = EnumSet.of( /* diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/Connect311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/Connect311OutPacket.java index 94a318da..144f8a1d 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/Connect311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/Connect311OutPacket.java @@ -4,7 +4,7 @@ import javasabr.mqtt.model.MqttVersion; import javasabr.mqtt.model.QoS; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.StringUtils; import lombok.RequiredArgsConstructor; @@ -15,7 +15,7 @@ @RequiredArgsConstructor public class Connect311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.CONNECT.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.CONNECT.ordinal(); private final String username; private final String willTopic; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/ConnectAck311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/ConnectAck311OutPacket.java index b71bc7bd..631fe6cc 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/ConnectAck311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/ConnectAck311OutPacket.java @@ -4,7 +4,7 @@ import javasabr.mqtt.base.utils.DebugUtils; import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; @@ -16,7 +16,7 @@ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class ConnectAck311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.CONNECT_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.CONNECT_ACK.ordinal(); static { DebugUtils.registerIncludedFields("reasonCode", "sessionPresent"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/Disconnect311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/Disconnect311OutPacket.java index 27bc4442..c35080ed 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/Disconnect311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/Disconnect311OutPacket.java @@ -1,14 +1,14 @@ package javasabr.mqtt.network.packet.out; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; /** * Disconnect notification. */ public class Disconnect311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.DISCONNECT.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.DISCONNECT.ordinal(); @Override public int expectedLength(MqttConnection connection) { diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PingRequest311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PingRequest311OutPacket.java index 85d59bc6..488d4863 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PingRequest311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PingRequest311OutPacket.java @@ -1,13 +1,13 @@ package javasabr.mqtt.network.packet.out; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; /** * PING request. */ public class PingRequest311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PING_REQUEST.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PING_REQUEST.ordinal(); @Override protected byte packetType() { diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PingResponse311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PingResponse311OutPacket.java index b708d12a..ed8cc29c 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PingResponse311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PingResponse311OutPacket.java @@ -1,13 +1,13 @@ package javasabr.mqtt.network.packet.out; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; /** * PING response. */ public class PingResponse311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PING_RESPONSE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PING_RESPONSE.ordinal(); @Override protected byte packetType() { diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck311OutPacket.java index a0b137b0..e40f465e 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck311OutPacket.java @@ -2,7 +2,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.RequiredArgsConstructor; /** @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class PublishAck311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_ACK.ordinal(); /** * Packet Identifier from the PUBLISH packet that is being acknowledged. diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck5OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck5OutPacket.java index 06e0e938..6b2ead54 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck5OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishAck5OutPacket.java @@ -59,7 +59,7 @@ protected boolean isPropertiesSupported(MqttConnection connection) { protected void writeVariableHeader(MqttConnection connection, ByteBuffer buffer) { super.writeVariableHeader(connection, buffer); // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901123 - writeByte(buffer, reasonCode.getValue()); + writeByte(buffer, reasonCode.value()); } @Override diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishComplete311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishComplete311OutPacket.java index 91694f96..ed0d9a41 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishComplete311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishComplete311OutPacket.java @@ -2,7 +2,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.RequiredArgsConstructor; /** @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class PublishComplete311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_COMPLETED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_COMPLETED.ordinal(); private final int packetId; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishOutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishOutPacket.java index 88efef7b..dbafc2e8 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishOutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishOutPacket.java @@ -1,7 +1,7 @@ package javasabr.mqtt.network.packet.out; import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; @@ -10,7 +10,7 @@ @RequiredArgsConstructor public abstract class PublishOutPacket extends MqttWritablePacket implements HasPacketId { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH.ordinal(); @Getter protected final int packetId; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived311OutPacket.java index c5d80ee9..f150e965 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived311OutPacket.java @@ -2,7 +2,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.RequiredArgsConstructor; /** @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class PublishReceived311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_RECEIVED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_RECEIVED.ordinal(); private final int packetId; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived5OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived5OutPacket.java index 43bc5151..694068bc 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived5OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishReceived5OutPacket.java @@ -65,7 +65,7 @@ protected void writeVariableHeader(MqttConnection connection, ByteBuffer buffer) super.writeVariableHeader(connection, buffer); // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901143 - writeByte(buffer, reasonCode.getValue()); + writeByte(buffer, reasonCode.value()); } @Override diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishRelease311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishRelease311OutPacket.java index 2cbaffe5..a1a6e9f7 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/PublishRelease311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/PublishRelease311OutPacket.java @@ -2,7 +2,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.RequiredArgsConstructor; /** @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class PublishRelease311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.PUBLISH_RELEASED.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.PUBLISH_RELEASED.ordinal(); private final int packetId; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/Subscribe311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/Subscribe311OutPacket.java index 1e384b6c..588a856e 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/Subscribe311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/Subscribe311OutPacket.java @@ -3,7 +3,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.model.subscriber.SubscribeTopicFilter; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.Array; import lombok.RequiredArgsConstructor; @@ -13,7 +13,7 @@ @RequiredArgsConstructor public class Subscribe311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.SUBSCRIBE.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.SUBSCRIBE.ordinal(); private final Array topicFilters; private final int packetId; diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/SubscribeAck311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/SubscribeAck311OutPacket.java index 59a11b94..f1cbc40b 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/SubscribeAck311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/SubscribeAck311OutPacket.java @@ -4,7 +4,7 @@ import javasabr.mqtt.base.utils.DebugUtils; import javasabr.mqtt.model.reason.code.SubscribeAckReasonCode; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.rlib.collections.array.Array; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -17,7 +17,7 @@ @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class SubscribeAck311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.SUBSCRIBE_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.SUBSCRIBE_ACK.ordinal(); static { DebugUtils.registerIncludedFields("reasonCodes", "packetId"); diff --git a/network/src/main/java/javasabr/mqtt/network/packet/out/UnsubscribeAck311OutPacket.java b/network/src/main/java/javasabr/mqtt/network/packet/out/UnsubscribeAck311OutPacket.java index 8f53e68a..48bf069b 100644 --- a/network/src/main/java/javasabr/mqtt/network/packet/out/UnsubscribeAck311OutPacket.java +++ b/network/src/main/java/javasabr/mqtt/network/packet/out/UnsubscribeAck311OutPacket.java @@ -2,7 +2,7 @@ import java.nio.ByteBuffer; import javasabr.mqtt.network.MqttConnection; -import javasabr.mqtt.network.packet.PacketType; +import javasabr.mqtt.network.packet.MqttPacketType; import lombok.RequiredArgsConstructor; /** @@ -11,7 +11,7 @@ @RequiredArgsConstructor public class UnsubscribeAck311OutPacket extends MqttWritablePacket { - private static final byte PACKET_TYPE = (byte) PacketType.UNSUBSCRIBE_ACK.ordinal(); + private static final byte PACKET_TYPE = (byte) MqttPacketType.UNSUBSCRIBE_ACK.ordinal(); private final int packetId; diff --git a/service/src/main/java/javasabr/mqtt/service/ConnectionService.java b/service/src/main/java/javasabr/mqtt/service/ConnectionService.java new file mode 100644 index 00000000..47210005 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/ConnectionService.java @@ -0,0 +1,8 @@ +package javasabr.mqtt.service; + +import javasabr.mqtt.network.MqttConnection; + +public interface ConnectionService { + + void processAcceptedConnection(MqttConnection connection); +} diff --git a/service/src/main/java/javasabr/mqtt/service/MessageOutFactoryService.java b/service/src/main/java/javasabr/mqtt/service/MessageOutFactoryService.java new file mode 100644 index 00000000..76acd42d --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/MessageOutFactoryService.java @@ -0,0 +1,12 @@ +package javasabr.mqtt.service; + +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.service.message.out.factory.MqttMessageOutFactory; + +public interface MessageOutFactoryService { + + MqttMessageOutFactory resolveFactory(MqttClient client); + + MqttMessageOutFactory resolveFactory(MqttConnection connection); +} diff --git a/service/src/main/java/javasabr/mqtt/service/PublishDeliveringService.java b/service/src/main/java/javasabr/mqtt/service/PublishDeliveringService.java new file mode 100644 index 00000000..0dada0e0 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/PublishDeliveringService.java @@ -0,0 +1,10 @@ +package javasabr.mqtt.service; + +import javasabr.mqtt.model.subscriber.SingleSubscriber; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; + +public interface PublishDeliveringService { + + PublishHandlingResult startDelivering(PublishInPacket publish, SingleSubscriber subscriber); +} diff --git a/service/src/main/java/javasabr/mqtt/service/PublishingService.java b/service/src/main/java/javasabr/mqtt/service/PublishReceivingService.java similarity index 52% rename from service/src/main/java/javasabr/mqtt/service/PublishingService.java rename to service/src/main/java/javasabr/mqtt/service/PublishReceivingService.java index 0fe6a1f9..4f4adf2e 100644 --- a/service/src/main/java/javasabr/mqtt/service/PublishingService.java +++ b/service/src/main/java/javasabr/mqtt/service/PublishReceivingService.java @@ -3,7 +3,7 @@ import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.packet.in.PublishInPacket; -public interface PublishingService { +public interface PublishReceivingService { - void publish(MqttClient client, PublishInPacket publish); + void processReceivedPublish(MqttClient client, PublishInPacket publish); } diff --git a/service/src/main/java/javasabr/mqtt/service/MqttSessionService.java b/service/src/main/java/javasabr/mqtt/service/SessionService.java similarity index 88% rename from service/src/main/java/javasabr/mqtt/service/MqttSessionService.java rename to service/src/main/java/javasabr/mqtt/service/SessionService.java index ffac55ec..23cb00d9 100644 --- a/service/src/main/java/javasabr/mqtt/service/MqttSessionService.java +++ b/service/src/main/java/javasabr/mqtt/service/SessionService.java @@ -3,7 +3,7 @@ import javasabr.mqtt.network.MqttSession; import reactor.core.publisher.Mono; -public interface MqttSessionService { +public interface SessionService { Mono restore(String clientId); diff --git a/service/src/main/java/javasabr/mqtt/service/SubscriptionService.java b/service/src/main/java/javasabr/mqtt/service/SubscriptionService.java index 5b91a6ce..4a1c0d5b 100644 --- a/service/src/main/java/javasabr/mqtt/service/SubscriptionService.java +++ b/service/src/main/java/javasabr/mqtt/service/SubscriptionService.java @@ -6,17 +6,29 @@ import javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode; import javasabr.mqtt.model.subscriber.SingleSubscriber; import javasabr.mqtt.model.subscriber.SubscribeTopicFilter; +import javasabr.mqtt.model.subscriber.Subscriber; import javasabr.mqtt.model.topic.TopicFilter; import javasabr.mqtt.model.topic.TopicName; import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.MqttSession; import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.MutableArray; /** * Subscription service */ public interface SubscriptionService { + boolean isValid(TopicName topicName); + + MqttClient resolveClient(Subscriber subscriber); + + default Array findSubscribers(TopicName topicName) { + return findSubscribersTo(MutableArray.ofType(SingleSubscriber.class), topicName); + } + + Array findSubscribersTo(MutableArray container, TopicName topicName); + /** * Runs function for each topic subscriber * diff --git a/service/src/main/java/javasabr/mqtt/service/handler/client/AbstractMqttClientReleaseHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/client/AbstractMqttClientReleaseHandler.java index 80cbe7ae..ce2d9215 100644 --- a/service/src/main/java/javasabr/mqtt/service/handler/client/AbstractMqttClientReleaseHandler.java +++ b/service/src/main/java/javasabr/mqtt/service/handler/client/AbstractMqttClientReleaseHandler.java @@ -5,7 +5,7 @@ import javasabr.mqtt.network.client.AbstractMqttClient; import javasabr.mqtt.network.handler.MqttClientReleaseHandler; import javasabr.mqtt.service.ClientIdRegistry; -import javasabr.mqtt.service.MqttSessionService; +import javasabr.mqtt.service.SessionService; import javasabr.mqtt.service.SubscriptionService; import javasabr.rlib.common.util.StringUtils; import lombok.CustomLog; @@ -18,7 +18,7 @@ public abstract class AbstractMqttClientReleaseHandler { +public class ExternalMqttClientReleaseHandler extends AbstractMqttClientReleaseHandler { - public DefaultMqttClientReleaseHandler( + public ExternalMqttClientReleaseHandler( ClientIdRegistry clientIdRegistry, - MqttSessionService sessionService, + SessionService sessionService, SubscriptionService subscriptionService) { super(clientIdRegistry, sessionService, subscriptionService); } diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/AbstractPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/AbstractPacketHandler.java deleted file mode 100644 index 1374fdb1..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/AbstractPacketHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.handler.PacketInHandler; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; - -public abstract class AbstractPacketHandler implements - PacketInHandler { - - @Override - public void handle(UnsafeMqttClient client, MqttReadablePacket packet) { - //noinspection unchecked - handleImpl((C) client, (R) packet); - } - - protected abstract void handleImpl(C client, R packet); -} - diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/DisconnetInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/DisconnetInPacketHandler.java deleted file mode 100644 index 32b171e8..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/DisconnetInPacketHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.model.reason.code.DisconnectReasonCode; -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.DisconnectInPacket; -import lombok.CustomLog; - -@CustomLog -public class DisconnetInPacketHandler extends AbstractPacketHandler { - - @Override - protected void handleImpl(UnsafeMqttClient client, DisconnectInPacket packet) { - - var reasonCode = packet.getReasonCode(); - - if (reasonCode == DisconnectReasonCode.NORMAL_DISCONNECTION) { - log.info(client, "Disconnect client:[%s]"::formatted); - } else { - log.error("Disconnect client:[%s] by error reason:[%s]".formatted(client, reasonCode)); - } - - client - .connection() - .close(); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PendingOutResponseInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PendingOutResponseInPacketHandler.java deleted file mode 100644 index 363afaf6..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PendingOutResponseInPacketHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.in.MqttReadablePacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PendingOutResponseInPacketHandler extends - AbstractPacketHandler { - - @Override - protected void handleImpl(UnsafeMqttClient client, R packet) { - var session = client.session(); - if (session != null) { - session.updateOutPendingPacket(client, packet); - } - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishAckInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PublishAckInPacketHandler.java deleted file mode 100644 index b2d1c3bc..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishAckInPacketHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.packet.in.PublishAckInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PublishAckInPacketHandler extends PendingOutResponseInPacketHandler {} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishCompleteInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PublishCompleteInPacketHandler.java deleted file mode 100644 index 55a85d9e..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishCompleteInPacketHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.packet.in.PublishCompleteInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PublishCompleteInPacketHandler extends PendingOutResponseInPacketHandler {} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PublishInPacketHandler.java deleted file mode 100644 index 1894225d..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishInPacketHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.PublishInPacket; -import javasabr.mqtt.service.PublishingService; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PublishInPacketHandler extends AbstractPacketHandler { - - private final PublishingService publishingService; - - @Override - protected void handleImpl(UnsafeMqttClient client, PublishInPacket packet) { - publishingService.publish(client, packet); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReceiveInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReceiveInPacketHandler.java deleted file mode 100644 index d6cae073..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReceiveInPacketHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.packet.in.PublishReceivedInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PublishReceiveInPacketHandler extends PendingOutResponseInPacketHandler {} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReleaseInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReleaseInPacketHandler.java deleted file mode 100644 index 5860c211..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/PublishReleaseInPacketHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.PublishReleaseInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PublishReleaseInPacketHandler extends AbstractPacketHandler { - - @Override - protected void handleImpl(UnsafeMqttClient client, PublishReleaseInPacket packet) { - var session = client.session(); - if (session != null) { - session.updateInPendingPacket(client, packet); - } - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/SubscribeInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/SubscribeInPacketHandler.java deleted file mode 100644 index d753b93e..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/SubscribeInPacketHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import static java.lang.Byte.toUnsignedInt; -import static javasabr.mqtt.model.reason.code.SubscribeAckReasonCode.SHARED_SUBSCRIPTIONS_NOT_SUPPORTED; -import static javasabr.mqtt.model.reason.code.SubscribeAckReasonCode.WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED; - -import java.util.Set; -import javasabr.mqtt.model.reason.code.DisconnectReasonCode; -import javasabr.mqtt.model.reason.code.SubscribeAckReasonCode; -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.SubscribeInPacket; -import javasabr.mqtt.network.packet.out.MqttWritablePacket; -import javasabr.mqtt.service.SubscriptionService; -import javasabr.rlib.collections.array.Array; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class SubscribeInPacketHandler extends AbstractPacketHandler { - - private final static Set INVALID_ACK_CODE = Set.of( - SHARED_SUBSCRIPTIONS_NOT_SUPPORTED, - WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED); - - private final SubscriptionService subscriptionService; - - @Override - protected void handleImpl(UnsafeMqttClient client, SubscribeInPacket packet) { - - Array ackReasonCodes = subscriptionService.subscribe(client, packet.getTopicFilters()); - MqttWritablePacket subscribeAck = client - .packetOutFactory() - .newSubscribeAck(packet.getPacketId(), ackReasonCodes); - - client.send(subscribeAck); - - SubscribeAckReasonCode anyReason = ackReasonCodes - .reversedIterations() - .findAny(INVALID_ACK_CODE, Set::contains); - - if (anyReason != null) { - var disconnectReasonCode = DisconnectReasonCode.of(toUnsignedInt(anyReason.getValue())); - MqttWritablePacket disconnect = client - .packetOutFactory() - .newDisconnect(client, disconnectReasonCode); - - client - .sendWithFeedback(disconnect) - .thenAccept(result -> client - .connection() - .close()); - } - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/UnsubscribeInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/in/UnsubscribeInPacketHandler.java deleted file mode 100644 index 0215dcd1..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/UnsubscribeInPacketHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package javasabr.mqtt.service.handler.in; - -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; -import javasabr.mqtt.network.packet.in.UnsubscribeInPacket; -import javasabr.mqtt.service.SubscriptionService; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class UnsubscribeInPacketHandler extends AbstractPacketHandler { - - private final SubscriptionService subscriptionService; - - @Override - protected void handleImpl(UnsafeMqttClient client, UnsubscribeInPacket packet) { - var ackReasonCodes = subscriptionService.unsubscribe(client, packet.getTopicFilters()); - client.send(client - .packetOutFactory() - .newUnsubscribeAck(packet.getPacketId(), ackReasonCodes)); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/AbstractPublishInHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/in/AbstractPublishInHandler.java deleted file mode 100644 index e6b74e38..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/AbstractPublishInHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package javasabr.mqtt.service.handler.publish.in; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.model.subscriber.SingleSubscriber; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.handler.PublishInHandler; -import javasabr.mqtt.network.packet.in.PublishInPacket; -import javasabr.mqtt.service.SubscriptionService; -import javasabr.mqtt.service.handler.publish.out.PublishOutHandler; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -abstract class AbstractPublishInHandler implements PublishInHandler { - - protected final SubscriptionService subscriptionService; - protected final PublishOutHandler[] publishOutHandlers; - - public void handle(MqttClient client, PublishInPacket packet) { - handleResult( - client, - packet, - subscriptionService.forEachTopicSubscriber(packet.getTopicName(), packet, this::sendToSubscriber)); - } - - private ActionResult sendToSubscriber(SingleSubscriber subscriber, PublishInPacket packet) { - return publishOutHandler(subscriber.getQos()).handle(packet, subscriber); - } - - private PublishOutHandler publishOutHandler(QoS qos) { - return publishOutHandlers[qos.ordinal()]; - } - - protected void handleResult(MqttClient client, PublishInPacket packet, ActionResult result) { - // nothing to do - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos0PublishInHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos0PublishInHandler.java deleted file mode 100644 index c9848b70..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos0PublishInHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package javasabr.mqtt.service.handler.publish.in; - -import javasabr.mqtt.service.SubscriptionService; -import javasabr.mqtt.service.handler.publish.out.PublishOutHandler; - -public class Qos0PublishInHandler extends AbstractPublishInHandler { - - public Qos0PublishInHandler(SubscriptionService subscriptionService, PublishOutHandler[] publishOutHandlers) { - super(subscriptionService, publishOutHandlers); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos1PublishInHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos1PublishInHandler.java deleted file mode 100644 index c5e179b8..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos1PublishInHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package javasabr.mqtt.service.handler.publish.in; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.reason.code.PublishAckReasonCode; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.packet.in.PublishInPacket; -import javasabr.mqtt.service.SubscriptionService; -import javasabr.mqtt.service.handler.publish.out.PublishOutHandler; - -public class Qos1PublishInHandler extends AbstractPublishInHandler { - - public Qos1PublishInHandler(SubscriptionService subscriptionService, PublishOutHandler[] publishOutHandlers) { - super(subscriptionService, publishOutHandlers); - } - - @Override - public void handle(MqttClient client, PublishInPacket packet) { - - var session = client.session(); - - // it means this client was already closed - if (session == null) { - return; - } - - super.handle(client, packet); - } - - @Override - protected void handleResult(MqttClient client, PublishInPacket packet, ActionResult result) { - - PublishAckReasonCode reasonCode; - - switch (result) { - case EMPTY: - reasonCode = PublishAckReasonCode.NO_MATCHING_SUBSCRIBERS; - break; - case SUCCESS: - reasonCode = PublishAckReasonCode.SUCCESS; - break; - default: - reasonCode = PublishAckReasonCode.UNSPECIFIED_ERROR; - break; - } - - client.send(client - .packetOutFactory() - .newPublishAck(packet.getPacketId(), reasonCode)); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos2PublishInHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos2PublishInHandler.java deleted file mode 100644 index 0c95f383..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/Qos2PublishInHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -package javasabr.mqtt.service.handler.publish.in; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.reason.code.PublishCompletedReasonCode; -import javasabr.mqtt.model.reason.code.PublishReceivedReasonCode; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.MqttSession; -import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.in.PublishInPacket; -import javasabr.mqtt.network.packet.in.PublishReleaseInPacket; -import javasabr.mqtt.service.SubscriptionService; -import javasabr.mqtt.service.handler.publish.out.PublishOutHandler; - -public class Qos2PublishInHandler extends AbstractPublishInHandler implements MqttSession.PendingPacketHandler { - - public Qos2PublishInHandler(SubscriptionService subscriptionService, PublishOutHandler[] publishOutHandlers) { - super(subscriptionService, publishOutHandlers); - } - - @Override - public void handle(MqttClient client, PublishInPacket packet) { - - var session = client.session(); - - // it means this client was already closed - if (session == null) { - return; - } - - // if this packet is re-try from client - if (packet.isDuplicate()) { - // if this packet was accepted before then we can skip it - if (session.hasInPending(packet.getPacketId())) { - return; - } - } - - super.handle(client, packet); - } - - @Override - protected void handleResult(MqttClient client, PublishInPacket packet, ActionResult result) { - - // because it was checked - final MqttSession session = client.session(); - - // it means this client was already closed - if (session == null) { - return; - } - - PublishReceivedReasonCode reasonCode; - - switch (result) { - case EMPTY: - reasonCode = PublishReceivedReasonCode.NO_MATCHING_SUBSCRIBERS; - break; - case SUCCESS: - reasonCode = PublishReceivedReasonCode.SUCCESS; - break; - default: - reasonCode = PublishReceivedReasonCode.UNSPECIFIED_ERROR; - break; - } - - session.registerInPublish(packet, this, packet.getPacketId()); - - client.send(client - .packetOutFactory() - .newPublishReceived(packet.getPacketId(), reasonCode)); - } - - @Override - public boolean handleResponse(MqttClient client, HasPacketId response) { - - if (!(response instanceof PublishReleaseInPacket)) { - throw new IllegalStateException("Unexpected response " + response); - } - - var packetOutFactory = client.packetOutFactory(); - client.send(packetOutFactory.newPublishCompleted(response.packetId(), PublishCompletedReasonCode.SUCCESS)); - - return true; - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/package-info.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/in/package-info.java deleted file mode 100644 index cb5838d9..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/in/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package javasabr.mqtt.service.handler.publish.in; - -import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/AbstractPublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/AbstractPublishOutHandler.java deleted file mode 100644 index ed0338a2..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/AbstractPublishOutHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.MqttProperties; -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.model.subscriber.SingleSubscriber; -import javasabr.mqtt.model.subscriber.Subscriber; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.MqttSession; -import javasabr.mqtt.network.packet.in.PublishInPacket; - -abstract class AbstractPublishOutHandler implements PublishOutHandler { - - @Override - public ActionResult handle(PublishInPacket packet, SingleSubscriber subscriber) { - - MqttClient client = (MqttClient) subscriber.getUser(); - var session = client.session(); - - // if session is null it means this client was already closed - if (session != null) { - return handleImpl(packet, subscriber, client, session); - } else { - return ActionResult.EMPTY; - } - } - - protected abstract ActionResult handleImpl( - PublishInPacket packet, - Subscriber subscriber, - MqttClient client, - MqttSession session); - - protected abstract QoS getQoS(); - - void sendPublish(MqttClient client, PublishInPacket packet, int packetId, boolean duplicate) { - - var packetOutFactory = client.packetOutFactory(); - client.send(packetOutFactory.newPublish( - packetId, - getQoS(), - packet.isRetained(), - duplicate, - packet - .getTopicName() - .toString(), - MqttProperties.TOPIC_ALIAS_NOT_SET, - packet.getPayload(), - packet.isPayloadFormatIndicator(), - packet.getResponseTopic(), - packet.getCorrelationData(), - packet.userProperties())); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PersistentPublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PersistentPublishOutHandler.java deleted file mode 100644 index 1c89e027..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PersistentPublishOutHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.subscriber.Subscriber; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.MqttSession; -import javasabr.mqtt.network.packet.in.PublishInPacket; - -public abstract class PersistentPublishOutHandler extends AbstractPublishOutHandler implements - MqttSession.PendingPacketHandler { - - @Override - protected ActionResult handleImpl( - PublishInPacket packet, - Subscriber subscriber, - MqttClient client, - MqttSession session) { - // generate new uniq packet id per client - var packetId = session.nextPacketId(); - - // register waiting async response - session.registerOutPublish(packet, this, packetId); - - // send publish - sendPublish(client, packet, packetId, false); - - return ActionResult.SUCCESS; - } - - @Override - public void resend(MqttClient client, PublishInPacket packet, int packetId) { - sendPublish(client, packet, packetId, true); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PublishOutHandler.java deleted file mode 100644 index ad0ae7cf..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/PublishOutHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.subscriber.SingleSubscriber; -import javasabr.mqtt.network.packet.in.PublishInPacket; - -/** - * Interface to handle outgoing publish packets. - */ -public interface PublishOutHandler { - - ActionResult handle(PublishInPacket packet, SingleSubscriber subscriber); -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos0PublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos0PublishOutHandler.java deleted file mode 100644 index 340ba959..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos0PublishOutHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import static javasabr.mqtt.model.ActionResult.SUCCESS; - -import javasabr.mqtt.model.ActionResult; -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.model.subscriber.Subscriber; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.MqttSession; -import javasabr.mqtt.network.packet.in.PublishInPacket; - -public class Qos0PublishOutHandler extends AbstractPublishOutHandler { - - @Override - protected QoS getQoS() { - return QoS.AT_MOST_ONCE; - } - - @Override - protected ActionResult handleImpl( - PublishInPacket packet, - Subscriber subscriber, - MqttClient client, - MqttSession session) { - sendPublish(client, packet, 0, false); - return SUCCESS; - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos1PublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos1PublishOutHandler.java deleted file mode 100644 index 8734070e..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos1PublishOutHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.in.PublishAckInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class Qos1PublishOutHandler extends PersistentPublishOutHandler { - - @Override - protected QoS getQoS() { - return QoS.AT_LEAST_ONCE; - } - - @Override - public boolean handleResponse(MqttClient client, HasPacketId response) { - - if (!(response instanceof PublishAckInPacket)) { - throw new IllegalStateException("Unexpected response: " + response); - } - - // just return 'true' to remove pending packet from session - return true; - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos2PublishOutHandler.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos2PublishOutHandler.java deleted file mode 100644 index 33e21cd2..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/Qos2PublishOutHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package javasabr.mqtt.service.handler.publish.out; - -import static javasabr.mqtt.model.reason.code.PublishReleaseReasonCode.SUCCESS; - -import javasabr.mqtt.model.QoS; -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.packet.HasPacketId; -import javasabr.mqtt.network.packet.in.PublishCompleteInPacket; -import javasabr.mqtt.network.packet.in.PublishReceivedInPacket; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public final class Qos2PublishOutHandler extends PersistentPublishOutHandler { - - @Override - protected QoS getQoS() { - return QoS.EXACTLY_ONCE; - } - - @Override - public boolean handleResponse(MqttClient client, HasPacketId response) { - - var packetOutFactory = client.packetOutFactory(); - - if (response instanceof PublishReceivedInPacket) { - client.send(packetOutFactory.newPublishRelease(response.packetId(), SUCCESS)); - return false; - } else if (response instanceof PublishCompleteInPacket) { - return true; - } else { - throw new IllegalStateException("Unexpected response: " + response); - } - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/package-info.java b/service/src/main/java/javasabr/mqtt/service/handler/publish/out/package-info.java deleted file mode 100644 index ed8abf71..00000000 --- a/service/src/main/java/javasabr/mqtt/service/handler/publish/out/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package javasabr.mqtt.service.handler.publish.out; - -import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultConnectionService.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultConnectionService.java new file mode 100644 index 00000000..cde9860d --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/DefaultConnectionService.java @@ -0,0 +1,98 @@ +package javasabr.mqtt.service.impl; + +import java.util.Collection; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.MqttReadablePacket; +import javasabr.mqtt.service.ConnectionService; +import javasabr.mqtt.service.message.handler.MqttInMessageHandler; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class DefaultConnectionService implements ConnectionService { + + @Nullable + MqttInMessageHandler[] inMessageHandlers; + + public DefaultConnectionService(Collection knownInMessageHandlers) { + int highestPacketType = knownInMessageHandlers + .stream() + .map(MqttInMessageHandler::messageType) + .mapToInt(MqttPacketType::typeIndex) + .max() + .orElse(0); + + var inMessageHandlers = new MqttInMessageHandler[highestPacketType + 1]; + + for (MqttInMessageHandler knownInMessageHandler : knownInMessageHandlers) { + MqttPacketType messageType = knownInMessageHandler.messageType(); + if (inMessageHandlers[messageType.typeIndex()] != null) { + throw new IllegalArgumentException("Found duplicate MqttInMessageHandler:[" + knownInMessageHandler + "]"); + } + inMessageHandlers[messageType.typeIndex()] = knownInMessageHandler; + } + + this.inMessageHandlers = inMessageHandlers; + log.info(inMessageHandlers, DefaultConnectionService::buildServiceDescription); + } + + @Override + public void processAcceptedConnection(MqttConnection connection) { + log.info(connection.remoteAddress(), "Accept new connection:[%s]"::formatted); + connection.onReceive(this::processReceivedMessage); + } + + protected void processReceivedMessage( + MqttConnection connection, + ReadableNetworkPacket networkPacket) { + + if (!(networkPacket instanceof MqttReadablePacket mrp)) { + log.warning(networkPacket, "Received not processable network packet:[%s]"::formatted); + return; + } + + log.debug( + connection.client().clientId(), + networkPacket.name(), + networkPacket, + "[%s] Received from client message:[%s] %s"::formatted); + + try { + //noinspection DataFlowIssue + inMessageHandlers[mrp.packetType()].processReceived(connection, mrp); + } catch (IndexOutOfBoundsException | NullPointerException ex) { + log.warning(mrp, "Received not supported MQTT message:[%s]"::formatted); + } + } + + private static String buildServiceDescription(@Nullable MqttInMessageHandler[] inMessageHandlers) { + var builder = new StringBuilder(); + builder.append("{\n"); + int count = 0; + for (MqttInMessageHandler inMessageHandler : inMessageHandlers) { + if (inMessageHandler == null) { + continue; + } + count++; + builder + .append(" \"") + .append(inMessageHandler.messageType()) + .append("\": \"") + .append(inMessageHandler + .getClass() + .getSimpleName()) + .append("\",") + .append("\n"); + } + builder + .delete(builder.length() - 2, builder.length()) + .append("\n}"); + + return "Registered [%s] MqttInMessageHandlers: %s".formatted(count, builder); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultMessageOutFactoryService.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultMessageOutFactoryService.java new file mode 100644 index 00000000..185163c9 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/DefaultMessageOutFactoryService.java @@ -0,0 +1,92 @@ +package javasabr.mqtt.service.impl; + +import java.util.Collection; +import javasabr.mqtt.model.MqttClientConnectionConfig; +import javasabr.mqtt.model.MqttVersion; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.message.out.factory.MqttMessageOutFactory; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class DefaultMessageOutFactoryService implements MessageOutFactoryService { + + @Nullable + MqttMessageOutFactory[] messageOutFactories; + + public DefaultMessageOutFactoryService(Collection knownFactories) { + + int maxVersion = knownFactories + .stream() + .map(MqttMessageOutFactory::mqttVersion) + .mapToInt(MqttVersion::version) + .max() + .orElse(0); + + var factories = new MqttMessageOutFactory[maxVersion + 1]; + + for (MqttMessageOutFactory knownFactory : knownFactories) { + MqttVersion version = knownFactory.mqttVersion(); + if (factories[version.version()] != null) { + throw new IllegalArgumentException("Found duplicate MessageOutFactory:[" + knownFactory + "]"); + } + factories[version.version()] = knownFactory; + } + + this.messageOutFactories = factories; + log.info(messageOutFactories, DefaultMessageOutFactoryService::buildServiceDescription); + } + + @Override + public MqttMessageOutFactory resolveFactory(MqttClient client) { + if (client instanceof UnsafeMqttClient unsafe) { + return resolveFactory(unsafe.connection()); + } + throw new IllegalArgumentException("Unsupported client: " + client); + } + + @Override + public MqttMessageOutFactory resolveFactory(MqttConnection connection) { + MqttClientConnectionConfig connectionConfig = connection.clientConnectionConfig(); + MqttVersion mqttVersion = connectionConfig.mqttVersion(); + try { + //noinspection DataFlowIssue + return messageOutFactories[mqttVersion.version()]; + } catch (IndexOutOfBoundsException | NullPointerException ex) { + log.warning(mqttVersion, "Received not supported mqtt version:[%s]"::formatted); + throw new IllegalArgumentException("Unsupported MQTT version:[" + mqttVersion + "]"); + } + } + + private static String buildServiceDescription(@Nullable MqttMessageOutFactory[] messageOutFactories) { + var builder = new StringBuilder(); + builder.append("{\n"); + int count = 0; + for (MqttMessageOutFactory mqttMessageOutFactory : messageOutFactories) { + if (mqttMessageOutFactory == null) { + continue; + } + count++; + builder + .append(" \"") + .append(mqttMessageOutFactory.mqttVersion()) + .append("\": \"") + .append(mqttMessageOutFactory + .getClass() + .getSimpleName()) + .append("\",") + .append("\n"); + } + builder + .delete(builder.length() - 2, builder.length()) + .append("\n}"); + + return "Registered [%s] MessageOutFactories: %s".formatted(count, builder); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultMqttConnectionFactory.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultMqttConnectionFactory.java new file mode 100644 index 00000000..fc01de6f --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/DefaultMqttConnectionFactory.java @@ -0,0 +1,33 @@ +package javasabr.mqtt.service.impl; + +import java.nio.channels.AsynchronousSocketChannel; +import javasabr.mqtt.model.MqttServerConnectionConfig; +import javasabr.mqtt.network.MqttClientFactory; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.MqttConnectionFactory; +import javasabr.rlib.network.Network; +import javasabr.rlib.network.impl.DefaultBufferAllocator; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class DefaultMqttConnectionFactory implements MqttConnectionFactory { + + MqttServerConnectionConfig serverConnectionConfig; + MqttClientFactory clientFactory; + int maxPacketsByRead; + + @Override + public MqttConnection newConnection(Network network, AsynchronousSocketChannel channel) { + DefaultBufferAllocator bufferAllocator = new DefaultBufferAllocator(network.config()); + return new MqttConnection( + network, + channel, + bufferAllocator, + maxPacketsByRead, + serverConnectionConfig, + clientFactory); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishDeliveringService.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishDeliveringService.java new file mode 100644 index 00000000..3a772512 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishDeliveringService.java @@ -0,0 +1,84 @@ +package javasabr.mqtt.service.impl; + +import java.util.Collection; +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.model.subscriber.SingleSubscriber; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.publish.handler.MqttPublishOutMessageHandler; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class DefaultPublishDeliveringService implements PublishDeliveringService { + + @Nullable + MqttPublishOutMessageHandler[] publishOutMessageHandlers; + + public DefaultPublishDeliveringService( + Collection knownPublishOutHandlers) { + + int maxIndex = knownPublishOutHandlers + .stream() + .map(MqttPublishOutMessageHandler::qos) + .mapToInt(QoS::index) + .max() + .orElse(0); + + var handlers = new MqttPublishOutMessageHandler[maxIndex + 1]; + + for (MqttPublishOutMessageHandler knownPublishOutHandler : knownPublishOutHandlers) { + QoS qos = knownPublishOutHandler.qos(); + if (handlers[qos.index()] != null) { + throw new IllegalArgumentException( + "Found duplicate MqttPublishOutMessageHandler:[" + knownPublishOutHandler + "]"); + } + handlers[qos.index()] = knownPublishOutHandler; + } + + this.publishOutMessageHandlers = handlers; + log.info(publishOutMessageHandlers, DefaultPublishDeliveringService::buildServiceDescription); + } + + @Override + public PublishHandlingResult startDelivering(PublishInPacket publish, SingleSubscriber subscriber) { + try { + //noinspection DataFlowIssue + return publishOutMessageHandlers[subscriber.getQos().index()].handle(publish, subscriber); + } catch (IndexOutOfBoundsException | NullPointerException ex) { + log.warning(publish, "Received not supported publish message:[%s]"::formatted); + return PublishHandlingResult.UNSPECIFIED_ERROR; + } + } + + private static String buildServiceDescription( + @Nullable MqttPublishOutMessageHandler[] publishOutMessageHandlers) { + var builder = new StringBuilder(); + builder.append("{\n"); + int count = 0; + for (MqttPublishOutMessageHandler publishOutMessageHandler : publishOutMessageHandlers) { + if (publishOutMessageHandler == null) { + continue; + } + count++; + builder + .append(" \"") + .append(publishOutMessageHandler.qos()) + .append("\": \"") + .append(publishOutMessageHandler + .getClass() + .getSimpleName()) + .append("\",") + .append("\n"); + } + builder + .delete(builder.length() - 2, builder.length()) + .append("\n}"); + + return "Registered [%s] MqttPublishOutMessageHandlers: %s".formatted(count, builder); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishReceivingService.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishReceivingService.java new file mode 100644 index 00000000..5a14542c --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishReceivingService.java @@ -0,0 +1,82 @@ +package javasabr.mqtt.service.impl; + +import java.util.Collection; +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.PublishReceivingService; +import javasabr.mqtt.service.publish.handler.MqttPublishInMessageHandler; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class DefaultPublishReceivingService implements PublishReceivingService { + + @Nullable + MqttPublishInMessageHandler[] publishInHandlers; + + public DefaultPublishReceivingService( + Collection knownPublishInHandlers) { + + int maxIndex = knownPublishInHandlers + .stream() + .map(MqttPublishInMessageHandler::qos) + .mapToInt(QoS::index) + .max() + .orElse(0); + + var handlers = new MqttPublishInMessageHandler[maxIndex + 1]; + + for (MqttPublishInMessageHandler knownPublishInHandler : knownPublishInHandlers) { + QoS qos = knownPublishInHandler.qos(); + if (handlers[qos.index()] != null) { + throw new IllegalArgumentException( + "Found duplicate MqttPublishInMessageHandler:[" + knownPublishInHandler + "]"); + } + handlers[qos.index()] = knownPublishInHandler; + } + + this.publishInHandlers = handlers; + log.info(publishInHandlers, DefaultPublishReceivingService::buildServiceDescription); + } + + @Override + public void processReceivedPublish(MqttClient client, PublishInPacket publish) { + try { + //noinspection DataFlowIssue + publishInHandlers[publish.getQos().index()].handle(client, publish); + } catch (IndexOutOfBoundsException | NullPointerException ex) { + log.warning(publish, "Received not supported publish message:[%s]"::formatted); + } + } + + private static String buildServiceDescription( + @Nullable MqttPublishInMessageHandler[] publishInMessageHandlers) { + var builder = new StringBuilder(); + builder.append("{\n"); + int count = 0; + for (MqttPublishInMessageHandler publishInMessageHandler : publishInMessageHandlers) { + if (publishInMessageHandler == null) { + continue; + } + count++; + builder + .append(" \"") + .append(publishInMessageHandler.qos()) + .append("\": \"") + .append(publishInMessageHandler + .getClass() + .getSimpleName()) + .append("\",") + .append("\n"); + } + builder + .delete(builder.length() - 2, builder.length()) + .append("\n}"); + + return "Registered [%s] MqttPublishInMessageHandlers: %s".formatted(count, builder); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishingService.java b/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishingService.java deleted file mode 100644 index 6caaa836..00000000 --- a/service/src/main/java/javasabr/mqtt/service/impl/DefaultPublishingService.java +++ /dev/null @@ -1,19 +0,0 @@ -package javasabr.mqtt.service.impl; - -import javasabr.mqtt.network.MqttClient; -import javasabr.mqtt.network.handler.PublishInHandler; -import javasabr.mqtt.network.packet.in.PublishInPacket; -import javasabr.mqtt.service.PublishingService; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class DefaultPublishingService implements PublishingService { - - private final PublishInHandler[] publishInHandlers; - - @Override - public void publish(MqttClient client, PublishInPacket publish) { - PublishInHandler publishInHandler = publishInHandlers[publish.getQos().ordinal()]; - publishInHandler.handle(client, publish); - } -} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/ExternalMqttClientFactory.java b/service/src/main/java/javasabr/mqtt/service/impl/ExternalMqttClientFactory.java new file mode 100644 index 00000000..38876d1d --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/impl/ExternalMqttClientFactory.java @@ -0,0 +1,22 @@ +package javasabr.mqtt.service.impl; + +import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; +import javasabr.mqtt.network.MqttClientFactory; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.handler.MqttClientReleaseHandler; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class ExternalMqttClientFactory implements MqttClientFactory { + + MqttClientReleaseHandler releaseHandler; + + @Override + public UnsafeMqttClient newClient(MqttConnection connection) { + return new ExternalMqttClient(connection, releaseHandler); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/impl/InMemoryMqttSessionService.java b/service/src/main/java/javasabr/mqtt/service/impl/InMemorySessionService.java similarity index 93% rename from service/src/main/java/javasabr/mqtt/service/impl/InMemoryMqttSessionService.java rename to service/src/main/java/javasabr/mqtt/service/impl/InMemorySessionService.java index 933938c6..d6eaa8be 100644 --- a/service/src/main/java/javasabr/mqtt/service/impl/InMemoryMqttSessionService.java +++ b/service/src/main/java/javasabr/mqtt/service/impl/InMemorySessionService.java @@ -1,10 +1,10 @@ package javasabr.mqtt.service.impl; import java.io.Closeable; -import javasabr.mqtt.network.DefaultMqttSession; import javasabr.mqtt.network.MqttSession; import javasabr.mqtt.network.MqttSession.UnsafeMqttSession; -import javasabr.mqtt.service.MqttSessionService; +import javasabr.mqtt.network.impl.DefaultMqttSession; +import javasabr.mqtt.service.SessionService; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.collections.dictionary.Dictionary; @@ -19,7 +19,7 @@ @CustomLog @FieldDefaults(level = AccessLevel.PRIVATE) -public class InMemoryMqttSessionService implements MqttSessionService, Closeable { +public class InMemorySessionService implements SessionService, Closeable { final LockableRefToRefDictionary storedSession; final Thread cleanThread; @@ -27,7 +27,7 @@ public class InMemoryMqttSessionService implements MqttSessionService, Closeable final int cleanInterval; volatile boolean closed; - public InMemoryMqttSessionService(int cleanInterval) { + public InMemorySessionService(int cleanInterval) { this.cleanInterval = cleanInterval; this.storedSession = DictionaryFactory.stampedLockBasedRefToRefDictionary(); this.cleanThread = new Thread(this::cleanup, "InMemoryMqttSessionService-Cleanup"); @@ -106,7 +106,7 @@ private void cleanup() { storedSession .operations() - .inWriteLock(toRemove, InMemoryMqttSessionService::removeExpiredSessions); + .inWriteLock(toRemove, InMemorySessionService::removeExpiredSessions); } } diff --git a/service/src/main/java/javasabr/mqtt/service/impl/SimpleSubscriptionService.java b/service/src/main/java/javasabr/mqtt/service/impl/SimpleSubscriptionService.java index 7d9375ab..78beac17 100644 --- a/service/src/main/java/javasabr/mqtt/service/impl/SimpleSubscriptionService.java +++ b/service/src/main/java/javasabr/mqtt/service/impl/SimpleSubscriptionService.java @@ -8,7 +8,6 @@ import static javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode.NO_SUBSCRIPTION_EXISTED; import static javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode.SUCCESS; import static javasabr.mqtt.model.utils.TopicUtils.hasWildcard; -import static javasabr.mqtt.model.utils.TopicUtils.isInvalid; import static javasabr.mqtt.model.utils.TopicUtils.isShared; import java.util.function.BiFunction; @@ -18,14 +17,17 @@ import javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode; import javasabr.mqtt.model.subscriber.SingleSubscriber; import javasabr.mqtt.model.subscriber.SubscribeTopicFilter; +import javasabr.mqtt.model.subscriber.Subscriber; import javasabr.mqtt.model.topic.TopicFilter; import javasabr.mqtt.model.topic.TopicName; import javasabr.mqtt.model.topic.TopicSubscribers; +import javasabr.mqtt.model.utils.TopicUtils; import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.MqttSession; import javasabr.mqtt.service.SubscriptionService; import javasabr.rlib.collections.array.Array; import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.collections.array.MutableArray; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; @@ -38,12 +40,32 @@ public class SimpleSubscriptionService implements SubscriptionService { TopicSubscribers topicSubscribers = new TopicSubscribers(); + @Override + public boolean isValid(TopicName topicName) { + return !TopicUtils.isInvalid(topicName); + } + + @Override + public MqttClient resolveClient(Subscriber subscriber) { + if (subscriber instanceof SingleSubscriber ss) { + return (MqttClient) ss.getUser(); + } + throw new IllegalArgumentException("Unsupported: " + subscriber); + } + + @Override + public Array findSubscribersTo(MutableArray container, TopicName topicName) { + Array matched = topicSubscribers.matches(topicName); + container.addAll(matched); + return container; + } + @Override public ActionResult forEachTopicSubscriber( TopicName topicName, A arg1, BiFunction action) { - if (isInvalid(topicName)) { + if (TopicUtils.isInvalid(topicName)) { return FAILED; } ActionResult result = EMPTY; @@ -75,14 +97,14 @@ private SubscribeAckReasonCode addSubscription(SubscribeTopicFilter subscribe, M return SHARED_SUBSCRIPTIONS_NOT_SUPPORTED; } else if (!connectionConfig.wildcardSubscriptionAvailable() && hasWildcard(topic)) { return WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED; - } else if (isInvalid(topic)) { + } else if (TopicUtils.isInvalid(topic)) { return UNSPECIFIED_ERROR; } else { session.addSubscriber(subscribe); topicSubscribers.addSubscriber(client, subscribe); return subscribe .getQos() - .getSubscribeAckReasonCode(); + .subscribeAckReasonCode(); } } @@ -99,7 +121,7 @@ private UnsubscribeAckReasonCode removeSubscription(TopicFilter topic, MqttClien var session = client.session(); if (session == null) { return null; - } else if (isInvalid(topic)) { + } else if (TopicUtils.isInvalid(topic)) { return UnsubscribeAckReasonCode.UNSPECIFIED_ERROR; } else if (topicSubscribers.removeSubscriber(client, topic)) { session.removeSubscriber(topic); diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/MqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/MqttInMessageHandler.java new file mode 100644 index 00000000..0f832b66 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/MqttInMessageHandler.java @@ -0,0 +1,12 @@ +package javasabr.mqtt.service.message.handler; + +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.MqttReadablePacket; + +public interface MqttInMessageHandler { + + MqttPacketType messageType(); + + void processReceived(MqttConnection connection, MqttReadablePacket networkPacket); +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/MqttOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/MqttOutMessageHandler.java new file mode 100644 index 00000000..4ae87ff2 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/MqttOutMessageHandler.java @@ -0,0 +1,3 @@ +package javasabr.mqtt.service.message.handler; + +public interface MqttOutMessageHandler {} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/AbstractMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/AbstractMqttInMessageHandler.java new file mode 100644 index 00000000..942914f9 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/AbstractMqttInMessageHandler.java @@ -0,0 +1,38 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.packet.in.MqttReadablePacket; +import javasabr.mqtt.service.message.handler.MqttInMessageHandler; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@CustomLog +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public abstract class AbstractMqttInMessageHandler + implements MqttInMessageHandler { + + Class expectedClient; + Class

expectedNetworkPacket; + + @Override + public void processReceived(MqttConnection connection, MqttReadablePacket networkPacket) { + MqttClient client = connection.client(); + if (!expectedClient.isInstance(client)) { + log.warning(client, "Received not expected client:[%s]"::formatted); + return; + } else if (!expectedNetworkPacket.isInstance(networkPacket)) { + log.warning(networkPacket, "Received not expected network packet:[%s]"::formatted); + return; + } + processReceived( + connection, + expectedClient.cast(client), + expectedNetworkPacket.cast(networkPacket)); + } + + protected abstract void processReceived(MqttConnection connection, C client, P networkPacket); +} diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/ConnectInPacketHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/ConnectInMqttInMessageHandler.java similarity index 59% rename from service/src/main/java/javasabr/mqtt/service/handler/in/ConnectInPacketHandler.java rename to service/src/main/java/javasabr/mqtt/service/message/handler/impl/ConnectInMqttInMessageHandler.java index cd155b13..91ccae66 100644 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/ConnectInPacketHandler.java +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/ConnectInMqttInMessageHandler.java @@ -1,4 +1,4 @@ -package javasabr.mqtt.service.handler.in; +package javasabr.mqtt.service.message.handler.impl; import static javasabr.mqtt.base.utils.ReactorUtils.ifTrue; import static javasabr.mqtt.model.MqttProperties.MAXIMUM_PACKET_SIZE_UNDEFINED; @@ -17,44 +17,78 @@ import javasabr.mqtt.model.exception.ConnectionRejectException; import javasabr.mqtt.model.exception.MalformedPacketMqttException; import javasabr.mqtt.model.reason.code.ConnectAckReasonCode; -import javasabr.mqtt.network.MqttClient.UnsafeMqttClient; +import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.MqttConnection; import javasabr.mqtt.network.MqttSession; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; import javasabr.mqtt.network.packet.in.ConnectInPacket; import javasabr.mqtt.service.AuthenticationService; import javasabr.mqtt.service.ClientIdRegistry; -import javasabr.mqtt.service.MqttSessionService; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SessionService; import javasabr.mqtt.service.SubscriptionService; import javasabr.rlib.common.util.StringUtils; +import lombok.AccessLevel; import lombok.CustomLog; -import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; import reactor.core.publisher.Mono; @CustomLog -@RequiredArgsConstructor -public class ConnectInPacketHandler extends AbstractPacketHandler { +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class ConnectInMqttInMessageHandler extends AbstractMqttInMessageHandler { + + ClientIdRegistry clientIdRegistry; + AuthenticationService authenticationService; + SessionService sessionService; + SubscriptionService subscriptionService; + MessageOutFactoryService messageOutFactoryService; + + public ConnectInMqttInMessageHandler( + ClientIdRegistry clientIdRegistry, + AuthenticationService authenticationService, + SessionService sessionService, + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(ExternalMqttClient.class, ConnectInPacket.class); + this.clientIdRegistry = clientIdRegistry; + this.authenticationService = authenticationService; + this.sessionService = sessionService; + this.subscriptionService = subscriptionService; + this.messageOutFactoryService = messageOutFactoryService; + } - private final ClientIdRegistry clientIdRegistry; - private final AuthenticationService authenticationService; - private final MqttSessionService mqttSessionService; - private final SubscriptionService subscriptionService; + @Override + public MqttPacketType messageType() { + return MqttPacketType.CONNECT; + } @Override - protected void handleImpl(UnsafeMqttClient client, ConnectInPacket packet) { - if (checkPacketException(client, packet)) { + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + ConnectInPacket networkPacket) { + + if (checkPacketException(client, networkPacket)) { return; } - resolveClientConnectionConfig(client, packet); + resolveClientConnectionConfig(client, networkPacket); authenticationService - .auth(packet.username(), packet.password()) - .flatMap(ifTrue(client, packet, this::registerClient, BAD_USER_NAME_OR_PASSWORD, client::reject)) - .flatMap(ifTrue(client, packet, this::restoreSession, CLIENT_IDENTIFIER_NOT_VALID, client::reject)) + .auth(networkPacket.username(), networkPacket.password()) + .flatMap(ifTrue(client, networkPacket, this::registerClient, BAD_USER_NAME_OR_PASSWORD, connectAckReasonCode -> reject(client, connectAckReasonCode))) + .flatMap(ifTrue(client, networkPacket, this::restoreSession, CLIENT_IDENTIFIER_NOT_VALID, connectAckReasonCode -> reject(client, connectAckReasonCode))) .subscribe(); } - private Mono registerClient(UnsafeMqttClient client, ConnectInPacket packet) { + private void reject(ExternalMqttClient client, ConnectAckReasonCode connectAckReasonCode) { + client.send(messageOutFactoryService + .resolveFactory(client) + .newConnectAck(client, connectAckReasonCode)); + } + + private Mono registerClient(ExternalMqttClient client, ConnectInPacket networkPacket) { - String requestedClientId = packet.clientId(); + String requestedClientId = networkPacket.clientId(); if (StringUtils.isNotEmpty(requestedClientId)) { return clientIdRegistry .register(requestedClientId) @@ -78,23 +112,22 @@ private Mono registerClient(UnsafeMqttClient client, ConnectInPacket pa .map(ifTrue(newClientId, client::clientId))); } - private Mono restoreSession(UnsafeMqttClient client, ConnectInPacket packet) { - + private Mono restoreSession(MqttClient.UnsafeMqttClient client, ConnectInPacket packet) { if (packet.cleanStart()) { - return mqttSessionService + return sessionService .create(client.clientId()) .flatMap(session -> onConnected(client, packet, session, false)); } else { - return mqttSessionService + return sessionService .restore(client.clientId()) .flatMap(session -> onConnected(client, packet, session, true)) - .switchIfEmpty(Mono.defer(() -> mqttSessionService + .switchIfEmpty(Mono.defer(() -> sessionService .create(client.clientId()) .flatMap(session -> onConnected(client, packet, session, false)))); } } - private void resolveClientConnectionConfig(UnsafeMqttClient client, ConnectInPacket packet) { + private void resolveClientConnectionConfig(MqttClient.UnsafeMqttClient client, ConnectInPacket packet) { MqttConnection connection = client.connection(); MqttServerConnectionConfig serverConfig = connection.serverConnectionConfig(); @@ -105,8 +138,8 @@ private void resolveClientConnectionConfig(UnsafeMqttClient client, ConnectInPac // select result session expiry interval long sessionExpiryInterval = serverConfig.sessionsEnabled() - ? packet.sessionExpiryInterval() - : SESSION_EXPIRY_INTERVAL_DISABLED; + ? packet.sessionExpiryInterval() + : SESSION_EXPIRY_INTERVAL_DISABLED; if (sessionExpiryInterval == SESSION_EXPIRY_INTERVAL_UNDEFINED) { sessionExpiryInterval = serverConfig.defaultSessionExpiryInterval(); @@ -114,8 +147,8 @@ private void resolveClientConnectionConfig(UnsafeMqttClient client, ConnectInPac // select result receive max int receiveMaxPublishes = packet.receiveMaxPublishes() == RECEIVE_MAXIMUM_UNDEFINED - ? serverConfig.receiveMaxPublishes() - : Math.min(packet.receiveMaxPublishes(), serverConfig.receiveMaxPublishes()); + ? serverConfig.receiveMaxPublishes() + : Math.min(packet.receiveMaxPublishes(), serverConfig.receiveMaxPublishes()); // select result maximum packet size var maximumPacketSize = packet.maxPacketSize() == MAXIMUM_PACKET_SIZE_UNDEFINED @@ -124,8 +157,8 @@ private void resolveClientConnectionConfig(UnsafeMqttClient client, ConnectInPac // select result topic alias maximum var topicAliasMaxValue = packet.topicAliasMaxValue() == TOPIC_ALIAS_MAXIMUM_UNDEFINED - ? TOPIC_ALIAS_MAXIMUM_DISABLED - : Math.min(packet.topicAliasMaxValue(), serverConfig.topicAliasMaxValue()); + ? TOPIC_ALIAS_MAXIMUM_DISABLED + : Math.min(packet.topicAliasMaxValue(), serverConfig.topicAliasMaxValue()); connection.configure(new MqttClientConnectionConfig( serverConfig.maxQos(), @@ -145,7 +178,7 @@ private void resolveClientConnectionConfig(UnsafeMqttClient client, ConnectInPac } private Mono onConnected( - UnsafeMqttClient client, + MqttClient.UnsafeMqttClient client, ConnectInPacket packet, MqttSession session, boolean sessionRestored) { @@ -157,13 +190,13 @@ private Mono onConnected( // if it was closed in parallel if (connection.closed() && serverConfig.sessionsEnabled()) { // store the session again - return mqttSessionService.store(client.clientId(), session, clientConfig.sessionExpiryInterval()); + return sessionService.store(client.clientId(), session, clientConfig.sessionExpiryInterval()); } client.session(session); - var connectAck = client - .packetOutFactory() + var connectAck = messageOutFactoryService + .resolveFactory(client) .newConnectAck( client, ConnectAckReasonCode.SUCCESS, @@ -180,7 +213,7 @@ private Mono onConnected( .thenApply(result -> onSentConnAck(client, session, result))); } - private boolean onSentConnAck(UnsafeMqttClient client, MqttSession session, boolean result) { + private boolean onSentConnAck(MqttClient.UnsafeMqttClient client, MqttSession session, boolean result) { if (!result) { log.warning(client.clientId(), "Was issue with sending conn ack packet to client:[%s]"::formatted); @@ -191,13 +224,17 @@ private boolean onSentConnAck(UnsafeMqttClient client, MqttSession session, bool return true; } - private boolean checkPacketException(UnsafeMqttClient client, ConnectInPacket packet) { + private boolean checkPacketException(MqttClient.UnsafeMqttClient client, ConnectInPacket packet) { Exception exception = packet.exception(); if (exception instanceof ConnectionRejectException cre) { - client.reject(cre.getReasonCode()); + client.send(messageOutFactoryService + .resolveFactory(client) + .newConnectAck(client, cre.getReasonCode())); return true; } else if (exception instanceof MalformedPacketMqttException) { - client.reject(ConnectAckReasonCode.MALFORMED_PACKET); + client.send(messageOutFactoryService + .resolveFactory(client) + .newConnectAck(client, ConnectAckReasonCode.MALFORMED_PACKET)); return true; } return false; diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/DisconnectMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/DisconnectMqttInMessageHandler.java new file mode 100644 index 00000000..186eeaa2 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/DisconnectMqttInMessageHandler.java @@ -0,0 +1,35 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.model.reason.code.DisconnectReasonCode; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.DisconnectInPacket; +import lombok.CustomLog; + +@CustomLog +public class DisconnectMqttInMessageHandler extends AbstractMqttInMessageHandler { + + public DisconnectMqttInMessageHandler() { + super(ExternalMqttClient.class, DisconnectInPacket.class); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.DISCONNECT; + } + + @Override + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + DisconnectInPacket networkPacket) { + DisconnectReasonCode reasonCode = networkPacket.getReasonCode(); + if (reasonCode == DisconnectReasonCode.NORMAL_DISCONNECTION) { + log.info(client.clientId(), "Disconnect client:[%s]"::formatted); + } else { + log.error("Disconnect client:[%s] by error reason:[%s]".formatted(client, reasonCode)); + } + connection.close(); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PendingOutResponseMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PendingOutResponseMqttInMessageHandler.java new file mode 100644 index 00000000..eeb3fd6d --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PendingOutResponseMqttInMessageHandler.java @@ -0,0 +1,23 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.MqttSession; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.HasPacketId; +import javasabr.mqtt.network.packet.in.MqttReadablePacket; + +public abstract class PendingOutResponseMqttInMessageHandler

+ extends AbstractMqttInMessageHandler { + + protected PendingOutResponseMqttInMessageHandler(Class

expectedNetworkPacket) { + super(ExternalMqttClient.class, expectedNetworkPacket); + } + + @Override + protected void processReceived(MqttConnection connection, ExternalMqttClient client, P networkPacket) { + MqttSession session = client.session(); + if (session != null) { + session.updateOutPendingPacket(client, networkPacket); + } + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishAckMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishAckMqttInMessageHandler.java new file mode 100644 index 00000000..e7c021ed --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishAckMqttInMessageHandler.java @@ -0,0 +1,16 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.PublishAckInPacket; + +public class PublishAckMqttInMessageHandler extends PendingOutResponseMqttInMessageHandler { + + public PublishAckMqttInMessageHandler() { + super(PublishAckInPacket.class); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.PUBLISH_ACK; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishCompleteMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishCompleteMqttInMessageHandler.java new file mode 100644 index 00000000..6d807774 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishCompleteMqttInMessageHandler.java @@ -0,0 +1,17 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.PublishCompleteInPacket; + +public class PublishCompleteMqttInMessageHandler extends + PendingOutResponseMqttInMessageHandler { + + public PublishCompleteMqttInMessageHandler() { + super(PublishCompleteInPacket.class); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.PUBLISH_COMPLETED; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishMqttInMessageHandler.java new file mode 100644 index 00000000..c61bcf1b --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishMqttInMessageHandler.java @@ -0,0 +1,33 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.PublishReceivingService; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class PublishMqttInMessageHandler extends AbstractMqttInMessageHandler { + + PublishReceivingService publishReceivingService; + + public PublishMqttInMessageHandler(PublishReceivingService publishReceivingService) { + super(ExternalMqttClient.class, PublishInPacket.class); + this.publishReceivingService = publishReceivingService; + } + + @Override + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + PublishInPacket networkPacket) { + publishReceivingService.processReceivedPublish(client, networkPacket); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.PUBLISH; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReceiveMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReceiveMqttInMessageHandler.java new file mode 100644 index 00000000..ee15cc93 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReceiveMqttInMessageHandler.java @@ -0,0 +1,17 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.PublishReceivedInPacket; + +public class PublishReceiveMqttInMessageHandler extends + PendingOutResponseMqttInMessageHandler { + + public PublishReceiveMqttInMessageHandler() { + super(PublishReceivedInPacket.class); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.PUBLISH_RECEIVED; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReleaseMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReleaseMqttInMessageHandler.java new file mode 100644 index 00000000..3d1103aa --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/PublishReleaseMqttInMessageHandler.java @@ -0,0 +1,31 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.MqttSession; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.PublishReleaseInPacket; + +public class PublishReleaseMqttInMessageHandler + extends AbstractMqttInMessageHandler { + + public PublishReleaseMqttInMessageHandler() { + super(ExternalMqttClient.class, PublishReleaseInPacket.class); + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.PUBLISH_RELEASED; + } + + @Override + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + PublishReleaseInPacket networkPacket) { + MqttSession session = client.session(); + if (session != null) { + session.updateInPendingPacket(client, networkPacket); + } + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/SubscribeMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/SubscribeMqttInMessageHandler.java new file mode 100644 index 00000000..c3329008 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/SubscribeMqttInMessageHandler.java @@ -0,0 +1,76 @@ +package javasabr.mqtt.service.message.handler.impl; + +import static java.lang.Byte.toUnsignedInt; +import static javasabr.mqtt.model.reason.code.SubscribeAckReasonCode.SHARED_SUBSCRIPTIONS_NOT_SUPPORTED; +import static javasabr.mqtt.model.reason.code.SubscribeAckReasonCode.WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED; + +import java.util.Set; +import javasabr.mqtt.model.reason.code.DisconnectReasonCode; +import javasabr.mqtt.model.reason.code.SubscribeAckReasonCode; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.SubscribeInPacket; +import javasabr.mqtt.network.packet.out.MqttWritablePacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.rlib.collections.array.Array; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class SubscribeMqttInMessageHandler extends + AbstractMqttInMessageHandler { + + private final static Set INVALID_ACK_CODE = Set.of( + SHARED_SUBSCRIPTIONS_NOT_SUPPORTED, + WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED); + + SubscriptionService subscriptionService; + MessageOutFactoryService messageOutFactoryService; + + public SubscribeMqttInMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(ExternalMqttClient.class, SubscribeInPacket.class); + this.subscriptionService = subscriptionService; + this.messageOutFactoryService = messageOutFactoryService; + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.SUBSCRIBE; + } + + @Override + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + SubscribeInPacket networkPacket) { + + Array ackReasonCodes = subscriptionService + .subscribe(client, networkPacket.getTopicFilters()); + MqttWritablePacket subscribeAck = messageOutFactoryService + .resolveFactory(client) + .newSubscribeAck(networkPacket.getPacketId(), ackReasonCodes); + + client.send(subscribeAck); + + SubscribeAckReasonCode anyReason = ackReasonCodes + .reversedIterations() + .findAny(INVALID_ACK_CODE, Set::contains); + + if (anyReason != null) { + var disconnectReasonCode = DisconnectReasonCode.of(toUnsignedInt(anyReason.getValue())); + MqttWritablePacket disconnect = messageOutFactoryService + .resolveFactory(client) + .newDisconnect(client, disconnectReasonCode); + + client + .sendWithFeedback(disconnect) + .thenAccept(_ -> client + .connection() + .close()); + } + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/UnsubscribeMqttInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/UnsubscribeMqttInMessageHandler.java new file mode 100644 index 00000000..c789849a --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/UnsubscribeMqttInMessageHandler.java @@ -0,0 +1,46 @@ +package javasabr.mqtt.service.message.handler.impl; + +import javasabr.mqtt.model.reason.code.UnsubscribeAckReasonCode; +import javasabr.mqtt.network.MqttConnection; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.MqttPacketType; +import javasabr.mqtt.network.packet.in.UnsubscribeInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.rlib.collections.array.Array; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class UnsubscribeMqttInMessageHandler extends AbstractMqttInMessageHandler { + + SubscriptionService subscriptionService; + MessageOutFactoryService messageOutFactoryService; + + public UnsubscribeMqttInMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(ExternalMqttClient.class, UnsubscribeInPacket.class); + this.subscriptionService = subscriptionService; + this.messageOutFactoryService = messageOutFactoryService; + } + + @Override + public MqttPacketType messageType() { + return MqttPacketType.UNSUBSCRIBE; + } + + @Override + protected void processReceived( + MqttConnection connection, + ExternalMqttClient client, + UnsubscribeInPacket networkPacket) { + + Array ackReasonCodes = subscriptionService + .unsubscribe(client, networkPacket.getTopicFilters()); + + client.send(messageOutFactoryService + .resolveFactory(client) + .newUnsubscribeAck(networkPacket.getPacketId(), ackReasonCodes)); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/message/handler/impl/package-info.java b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/package-info.java new file mode 100644 index 00000000..00eced51 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.mqtt.service.message.handler.impl; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/network/src/main/java/javasabr/mqtt/network/handler/client/package-info.java b/service/src/main/java/javasabr/mqtt/service/message/handler/package-info.java similarity index 54% rename from network/src/main/java/javasabr/mqtt/network/handler/client/package-info.java rename to service/src/main/java/javasabr/mqtt/service/message/handler/package-info.java index 4af32af9..c74e0615 100644 --- a/network/src/main/java/javasabr/mqtt/network/handler/client/package-info.java +++ b/service/src/main/java/javasabr/mqtt/service/message/handler/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package javasabr.mqtt.network.handler.client; +package javasabr.mqtt.service.message.handler; import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/network/src/main/java/javasabr/mqtt/network/out/Mqtt311PacketOutFactory.java b/service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt311MessageOutFactory.java similarity index 95% rename from network/src/main/java/javasabr/mqtt/network/out/Mqtt311PacketOutFactory.java rename to service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt311MessageOutFactory.java index 74aa7e7a..0351f9ca 100644 --- a/network/src/main/java/javasabr/mqtt/network/out/Mqtt311PacketOutFactory.java +++ b/service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt311MessageOutFactory.java @@ -1,5 +1,6 @@ -package javasabr.mqtt.network.out; +package javasabr.mqtt.service.message.out.factory; +import javasabr.mqtt.model.MqttVersion; import javasabr.mqtt.model.QoS; import javasabr.mqtt.model.data.type.StringPair; import javasabr.mqtt.model.reason.code.AuthenticateReasonCode; @@ -27,7 +28,12 @@ import javasabr.mqtt.network.packet.out.UnsubscribeAck311OutPacket; import javasabr.rlib.collections.array.Array; -public class Mqtt311PacketOutFactory extends MqttPacketOutFactory { +public class Mqtt311MessageOutFactory extends MqttMessageOutFactory { + + @Override + public MqttVersion mqttVersion() { + return MqttVersion.MQTT_3_1_1; + } @Override public MqttWritablePacket newConnectAck( diff --git a/network/src/main/java/javasabr/mqtt/network/out/Mqtt5PacketOutFactory.java b/service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt5MessageOutFactory.java similarity index 95% rename from network/src/main/java/javasabr/mqtt/network/out/Mqtt5PacketOutFactory.java rename to service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt5MessageOutFactory.java index 72d2f15b..5329f602 100644 --- a/network/src/main/java/javasabr/mqtt/network/out/Mqtt5PacketOutFactory.java +++ b/service/src/main/java/javasabr/mqtt/service/message/out/factory/Mqtt5MessageOutFactory.java @@ -1,6 +1,7 @@ -package javasabr.mqtt.network.out; +package javasabr.mqtt.service.message.out.factory; import javasabr.mqtt.model.MqttClientConnectionConfig; +import javasabr.mqtt.model.MqttVersion; import javasabr.mqtt.model.QoS; import javasabr.mqtt.model.data.type.StringPair; import javasabr.mqtt.model.reason.code.AuthenticateReasonCode; @@ -27,7 +28,12 @@ import javasabr.mqtt.network.packet.out.UnsubscribeAck5OutPacket; import javasabr.rlib.collections.array.Array; -public class Mqtt5PacketOutFactory extends Mqtt311PacketOutFactory { +public class Mqtt5MessageOutFactory extends Mqtt311MessageOutFactory { + + @Override + public MqttVersion mqttVersion() { + return MqttVersion.MQTT_5; + } @Override public MqttWritablePacket newConnectAck( diff --git a/network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactory.java b/service/src/main/java/javasabr/mqtt/service/message/out/factory/MqttMessageOutFactory.java similarity index 97% rename from network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactory.java rename to service/src/main/java/javasabr/mqtt/service/message/out/factory/MqttMessageOutFactory.java index 3f687841..e5f79952 100644 --- a/network/src/main/java/javasabr/mqtt/network/out/MqttPacketOutFactory.java +++ b/service/src/main/java/javasabr/mqtt/service/message/out/factory/MqttMessageOutFactory.java @@ -1,6 +1,7 @@ -package javasabr.mqtt.network.out; +package javasabr.mqtt.service.message.out.factory; import javasabr.mqtt.model.MqttClientConnectionConfig; +import javasabr.mqtt.model.MqttVersion; import javasabr.mqtt.model.QoS; import javasabr.mqtt.model.data.type.StringPair; import javasabr.mqtt.model.reason.code.AuthenticateReasonCode; @@ -20,7 +21,9 @@ import javasabr.rlib.common.util.ArrayUtils; import javasabr.rlib.common.util.StringUtils; -public abstract class MqttPacketOutFactory { +public abstract class MqttMessageOutFactory { + + public abstract MqttVersion mqttVersion(); public abstract MqttWritablePacket newConnectAck( MqttClient client, diff --git a/application/src/main/java/javasabr/mqtt/application/config/package-info.java b/service/src/main/java/javasabr/mqtt/service/message/out/factory/package-info.java similarity index 52% rename from application/src/main/java/javasabr/mqtt/application/config/package-info.java rename to service/src/main/java/javasabr/mqtt/service/message/out/factory/package-info.java index 36f8e56b..b5d182f4 100644 --- a/application/src/main/java/javasabr/mqtt/application/config/package-info.java +++ b/service/src/main/java/javasabr/mqtt/service/message/out/factory/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package javasabr.mqtt.application.config; +package javasabr.mqtt.service.message.out.factory; import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/network/src/main/java/javasabr/mqtt/network/handler/PublishInHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishInMessageHandler.java similarity index 53% rename from network/src/main/java/javasabr/mqtt/network/handler/PublishInHandler.java rename to service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishInMessageHandler.java index a4423e02..24f76c29 100644 --- a/network/src/main/java/javasabr/mqtt/network/handler/PublishInHandler.java +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishInMessageHandler.java @@ -1,12 +1,12 @@ -package javasabr.mqtt.network.handler; +package javasabr.mqtt.service.publish.handler; +import javasabr.mqtt.model.QoS; import javasabr.mqtt.network.MqttClient; import javasabr.mqtt.network.packet.in.PublishInPacket; -/** - * Interface to handle incoming publish packets. - */ -public interface PublishInHandler { +public interface MqttPublishInMessageHandler { + + QoS qos(); void handle(MqttClient client, PublishInPacket packet); } diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishOutMessageHandler.java new file mode 100644 index 00000000..c6c00508 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/MqttPublishOutMessageHandler.java @@ -0,0 +1,12 @@ +package javasabr.mqtt.service.publish.handler; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.model.subscriber.SingleSubscriber; +import javasabr.mqtt.network.packet.in.PublishInPacket; + +public interface MqttPublishOutMessageHandler { + + QoS qos(); + + PublishHandlingResult handle(PublishInPacket packet, SingleSubscriber subscriber); +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/PublishHandlingResult.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/PublishHandlingResult.java new file mode 100644 index 00000000..864e8d81 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/PublishHandlingResult.java @@ -0,0 +1,46 @@ +package javasabr.mqtt.service.publish.handler; + +import javasabr.mqtt.model.reason.code.PublishAckReasonCode; +import javasabr.mqtt.model.reason.code.PublishReceivedReasonCode; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; + +@Getter +@RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public enum PublishHandlingResult { + SUCCESS(false, PublishAckReasonCode.SUCCESS, PublishReceivedReasonCode.SUCCESS), + SKIPPED(false, PublishAckReasonCode.SUCCESS, PublishReceivedReasonCode.SUCCESS), + + // ERRORS + UNSPECIFIED_ERROR(true, PublishAckReasonCode.UNSPECIFIED_ERROR, PublishReceivedReasonCode.UNSPECIFIED_ERROR), + IMPLEMENTATION_SPECIFIC_ERROR( + true, + PublishAckReasonCode.IMPLEMENTATION_SPECIFIC_ERROR, + PublishReceivedReasonCode.IMPLEMENTATION_SPECIFIC_ERROR), + NOT_AUTHORIZED(true, PublishAckReasonCode.NOT_AUTHORIZED, PublishReceivedReasonCode.NOT_AUTHORIZED), + TOPIC_NAME_INVALID(true, PublishAckReasonCode.TOPIC_NAME_INVALID, PublishReceivedReasonCode.TOPIC_NAME_INVALID), + PACKET_IDENTIFIER_IN_USE( + true, + PublishAckReasonCode.PACKET_IDENTIFIER_IN_USE, + PublishReceivedReasonCode.PACKET_IDENTIFIER_IN_USE), + QUOTA_EXCEEDED(true, PublishAckReasonCode.QUOTA_EXCEEDED, PublishReceivedReasonCode.QUOTA_EXCEEDED), + PAYLOAD_FORMAT_INVALID( + true, + PublishAckReasonCode.PAYLOAD_FORMAT_INVALID, + PublishReceivedReasonCode.PAYLOAD_FORMAT_INVALID), + + // CUSTOM + NOT_EXPECTED_CLIENT( + true, + PublishAckReasonCode.UNSPECIFIED_ERROR, + PublishReceivedReasonCode.UNSPECIFIED_ERROR); + + boolean error; + PublishAckReasonCode ackReasonCode; + PublishReceivedReasonCode receivedReasonCode; +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishInMessageHandler.java new file mode 100644 index 00000000..dfb4914d --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishInMessageHandler.java @@ -0,0 +1,93 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.subscriber.SingleSubscriber; +import javasabr.mqtt.model.topic.TopicName; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.MqttPublishInMessageHandler; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import javasabr.rlib.collections.array.Array; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@CustomLog +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public abstract class AbstractMqttPublishInMessageHandler + implements MqttPublishInMessageHandler { + + Class expectedClient; + SubscriptionService subscriptionService; + PublishDeliveringService publishDeliveringService; + + @Override + public void handle(MqttClient client, PublishInPacket packet) { + if (!expectedClient.isInstance(client)) { + log.warning(client, "Not expected client:[%s]"::formatted); + return; + } + handleImpl(expectedClient.cast(client), packet); + } + + protected void handleImpl(C client, PublishInPacket packet) { + TopicName topicName = packet.getTopicName(); + if (!subscriptionService.isValid(topicName)) { + handleInvalidTopic(client, packet.getPacketId(), topicName); + return; + } + + Array subscribers = subscriptionService.findSubscribers(topicName); + if (subscribers.isEmpty()) { + handleEmptySubscriptions(client, packet.getPacketId(), topicName); + return; + } + + for (SingleSubscriber subscriber : subscribers) { + PublishHandlingResult checkResult = checkSubscriber(client, packet, subscriber); + if (checkResult.error()) { + handleError(client, packet.getPacketId(), checkResult); + return; + } + } + + int count = 0; + PublishHandlingResult errorResult = null; + for (SingleSubscriber subscriber : subscribers) { + PublishHandlingResult result = startDelivering(client, packet, subscriber); + if (result.error()) { + errorResult = result; + } else if(result == PublishHandlingResult.SUCCESS) { + count++; + } + } + + if (errorResult != null) { + handleError(client, packet.getPacketId(), errorResult); + } else { + handleSuccessfulResult(client, packet, count); + } + } + + protected void handleInvalidTopic(C client, int messageId, TopicName topicName) {} + + protected void handleEmptySubscriptions(C client, int messageId, TopicName topicName) {} + + protected void handleError(C client, int messageId, PublishHandlingResult handlingResult) {} + + protected void handleSuccessfulResult(C client, PublishInPacket packet, int subscribers) {} + + protected PublishHandlingResult checkSubscriber( + C client, + PublishInPacket packet, + SingleSubscriber subscriber) { + return PublishHandlingResult.SUCCESS; + } + + protected PublishHandlingResult startDelivering(C client, PublishInPacket packet, SingleSubscriber subscriber) { + return publishDeliveringService.startDelivering(packet, subscriber); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishOutMessageHandler.java new file mode 100644 index 00000000..6de94050 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/AbstractMqttPublishOutMessageHandler.java @@ -0,0 +1,62 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.MqttProperties; +import javasabr.mqtt.model.subscriber.SingleSubscriber; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.network.packet.out.PublishOutPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.MqttPublishOutMessageHandler; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@CustomLog +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public abstract class AbstractMqttPublishOutMessageHandler + implements MqttPublishOutMessageHandler { + + Class expectedClient; + SubscriptionService subscriptionService; + MessageOutFactoryService messageOutFactoryService; + + @Override + public PublishHandlingResult handle(PublishInPacket packet, SingleSubscriber subscriber) { + MqttClient mqttClient = subscriptionService.resolveClient(subscriber); + if (!expectedClient.isInstance(mqttClient)) { + log.warning(mqttClient, "Accepted not expected client:[%s]"::formatted); + return PublishHandlingResult.NOT_EXPECTED_CLIENT; + } + return handleImpl(packet, expectedClient.cast(mqttClient)); + } + + protected abstract PublishHandlingResult handleImpl(PublishInPacket packet, C client) ; + + protected void startDelivering( + MqttClient client, + PublishInPacket packet, + int messageId, + boolean duplicate) { + PublishOutPacket publish = messageOutFactoryService + .resolveFactory(client) + .newPublish( + messageId, + qos(), + packet.isRetained(), + duplicate, + packet + .getTopicName() + .toString(), + MqttProperties.TOPIC_ALIAS_NOT_SET, + packet.getPayload(), + packet.isPayloadFormatIndicator(), + packet.getResponseTopic(), + packet.getCorrelationData(), + packet.userProperties()); + client.send(publish); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/PersistedMqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/PersistedMqttPublishOutMessageHandler.java new file mode 100644 index 00000000..e596bdf7 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/PersistedMqttPublishOutMessageHandler.java @@ -0,0 +1,61 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.MqttSession; +import javasabr.mqtt.network.MqttSession.PendingMessageHandler; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.HasPacketId; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public abstract class PersistedMqttPublishOutMessageHandler extends AbstractMqttPublishOutMessageHandler { + + PendingMessageHandler pendingMessageHandler; + + protected PersistedMqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(ExternalMqttClient.class, subscriptionService, messageOutFactoryService); + this.pendingMessageHandler = new PendingMessageHandler() { + @Override + public boolean handleResponse(MqttClient client, HasPacketId response) { + return handleReceivedResponse(client, response); + } + @Override + public void resend(MqttClient client, PublishInPacket packet, int packetId) { + tryToDeliverAgain(client, packet, packetId); + } + }; + } + + @Override + protected PublishHandlingResult handleImpl(PublishInPacket packet, ExternalMqttClient client) { + + MqttSession session = client.session(); + if (session == null) { + return PublishHandlingResult.SKIPPED; + } + + // generate new uniq packet id per client + int packetId = session.nextPacketId(); + // register waiting async response + session.registerOutPublish(packet, pendingMessageHandler, packetId); + + // send publish + startDelivering(client, packet, packetId, false); + return PublishHandlingResult.SUCCESS; + } + + protected boolean handleReceivedResponse(MqttClient client, HasPacketId response) { + return false; + } + + protected void tryToDeliverAgain(MqttClient client, PublishInPacket packet, int messageId) { + startDelivering(client, packet, messageId, true); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishInMessageHandler.java new file mode 100644 index 00000000..5fc10f21 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishInMessageHandler.java @@ -0,0 +1,20 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.SubscriptionService; + +public class Qos0MqttPublishInMessageHandler extends AbstractMqttPublishInMessageHandler { + + public Qos0MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService) { + super(ExternalMqttClient.class, subscriptionService, publishDeliveringService); + } + + @Override + public QoS qos() { + return QoS.AT_MOST_ONCE; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishOutMessageHandler.java new file mode 100644 index 00000000..39a6aad7 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos0MqttPublishOutMessageHandler.java @@ -0,0 +1,28 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; + +public class Qos0MqttPublishOutMessageHandler extends AbstractMqttPublishOutMessageHandler { + + public Qos0MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(ExternalMqttClient.class, subscriptionService, messageOutFactoryService); + } + + @Override + public QoS qos() { + return QoS.AT_MOST_ONCE; + } + + @Override + protected PublishHandlingResult handleImpl(PublishInPacket packet, ExternalMqttClient client) { + startDelivering(client, packet, packet.getPacketId(), false); + return PublishHandlingResult.SUCCESS; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishInMessageHandler.java new file mode 100644 index 00000000..c7b0218e --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishInMessageHandler.java @@ -0,0 +1,64 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.model.reason.code.PublishAckReasonCode; +import javasabr.mqtt.model.topic.TopicName; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class Qos1MqttPublishInMessageHandler extends Qos0MqttPublishInMessageHandler { + + MessageOutFactoryService messageOutFactoryService; + + public Qos1MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService, + MessageOutFactoryService messageOutFactoryService) { + super(subscriptionService, publishDeliveringService); + this.messageOutFactoryService = messageOutFactoryService; + } + + @Override + public QoS qos() { + return QoS.AT_LEAST_ONCE; + } + + @Override + protected void handleEmptySubscriptions(ExternalMqttClient client, int messageId, TopicName topicName) { + super.handleEmptySubscriptions(client, messageId, topicName); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishAck(messageId, PublishAckReasonCode.NO_MATCHING_SUBSCRIBERS)); + } + + @Override + protected void handleInvalidTopic(ExternalMqttClient client, int messageId, TopicName topicName) { + super.handleInvalidTopic(client, messageId, topicName); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishAck(messageId, PublishAckReasonCode.TOPIC_NAME_INVALID)); + } + + @Override + protected void handleError(ExternalMqttClient client, int messageId, PublishHandlingResult handlingResult) { + super.handleError(client, messageId, handlingResult); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishAck(messageId, handlingResult.ackReasonCode())); + } + + @Override + protected void handleSuccessfulResult(ExternalMqttClient client, PublishInPacket packet, int subscribers) { + super.handleSuccessfulResult(client, packet, subscribers); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishAck(packet.getPacketId(), PublishAckReasonCode.SUCCESS)); + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishOutMessageHandler.java new file mode 100644 index 00000000..332d3dc0 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos1MqttPublishOutMessageHandler.java @@ -0,0 +1,31 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.packet.HasPacketId; +import javasabr.mqtt.network.packet.in.PublishAckInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; + +public class Qos1MqttPublishOutMessageHandler extends PersistedMqttPublishOutMessageHandler { + + public Qos1MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(subscriptionService, messageOutFactoryService); + } + + @Override + public QoS qos() { + return QoS.AT_LEAST_ONCE; + } + + @Override + protected boolean handleReceivedResponse(MqttClient client, HasPacketId response) { + if (!(response instanceof PublishAckInPacket)) { + throw new IllegalStateException("Unexpected response: " + response); + } + // just return 'true' to remove pending packet from session + return true; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishInMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishInMessageHandler.java new file mode 100644 index 00000000..301a8d75 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishInMessageHandler.java @@ -0,0 +1,106 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.model.reason.code.PublishCompletedReasonCode; +import javasabr.mqtt.model.reason.code.PublishReceivedReasonCode; +import javasabr.mqtt.model.topic.TopicName; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.MqttSession; +import javasabr.mqtt.network.MqttSession.PendingMessageHandler; +import javasabr.mqtt.network.client.ExternalMqttClient; +import javasabr.mqtt.network.packet.HasPacketId; +import javasabr.mqtt.network.packet.in.PublishInPacket; +import javasabr.mqtt.network.packet.in.PublishReleaseInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.PublishDeliveringService; +import javasabr.mqtt.service.SubscriptionService; +import javasabr.mqtt.service.publish.handler.PublishHandlingResult; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; + +@CustomLog +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class Qos2MqttPublishInMessageHandler extends Qos0MqttPublishInMessageHandler { + + MessageOutFactoryService messageOutFactoryService; + PendingMessageHandler pendingMessageHandler; + + public Qos2MqttPublishInMessageHandler( + SubscriptionService subscriptionService, + PublishDeliveringService publishDeliveringService, + MessageOutFactoryService messageOutFactoryService) { + super(subscriptionService, publishDeliveringService); + this.messageOutFactoryService = messageOutFactoryService; + this.pendingMessageHandler = this::processPublishRelease; + } + + @Override + public QoS qos() { + return QoS.EXACTLY_ONCE; + } + + @Override + protected void handleImpl(ExternalMqttClient client, PublishInPacket packet) { + MqttSession session = client.session(); + if (session == null) { + return; + } + // if this packet is re-try from client + if (packet.isDuplicate()) { + // if this packet was accepted before then we can skip it + if (session.hasInPending(packet.getPacketId())) { + return; + } + } + super.handleImpl(client, packet); + } + + @Override + protected void handleInvalidTopic(ExternalMqttClient client, int messageId, TopicName topicName) { + super.handleInvalidTopic(client, messageId, topicName); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishReceived(messageId, PublishReceivedReasonCode.TOPIC_NAME_INVALID)); + } + + @Override + protected void handleEmptySubscriptions(ExternalMqttClient client, int messageId, TopicName topicName) { + super.handleEmptySubscriptions(client, messageId, topicName); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishReceived(messageId, PublishReceivedReasonCode.NO_MATCHING_SUBSCRIBERS)); + } + + @Override + protected void handleError(ExternalMqttClient client, int messageId, PublishHandlingResult handlingResult) { + super.handleError(client, messageId, handlingResult); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishReceived(messageId, handlingResult.receivedReasonCode())); + } + + @Override + protected void handleSuccessfulResult(ExternalMqttClient client, PublishInPacket packet, int subscribers) { + super.handleSuccessfulResult(client, packet, subscribers); + MqttSession session = client.session(); + if (session == null) { + return; + } + session.registerInPublish(packet, pendingMessageHandler, packet.getPacketId()); + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishReceived(packet.getPacketId(), PublishReceivedReasonCode.SUCCESS)); + } + + private boolean processPublishRelease(MqttClient client, HasPacketId response) { + if (!(response instanceof PublishReleaseInPacket)) { + throw new IllegalStateException("Unexpected response " + response); + } + + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishCompleted(response.packetId(), PublishCompletedReasonCode.SUCCESS)); + return true; + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishOutMessageHandler.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishOutMessageHandler.java new file mode 100644 index 00000000..86ff975a --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/Qos2MqttPublishOutMessageHandler.java @@ -0,0 +1,39 @@ +package javasabr.mqtt.service.publish.handler.impl; + +import static javasabr.mqtt.model.reason.code.PublishReleaseReasonCode.SUCCESS; + +import javasabr.mqtt.model.QoS; +import javasabr.mqtt.network.MqttClient; +import javasabr.mqtt.network.packet.HasPacketId; +import javasabr.mqtt.network.packet.in.PublishCompleteInPacket; +import javasabr.mqtt.network.packet.in.PublishReceivedInPacket; +import javasabr.mqtt.service.MessageOutFactoryService; +import javasabr.mqtt.service.SubscriptionService; + +public class Qos2MqttPublishOutMessageHandler extends PersistedMqttPublishOutMessageHandler { + + public Qos2MqttPublishOutMessageHandler( + SubscriptionService subscriptionService, + MessageOutFactoryService messageOutFactoryService) { + super(subscriptionService, messageOutFactoryService); + } + + @Override + public QoS qos() { + return QoS.EXACTLY_ONCE; + } + + @Override + protected boolean handleReceivedResponse(MqttClient client, HasPacketId response) { + if (response instanceof PublishReceivedInPacket) { + client.send(messageOutFactoryService + .resolveFactory(client) + .newPublishRelease(response.packetId(), SUCCESS)); + return false; + } else if (response instanceof PublishCompleteInPacket) { + return true; + } else { + throw new IllegalStateException("Unexpected response: " + response); + } + } +} diff --git a/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/package-info.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/package-info.java new file mode 100644 index 00000000..e72d7230 --- /dev/null +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.mqtt.service.publish.handler.impl; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/service/src/main/java/javasabr/mqtt/service/handler/in/package-info.java b/service/src/main/java/javasabr/mqtt/service/publish/handler/package-info.java similarity index 54% rename from service/src/main/java/javasabr/mqtt/service/handler/in/package-info.java rename to service/src/main/java/javasabr/mqtt/service/publish/handler/package-info.java index c8c50cbb..9bf8dcb7 100644 --- a/service/src/main/java/javasabr/mqtt/service/handler/in/package-info.java +++ b/service/src/main/java/javasabr/mqtt/service/publish/handler/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package javasabr.mqtt.service.handler.in; +package javasabr.mqtt.service.publish.handler; import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 78ddf787..ddddae46 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,4 +7,5 @@ include(":model") include(":network") include(":service") include(":application") +include(":library") include(":test-support") \ No newline at end of file