View Javadoc
1   package org.metricshub.ipmi.core.connection;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * IPMI Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 Verax Systems, MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import org.metricshub.ipmi.core.coding.PayloadCoder;
26  import org.metricshub.ipmi.core.coding.protocol.Ipmiv20Message;
27  import org.metricshub.ipmi.core.connection.queue.MessageQueue;
28  import org.metricshub.ipmi.core.sm.StateMachine;
29  import org.metricshub.ipmi.core.sm.events.Sendv20Message;
30  import org.metricshub.ipmi.core.sm.states.SessionValid;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /**
36   * Class used for handling outgoing and incoming messages for a {@link Connection}.
37   */
38  public abstract class MessageHandler {
39  
40      private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class);
41  
42      protected int lastReceivedSequenceNumber = 0;
43      protected final MessageQueue messageQueue;
44      protected final Connection connection;
45  
46      public MessageHandler(Connection connection, int timeout, int minSequenceNumber, int maxSequenceNumber) {
47          this.messageQueue = new MessageQueue(connection, timeout, minSequenceNumber, maxSequenceNumber);
48          this.connection = connection;
49      }
50  
51      /**
52       * Attempts to send message encoded by given {@link PayloadCoder} to the remote system.
53       *
54       * @param payloadCoder
55       *          instance of {@link PayloadCoder} that will produce payload for the message being sent.
56       * @param stateMachine
57       *          {@link StateMachine} for the currenr connection.
58       * @param sessionId
59       *          ID of the current session.
60       * @param isOneWay
61       *          flag indicating, if message is one way and we shouldn't await response,
62       *          or it isn't and needs response from remote system
63       * @return sequence number of the sent message
64       * @throws ConnectionException when could not send message due to some problems with connection
65       */
66      public int sendMessage(PayloadCoder payloadCoder, StateMachine stateMachine, int sessionId, boolean isOneWay)
67              throws ConnectionException {
68          validateSessionState(stateMachine);
69  
70          int seq = isOneWay ? messageQueue.getSequenceNumber() : messageQueue.add(payloadCoder);
71          if (seq > 0) {
72              stateMachine.doTransition(new Sendv20Message(payloadCoder, sessionId, seq, connection.getNextSessionSequenceNumber()));
73          }
74  
75          return seq;
76      }
77  
78      /**
79       * Attempts to retry sending message with given tag, assuming that this message exists in message queue.
80       *
81       * @param tag
82       *          tag of the message that we want to resend
83       * @param stateMachine
84       *          {@link StateMachine} for the currenr connection.
85       * @param sessionId
86       *          ID of the current session.
87       * @return sequence number of the retried message (should be the same as original message tag) or -1 if no message was found in the queue.
88       * @throws ConnectionException when could not send message due to some problems with connection
89       */
90      public int retryMessage(int tag, StateMachine stateMachine, int sessionId) throws ConnectionException {
91          validateSessionState(stateMachine);
92  
93          PayloadCoder payloadCoder = messageQueue.getMessageFromQueue(tag);
94  
95          if (payloadCoder == null) {
96              return  -1;
97          }
98  
99          stateMachine.doTransition(new Sendv20Message(payloadCoder, sessionId, tag, connection.getNextSessionSequenceNumber()));
100 
101         return tag;
102     }
103 
104     private void validateSessionState(StateMachine stateMachine) throws ConnectionException {
105         if (stateMachine.getCurrent().getClass() != SessionValid.class) {
106             throw new ConnectionException("Illegal connection state: " + stateMachine.getCurrent().getClass().getSimpleName());
107         }
108     }
109 
110     /**
111      * Checks if received message is inside "sliding window range", and if it is,
112      * further processes the message in a cimplementation-specific way.
113      *
114      * @param message
115      */
116     public void handleIncomingMessage(Ipmiv20Message message) {
117 
118         int seq = message.getSessionSequenceNumber();
119 
120         if (seq != 0 && (seq > lastReceivedSequenceNumber + 15 || seq < lastReceivedSequenceNumber - 16)) {
121             logger.debug("Dropping message " + seq);
122             return; // if the message's sequence number gets out of the sliding
123             // window range we need to drop it
124         }
125 
126         if (seq != 0) {
127             lastReceivedSequenceNumber = (seq > lastReceivedSequenceNumber ? seq : lastReceivedSequenceNumber);
128         }
129 
130         handleIncomingMessageInternal(message);
131     }
132 
133     public void setTimeout(int timeout) {
134         messageQueue.setTimeout(timeout);
135     }
136 
137     public void tearDown() {
138         messageQueue.tearDown();
139     }
140 
141     public int getSequenceNumber() {
142         return messageQueue.getSequenceNumber();
143     }
144 
145     /**
146      * Abstract method for implementation-specific logic for handling incomming IPMI message.
147      *
148      * @param message
149      *          IPMI message received from BMC
150      */
151     protected abstract void handleIncomingMessageInternal(Ipmiv20Message message);
152 
153 }