How to mask an input string in Spring-Shell - java

I am using Spring-Shell and I would like to mask the input when typing the password field for a particular method.
Looking on the internet and here in the forum, I found many people suggesting to use the console.readPassword() command but, creating the console from inside the IDE gives me a null result.
Scanner in= new Scanner(System.in)
-------------------------------OR------------------------------------
BufferedReader in = new BufferedReader(new InputStreamReader(System.in)
these are the code lines I tried to get the input from the user, but I can't find a way to mask the input, so when someone types the password it shows on the screen.
Looking around I found out that to make the console command work I could use an external terminal instead of the IDE but, when starting SpringBoot (a Spring-Shell project) I get the Jline Warning:
"Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)".
So is there an easy way to mask the password using the scanner/BufferedReader classes, or do I need to enable the system terminal to use the console?
Thank you

You can use org.jline.reader.LineReader from JLine library which you get by default in a Spring Shell application.
Here's some example code:
import org.jline.reader.LineReader;
public class InputReader {
private static final Character DEFAULT_MASK = '*';
private Character mask;
private LineReader lineReader;
public InputReader(LineReader lineReader) {
this(lineReader, null);
}
public InputReader(LineReader lineReader, Character mask) {
this.lineReader = lineReader;
this.mask = mask != null ? mask : DEFAULT_MASK;
}
public String prompt(String prompt) {
return prompt(prompt, true);
}
public String prompt(String prompt, boolean echo) {
String answer;
if (echo) {
answer = lineReader.readLine(prompt + ": ");
} else {
answer = lineReader.readLine(prompt + ": ", mask);
}
return answer;
}
}
Then, make it a bean:
#Bean
public InputReader inputReader(#Lazy LineReader lineReader) {
return new InputReader(lineReader);
}
and finally use it in your app:
#ShellComponent
public class YourShellComponent {
private final InputReader inputReader;
#Autowired
public YourShellComponent(InputReader inputReader) {
this.inputReader = inputReader;
}
#ShellMethod(value = "connect")
public void connect() throws Exception {
String username = this.inputReader.prompt("Username");
String password = this.inputReader.prompt("Password", false);
// other code
}
}

Related

How can I run a java web application interacting with another java cli application?

I want to run a command line application and command it over the REST API. For example, this CLI-application has a login command where it first takes "Login" as a command then asks you to enter a password, after entering the password, the CLI-application asks you to enter the password again. I want this connection stay alive. I want to send any command to the CLI-application in the form of a REST request, and then get a response from CLI-app. The CLI-application constantly takes commands from the user and acts accordingly. I want the CLI-application to interact with the user through the REST. Does anyone have any ideas?
I used Quarkus as REST application and my CLI-application placed in the "C:/my_files/working/test_n_learn/wallet-cli/build/libs/wallet-cli.jar" directory.
public class ConnectToWallet {
private final Runtime rt;
private final String command ;
public Process process;
public ConnectToWallet() throws IOException {
rt = Runtime.getRuntime();
command = "java -jar C:/my_files/working/test_n_learn/wallet-cli/build/libs/wallet-cli.jar";
process = rt.exec(command);
}
}
public class In {
private String command;
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}
#Path("/command")
public class ExampleResource {
private static final ConnectToWallet CONNECT;
static {
try {
CONNECT = new ConnectToWallet();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response hello(In in) throws IOException {
BufferedWriter processInput = new BufferedWriter(new OutputStreamWriter(CONNECT.process.getOutputStream()));
BufferedReader processOutput = new BufferedReader(new InputStreamReader(CONNECT.process.getInputStream()));
processInput.write(in.getCommand());
processInput.flush();
StringBuilder builder = new StringBuilder();
processOutput.lines().forEach(builder::append);
return Response.ok().entity(builder.toString()).build();
}
}
I used the process and runtime to execute but it only responds for the first commands and I don't know how to send the next commands and get the responses.

JUnit5: How can I simulate input from System.in to test the following method? [duplicate]

I have a Java command-line program. I would like to create JUnit test case to be able to simulate System.in. Because when my program runs it will get into the while loop and waits for input from users. How do I simulate that in JUnit?
Thanks
It is technically possible to switch System.in, but in general, it would be more robust not to call it directly in your code, but add a layer of indirection so the input source is controlled from one point in your application. Exactly how you do that is an implementation detail - the suggestions of dependency injection are fine, but you don't necessarily need to introduce 3rd party frameworks; you could pass round an I/O context from the calling code, for example.
How to switch System.in:
String data = "Hello, World!\r\n";
InputStream stdin = System.in;
try {
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
} finally {
System.setIn(stdin);
}
Based on #McDowell's answer and another answer that shows how to test System.out, I would like to share my solution to give an input to a program and test its output.
As a reference, I use JUnit 4.12.
Let's say we have this program that simply replicates input to output:
import java.util.Scanner;
public class SimpleProgram {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print(scanner.next());
scanner.close();
}
}
To test it, we can use the following class:
import static org.junit.Assert.*;
import java.io.*;
import org.junit.*;
public class SimpleProgramTest {
private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
#Before
public void setUpOutput() {
testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
}
private void provideInput(String data) {
testIn = new ByteArrayInputStream(data.getBytes());
System.setIn(testIn);
}
private String getOutput() {
return testOut.toString();
}
#After
public void restoreSystemInputOutput() {
System.setIn(systemIn);
System.setOut(systemOut);
}
#Test
public void testCase1() {
final String testString = "Hello!";
provideInput(testString);
SimpleProgram.main(new String[0]);
assertEquals(testString, getOutput());
}
}
I won't explain much, because I believe the code is readable and I cited my sources.
When JUnit runs testCase1(), it is going to call the helper methods in the order they appear:
setUpOutput(), because of the #Before annotation
provideInput(String data), called from testCase1()
getOutput(), called from testCase1()
restoreSystemInputOutput(), because of the #After annotation
I didn't test System.err because I didn't need it, but it should be easy to implement, similar to testing System.out.
There are a few ways to approach this. The most complete way is to pass in an InputStream while running the class under test which is a fake InputStream which passes simulated data to your class. You can look at a dependency injection framework (such as Google Guice) if you need to do this a lot in your code, but the simple way is:
public class MyClass {
private InputStream systemIn;
public MyClass() {
this(System.in);
}
public MyClass(InputStream in) {
systemIn = in;
}
}
Under test you would call the constructor that takes the input stream. You cloud even make that constructor package private and put the test in the same package, so that other code would not generally consider using it.
Try to refactor your code to use dependency injection. Instead of having your a method that uses System.in directly, have the method accept an InputStream as an argument. Then in your junit test, you'll be able to pass a test InputStream implementation in place of System.in.
You can write a clear test for the command line interface by using the TextFromStandardInputStream rule of the System Rules library.
public void MyTest {
#Rule
public final TextFromStandardInputStream systemInMock
= emptyStandardInputStream();
#Test
public void readTextFromStandardInputStream() {
systemInMock.provideLines("foo");
Scanner scanner = new Scanner(System.in);
assertEquals("foo", scanner.nextLine());
}
}
Full disclosure: I'm the author of that library.
You could create a custom InputStream and attach it to the System class
class FakeInputStream extends InputStream {
public int read() {
return -1;
}
}
And then use it with your Scanner
System.in = new FakeInputStream();
Before:
InputStream in = System.in;
...
Scanner scanner = new Scanner( in );
After:
InputStream in = new FakeInputStream();
...
Scanner scanner = new Scanner( in );
Although I think you should better to test how your class should work with the data read from the input stream and not really how it reads from there.
The problem with BufferedReader.readLine() is that it is a blocking method which waits for user input. It seems to me that you don't particularly want to simulate that (i.e. you want tests to be fast). But in a testing context it continually returns null at high speed during testing, which is irksome.
For a purist you can make the getInputLine below package-private, and mock it: easy-peezy.
String getInputLine() throws Exception {
return br.readLine();
}
... you'd have to make sure that you had a way of stopping (typically) a loop of user interaction with the app. You'd also have to cope with the fact that your "input lines" would always be the same until you somehow changed the doReturn of your mock: hardly typical of user input.
For a non-purist who wishes to make life easy for themselves (and produce readable tests) you could put all this stuff below in your app code:
private Deque<String> inputLinesDeque;
void setInputLines(List<String> inputLines) {
inputLinesDeque = new ArrayDeque<String>(inputLines);
}
private String getInputLine() throws Exception {
if (inputLinesDeque == null) {
// ... i.e. normal case, during app run: this is then a blocking method
return br.readLine();
}
String nextLine = null;
try {
nextLine = inputLinesDeque.pop();
} catch (NoSuchElementException e) {
// when the Deque runs dry the line returned is a "poison pill",
// signalling to the caller method that the input is finished
return "q";
}
return nextLine;
}
... in your test you might then go like this:
consoleHandler.setInputLines( Arrays.asList( new String[]{ "first input line", "second input line" }));
before triggering off the method in this "ConsoleHandler" class which needs input lines.
maybe like this (not tested):
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
more parts:
//PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
//start something that reads stdin probably in a new thread
// Thread thread=new Thread(new Runnable() {
// #Override
// public void run() {
// CoursesApiApp.main(new String[]{});
// }
// });
// thread.start();
//maybe wait or read the output
// for(int limit=0; limit<60 && not_ready ; limit++)
// {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
//System.setOut(save_out);
#Stefan Birkner, Thanks!
Modify Pom.xml
Ref:
https://stackoverflow.com/a/66127606/8317677
https://github.com/stefanbirkner/system-lambda/blob/master/pom.xml
https://github.com/stefanbirkner/system-lambda/blob/master/src/test/java/com/github/stefanbirkner/systemlambda/WithTextFromSystemInTest.java
<properties>
<system-lambda.version>1.2.1</system-lambda.version>
</properties>
<dependencies>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>${system-lambda.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Add function code
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class SimpleProgram003 {
public static void main(String[] args) {
try{
String c;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
do{
c = in.readLine();
System.out.println(c);
String d = c;
}while(!c.equals("q"));
}catch(Exception e){
System.out.println("catch Exception");
}
}
}
Add test code
import static com.github.stefanbirkner.systemlambda.SystemLambda.*;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for simple App. JUnit 4.x.
*/
public class SimpleProgram003Test {
private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
#Before
public void setUpOutput() {
testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
}
private void setInput(String data) {
testIn = new ByteArrayInputStream(data.getBytes());
System.setIn(testIn);
}
private String getOutput() {
return testOut.toString();
}
#After
public void restoreSystemInputOutput() {
System.setIn(systemIn);
System.setOut(systemOut);
}
#Test
public void testCase1() {
final String testString = "Hello 1\nq\n";
setInput(testString);
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Hello 1\r\nq\r\n", getOutput());
}
#Test // Multiply inputs
public void testCase2() throws Exception {
withTextFromSystemIn(
"Input1",
"Input2",
"q",
"Input3"
).execute(() -> {
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Input1\r\nInput2\r\nq\r\n", getOutput());
});
}
}

creating unit testing for write to file method in junit.

I am trying to write test methods in Intellij with jUnit. I can succesfully write to the file, however, I need to show that I can write a test method for this. I push in the write direction would be great. My ServerController class write to a file, checks a file for a username, and verifies username and password. This project is for a class and it appears that the most important lesson is learning about documentation (requirements, design, and testing). So, here I am trying to test.
package appLayer;
import java.io.*;
import java.util.Scanner;
public class ServerController {
public void writetoFile(String writeUsername, String writePassword) throws IOException {
PrintWriter fileWriting = new PrintWriter(new FileOutputStream("/Users/dannielsotelo/Documents/database.txt", true));
fileWriting.println(writeUsername + "," + writePassword); //
System.out.println(writeUsername + " was saved to database.txt");
fileWriting.close();
}
public boolean findUsername(String fUsername) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(fUsername))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
public boolean verifyCredentials(String lUsername, String lPassword) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(lUsername) && line.contains(lPassword))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
}
and my ServerControllerTest class
package appLayer;
import org.junit.Test;
import static org.junit.Assert.*;
public class ServerControllerTest {
#Test
public void testwritetoFile() {
}
#Test
public void findUsername() {
}
#Test
public void verifyCredentials() {
}
}
First, you should get rid of the redundant code when scanning the files. It would be sufficient to have one method that searches for arbitrary strings since technically it doesn't matter if you search for a username or a password.
Second, for testing purposes you really should not hard-code the database file. When writing JUnit tests you don't want your actual file to be written or read. Pass the file or the path to it as a constructor parameter.
Writing a test method is quite straight-forward. Write a value to the database, then read it and check if the previously written data is there. It's a good idea to start with a blank file for every test.
PS.: In terms of security, the approach of checking if a line contains username and password is a little disaster :D
Search this forum for testing void methods. But anyway your code is not really testable in sensible way. While you can test methods that return some value, you don't give it enough starting conditions to be sure they do what you want.
Don't hardcode path to files in the method, you'll be better off if it's an argument to the method. Then in tests you can be sure that if you create file with a specified content the method will do this or that or throw an exception if you don't create the file at all.

Having trouble with text file input in Java

My professor really threw us into this project with a blindfold on. We didn't go into depth on using and inserting files into Java. I'm getting a ton of errors, which are most likely due to my incorrect insertion of the file. I saved the text file in the same place the class file is saved on my computer, assuming that would be necessary. I've moved it around multiple places on my computer trying to get it to work. Here is the main program. I'm sorry if it's completely incorrect.
To explain what we're supposed to be doing further, here is the link to the prompt with the pseudocode. I haven't attempted to do all the actions listed because I haven't gotten the file to insert correctly yet.
http://jcsites.juniata.edu/faculty/rhodes/cs1/projects/program9Gen.html
Edit: This is the whole program in its glory. The class was created in a separate project as our introduction to Java classes. We were just told to use it again and insert the main program at the bottom just for ease of grading.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class GenSeq
{
private String seq;
private String species;
private String startCodon;
private String stopCodon;
private String shape;
private String chromosomeLocation;
public GenSeq (String seq, String species, String startCodon, String stopCodon,
String shape, String chromosomeLocation){
this.seq = seq;
this.species = species;
this.startCodon = startCodon;
this.stopCodon = stopCodon;
this.shape = shape;
this.chromosomeLocation = chromosomeLocation;
}
//Allowing the program to later set constructors
//Creating all the appropriate getters and setters for the instance variables
public void setSpecies(String newSpecies){
species = newSpecies;
}
public String getSpecies(){
return species;
}
public void setStartCodon(String newStartCodon){
startCodon = newStartCodon;
}
public String getStartCodon(){
return startCodon;
}
public void setStopCodon(String newStopCodon){
stopCodon = newStopCodon;
}
public String getStopCodon(){
return stopCodon;
}
public void setShape(String newShape){
shape = newShape;
}
public String getShape(){
return shape;
}
public void setChromosomeLocation(String newChromosomeLocation){
chromosomeLocation = newChromosomeLocation;
}
public String getChromosomeLocation(){
return chromosomeLocation;
}
public String toString(){
return "Sequence length: " + seq.length() +
"\nSpecies: "+ species +
"\nStart Codon: "+ startCodon +
"\nStart Codon: "+ stopCodon+
"\nShape: "+ shape +
"\nChromosomal Location: " + chromosomeLocation;
//Creating a toString method to hold all the class data
}
}
public static void main (String args[ ])
{
GenSeq seqA = null;
//Setting constructor to default if not set
//Opening the file
Scanner inputStream = null;
String seq;
try
{
inputStream = new Scanner (new File ("W:\jcsites.junata.edu\students\morrian14\seq.txt"));
}
catch (FileNotFoundException e)
{
System.out.println ("Error opening the file ");
System.exit (0);
}
do{
inputStream = inputStream.trim();
if ('>' == inputStream.charAt(0)){
seq = inputStream.nextLine();
}
}
while (inputStream.hasNextLine());
inputStream.close();
}
The error is this same one repeated continuously
File: C:\LEXIPC\Users\Alexis\GenSeq.java [line: 83]
Error: class, interface, or enum expected
One obvious issue, the last line is clearly meant to have been written as inputStream.close(); and not input.Stream.close(); you will probably need a try .. catch ... around closing the stream too
What exactly is your question? A few notes though...
Get rid of the do{} while() and just do something like this:
while(inputStream.hasNextLine(){
if('>' == inputStream.charAt(0))
seq = inputStream.nextLine();
}
inputStream.close();
I am a bit confused as to why you are recycling seq to read from the file, as that is what you are using as your file's name. A better way to do this would be to use a File class for your file names. Consider: File seq = new File(.../filename.txt).
Also, if you find that you are using too many try/catch blocks, consider using an exception handling class to clean up your code.

Some informations of how handle the main() args in Java [duplicate]

This question already has answers here:
How to make IntelliJ prompt me for command line arguments
(3 answers)
Closed 8 years ago.
I have to develop a command line Java application in which the main() method accept 2 String parameters named respetivelly partitaIVA and nomePDF.
So, as starting point, I created this simple Main class:
public class Main {
public static void main(String[] args) {
System.out.println("Hello World !!!");
}
}
I think that I can perform this minimalistic application from the Windows console and that I can perform my application passion these parameters to it doing something like this in the Windows console (or in the Linux shell):
java Main 123456789 myDocument.pdf
and I think that I can retrieve it inside my application modifying the original code in this way:
public class Main {
public static void main(String[] args) {
System.out.println("Hello World !!!");
String partitaIVA = args[0];
String nomePDF = args[1];
}
}
So now I have 2 doubts about this topic:
1) I know that I can perform this application specifying my 2 parameters using the Windows command line or the Linux shell but can I do the same thing into my IDE console? Specifically in the Run tab of IntelliJ?
2) Can I specify in some way that the parameters that the user can specify are only 2?
1) There is something called run/debug configuration https://www.jetbrains.com/idea/help/creating-and-editing-run-debug-configurations.html (here are also sone details about the specific options you have: https://www.jetbrains.com/idea/help/creating-and-editing-run-debug-configurations.html#d1628194e152)
2) No, you can only print an error and guide the user
You should invest the time in learning a modern CLI argument parser:
I prefer JewelCli
<dependency>
<groupId>com.lexicalscope.jewelcli</groupId>
<artifactId>jewelcli</artifactId>
<version>0.8.9</version>
</dependency>
Here is an example that can be used as a base class:
public class Main
{
private static final Logger LOG;
static
{
LOG = LoggerFactory.getLogger(Main.class);
}
private static Args init(#Nonnull final String[] args)
{
final Cli<Args> cli = CliFactory.createCli(Args.class);
try
{
return cli.parseArguments(args);
}
catch (final ArgumentValidationException e)
{
for (final ValidationFailure vf : e.getValidationFailures())
{
LOG.error(vf.getMessage());
}
LOG.info(cli.getHelpMessage());
System.exit(2); // Bash standard for arg parsing errors
return null; // This is to make the compiler happy!
}
}
private static List<String> parseKey(#Nonnull final String key)
{
return new ArrayList<String>(Arrays.asList(key.toLowerCase().split("\\.")));
}
#SuppressWarnings("unchecked")
private static Map<String, Object> addNode(#Nonnull Map<String, Object> node, #Nonnull final List<String> keys, #Nonnull final String value)
{
if (keys.isEmpty())
{
return node;
}
else if (keys.size() == 1)
{
node.put(keys.remove(0), value.trim());
return node;
}
else if (node.containsKey(keys.get(0)))
{
return addNode((Map<String, Object>) node.get(keys.remove(0)), keys, value);
}
else
{
final Map<String, Object> map = new HashMap<String, Object>();
node.put(keys.remove(0), map);
return addNode(map, keys, value);
}
}
public static void main(final String[] args)
{
try
{
final Args a = init(args);
final Properties p = new Properties();
p.load(new FileInputStream(a.getInputFile()));
final HashMap<String, Object> root = new HashMap<String, Object>();
for (final String key : p.stringPropertyNames())
{
addNode(root, parseKey(key), p.getProperty(key));
}
switch (a.getFormat().toLowerCase().charAt(0))
{
case 'j': LOG.info(mapToJson(root)); break;
case 'b' : LOG.info(Strings.bytesToHex(mapToCbor(root))); break;
case 'x' : LOG.error("XML not implemented at this time!"); break;
default : LOG.error("Invalid format {}", a.getFormat());
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
interface Args
{
#Option(shortName = "i", longName = "input", description = "Properties file to read from.")
File getInputFile();
#Option(shortName = "o", longName = "output", description = "JSON file to output to.")
File getOutputFile();
#Option(shortName = "f", longName = "format", description = "Format of output Json|Binary|Xml")
String getFormat();
#Option(helpRequest = true, description = "Display Help", shortName = "h")
boolean getHelp();
}
}
In Intellij (Linux) you do:
Press Alt + Shift + F10 (the run shortcut)
Press right key
Go down to Edit
Then press Tab to go to "Program arguments".
This is where you pass the arugments in IntelliJ. After that just hit run.

Categories

Resources