package org.opennaas.extensions.protocols.cli; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.opennaas.core.resources.protocol.IProtocolMessageFilter; import org.opennaas.core.resources.protocol.IProtocolSession; import org.opennaas.core.resources.protocol.IProtocolSessionListener; import org.opennaas.core.resources.protocol.ProtocolException; import org.opennaas.core.resources.protocol.ProtocolSessionContext; import org.opennaas.core.resources.transport.IStreamTransport; import org.opennaas.core.resources.transport.ITransport; import org.opennaas.core.resources.transport.TransportException; import org.opennaas.extensions.protocols.cli.message.CLIInputMessage; import org.opennaas.extensions.protocols.cli.message.CLIResponseMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents a CLI Session. When it is created, it logs in the remote agent. Then it sends messages to the agent, parses its responses and * sends them back to the CLI Session client. A timer task wakes up every 90 seconds and sends a message specified by the client when initializing the * session (or a dummy message if no message is specified) * * @author Eduard Grasa * */ public class CLIProtocolSession implements IProtocolSession { public static final String MODULE_NAME = "CLI Protocol Session"; public static final String MODULE_DESCRIPTION = "CLI Protocol library"; public static final String MODULE_VERSION = "1.0.0"; /** CLI Session Log */ static private Logger logger = LoggerFactory.getLogger(CLIProtocolSession.class); /** Some constants **/ public static final String HAS_GREETING = "Greeting"; // CLI has to // wait for a // greeting // message // from // transport public static final String ENABLE_COMMAND = "enableCommand"; public static final String ENABLE_USERNAME = "enableUsername"; public static final String ENABLE_PASSWORD = "enablePassword"; public static final String PROMPT = "prompt"; public static final String INTERMEDIATE_PROMPT = "intermediatePrompt"; public static final String INTERMEDIATE_PROMPT_COMMAND = "intermediatePromptCommand"; public static final String ERROR_MESSAGE_PATTERNS = "errorMessagePatterns"; public static final String KEEP_ALIVE_MESSAGE = "keepAliveMessage"; /** The prompts used to detect the end of the message **/ private List<String> prompts; /** * Contains a pair of <intermediate_prompt, string to be send>, to make the remote agent continue sending the response message **/ private Hashtable<String, String> intermediatePrompts; /** Contains some of the strings that appear when the response message is an error message **/ private List<String> errorMessagePatterns; /** The class that will listen for response messages **/ private CLIStreamReader cliStreamReader; /** Contains information about the protocol session configuration: transport, host, port, ... **/ private ProtocolSessionContext protocolSessionContext = null; private String sessionID = null; private IStreamTransport transport = null; private Status status = null; private Map<String, IProtocolSessionListener> protocolListeners = null; private Map<String, IProtocolMessageFilter> protocolMessageFilters = null; public CLIProtocolSession(ProtocolSessionContext protocolSessionContext, String sessionID) throws ProtocolException { this.protocolSessionContext = protocolSessionContext; this.sessionID = sessionID; this.protocolListeners = new HashMap<String, IProtocolSessionListener>(); this.protocolMessageFilters = new HashMap<String, IProtocolMessageFilter>(); this.status = Status.DISCONNECTED_BY_USER; setPromptsAndErrorPatterns(); } private void setPromptsAndErrorPatterns() throws ProtocolException { initializePromptsAndErrorPatterns(); extractPromptsAndErrorPatternsFromModuleProperties(); checkMissingParameters(); } private void initializePromptsAndErrorPatterns() { prompts = new ArrayList<String>(); // TODO hardcoded since some of these parameters are not allowed in a URL // FIXME Harcoded for catalyst session context, it may change for other resources!!! // TODO need to find a way to specify these delimiters in catalyst ProtocolSessionContext prompts.add("#"); prompts.add(":"); prompts.add(">"); intermediatePrompts = new Hashtable<String, String>(); errorMessagePatterns = new ArrayList<String>(); } private void extractPromptsAndErrorPatternsFromModuleProperties() { Iterator<Entry<String, Object>> sessionParametersIterator = this.protocolSessionContext.getSessionParameters().entrySet().iterator(); Entry<String, Object> currentEntry = null; Entry<String, Object> currentEntry2 = null; while (sessionParametersIterator.hasNext()) { currentEntry = sessionParametersIterator.next(); if (currentEntry.getKey().equals(PROMPT)) { String[] aux = ((String) currentEntry.getValue()).split(""); for (int i = 1; i < aux.length; i++) { prompts.add(aux[i]); } } else if (currentEntry.getKey().equals(INTERMEDIATE_PROMPT)) { currentEntry2 = sessionParametersIterator.next(); if (currentEntry2.getKey().equals(INTERMEDIATE_PROMPT_COMMAND)) { intermediatePrompts.put((String) currentEntry.getValue(), (String) currentEntry2.getValue()); } } else if (currentEntry.getKey().equals(ERROR_MESSAGE_PATTERNS)) { errorMessagePatterns.add((String) currentEntry.getValue()); } } } private void checkMissingParameters() throws ProtocolException { // Check there is enough info to segment the messages and identify errors if (prompts.size() == 0) { throw new ProtocolException("No prompts received. CLI Session needs to know the prompt strings in order to " + "segment the response messages."); } if (intermediatePrompts.isEmpty()) { logger.warn("No intermediate prompts received, strange."); } } public void wireTransport(ITransport transport) throws ProtocolException { if (!(transport instanceof IStreamTransport)) { throw new ProtocolException("CLI transports must be stream transports"); } this.transport = (IStreamTransport) transport; } @Override public void asyncSend(Object requestMessage) throws ProtocolException { this.sendCmdNoWait((String) requestMessage); } @Override public Object sendReceive(Object requestMessage) throws ProtocolException { return this.sendCmdWait((String) requestMessage); } @Override public void connect() throws ProtocolException { startSession(); } @Override public void disconnect() throws ProtocolException { stopSession(); } /** * Opens a connection to the specific host/port */ private void startSession() throws ProtocolException { logger.info("Starting protocol session"); try { transport.connect(); } catch (TransportException ex) { throw new ProtocolException("Problems connecting to the managed device", ex); } createCLIStreamReader(); loginToDevice(); } public void restartSession() { try { stopSession(); } catch (Exception ex) { ex.printStackTrace(); } try { transport.connect(); loginToDevice(); } catch (Exception ex) { ex.printStackTrace(); } } private void createCLIStreamReader() throws ProtocolException { try { cliStreamReader = new CLIStreamReader(getTransport().getInputStream(), getTransport().getOutputStream(), prompts, intermediatePrompts, errorMessagePatterns); } catch (TransportException ex) { throw new ProtocolException("Problems getting the transport input stream", ex); } } private IStreamTransport getTransport() { return transport; } private void loginToDevice() throws ProtocolException { if (this.protocolSessionContext.getSessionParameters().get(CLIProtocolSession.HAS_GREETING) != null) { try { cliStreamReader.getResponse("");// wait for greeting message } catch (IOException e) { e.printStackTrace(); throw new ProtocolException("CLI cannot read greeting message: " + e.toString()); } } String username = (String) this.protocolSessionContext.getSessionParameters().get(ProtocolSessionContext.USERNAME); if (username != null) { this.sendWaitResponse(username); } String password = (String) this.protocolSessionContext.getSessionParameters().get(ProtocolSessionContext.PASSWORD); if (password != null) { this.sendWaitResponse(password); } } /** * Stop the protocol session: log out, stop the session keep-alive thread, disconnect the transport */ public void stopSession() throws ProtocolException { try { getTransport().disconnect(); } catch (TransportException ex) { throw new ProtocolException("Problems disconnecting the transport", ex); } } /** * Send a message without waiting for the response */ public synchronized void sendDontWaitResponse(Object message) throws ProtocolException { if (message instanceof CLIInputMessage) { CLIInputMessage request = (CLIInputMessage) message; this.sendCmdNoWait(request.toString()); } else { this.sendCmdNoWait((String) message); } } /** * Sends an array of messages without waiting for the response */ public synchronized void sendDontWaitResponse(Object[] messageList) throws ProtocolException { for (int i = 0; i < messageList.length; i++) { sendDontWaitResponse(messageList[i]); } } /** * Sends out CLI command to the agent. * * @param message * Command to send in CLIInputMessage Format * @return CLIResponseMesssage * @throws FailedCmdException * Exception thrown if command failed */ public synchronized Object sendWaitResponse(Object message) throws ProtocolException { Object msg; if (message instanceof CLIInputMessage) { CLIInputMessage request = (CLIInputMessage) message; msg = (Object) this.sendCmdWait(request.toString()); } else { msg = (Object) this.sendCmdWait((String) message); } return msg; } /** * Sends an array of CLI commands to the device */ public synchronized Object[] sendWaitResponse(Object[] messageList) throws ProtocolException { Object[] msg = new Object[messageList.length]; for (int i = 0; i < messageList.length; i++) { msg[i] = sendWaitResponse(messageList[i]); } return msg; } /** * Sends a CLI message and waits for the response * * @param message * @return */ private synchronized CLIResponseMessage sendCmdWait(String message) throws ProtocolException { // Send the message this.sendCmdNoWait(message); // Get the response try { return cliStreamReader.getResponse(message); } catch (IOException e) { e.printStackTrace(); restartSession(); return this.sendCmdWait(message); } } /** * Sends a command to the stream transport, without waiting for the request * * @param request * @throws ProtocolException */ private void sendCmdNoWait(String request) throws ProtocolException { try { getTransport().send(request.toCharArray()); } catch (TransportException ex) { throw new ProtocolException("Problems sending this message to the managed device: " + request, ex); } } @Override public ProtocolSessionContext getSessionContext() { return protocolSessionContext; } @Override public String getSessionId() { return sessionID; } @Override public Status getStatus() { return status; } @Override public void registerProtocolSessionListener( IProtocolSessionListener protocolSessionListener, IProtocolMessageFilter protocolMessageFilter, String idListener) { protocolMessageFilters.put(idListener, protocolMessageFilter); protocolListeners.put(idListener, protocolSessionListener); } @Override public void unregisterProtocolSessionListener( IProtocolSessionListener protocolSessionListener, String idListener) { protocolMessageFilters.remove(idListener); protocolListeners.remove(idListener); } @Override public void setSessionContext(ProtocolSessionContext context) { this.protocolSessionContext = context; } @Override public void setSessionId(String sessionId) { this.sessionID = sessionId; } }