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
Related
I recently asked a question that helped me understand what a C program is doing in terms of Java. My goal is now to perform a C-like fork of two new child processes, while keeping track of parent process IDs and child process Ids. I am unsure, however, if this is possible.
Here, I've attempted to initiate process P and run two child processes (which I believed to be the InputStreamReaders isr and wasr ). I've gotten the PID of process P. But, where I'm stuck is the fact that InputStreamReader is not really a process, so I can't get the process ID.
I'll mention like I did in the other post that this is a homework assignment which provides C code instruction but urging Java code responses. The end goal is to be able to print "Child process ID of Parent process ID has begun" and "Child process ID has terminated" -- this is why it's important that I keep track of everyone's ID.
I found this other post, but I'm not sure how to adapt it.
UPDATE : With help of another SO user, I've realized that i may be approaching the problem in the wrong way. Here, parent process is the java process and child process is the native process. Updated code below.
Original Code
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class processesorigin {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("/bin/ls");
final InputStream is = p.getInputStream();
final InputStream was = p.getInputStream();
Thread t = new Thread(new Runnable() {
public void run() {
InputStreamReader isr = new InputStreamReader(is);
InputStreamReader wasr = new InputStreamReader(was);
int ch;
try {
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
while ((ch = wasr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
p.waitFor();
t.join();
int pid = 0;
if(p.getClass().getName().equals("java.lang.UNIXProcess")) {
try {
Field f = p.getClass().getDeclaredField("pid");
f.setAccessible(true);
pid = f.getInt(p);
}
catch (Throwable e) {
}
}
System.out.println("Process " + pid + " terminates.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
UPDATED CODE Using these tips and another SO user, I have found how to capture the Java process ID. There is however, still an issue with signaling of the start of each process. This might be another question though.
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import sun.management.VMManagement;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
class processesorigin {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("/bin/ls");
Process q = Runtime.getRuntime().exec("/bin/ls");
final InputStream is = p.getInputStream();
final InputStream was = q.getInputStream();
/*get PID of Java process*/
final String runtimeName = ManagementFactory.getRuntimeMXBean().getName();
final String jvmPid = runtimeName.split("#")[0];
int pid = 0;
int qid = 0;
if(p.getClass().getName().equals("java.lang.UNIXProcess")) {
/* get the PID of child processes : native ls command*/
try {
Field f = p.getClass().getDeclaredField("pid");
Field g = q.getClass().getDeclaredField("pid");
f.setAccessible(true);
g.setAccessible(true);
pid = f.getInt(p);
qid = g.getInt(q);
}
catch (Throwable e) {
}
}
final int pidCopy = pid;
final int qidCopy = qid;
Thread t = new Thread(new Runnable() {
public void run() {
InputStreamReader isr = new InputStreamReader(is);
InputStreamReader wasr = new InputStreamReader(was);
int ch;
try {
System.out.print("Child process " + pidCopy + " of Parent process " + jvmPid + " begun.");
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
System.out.print("Child process " + qidCopy + " of Parent process " + jvmPid + " begun.");
while ((ch = wasr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
p.waitFor();
q.waitFor();
t.join();
System.out.println("Child process " + pidCopy + " terminated.");
System.out.println("Child process " + qidCopy + " terminated.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Regarding your update: The way how you are getting the PID of the native child process should also work on OSX.
Since your assignment is to start two child processes from Java (right?) you can just start two native processes using Runtime.getRuntime().exec(), read and print stdout of each of them, and get and print the PID of each of them.
You can choose to run both processes in the main thread or start each process in a separate thread, basically like it is done in your example.
I am not sure if I fully understand what you want to achieve but maybe this helps you anyway. It is a tiny C program, a kind of wrapper that you can use to start a native process and obtain its PID from Java code:
#include <unistd.h>
#include <stdio.h>
/*
* Purpose of this "program" is to write its PID to stdout so that
* it can be read by a Java program which then can shutdown this
* process gently by sending for example SIGINT to it.
* After printig its PID the process executes the program given
* as argument.
*/
int main(int argc, char *argv[]) {
if(argc < 3) {
fprintf(stderr,"Usage: %s <program> <name> [arg] ...\n", argv[0]);
return -1;
}
/* Write the PID of the process to stdout */
fprintf(stdout,"%i\n", getpid());
/* Flush the buffers */
fflush(NULL);
/*
* Execute the program (specified as file or full path) given as first
* argument in the current process.
*/
return execvp(argv[1], argv + 2);
}
Assuming above code was compiled and the executable copied to /usr/local/bin/jpwrapper, it can be used from Java to run a native process like ls -l /dev, print out the PID when the process started, print its stdout and again the PID when it terminated:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class PID {
public static void main(final String[] args) throws Exception {
final Process process = Runtime.getRuntime().exec(new String[] {
"/usr/local/bin/jpwrapper",
"/bin/ls", "ls", "-l", "/dev"});
String pid = null;
try (final BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
// First line of output from the wrapper is the PID
String line = reader.readLine();
pid = line;
System.out.println(String.format(
"Process with PID %s started", pid));
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
final int exitCode = process.waitFor();
System.out.println(String.format(
"Process with PID %s terminated with exit code %s",
pid, exitCode));
}
}
But, like I already admitted in my comment, getting the PID from UnixProcess is easier (but also hacky).
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 is my first time posting here, so I'm not really sure what to say/ask.
Anyways, I am trying to make a simple java program that runs command prompt commands from the java program, mainly used for ping flood (ping flooding myself).
Here is my current code
public class Core extends JFrame {
JTextField ipTextField;
int packets = 0;
boolean running = false;
public Core() {
super("Fatique");
Container container = getContentPane();
JButton bAttack = new JButton("Start Attack");
JButton bStop = new JButton("Stop Attack");
JPanel jPanel = new JPanel();
container.setLayout(new FlowLayout());
ipTextField = new JTextField("IP Address", 30);
container.add(ipTextField);
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
exec("cmd /c" + input + " -t -n " + amountOfPackets);
running = true;
}
});
bStop.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
stop();
}
});
if(!running) {
jPanel.add(bAttack);
} else {
jPanel.add(bStop);
}
add(jPanel);
}
public void exec(String cmd) {
try {
Process p = Runtime.getRuntime().exec(cmd);
System.out.println(getOutput(p) + " - " + getPacketsSent());
} catch (IOException e) {
e.printStackTrace();
}
}
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
public int getPacketsSent() {
return packets;
}
public void stop() {
exec("cmd /c break");
running = false;
}
public static void main(String[] args) {
Core c = new Core();
c.setSize(500, 300);
c.setVisible(true);
c.setResizable(false);
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setLocationRelativeTo(null);
}
I'm quite new at java, so that might not do what I want it to do.
What I want it to do is I enter an ip address in the textfield, and split it with ":", and after that the amount of packets, for instance
127.0.0.1:100
Though now when I try to use that ip and packet amount, it returns "null - 0" (from exec method), and I'm not even sure if it did anything related to ping.
What I am trying to accomplish is as I already said, ping flood myself, and then output whatever I get as response, though I have no idea if this code does anything even related to that, I mostly use logic when coding java.
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Could someone explain me why my code code is not working how I want it to work? Please don't judge, as I already said, I'm quite new to java programming.
EDIT: Here is a quick "informative" explanation of what I am trying to accomplish.
I type in an ip address and how many packets I want to send. In this explanation, I am using localhost ip, and 5 packets.
I start the attack. At this part, I want the program to run cmd prompt command
ping 127.0.0.1 -t -n 5
127.0.0.1 being the ip that I put in the textfield in my program, and 5 is the amount of packets I put in the textfield.
I started the attack, so this is what should happen in the command prompt:
The language is Finnish, but still the same thing.
This is the basic explanation of what I am trying to accomplish, hopefully someone understood and can help/tell why my code is not working, or is working but not printing the proper lines in eclipse console.
There is a problem with your getOutput method. It looks like you intend to collect every line of output. But in fact, since you are assigning line to output, you will only return the last line before the end of stream.
To fix this, change
output = line;
to
output += line + "\n";
Or to be more correct:
output += line + LINE_SEPARATOR;
where you previously declared the latter as:
final String LINE_SEPARATOR = System.getProperty("line.separator");
That doesn't directly explain why you are getting null, but that might be because the command you are running is writing output to the 'error' stream rather than the 'output' stream.
Try something like this:
try {
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("ping 192.168.16.67");
InputStream in = p.getInputStream();
OutputStream out = p.getOutputStream ();
InputStream err = p.getErrorStream();
p.destroy();
} catch(Exception exc) {}
Then, you'll have to read the out variable to parse the ping command output continuously.
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
try {
p=Runtime.getRuntime().exec("ping -n "+amountOfPackets+" "+value[0]);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
running = true;
}
Just a small modification of your code. get output is as:
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output =output+ line+"\n";
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Here output is JTextArea I have taken to display the output of PING process. I cannot show you the output because I lack reputation.
I don't know why first line is null. Anyway, it works.
Hope this help you. Have good time coding.
Why CTRL + M gives an ASCII value of 10 (decimal value). It should actually give 13. I am connecting to Amazon EC2 linux instance through putty. I execute the below program
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
public class NumbersConsole {
private static String ttyConfig;
public static void main(String[] args) {
try {
setTerminalToCBreak();
int i=0;
while (true) {
//System.out.println( ""+ i++ );
if ( System.in.available() != 0 ) {
int c = System.in.read();
System.out.println(c);
if ( c == 13 ) {
break;
}
}
} // end while
}
catch (IOException e) {
System.err.println("IOException");
}
catch (InterruptedException e) {
System.err.println("InterruptedException");
}
finally {
try {
stty( ttyConfig.trim() );
}
catch (Exception e) {
System.err.println("Exception restoring tty config");
}
}
}
private static void setTerminalToCBreak() throws IOException, InterruptedException {
ttyConfig = stty("-g");
// set the console to be character-buffered instead of line-buffered
stty("-icanon min 1");
// disable character echoing
stty("-echo");
}
/**
* Execute the stty command with the specified arguments
* against the current active terminal.
*/
private static String stty(final String args)
throws IOException, InterruptedException {
String cmd = "stty " + args + " < /dev/tty";
return exec(new String[] {
"sh",
"-c",
cmd
});
}
/**
* Execute the specified command and return the output
* (both stdout and stderr).
*/
private static String exec(final String[] cmd)
throws IOException, InterruptedException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Process p = Runtime.getRuntime().exec(cmd);
int c;
InputStream in = p.getInputStream();
while ((c = in.read()) != -1) {
bout.write(c);
}
in = p.getErrorStream();
while ((c = in.read()) != -1) {
bout.write(c);
}
p.waitFor();
String result = new String(bout.toByteArray());
return result;
}
}
and when I give the input as (CTRL + M), I am getting displayed a value of 10. But I am expecting a value of 13. Please let me know if I am missing anything??
The translation of CR to LF is handled by the tty driver. You're calling setTerminalToCBreak(), which manipulates the tty settings (I think it disables the erase, kill, werase, and rprnt special characters).
The icrnl setting, which is enabled by default, causes carriage return (CR) to be translated to newline (LF). Disabling that should let you see CR characters directly. Setting raw mode changes a number of flags, including turning off icrnl. (Figuring out how to do that in Java is left as an exercise.)
But beware of doing this. The Enter or Return key typically sends a CR character. Translating it to LF is what allows it to mark the end of a line. If you turn off that translation, you might break that behavior unless you handle CR yourself.
For more information on tty settings, man tty or follow this link.
My other answer started on totally the wrong page.
stty ("-cooked")
works for me.
Something in the depths of teletype land wants you to have happy little ^Js instead of ^Ms, but cooking the terminal stops it.
$ stty -cooked ; java -cp /tmp NumbersConsole
13
$
Back in the Good Ol' Days, some computers (Commodore, Apple) used ^M (13) for their Return key; some (IBM) used a combination ^M^J; others (Unix) used ^J (10).
Now, in the modern world, it's almost always ^J (although I think Windows code still has some legacy ^M^J stuff under the hood sometimes?)
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