I have written a Java application that analyzes my phone bills and calculates the average. At the moment I execute it like:
$ java -jar analyze.jar bill_1.pdf bill_2.pdf
But I want to install the application to my system. So I can just fire up a terminal type in the name of the application and the arguments and hit enter. Like any other "normal" program.
$ analyze bill_1.pdf bill_2.pdf bill_3.pdf
I know I can write a shell script and install it to "/usr/bin/" but I can't believe that there is no "native" way.
So please help, sorry for dump question.
Thank's in advance
One neat little trick is that you can append a basic shell script to the start of the jar file which will run it appropriately. See here for the full example but the basics are:
stub.sh
#!/bin/sh
MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$#"
exit 1
Then do...
cat stub.sh helloworld.jar > hello.run && chmod +x helloworld.run
And you should be all set! Now you can just call the script-ified jar directly.
./helloworld.run
What you did so far is basically "the native" way.
You have to keep in mind: Java applications are compiled to byte code. There simply is no binary for your application that you could invoke. You do need this detour of calling some JVM installation with a pointer to the main class you want to run. In owhther words; that is what the vast majority of java applications are doing.
Theoretically, there are products out that there that you could use to actually create a "true" binary from your application; but that isn't an easy path (see here for first starters); and given your statement that your just looking for more "convenience" it is definitely inappropriate here.
Related
I am running a minecraft server for my son, i'm new at minecraft servers, anyway it seems it keeps dying as it cant keep up? ok so its a VM and i will be assigning more resources to it at some point but my question is as follows
I initiate the server to load up via a script
bukkit.sh is the script an inside it has
#!/bin/bash
java -Xmx1024M -jar /minecraftserver/bukkitserver/craftbukkit-1.7.1.jar -o true
now at somepoint during the day or night it will die as it cant keep up, is there a way i can have some other script run alongside it and see that the process has died and to run that script again to start the server.
when i run top i can see that java is at the top using all the resources so im 100% thats the minecraft java. Does the PID stay the same each time it loads up?
Would be great if someone could let a hand on this ...
I assume the java process remains in the foreground once it's launched? Is there any reason you can't just do:
while :
do
java -Xmx1024M -jar /minecraftserver/bukkitserver/craftbukkit-1.7.1.jar -o true
done
Then any time the java exits, the script will simply restart it.
EDIT:
You could create a script, lets call it craftbukkit.sh
Make sure it is execute by using: chmod +x craftbukkit.sh
Then inside the script you would write:
#!/bin/sh
ps auxw | grep craftbukkit-1.7.1.jar| grep -v grep > /dev/null
if [ $? != 0 ]
then
/path/to/your/bukkit.sh
fi
Change /path/to/your/bukkit.sh to wherever that minecraft startup script is.
Then you need to add this script to your crontab, this is accomplished by entering the crontab editor: crontab -e
If you want the script to run every 5 minutes, add a line that looks like this:
*/5 * * * * /path/to/craftbukkit.sh
Note that you must change the /path/to to point to where you have created the craftbukkit.sh script.
I'm trying to restart process when OOME happens. Java binary is launched using two shell scripts, one of them imports other. I don't have any control of the first one but can modify the second one as I want.
This is a prototype what I'm trying to do:
First shell script test.sh:
#!/bin/sh
JAVA_OPTS="$JAVA_OPTS -Xmx10m"
. test1.sh
echo $JAVA_OPTS
java $JAVA_OPTS $es_params TestMemory
Second shell script test1.sh:
#!/bin/sh
pidfile="test.pid"
touch $pidfile
params="$parms -Dpidfile=$pidfile"
kill_command="kill -9 \$(cat $pidfile)"
dir=$( cd $(dirname $0) ; pwd -P )
path="$dir/$(basename $0)"
start_command="$path $#"
restart_command="$kill_command;sleep 2;$start_command"
JAVA_OPTS="$JAVA_OPTS -XX:OnOutOfMemoryError=\"$restart_command\""
Generally what it does is JAVA_OPTS is constructed inside test1.sh and then used to run Java binary, which just writes PID in pidfile and then creates OOME.
Problem happens during execution, java can't understand what is a parameter and what is a class to run. I think it might be a problem of quoting, I tried different ways to escape JAVA_OPTS, but without any result. I'm either getting:
Unrecognized option: -9
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Or
Error: Could not find or load main class "-XX:OnOutOfMemoryError=kill
If I just take a value of JAVA_OPTS and put it manually in test.sh it runs perfectly.
Any ideas how can I change test1.sh to make it work? I think I tried almost every possible way of putting double and single quotes, but without any success. Also if I put restart_command in restart.sh file and use it instead of the variable, it works fine.
After running set -x I saw that shell modifies every single space character to ' ' - adds ' on both sides. Escaping doesn't gives any result. Any idea how to avoid this? So final commend is:
+ java -Xmx10m '"-XX:OnOutOfMemoryError=kill' '$(cat' 'test.pid);sleep' '2;/Users/davidt/test/TestMemory/bin/test.sh' '")' -Des.pidfile=test.pid TestMemory
Update
I can run simplified command successfully
java "-XX:OnOutOfMemoryError=echo 'Ups'" $es_params TestMemory
But it seems a general problem, shell just hates spaces into variables I guess:
JAVA_OPTS="\"-XX:OnOutOfMemoryError=echo 'Ups'\""
set -x
java $JAVA_OPTS TestMemory
This script fails and the last line is interpreted as:
java '"-XX:OnOutOfMemoryError=echo' ''\''Ups'\''"' TestMemory
I tried different options to escape
This is a shell problem. Based on the evidence, I'd say that one of the ; characters ... and possibly some why space ... is being interpretted by the shell when you don't want / need this to happen.
If you run set -x in the shell before running the command that is trying to start the JVM, you will see the actual command that is being used.
It seems shell translates every single space to ' ',
Not exactly. The single quotes are inserted by the shell into the output you are getting from set -x. They simply indicating where the argument boundaries are. They are not really there ... and they are certainly NOT being passed to the java command.
Any idea how to [a]void it?
What you need to do is start from the (final) command that you are trying execute ...
java -Xmx10m -XX:OnOutOfMemoryError="kill NNNN;sleep 2;/Users/davidt/test/TestMemory/bin/test.sh" -Des.pidfile=test.pid TestMemory
... and work backwards, so that the shell variables, expansions and escaping give you what you need.
The other thing to note is that this:
java -Xmx10m -XX:OnOutOfMemoryError="kill $(cat test.pid); ..."
probably won't work. The kill $(cat test.pid) command is using shell syntax and requires shell functionality to interpolate the contents of the PID file. I doubt that the JVM is going to know what to do with that. (Or more accurately. It will do what you have literally told it to do, but that will not be what you want ...)
If you really need to interpolate the pid file content when the restart command is run as you appear to be trying to do, then suggest that turn the restart command into a free-standing shell script, and set the file mode so that it is executable. It will be simpler and a lot easier to get working.
As a general piece of advice, is is a bad idea to be too clever with shell scripts. The exact semantics of variable expansion and command parsing are rather tricky, and it is easy to get yourself really confused ... if you are trying to do this at multiple levels.
I ended up put the script I wanted to execute in a separate file and gave it as a parameter to JVM to execute when OOME happens.
echo "echo 'UPS'" >> oome_happened.sh
JAVA_OPTS="\"-XX:OnOutOfMemoryError='oome_happened.sh'\""
set -x
java $JAVA_OPTS TestMemory
Like #DaTval said, you should put the command in a script. The script should be someting like.
#!/bin/bash
kill -9 $PPID
Kill the caller of scripts.
I just want to be able to do ./whatever.jar instead of java -jar whatever.jar.
I've found a way:
#!/bin/bash
java -jar $0 $*
exit
# jar goes here...
but it doesn't work. Java just complains that it's an invalid/corrupt jarfile.
I also tried piping:
#!/bin/bash
tail -n +4 $0 | java -jar
exit
# jar goes here...
but this doesn't work.
One way to do it is to somehow split the file into two separate parts (the script part and the jar part), and then execute the jar, but that'd be redundant. You'd might as well make a script that executes the jar and execute that script.
So I need to figure out how to somehow tail it and fake the file.
I thought I could do it using /dev/stdout:
#!/bin/bash
java -jar /dev/stdout
tail -n +5 $0
exit
# jar goes here...
That doesn't work either. It just prints the contents of the jar and java complains that it's invalid. (I figured out later that there's nothing to read in /dev/stdout)
So I need to read from stdout some other way. I really wish I could pipe it though. It would make things SO much easier :)
You need a service called jexec some linux distros come with this installed check for /etc/init.d/jexec. My CentOS 5.5 definitely does.
What it does is register the jexec interpreter with the binfmt system.
For more information you might what to have a quick read of binfmt_misc.
Assuming you have the kernel source code installed, check out /usr/src/linux/Documentation/java.txt for a way to run Java code directly using the kernel's BINFMT_MISC support (assuming it's compiled into the version of the kernel you're running, but I think it is on most major distros). If you don't have the source installed, you should be able to find it online easy enough (here's one example).
FYI, if you wanted to do it your original way it would go like this:
$ cat jar.sh
#!/usr/bin/env bash
java -jar <(tail -n +4 "$0")
exit
$ cat jar.sh runme.jar > works.jar
$ chmod a+x works.jar
$ ./works.jar
Presuming a recent bash with support for <()
java -jar does not work with stdin, apparently it does some seeks rather than straight reads.
On a system you can't mod, you have to use a tmp. for example.
#!/bin/bash
JF=/tmp/junk$$.jar
(uudecode -o /dev/stdout >$JF;java -jar $JF;unlink $JF) <<JAR
begin-base64 644 junk.jar
UEsDBBQACAAIAEaBz0AAAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMA
UEsHCAAAAAACAAAAAAAAAFBLAwQUAAgACABGgc9AAAAAAAAAAAAAAAAAFAAA
AE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVy
LkpNLElN0XWqBAmY6RnEG5koaASX5in4ZiYX5RdXFpek5hYreOYl62nycvkm
ZubpOuckFhdbKWSV5mXzcvFyAQBQSwcIBHn3CVkAAABZAAAAUEsDBBQACAAI
AEd+z0AAAAAAAAAAAAAAAAAKAAAAanVuay5jbGFzc21QTUvDQBB926RJE6Ot
ramfhXoQogcDXiteBPFQVIjowdOmXcrGZCMxEfxZelDw4A/wR4mzUShCF3Zn
9s2bt2/26/vjE8ARBi4stB10sNpC10UPazZ8G30G61gqWZ4wGMH+DYN5mk8F
Q3sslbioslgU1zxOCTEzLhVDP7gbJ/yJhylXszAqC6lmI93oRnlVTMSZ1GQn
qdT9oeZ5sNGyse5hA5sM3rlI03x4mxfpdNfGlodt7JC45jN05sqXcSIm5T8o
en4sRUZG84oK/q8NmYdX5KEkJ4JnI4beApjBftC3lAbwg0X+MUSTvkivBm3y
DJqCsgFFRrF58A72QglNSqdVg5qyBO+PuketGnVe0egabzDndLdWNUjVJGS5
fmXlB1BLBwjDUWL/IAEAAJ4BAABQSwECFAAUAAgACABGgc9AAAAAAAIAAAAA
AAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAAI
AEaBz0AEefcJWQAAAFkAAAAUAAAAAAAAAAAAAAAAAD0AAABNRVRBLUlORi9N
QU5JRkVTVC5NRlBLAQIUABQACAAIAEd+z0DDUWL/IAEAAJ4BAAAKAAAAAAAA
AAAAAAAAANgAAABqdW5rLmNsYXNzUEsFBgAAAAADAAMAtQAAADACAAAAAA==
====
JAR
Or I could just install the jarwrapper (Ubuntu) package.
Write a separate shell script:
whatever.sh
#!/bin/bash
java -jar whatever.jar $*
You can't make the JAR file directly executable because it's not an executable file. It's Java bytecode which can't be read directly by the machine nor any standard shell interpreter that I know of.
I'm trying to write Java code that executes some terminal commands. The code should execute this command sudo mount -o loop system.img system. But there are several problems. First, to execute this command I have to be root. I know that I can be by sudo su, but how can I stay as root when I close the terminal window? If I use the command sudo mount -o loop system.img system how can I provide the password in the Java code?
The second issue is: can I execute the command as below?
File f2 = new File("/home/user1/Desktop/aDirectory");
String[] commands = new String[]{"sudo mount", "-o", "loop", "/home/user1/Desktop/aDirectory/system.img"};
Runtime.getRuntime().exec(commands, null, f2);
I think I can't. So how can I do it? Any ideas?
Notes: system.img is a compiled Android os file. and the system is an empty directory. The thing I'm trying to do is mount the system.img file into the system directory.
Programs like sudo read the password directly from the terminal device, not from stdin, so this is unfortunately not a trivial thing to do. I'm not sure if this is realistic for Android or not, but on a general UNIX system the easiest solution is to use expect, which is a library for simulating a terminal and thereby automating these kinds of interactions. It's often used as a standalone program embedded in Tcl, and I've thrown together systems in which Java launched expect to talk to tools like sudo, and it works fine.
expect includes a sort of declarative scripting language that tells it how to run another program and how to react to that program's output.
What you would do is use Runtime.exec() to execute the expect program, supplying a script that just runs "sudo mount", watches for the password prompt, and provides the password. The script would probably just look something like (G4rb4geB4rg3 is the password):
spawn sudo mount -o loop /home/user1/Desktop/aDirectory/system.img
expect "password:"
send "G4rb4geB4rg3\r"
expect eof
The problem was solved, by using shell script.
I wrote a script includes just this line :
echo myPassword | sudo -S mount -o loop system.img system
then I run it in my java code, such :
Runtime.getRuntime().exec("sh 1.sh");
I'm pretty sure 'sudo' and 'mount' would be separate, since it's not a single executable you're invoking. Also, if you start sudo with the -S command line switch it can take the password directly from stdin, so you just need to start the process and pass in whatever the password's configured as to the input stream.
I need to limit the CPU and bandwidth of my application for testing purposes during the development process and wrote a small bash script wrapper around the java command, but I'm not sure how I can integrate this approach with IDEA's run/debug configuration.
#!/bin/bash
if [ $# -eq 0 ]
then
echo "Usage: javalimit [CPU Percentage] [download in kbps] [upload in kbps] [normal java args]"
echo "Required packages: trickle, cpulimit"
fi
CPU_PERCENTAGE=$1
DOWNLOAD_KBPS=$2
UPLOAD_KBPS=$3
shift 3
trickle -s -d $DOWNLOAD_KBPS -u $UPLOAD_KBPS java $#
TRICKLED_PID=$!
cpulimit --limit=$CPU_PERCENTAGE --pid=$TRICKLED_PID
My first failed approach for IDEA integration was
Statically set the cpu/down/up, removed shift
Copied my java folder to a new one, renamed java command, symbolic linked my script to bin/java
Told IDEA to use this JRE config - Crashed
Any insights on a better way to approach this problem would be appreciated!
I guess the best solution would be to use the Remote Debug as I've suggested in the similar question.