All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpac.console.TelnetServerHandler Maven / Gradle / Ivy

Go to download

Open-source runtime system for component-based implementation of automation solutions with Java

The newest version!
/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package org.jpac.console;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import org.jpac.AbstractModule;
import org.jpac.CharString;
import org.jpac.Decimal;
import org.jpac.JPac;
import org.jpac.Logical;
import org.jpac.Signal;
import org.jpac.SignalRegistry;
import org.jpac.SignedInteger;
import org.jpac.snapshot.Snapshot;
import org.jpac.statistics.Histogram;
import org.naturalcli.Command;
import org.naturalcli.ExecutionException;
import org.naturalcli.ICommandExecutor;
import org.naturalcli.IParameterType;
import org.naturalcli.NaturalCLI;
import org.naturalcli.ParseResult;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

/**
 * Handles a server-side channel.
 */
@Sharable
public class TelnetServerHandler extends SimpleChannelInboundHandler {
    private static final String ANSI_RESET  = "\u001B[0m";
    private static final String ANSI_BLACK  = "\u001B[30m";
    private static final String ANSI_RED    = "\u001B[31m";
    private static final String ANSI_GREEN  = "\u001B[32m";
    private static final String ANSI_YELLOW = "\u001B[33m";
    private static final String ANSI_BLUE   = "\u001B[34m";
    private static final String ANSI_PURPLE = "\u001B[35m";
    private static final String ANSI_CYAN   = "\u001B[36m";
    private static final String ANSI_WHITE  = "\u001B[37m";  
    private static final String ANSI_BOLD   = "\u001B[1m";
    
    protected Set        commandSet;
    protected Set parameterTypeSet;
    protected String              response;
    protected boolean             exitRequested;
    protected NaturalCLI          naturalCliInstance;
    
    public TelnetServerHandler(){
        super();
        commandSet      = new HashSet<>();
        parameterTypeSet = new HashSet<>();
           
        try{
            Command quitCommand =
              new Command(
              "quit", 
              "                                                       " + ANSI_GREEN + "exit the command line interpreter." + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ) 
                {  response = ANSI_GREEN + "bye !" + ANSI_RESET + "\r\n"; exitRequested = true;}
              }		
            );    
            commandSet.add(quitCommand);
            Command helpCommand =
              new Command(
              "help", 
              "                                                       " + ANSI_GREEN + "show this help menue" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ) 
                {StringBuffer sb = new StringBuffer();
                 commandSet.stream()
                    .sorted((c1,c2) -> c1.getSyntax().getDefinition().compareTo(c2.getSyntax().getDefinition()))
                    .forEach(cmd -> sb.append(ANSI_BOLD + cmd.getSyntax() + cmd.getHelp() + "\r\n"));
                 response = sb.toString();
                }
              }		
            );    
            commandSet.add(helpCommand);
/* removed until list of active loggers can be accessed over slf4j
            Command listLoggersCommand =
              new Command(
              "list loggers", 
              "                                               " + ANSI_GREEN + "list currently installed loggers and their logging levels" + ANSI_RESET,  
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ) 
                {
                  StringBuffer sb            = new StringBuffer();
                  List listOfLoggers = new ArrayList<>();
                  Enumeration loggers = LoggerFactory.getCurrentLoggers();
                  while(loggers.hasMoreElements()){
                      Logger l = (Logger)loggers.nextElement();
                      listOfLoggers.add(l.getName() + "= " + l.getLevel() + "\r\n");
                  }
                  Collections.sort(listOfLoggers, String.CASE_INSENSITIVE_ORDER);
                  listOfLoggers.forEach(l -> sb.append(l));
                  response = sb.toString();
                 }
              }		
            );    
            commandSet.add(listLoggersCommand);            
            Command setLevelForLoggerCommand =
              new Command(
              "set level  for logger ", 
              " " + ANSI_GREEN + " = [ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF],  = [*][*]" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ) { 
                  String result = null;
                  Level logLevel  = getLevel(pr.getParameterValue(0).toString());
                  if (logLevel != null){
                    result = setLevel(pr.getParameterValue(1).toString(), logLevel);
                    if(result.length() == 0){
                        result = ANSI_RED + "logger '" + pr.getParameterValue(1) + "'not found" + ANSI_RESET + "\r\n";
                    }
                  }
                  else{
                    result = ANSI_RED + "invalid log level '" + pr.getParameterValue(0) + "'" + ANSI_RESET + "\r\n";
                  }
                  response = result.toString();
                }
                private Level getLevel(String logLevelAsString){
                    Level logLevel = null;
                    if (logLevelAsString.toUpperCase().equals("TRACE")){
                        logLevel = Level.TRACE;
                    } else if (logLevelAsString.toUpperCase().equals("DEBUG")){
                        logLevel = Level.DEBUG;
                    } else  if (logLevelAsString.toUpperCase().equals("INFO")){
                        logLevel = Level.INFO;
                    } else if (logLevelAsString.toUpperCase().equals("WARN")){
                        logLevel = Level.WARN;
                    } else if (logLevelAsString.toUpperCase().equals("ERROR")){
                        logLevel = Level.ERROR;
                    };
                    return logLevel;
                }
                private String setLevel(String loggerName, Level level){
                    StringBuffer result = new StringBuffer();
                    Enumeration loggers = LogManager.getCurrentLoggers();
                    LoggerFactory.getLogger(loggerName).setLevel(level);
                    while(loggers.hasMoreElements()){
                      Logger l = (Logger)loggers.nextElement();
                      if (matches(l.getName(),loggerName)){
                          l.setLevel(level);
                          result.append(ANSI_GREEN + "level of logger " + l.getName() + " set to " + level + ANSI_RESET + "\r\n");
                      }
                    }
                    return result.toString();
                }
              }		
            );    
            commandSet.add(setLevelForLoggerCommand);
*/            
            Command generateHistogramCommand =
              new Command(
              "generate histogram", 
              "                                         " + ANSI_GREEN + "generates a histogram about the time consumption of modules" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    String result = null;
                    
                    RunnableFuture> rf = new FutureTask<>(() -> JPac.getInstance().getHistograms());
                    JPac.getInstance().invokeLater(rf);
                    try{
                        File file = new File(JPac.getInstance().getHistogramFile());                        
                        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)), true);
                        rf.get().forEach(h -> out.println(h.toCSV()));
                        out.close();
                        result =  "histogram stored to " + file.getCanonicalPath() + "\r\n";
                    } 
                    catch (IOException | InterruptedException | java.util.concurrent.ExecutionException exc) {
                        result = ANSI_RED + exc.getMessage() + ANSI_RESET + "\r\n";
                    }
                    response = result;
                }
              }		
            );    
            commandSet.add(generateHistogramCommand);            
            Command generateSnapshotCommand =
              new Command(
              "generate snapshot", 
              "                                          " + ANSI_GREEN + "generates a snapshot of the actual state of this elbfisch instance" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    String result = null;
                    RunnableFuture rf = new FutureTask<>(() -> JPac.getInstance().getSnapshot());
                    JPac.getInstance().invokeLater(rf);
                    try{
                        rf.get().dump(JPac.getInstance().getDataDir());
                        result =  "snapshot stored to " + rf.get().getFilename() + "\r\n";
                    } catch (Exception exc) {
                        result = ANSI_RED + "failed to store snapshot: " + exc.getMessage() + ANSI_RESET + "\r\n";
                    }
                    response = result;
                }
              }		
            );    
            commandSet.add(generateSnapshotCommand);
            Command setSignalCommand =
              new Command(
              "set signal  to ", 
              "           " + ANSI_GREEN + "sets a signal to the given value" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    StringBuffer result     = new StringBuffer();
                    String searchString  = (String)pr.getParameterValue(0);
                    String valueAsString = (String)pr.getParameterValue(1);
                    if (searchString.endsWith("*")){
                        result.append(ANSI_RED + "search string ending with '*' not allowed in this context" + ANSI_RESET  + "\r\n");
                    }
                    else{
                        SignalRegistry.getInstance().getSignals().values().stream()
                            .filter(s -> matches(s.getQualifiedIdentifier(), searchString))
                            .forEach(s -> {result.append(setValue(s, valueAsString) + "\r\n");});
                        if (result.length() == 0){
                            result.append(ANSI_RED + "no signals matching '" + searchString + "' found" + ANSI_RESET  + "\r\n");
                        }
                    }
                    response = result.toString();
                }
              }		
            );    
            commandSet.add(setSignalCommand);
            Command listSignalsCommand =
              new Command(
              "list signals :string>", 
              "                       " + ANSI_GREEN + "list signals which match the given search string" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    StringBuffer listOfSignalIdentifiers = new StringBuffer();
                    String searchString                  = (String)pr.getParameterValue(0);
                    listOfSignalIdentifiers.append(ANSI_GREEN);
                    SignalRegistry.getInstance().getSignals().values().stream()
                            .filter(s -> matches(s.getQualifiedIdentifier(), searchString))
                            .sorted((s1,s2) -> s1.getQualifiedIdentifier().compareTo(s2.getQualifiedIdentifier()))
                            .forEach(s -> listOfSignalIdentifiers.append(s + "\r\n"));
                    listOfSignalIdentifiers.append(ANSI_RESET);
                    response = listOfSignalIdentifiers.toString();
                }
              }		
            );    
            commandSet.add(listSignalsCommand);
            Command showStatisticsCommand =
              new Command(
              "show statistics", 
              "                                            " + ANSI_GREEN + "used to show some statistical informantion" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    StringBuffer result = new StringBuffer();
                    RunnableFuture> rf = new FutureTask<>(() -> JPac.getInstance().logStatistics());
                    JPac.getInstance().invokeLater(rf);
                    try{
                        rf.get().forEach(s -> result.append(ANSI_GREEN + s + "\r\n" + ANSI_RESET));
                    }
                    catch(java.util.concurrent.ExecutionException | InterruptedException exc){/*ignore*/};
                    response = result.toString();
                }
              }		
            );    
            commandSet.add(showStatisticsCommand);
            Command showStateOfModuleCommand =
              new Command(
              "show state of ", 
              "                              " + ANSI_GREEN + "show state of the given module" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    StringBuffer result = new StringBuffer();
                    String       module = (String)pr.getParameterValue(0);
                    RunnableFuture> rf = new FutureTask<>(() -> JPac.getInstance().showStateOfModule(module));
                    JPac.getInstance().invokeLater(rf);
                    try{
                        rf.get().forEach(s -> result.append(ANSI_GREEN + s + "\r\n" + ANSI_RESET));
                    }
                    catch(java.util.concurrent.ExecutionException | InterruptedException exc){
                        result.append(ANSI_RED + "module '" + module + "' not found" + ANSI_RESET  + "\r\n");
                    };
                    response = result.toString();
                }
              }		
            );    
            commandSet.add(showStateOfModuleCommand);
            Command listModulesCommand =
              new Command(
              "list modules ", 
              "                        " + ANSI_GREEN + "list the modules which match the given search string" + ANSI_RESET, 
              new ICommandExecutor ()
              {
                @Override
                public void execute(ParseResult pr ){ 
                    StringBuffer result       = new StringBuffer();
                    String       searchString = (String)pr.getParameterValue(0);
                    RunnableFuture> rf = new FutureTask<>(() -> JPac.getInstance().getModules().values());
                    JPac.getInstance().invokeLater(rf);
                    try{
                        rf.get().stream()
                            .filter(m -> matches(m.getQualifiedName(), searchString))
                            .sorted((m1,m2) -> m1.getQualifiedName().compareTo(m2.getQualifiedName()))
                            .forEach(m -> result.append(ANSI_GREEN + m.getQualifiedName() + "\r\n" + ANSI_RESET));
                    }
                    catch(Exception exc){
                        //nothing to do
                    };
                    if (result.length() == 0){
                        result.append(ANSI_RED + "no matching modules found" + ANSI_RESET  + "\r\n");                        
                    }
                    response = result.toString();
                }
              }		
            );    
            commandSet.add(listModulesCommand);
            naturalCliInstance    = new NaturalCLI(commandSet);
        }
        
        catch(Exception exc){
            exc.printStackTrace();
        }
        
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    	this.exitRequested = false;
        // Send greeting for a new connection.
        ctx.write("Elbfisch command line interpreter. Connected to instance '" + InetAddress.getLocalHost().getHostName() + "'\r\n");
        ctx.write("type 'help' for detailed instructions, 'quit' to exit.\r\n\r\n");
        ctx.write("elbfisch>");
        ctx.flush();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {
        try{
            //execute incoming command and
            //place textual feedback for the user in 'response'
            //if request == 'quit' set 'exitRequested' accordingly     
            naturalCliInstance.execute(request);
            if (response != null){
                ChannelFuture future = ctx.write(response);
                if (exitRequested){
                    future.addListener(ChannelFutureListener.CLOSE);
                }
            }
        }
        catch(ExecutionException exc){
            ctx.write(exc.getMessage() + "\r\n");            
        }
        finally{
            if (!exitRequested){
                ctx.write("elbfisch>");            
            }
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
    
    protected String setValue(Signal signal, String valueAsString){
        String result = null;
        try{
            if (signal instanceof Logical){
                ((Logical)signal).setDeferred(Boolean.parseBoolean(valueAsString));
            } else if (signal instanceof SignedInteger){
                ((SignedInteger)signal).setDeferred(Integer.parseInt(valueAsString));
            } else if (signal instanceof Decimal){
                ((Decimal)signal).setDeferred(Double.parseDouble(valueAsString));            
            } else if (signal instanceof CharString){
                ((CharString)signal).setDeferred(valueAsString);                                
            }
            result = ANSI_GREEN + signal + " set to '" + valueAsString + "'" + ANSI_RESET;
        }
        catch(NumberFormatException exc){
            result = ANSI_RED + "failed to set " + signal + ANSI_RESET;
        }
        return result;
    }
    
    protected boolean matches(String identifier , String searchString){
        boolean match = false;
        if (searchString.startsWith("*")){
            if (searchString.endsWith("*")){
                // searchString = "*" or "*xxxxxx*"
                match = searchString.trim().length() == 1 || identifier.contains(searchString.substring(1, searchString.length()-1));
            }
            else{
                // searchString = "*xxxxxx"
                match = identifier.endsWith(searchString.substring(1));                
            }
        }
        else if (searchString.endsWith("*")){
            // searchString = "xxxxxx*"
            String subString = searchString.substring(0,searchString.length()-1);
            match = identifier.startsWith(subString);                            
        }
        else{
            // searchString = "xxxxxx"
            match = identifier.equals(searchString);                                        
        }
        return match;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy