Is there any way I can make an executable .jar that will open up the command line when double clicked?
I'm making a text-based adventure game. As of right now it is just a maze with rooms. Eventually it is going to be much bigger and more in depth but for now I just want to get the basic structure down. Anyways, to make this work I've been getting output and input from the System.out.printf command and the java.util.Scanner. It's all working beautifully so far but I've realized I'm going to run into a problem when I try to send this to other people that don't know how or just don't want to run the program from the command line.
I found this while looking for an answer myself, I ended up writing this bit:
/**
* This opens a command line and runs some other class in the jar
* #author Brandon Barajas
*/
import java.io.*;
import java.awt.GraphicsEnvironment;
import java.net.URISyntaxException;
public class Main{
public static void main (String [] args) throws IOException, InterruptedException, URISyntaxException{
Console console = System.console();
if(console == null && !GraphicsEnvironment.isHeadless()){
String filename = Main.class.getProtectionDomain().getCodeSource().getLocation().toString().substring(6);
Runtime.getRuntime().exec(new String[]{"cmd","/c","start","cmd","/k","java -jar \"" + filename + "\""});
}else{
THEMAINCLASSNAMEGOESHERE.main(new String[0]);
System.out.println("Program has ended, please type 'exit' to close the console");
}
}
}
not sure if my answer is still relevant, but feel free to use it with the comment kept in o/
Only flaw I can think of is that it leaves the cmd window open after the program completes.
Usage: place this class in the same package as your main class and set it as the main class, it will open a command prompt window if one is not open, or if one is open launch the main class. Name / location of jar file is automatic. Designed for windows, but if you want it for another system just message me and I'll fix it. (I could do OS detection but I'm lazy and just making this so I can turn in a double-click jar file to my professor who uses windows).
If you want full control, you can implement a Console window in Swing which does what you have now.
If you cannot open said window (if headless) or the user asks for it on the command line, then just default to your current behaviour.
Double-clicking a jar opens it with whatever application you've associated to it in your OS. By default, javaw[.exe] is normally associated to jar files. That's the binary that runs without a terminal window. To see a terminal on double-click, you'd need to associate the java[.exe] binary with jar files.
Or you can provide a .sh .bat that will open a terminal and call your java in it.
As long as the .jar is executed with java.exe, a commandline window will always show up. executing it with javaw.exe would prevent this.
for further reading:
http://download.oracle.com/javase/1.4.2/docs/tooldocs/windows/java.html
So this is my solution, I used the code from #Brandon Barajas and modified it. It creates a batchfile that starts the program by itself.
public static void main(String[] args){
Console console = System.console();
if(console == null && !GraphicsEnvironment.isHeadless()) {
String filename = YOURMAINCALSS.class.getProtectionDomain().getCodeSource().getLocation().toString().substring(6);
try {
File batch = new File("Launcher.bat");
if(!batch.exists()){
batch.createNewFile();
PrintWriter writer = new PrintWriter(batch);
writer.println("#echo off");
writer.println("java -jar "+filename);
writer.println("exit");
writer.flush();
}
Runtime.getRuntime().exec("cmd /c start \"\" "+batch.getPath());
} catch(IOException e) {
e.printStackTrace();
}
} else {
//your program code...
}
}
If you want you can add a writer.println("pause"); before the "exit" print, to keep the window open after the progam finishes. Then you need to hit ENTER to close the window.
You can use this program. This program creates a console for JAR programs (when JAR programs are run by double clicking on them).
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.border.*;
class InitComponents {
public static JFrame setupJFrameAndGet(String title, int width, int height) {
JFrame tmpJF = new JFrame(title);
tmpJF.setSize(width, height);
tmpJF.setLocationRelativeTo(null);
tmpJF.setLayout(null);
tmpJF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
return tmpJF;
} // end of setupJFrameAndGet
public static JTextArea setupJTextAreaAndGet(String text, int rows, int columns, boolean setEditableFlag, boolean setLineWrapFlag, boolean setWrapStyleWordFlag, boolean setBoundsFlag, int xpos, int ypos, int width, int height) {
JTextArea tmpJTA = new JTextArea(text, rows, columns);
tmpJTA.setEditable(setEditableFlag);
tmpJTA.setLineWrap(setLineWrapFlag);
tmpJTA.setWrapStyleWord(setWrapStyleWordFlag);
if (setBoundsFlag == true) {
tmpJTA.setBounds(xpos, ypos, width, height);
}
return tmpJTA;
} // end of setupJTextAreaAndGet
public static JScrollPane setupScrollableJTextAreaAndGet(JTextArea jta, int xpos, int ypos, int width, int height) {
JScrollPane tmpJSP = new JScrollPane(jta);
tmpJSP.setBounds(xpos, ypos, width, height);
return tmpJSP;
} // end of setupScrollableJTextAreaAndGet
public static JMenuBar setupJMenuBarAndGet() {
JMenuBar tmpJMB = new JMenuBar();
return tmpJMB;
} // end of setupJMenuBarAndGet
public static JMenu setupJMenuAndGet(String text) {
JMenu tmpJM = new JMenu(text);
return tmpJM;
} // end of setupJMenuAndGet
public static JMenuItem setupJMenuItemAndGet(String text) {
JMenuItem tmpJMI = new JMenuItem(text);
return tmpJMI;
} // end of setupJMenuItemAndGet
}// end of InitComponents
public class ConsoleForJARPrograms implements KeyListener, ActionListener {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
String title = null;
String text = null;
JFrame jf = null;
JTextArea jta = null;
JScrollPane jsp = null;
JMenuBar jmb = null;
JMenu jm = null;
JMenuItem jmi = null;
int initialCaretPosition = 0;
int currentCaretPosition = 0;
boolean inputAvailable = false;
// key codes
int BACKSPACE = 8;
int ENTER = 10;
int PG_UP = 33; // do nothing for this key pressed
int PG_DN = 34; // do nothing for this key pressed
int END = 35;
int HOME = 36;
int LEFT_ARROW = 37;
int UP_ARROW = 38; // do nothing for this key pressed
//int RIGHT_ARROW = 39; // handled by JTextArea
int DOWN_ARROW = 40; // do nothing for this key pressed
int CTRL = 128;
int A = 65; // disable ctrl-a
int H = 72; // handle ctrl-h
//int DELETE = 127; // handled by JTextArea
public void actionPerformed(ActionEvent ae) {
int cCurrPos = jta.getCaretPosition();
jta.selectAll();
jta.copy();
jta.select(cCurrPos, cCurrPos);
} // end of actionPerformed
public void keyTyped(KeyEvent ke) {
} // end of keyTyped
public void keyReleased(KeyEvent ke) {
} // end of keyReleased
public void keyPressed(KeyEvent ke) {
int keyCode = ke.getKeyCode();
if ((keyCode == PG_UP) || (keyCode == PG_DN) || (keyCode == UP_ARROW) || (keyCode == DOWN_ARROW) || ((keyCode == A) && (ke.getModifiersEx() == CTRL))) {
ke.consume();
} else if ((keyCode == LEFT_ARROW) || (keyCode == BACKSPACE) || ((keyCode == H) && (ke.getModifiersEx() == CTRL))) {
synchronized(this) {
if (jta.getCaretPosition() <= initialCaretPosition) {
ke.consume();
}
} // end of synchronized block
} else if (keyCode == HOME) {
synchronized(this) {
jta.setCaretPosition(initialCaretPosition);
ke.consume();
} // end of synchronized block
} else if (keyCode == END) {
synchronized(this) {
jta.setCaretPosition(jta.getDocument().getLength());
ke.consume();
} // end of synchronized block
} else if (keyCode == ENTER) {
jta.setCaretPosition(jta.getDocument().getLength());
synchronized(this) {
currentCaretPosition = jta.getCaretPosition();
// If character at initial caret position is newline then it means that the user has
// pressed enter without enetring any other character. Also, the code gets called here
// as soon as enter is pressed which means that the caret position (jta.getCaretPosition())
// of the document will be incremented by 1 by the system after this code returns.
// This means that if at initial caret position, the character is newline, then we must ignore
// this enter and increment initial caret position by 1 and do not set inputAvailable to true.
try {
String charAtInitialCaretPosition = jta.getText(initialCaretPosition, 1);
if ((charAtInitialCaretPosition.equals("\n")) == true) {
initialCaretPosition++;
}
} catch (Exception e) {
}
/*
debug: start
try {
System.out.println("keyPressed (1): initial = " + initialCaretPosition + ", current = " + currentCaretPosition + ", System current = " + jta.getDocument().getLength());
String initialString = jta.getText(initialCaretPosition, 1);
String currentString = jta.getText(currentCaretPosition, 1);
System.out.println("char at initial = " + initialString + ", char at current = " + currentString);
if ((initialString.equals("\n")) == true) {
System.out.println("char at initial is newline");
}
if ((currentString.equals("\n")) == true) {
System.out.println("char at current is newline");
}
} catch (Exception e) {
}
debug:end
*/
if ((currentCaretPosition - initialCaretPosition) > 0) {
inputAvailable = true;
notifyAll();
}
} // end of synchronized block
} // end of if else if
} // end of keyPressed
String getInputFromJTextArea(JTextArea jta) {
int len = 0;
String inputFromUser = "";
while (true) {
synchronized(this) {
if (inputAvailable == true) {
len = currentCaretPosition - initialCaretPosition;
try {
inputFromUser = jta.getText(initialCaretPosition, len);
initialCaretPosition = currentCaretPosition;
} catch (Exception e) {
inputFromUser = "";
return inputFromUser;
} // end of outer try catch
/*
The following lines of code are not needed now.
if ((len == 1) && (inputFromUser.equals("\n")) == true) {
try {
wait();
continue;
} catch (Exception e) {
} // end of try catch
} else if (Character.compare(inputFromUser.charAt(0), '\n') == 0) { // matched
// remove first character from inputFromUser
inputFromUser = inputFromUser.substring(1);
}
*/
inputAvailable = false;
return inputFromUser;
} else {
try {
wait();
continue;
} catch (Exception e) {
} // end of try catch
} // end of if else inputAvailable
} // end of synchronized block
} // end of while true
} // end of getInoutFromJtextArea
void outputToJTextArea(JTextArea jta, String text) {
jta.append(text);
jta.setCaretPosition(jta.getDocument().getLength());
synchronized(this) {
initialCaretPosition = jta.getCaretPosition();
}
} // end of outputToJTextArea
void begin() {
while (true) {
outputToJTextArea(jta, "Enter some input (press enter after inputting): ");
String input = getInputFromJTextArea(jta);
outputToJTextArea(jta, "User input was: " + input + "\n\n");
}
} // end of begin
void configureJTextAreaForInputOutput(JTextArea jta) {
jta.addKeyListener(this);
// remove all mouse listeners
for (MouseListener listener : jta.getMouseListeners()) {
//outputToJTextArea(jta, "\nRemoving mouse listener\n");
jta.removeMouseListener(listener);
}
// remove all mouse motion listeners
for (MouseMotionListener listener : jta.getMouseMotionListeners()) {
//outputToJTextArea(jta, "\nRemoving mouse motion listener\n");
jta.removeMouseMotionListener(listener);
}
// remove all mouse wheel listeners
for (MouseWheelListener listener : jta.getMouseWheelListeners()) {
//outputToJTextArea(jta, "\nRemoving mouse wheel listener\n");
jta.removeMouseWheelListener(listener);
}
} // end of configureJTextAreaForInputOutput
void createAndShowGUI() {
title = "Console";
jf = InitComponents.setupJFrameAndGet(title, screenWidth - 150, screenHeight - 100);
jta = InitComponents.setupJTextAreaAndGet("", 1000, 100, true, true, true, false, 0, 0, 0, 0);
configureJTextAreaForInputOutput(jta);
jsp = InitComponents.setupScrollableJTextAreaAndGet(jta, 10, 10, screenWidth - 180, screenHeight - 180);
jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jf.add(jsp);
//jf.setLocation(screenWidth / 5, screenHeight / 6);
jmb = InitComponents.setupJMenuBarAndGet();
jm = InitComponents.setupJMenuAndGet("Copy All to Clipboard");
jm.setBorder(BorderFactory.createLineBorder(Color.green, 2));
jmi = InitComponents.setupJMenuItemAndGet("Copy All to Clipboard");
jm.add(jmi);
jmb.add(jm);
jmi.addActionListener(this);
jf.setJMenuBar(jmb);
jf.setVisible(true);
} // end of createAndShowGUI
public static void main(String[] args) {
ConsoleForJARPrograms cfjp = new ConsoleForJARPrograms();
cfjp.createAndShowGUI();
cfjp.begin();
} // end of main
} // end of ConsoleForJARPrograms
I guess the simplest way is to write a simple shortcut for your jar file. e.g. eclipse (like the most ide's) is able to export a jar file with all necessary libraries, etc. so you just need to set the shortcut command like "java -jar filePath/file.jar" (filePath: e.g. ./bin/file.jar)
One way to accomplish this is to create a .bat file with the command: "java -jar filePath/yourfile.jar" (without ""). Make sure to include the file path or else your file won't be found.
Though the question was answered already, this is a simple way to do it.
You could create your own window, using Swing or Awt, using a TextPane, the only problem... is how to input and use like the cmd should do. But you can always do this with alerts and all...
Other way, is running directly from a batch file, showing on the console.
You should consider too, make your game DIRECTLY on batch... is not a bad language to make and is present on every Windows Operating Systems.
(Hope was useful ('cause i'm new), and my english was not THAT BAD...)
Use Launch4j and in Basic Tab give your exe name in Output file and then load a jar file in Jar Tab.
Go to Header Tab and select Console.
Then go to JRE tab and give the JRE version e.g 1.8.0
Then click the build wrapper button (kind of setting icon)
It will ask you to save a xaml file just put some random name and click save.
Finally your .exe is created and you can run now.
I found another way ... for example for project myproject with a class foo in package bar :
java -cp myproject.jar; bar.foo
I wrote a little snippet which uses a similar approach to Brandon Barajas' solution.
It supports Mac (untested), Linux (tested with xfce) and Windows (cmd) in one go (although you might need to customize the linux terminal commands, since there are many different ones).
https://github.com/Lartsch/java-selfconsole/
Really helpful in the following case:
you don't want the jar to have to be directly executed in a console
you can't use any custom made console / can't use any GUI library
you don't want to ship bridge starting files like sh, bat, ...
you simply want the jar to open itself in a console on double-click
Related
for some reason, I can do just about any method for a string. This includes:
*Getting the length of the string
*Adding to the string
*Using substring
*And probably everythng else
Except, I cant get the value of the string except when using my drawString method to draw to the screen in lwjgl. Here is my code before i further explain the problem.
public static boolean chatOn = false;
public static String text = "";
public static float typetimer = 0;
public static int ctrltimer = 0;
public static boolean runcmd = false;
public static void chat() {
if (Keyboard.isKeyDown(Keyboard.KEY_TAB)) {
if (ctrltimer < 0) {
chatOn = !chatOn;
Keyboard.destroy();
try {Keyboard.create();} catch (LWJGLException e) {}
ctrltimer = 10;
}
}
ctrltimer -= Game.delta;
typetimer -= Game.delta;
if (chatOn) {
//try {text.replace(null, "happy");} catch(NullPointerException e) {}
System.out.println(text);//print to console, dosen't
Text.drawString(text, 0, 0);//write the text on the screen with my draw method, does work
System.out.println(text);//print to console, dosen't, yet the one drawstring worked
if (typetimer < 0) {
while (Keyboard.next()) {
try {
if (Keyboard.getEventKey() == Keyboard.KEY_BACK) {
text = text.substring(0, text.length()-1);
typetimer = 1;
System.out.println(text);//print to console, doesn't
}
else if (Keyboard.getEventKey() == Keyboard.KEY_RETURN) {
System.out.println(text);//print to console, doesn't
runCommand();
text = "";
chatOn = false;
}
else {
System.out.println(text);//print to console, doesn't
text = text + Keyboard.getEventCharacter();
}
typetimer = 10;
} catch(Exception e){
}
}
}
}
}
public static void runCommand() {
String command = text;
System.out.println(command);//print to console, doesnt
if (command.startsWith("time")) {
try {
String[] time = new String[1];
time = command.split(" ", 0);
Camera.nighttimeASecond = Integer.parseInt(time[0]);
} catch (Exception e){
System.out.println("could not set time");
}
}
}
If you read my notes inside the code you can see where I have put print methods and drawString method. The print methods print nothing and sometimes might print the first few words of the string, although the drawString method worked fine. Thanks - Tyler
If System.out.println(text); is empty before Text.drawString(text, 0, 0); is called, then text should be empty when Text.drawString() is called. You should follow mattias' suggested debugging guide and find out where your issue is occuring (or add some System.out.println()s following text through your code and tracing it if you're particularly lazy :p). Without having a look at your Text class, I'd be willing to bet that the string to be printed is never getting set, and Text is printing a static string, or that the string to be printed is getting changed by the Text class itself.
Judging by:
String command = text;
System.out.println(command);//print to console, doesnt
text is never getting set.
I was writing a java tool that toggles wether a game mod is active (specifically, the game is Dwarf Fortress, and the mod is DFHack) and the program is nearly done. However, I cant seem to get the variables right that tell the program how to check if the mod is active or not.
I got this status file containing a single character, being 1 for active and 0 for inactive.
This is the code (By the way, if it is needed: I checked the name of the file and it matches the class declaration).
package nl.dirkkok.dfhacktoggle;
/*
* DFHacktoggle
* 28-12-2013 # 2:02 PM
*
* This program will disable DFHack if it is enabled, and enable it if it is disabled.
*
* Using this tool is at your own risk. If you send me an email complaining about this program doing anything you dont want, then I will laugh at you, and tell you that you cant read. :)
*
* Created by Dirk Kok <d.kok.2000#gmail.com>. This tool is my property, but I do not claim rights of neither Dwarf Fortress nor DFHack.
*/
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.IOException;
import java.nio.file.NoSuchFileException;
public class Dfhacktoggle {
private static Path statusfile = "dfhack-status";
private boolean dfhack = false;
private byte[] fileArray = new byte[1];
private byte[] active = new byte[1];
private byte[] inactive = new byte[1];
private file dfhacksdl = "SDLdfhack.dll";
private file vanillasdl = "SDLreal.dll";
private file sdl = "SDL.dll";
active[] <byte> = 1;
inactive[] <byte> = 0;
public static void main(String[] args) {
try {
fileArray = Files.readAllBytes(file);
if (fileArray[0] == 1) {
p("DFHack is active. Deactivating...");
try {
if (vanillasdl.exists()) throw new java.io.IOException("File exists");
sdl.renameTo(dfhacksdl);
vanillasdl.renameTo(sdl);
Files.write(statusfile, inactive, WRITE);
} catch (IOException x) {
errp("DFHack could not be deactivated. Reinstalling the program will solve this.");
errp("Detailed info:");
errp("IOException: " + x);
} catch (NoSuchFileException x) {
errp("Status file could not be found. Reinstalling the program will solve this.");
errp("Detailed info:");
errp("NoSuchFileException: " + x);
}
} else if (fileArray[0] == 0) {
p("DFHack is inactive. Activating...");
try {
if(vanillasdl.exists()) throw new java.io.IOException("File exists");
sdl.renameTo(vanillasdl);
dfhacksdl.renameTo(sdl);
Files.write(statusfile, active, WRITE);
} catch (IOException x) {
errp("Status could not be checked. Reinstalling the program will solve this.");
errp("Detailed info:");
errp("IOException: " + x);
} catch (NoSuchFileException x) {
errp("Status file could not be found. Reinstalling the program will solve this.");
errp("Detailed info:");
errp("NoSuchFileException: " + x);
}
} else {
errp("DFHack's status could not be checked. Reinstalling the program will solve this.");
}
} catch (IOException x) {
errp("Status could not be checked. Reinstalling the program will solve this.");
errp("Detailed info:");
errp(x);
}
}
public void p(String txt) {
System.out.println(txt);
}
public void errp(String txt) {
System.err.println(txt);
}
}
The compiler returns this:
F:\Dfhack-swap>javac Dfhacktoggle.java
Dfhacktoggle:29: error: <identifier> expected
active[] <byte> = 1;
^
Dfhacktoggle:30: error: <identifier> expected
inactive[] <byte> = 0;
^
Your syntax is wrong there's no active[] <byte> = 1; in Java
If you want to assign it a value You can do it in this way
active[0] = 1;
inactive[0] = 0;
But there's no need for array to use it as a boolean you can just define it as a byte
byte active= 1;
byte inactive= 0;
My question is this, I am running some adb commands through the terminal. I wrote a tool; that will help make things easier. So back to the question, in order to make the command run, I have to enter a "password" on the terminal. So how do I what do I do to make the "password" part appear on a JOptionPane.showInputDialog box?
Here is what I have so far:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
public class flash implements ActionListener {
private File runfile;
#Override
public void actionPerformed(ActionEvent arg0) {
{
JFileChooser adbflashfile = new JFileChooser("/home/local/ANT/arthm/Desktop/os");
FileNameExtensionFilter filter = new FileNameExtensionFilter(".py", "py");
adbflashfile.setFileFilter(filter);
int returnVal = adbflashfile.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
runfile = adbflashfile.getSelectedFile();
try {
Runtime.getRuntime().exec("sudo python ./flashimage.py");
} catch (IOException e1) {
e1.printStackTrace();
}
//This is where a real application would open the file.
System.out.println("File: " + runfile.getName() + ".");
} else {
JOptionPane.showMessageDialog(null, "Open command cancelled by user.");
}
System.out.println(returnVal);
}
};
}
You "could" read the process input and when you "detect" the password prompt, display a JOptionPane and request that the user enter the password.
You "could" prompt the user for the password before you start the process, knowing that you are going to need to send it to the process.
You would probably still need to monitor the output of the process to determine when you need to send the password though.
Let's start with...
Runtime.getRuntime().exec("sudo python ./flashimage.py");
You are ignoring the Process completely. Neither are you processing the output, but you have no means to provide input to the process...
Generally, Runtime#exec is problematic at best. You are far better of using ProcessBuilder....
// Build the command to be executed. Note that each parameter becomes
// it's own argument, this deals with parameters that contain spaces
// much better then Runtime#exec alone...
ProcessBuilder pb = new ProcessBuilder("sudo", "python", "./flashimage.py");
pb.redirectError();
InputStream is = null;
try {
Process p = pb.start();
is = p.getInputStream();
StringBuilder output = new StringBuilder(80);
int in = -1;
while ((in = is.read()) != -1) {
if (in != '\n') {
output.append((char)in);
// You will need to define PASSWORD_PROMPT
if (PASSWORD_PROMPT.equals(output.toString())) {
String text = JOptionPane.showInputDialog("Password");
OutputStream os = p.getOutputStream();
os.write(text.getBytes());
}
} else {
System.out.println(output.toString());
output.delete(0, output.length());
}
}
} catch (IOException exp) {
exp.printStackTrace();
} finally {
try {
is.close();
} catch (Exception e) {
}
}
Now, undoubtedly, someone will point out (at least) two problems with this approach...
JOptionPane.showInputDialog("Password"); will present a normal JTextField, which won't hide the password characters and
String is not the safest way to to store a password...
Instead, we should use a JPasswordField and convert the resulting char array to a byte array...
JPasswordField password = new JPasswordField(10);
JLabel label = new JLabel("Password: ");
JPanel panel = new JPanel();
panel.add(label);
panel.add(password);
int option = JOptionPane.showConfirmDialog(null, panel, "Password", JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.OK_OPTION) {
char[] userPassword = password.getPassword();
byte[] bytes = new byte[userPassword.length * 2];
for (int i = 0; i < userPassword.length; i++) {
bytes[i * 2] = (byte) (userPassword[i] >> 8);
bytes[i * 2 + 1] = (byte) userPassword[i];
}
os.write(bytes);
}
Child process I/O can be redirected, using Process class. You should be able to monitor the output & provide input (the password) into the child process.
See:
http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html
http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html
I am creating a custom shell in Java. I have added history to it so that when up arrow is pressed it goes to the previous command, but the up arrow seems to not be working
Here is my code:
public class MyShell {
public static class JavaStringHistory
{
private List<String> history = new ArrayList<String>();
}
public static void main(String[] args) throws java.io.IOException {
JavaStringHistory javaStringHistory = new JavaStringHistory();
javaStringHistory.history.add("");
Integer indexOfHistory = 0;
String commandLine;
BufferedReader console = new BufferedReader
(new InputStreamReader(System.in));
//Break with Ctrl+C
while (true) {
//read the command
System.out.print("Shell>");
commandLine = console.readLine();
javaStringHistory.history.add(commandLine);
//if just a return, loop
if (commandLine.equals(""))
continue;
//history
if (commandLine.equals(KeyEvent.VK_UP))
{
System.out.println("up arrow");
}
//help command
if (commandLine.equals("help"))
{
System.out.println();
System.out.println();
System.out.println("Welcome to the shell");
System.out.println("Written by: Alex Frieden");
System.out.println("--------------------");
System.out.println();
System.out.println("Commands to use:");
System.out.println("1) cat");
System.out.println("2) exit");
System.out.println("3) clear");
System.out.println();
System.out.println();
System.out.println("---------------------");
System.out.println();
}
if (commandLine.equals("clear"))
{
for(int cls = 0; cls < 10; cls++ )
{
System.out.println();
}
}
if(commandLine.startsWith("cat"))
{
System.out.println("test");
//ProcessBuilder pb = new ProcessBuilder();
//pb = new ProcessBuilder(commandLine);
}
else
{
System.out.println("Incorrect Command");
}
if (commandLine.equals("exit"))
{
System.out.println("...Terminating the Virtual Machine");
System.out.println("...Done");
System.out.println("Please Close manually with Options > Close");
System.exit(0);
}
indexOfHistory++;
}
}
}
All I am getting is
Shell>^[[A
Incorrect Command
Shell>
Any thoughts?
There are several problems with your approach:
User blackSmith has mentioned before me that system console handling is platform-dependent when it comes to cursor key handling and similar topics.
BufferedReader.readLine is not a smart choice to use for history cycling in a shell because you want the shell to immediately react to cursor keys and not force the user to press Return or Enter. Reading whole lines is only required for user commands. Thus, you need to scan the keyboard input for each single character or key code and decide by yourself if it is e.g. a cursor key (up/down for history cycling, left/right for cursor movement within the command line) or delete/backspace for command line editing and so forth.
The text strings which are created by reading control characters via readLine can depend on the OS, maybe even on the shell and the character set (UTF-8, ISO-8859-1, US ASCII etc.) on the console.
Built-in shell editing functions like command history might get in the way with readLine, e.g. on Linux I see the "^[[A" stuff for cursor up, on Windows the cursor keys are passed through to the built-in command history feature of cmd.exe. I.e. you need to put the console in raw mode (line editing bypassed and no Enter key required) as opposed to cooked mode (line editing with Enter key required).
Anyway, so as to answer your initial question about how to find out which key codes are produced by BufferedReader.readLine, it is actually quite simple. Just dump the bytes to the console like so:
commandLine = console.readLine();
System.out.println("Entered command text: " + commandLine);
System.out.print ("Entered command bytes: ");
for (byte b : commandLine.getBytes())
System.out.print(b + ", ");
System.out.println();
Under Linux cursor up might be something like "27, 91, 65" or just "91, 65", depending on the terminal. cursor down ends with "66" instead on my system. So you could do something like:
public class MyShell {
private static final String UP_ARROW_1 = new String(new byte[] {91, 65});
private static final String UP_ARROW_2 = new String(new byte[] {27, 91, 65});
private static final String DN_ARROW_1 = new String(new byte[] {91, 66});
private static final String DN_ARROW_2 = new String(new byte[] {27, 91, 66});
// (...)
public static void main(String[] args) throws IOException {
// (...)
// history
else if (commandLine.startsWith(UP_ARROW_1) || commandLine.startsWith(UP_ARROW_2)) {
System.out.println("up arrow");
}
else if (commandLine.startsWith(DN_ARROW_1) || commandLine.startsWith(DN_ARROW_2)) {
System.out.println("down arrow");
}
// (...)
}
}
But all this is just for explanation or demonstration and so as to answer your question - I do like to get the bounty. ;-)
Maybe a way to go is not to re-invent the wheel and use the work of others, e.g. a framework like JLine. It is not perfect either from what I have heard, but goes way further than anything you can develop by yourself in a short time. Someone has written a short introductory blog post about JLine. The library seems to do just what you need. Enjoy!
Update: I gave JLine 2.11 a little try with this code sample (basically the one from the blog post plus tab filename completion:
import java.io.IOException;
import jline.TerminalFactory;
import jline.console.ConsoleReader;
import jline.console.completer.FileNameCompleter;
public class MyJLineShell {
public static void main(String[] args) {
try {
ConsoleReader console = new ConsoleReader();
console.addCompleter(new FileNameCompleter());
console.setPrompt("prompt> ");
String line = null;
while ((line = console.readLine()) != null) {
console.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
It works nicely on Windows and Linux, but for me tab completion only works on Linux, not on Windows. Anyway, command history works well on both platforms.
VK_UP is an integer constant, while in.readLine() is a string.
They won't equal each other. Why don't you try to test for the code that appears in console usually when you click up arrow? So like:
if (in.readLine().equals("^[[A"))
and then you could clear the line, and write the command in the arraylist with the highest index.
Also, I tested this and found a bug. Change your if statements besides the first to else if; after any command it will eventually get to the else and display "Incorrect Command"
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to read a single char from the console in Java (as the user types it)?
Im making a rogu like for the console, but is it possible to get keystrokes as they are typed?
*edit i found JLine, can it do the job? and if so how?
No, you can't get keypresses from the console. Because the console is dependent on the system and changes from environment to environment and from run to run, you might have one console written in Java, one written in Perl, and one written in C++, depending on where you open the program. You might also have an invisible console (you double click the JAR file).
Fortunately, you can write your own console. Doing so is fairly simple.
Here's a sample one I wrote recently:
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Input extends JFrame implements KeyListener{
private JTextArea out;
private JTextField in;
public Input(){
out = new JTextArea();
in = new JTextField();
this.add(out, BorderLayout.CENTER);
this.add(in, BorderLayout.SOUTH);
in.addKeyListener(this);
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == (int)'\n'){
e.consume();
process(in.getText());
in.setText("");
}
}
public void process(String s){
String text = out.getText();
out.setText(text + ((text.length() == 0) ? "" : "\n") + s);
String query = s.substring(0, s.indexOf(' '));
String param = s.substring(s.indexOf(' ') + 1);
ArrayList<String> args = new ArrayList<String>();
int i = 0;
boolean inQuotes = false;
StringBuffer activeString = new StringBuffer();
while(i < param.length()){
if(param.charAt(i) == ' ' && !inQuotes){
args.add(activeString.toString());
System.out.println(activeString.toString());
activeString.setLength(0); //FIXME:works???
} else if(param.charAt(i) == '"'){
inQuotes = !inQuotes;
} else {
activeString.append(param.charAt(i));
}
i++;
}
args.add(activeString.toString());
System.out.println(activeString.toString());
activeString.setLength(0); //FIXME:works???
for (String string : args) {
activeString.append(string + ", ");
}
out.setText(out.getText() + "\n\t" + "Query: " + query + "; args: " + activeString.toString());
}
public static void main(String[] args){
Input x = new Input();
x.setVisible(true);
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
Basically, make a text field at BorderLayout.SOUTH, a text area at BorderLayout.CENTER and add a KeyListener to the text field. Then you will be able to read keypresses from the console. You'll also be able to launch the application without a console (e.g. put it in a JAR file and open it by clicking it)
TL;DR: No, but you can make your own and use that :D
Good luck!