View Javadoc
1   package org.metricshub.ipmi.core.sm;
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 java.io.IOException;
26  import java.net.InetAddress;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.metricshub.ipmi.core.coding.rmcp.RmcpDecoder;
31  import org.metricshub.ipmi.core.common.Constants;
32  import org.metricshub.ipmi.core.sm.actions.StateMachineAction;
33  import org.metricshub.ipmi.core.sm.events.StateMachineEvent;
34  import org.metricshub.ipmi.core.sm.states.SessionValid;
35  import org.metricshub.ipmi.core.sm.states.State;
36  import org.metricshub.ipmi.core.sm.states.Uninitialized;
37  import org.metricshub.ipmi.core.transport.Messenger;
38  import org.metricshub.ipmi.core.transport.UdpListener;
39  import org.metricshub.ipmi.core.transport.UdpMessage;
40  
41  /**
42   * State machine for connecting and acquiring session with the remote host via
43   * IPMI v.2.0.
44   */
45  public class StateMachine implements UdpListener {
46  
47      private List<MachineObserver> observers;
48  
49      private State current;
50  
51      private Messenger messenger;
52      private InetAddress remoteMachineAddress;
53      private int remoteMachinePort;
54  
55      private boolean initialized;
56  
57      public State getCurrent() {
58          return current;
59      }
60  
61      public void setCurrent(State current) {
62          this.current = current;
63          current.onEnter(this);
64      }
65  
66      /**
67       * Initializes the State Machine
68       *
69       * @param messenger
70       *            - {@link Messenger} connected to the
71       *            {@link Constants#IPMI_PORT}
72       */
73      public StateMachine(Messenger messenger) {
74          this.messenger = messenger;
75          observers = new ArrayList<MachineObserver>();
76          initialized = false;
77      }
78  
79      /**
80       * Sends message via {@link #messenger} to the managed system.
81       *
82       * @param message
83       *            - the encoded message
84       * @throws IOException
85       *             - when sending of the message fails
86       */
87      public void sendMessage(byte[] message) throws IOException {
88          UdpMessage udpMessage = new UdpMessage();
89          udpMessage.setAddress(getRemoteMachineAddress());
90          udpMessage.setPort(getRemoteMachinePort());
91          udpMessage.setMessage(message);
92          messenger.send(udpMessage);
93      }
94  
95      public InetAddress getRemoteMachineAddress() {
96          return remoteMachineAddress;
97      }
98  
99      public int getRemoteMachinePort() {
100         return remoteMachinePort;
101     }
102 
103     /**
104      * Sends a notification of an action to all {@link MachineObserver}s
105      *
106      * @param action
107      *            - a {@link StateMachineAction} to perform
108      */
109     public void doExternalAction(StateMachineAction action) {
110         for (MachineObserver observer : observers) {
111             if (observer != null) {
112                 observer.notify(action);
113             }
114         }
115     }
116 
117     /**
118      * Sets the State Machine in the initial state.
119      *
120      * @param address
121      *            - IP address of the remote machine.
122      * @param port
123      *             - UDP remoteMachinePort of the remote machine
124      * @see #stop()
125      */
126     public void start(InetAddress address, int port) {
127         messenger.register(this);
128         remoteMachineAddress = address;
129         this.remoteMachinePort = port;
130         setCurrent(new Uninitialized());
131         initialized = true;
132     }
133 
134     /**
135      * Cleans up the machine resources.
136      *
137      * @see #start(InetAddress, int)
138      */
139     public void stop() {
140         messenger.unregister(this);
141         initialized = false;
142     }
143 
144     /**
145      * @return true if {@link StateMachine} is initialized, false otherwise.
146      * @see #start(InetAddress, int)
147      * @see #stop()
148      */
149     public boolean isActive() {
150         return initialized;
151     }
152 
153     /**
154      * Performs a {@link State} transition according to the event and
155      * {@link #current} state
156      *
157      * @param event
158      *            - {@link StateMachineEvent} invoking the transition
159      * @throws NullPointerException
160      *             - when machine was not yet started
161      * @see #start(InetAddress, int)
162      */
163     public void doTransition(StateMachineEvent event) {
164         if (!initialized) {
165             throw new NullPointerException("State machine not started");
166         }
167         current.doTransition(this, event);
168     }
169 
170     @Override
171     public void notifyMessage(UdpMessage message) {
172         if (message.getAddress().equals(getRemoteMachineAddress()) && message.getPort() == getRemoteMachinePort()) {
173             current.doAction(this, RmcpDecoder.decode(message.getMessage()));
174         }
175     }
176 
177     /**
178      * Registers the listener in the {@link StateMachine} so it will be notified
179      * of the {@link StateMachineAction}s performed via
180      * {@link #doExternalAction(StateMachineAction)}
181      *
182      * @param observer
183      *            - {@link MachineObserver} to register
184      */
185     public void register(MachineObserver observer) {
186         observers.add(observer);
187     }
188 
189     /**
190      * @return true if {@link StateMachine} is at the point when it acquires
191      *         session and will send sessionless messages
192      */
193     public boolean isSessionChallenging() {
194         return !initialized || getCurrent().getClass() == SessionValid.class;
195     }
196 }