An integral part of the Java Native Interface (JNI), is the bridging of JVM code and native code through C headers. The way to generate these header files used to be quite straight forward: simply call the command line utility javah on class files. This process would then generate prototypes for any method marked with the native modifier.
As of Java 10 however, the javah utility has been removed, and its suggested replacement is a new flag "-h" to javac. The replacement works fine if one has the Java source files available, however falls short in cases where only compiled class files are available. (The issue that sparked this question is that I'm trying to generate JNI bindings from Scala sources. My current approach has been to compile them first and then run javah over the resulting class files.)
In a situation where only compiled class files are available, is there a way to generate C header files, similar to the way javah used to?
You can always go via javap. I know, I know. It's ugly, has lots of assumptions, but in case you desperately need to generate headers for lots of files it might be the only option.
#!/bin/bash
# FIRST_ARG - full class name (with package)
# SECOND_ARG - class path
CLASS_NAME=`javap -cp $2 $1 | \
grep -v "Compiled from" | \
grep "public class" | \
cut -f3 -d" " | \
awk -F"." '{ print $NF }'`
PACKAGE_NAME=`javap -cp $2 $1 | \
grep -v "Compiled from" | \
grep "public class" | \
cut -f3 -d" " | \
sed s/\.${CLASS_NAME}$//`
DIR_NAME=`echo $PACKAGE_NAME | sed 's|\.|/|g'`
mkdir -p java_jni/${DIR_NAME}
JAVA_FILE_NAME="java_jni/${DIR_NAME}/${CLASS_NAME}.java"
echo "package ${PACKAGE_NAME};" > ${JAVA_FILE_NAME}
echo "public class ${CLASS_NAME} {" >> ${JAVA_FILE_NAME}
javap -cp $2 $1 | grep "native" | while read line; do
param=0
comma=`echo $line | grep "," | wc -l`
while [ $comma -gt 0 ]; do
line=`echo $line | sed "s/,/ param_${param}|/"`
let param=param+1
comma=`echo $line | grep "," | wc -l`
done
line=`echo $line | sed "s/)/ param_${param})/" | sed 's/|/,/g'`
echo " $line" >> ${JAVA_FILE_NAME}
done
echo "}" >> ${JAVA_FILE_NAME}
mkdir -p c_header
javac -h c_header ${JAVA_FILE_NAME}
I bet it can be made way more beautiful.
For me, now, when I slowly start to think about inevitable move towards Java 10, and all these cases, where I might be surprised by non existing Java source code, I think it's not a bad idea to have some tool at my disposal. Just in case.
We can use gjavah to generate JNI header files.
The best solution is just install a jdk8, i think.
And no need to uninstall jdk10, just modify the environment variable.
Related
I know I can get all the Java System properties from the terminal using
java -XshowSettings:properties -version
How do I access just one specific java system property?
For example, like "user.name"?
I want to do this in the terminal, not with Java.
Solution as a one liner script. Just change the val variable to the key you want to print:
val='java.library.path'; java -XshowSettings:properties -version 2>&1 | sed -re 's/^ +[^=]+ =/_&/' | gawk -v key=$val 'BEGIN{ RS="_"; IFS=" = "} { if($1 ~ key){ print $0 }}'
Details
Some property values like java.library.path contain new lines so we need to mark records before filtering and printing them.
sed allows us to do that, then awk can be used to filter and print.
java -XshowSettings:properties -version 2>&1 |\
sed -re 's/^ +[^=]+ =/_&/' |\
gawk -v key=java.library.path 'BEGIN{ RS="_"; IFS=" = "} { if($1 ~ key){ print $0 }}'
Result:
java.library.path = /usr/java/packages/lib/amd64
/usr/lib64
/lib64
/lib
/usr/lib
Pipeline parts explained:
2>&1: properties are printed to stderr so we need to redirect them to stdin.
sed -re 's/^ +[^=]+ =/_&/' : add an underscore in front of interesting lines, those starting with 4 spaces and containing =.
gawk -v key=java.library.path: set keyawk variable to the selected property key.
'BEGIN{ RS="_"; IFS=" = "}: set record separator to '_' and input field separator IFS to =.
If you need the current logged in user in bash just use whoami command. If you want to get the java property from terminal you can use the following command
java -XshowSettings:properties -version 2>&1 | grep user.name
which will print
$java -XshowSettings:properties -version 2>&1 | grep user.name
user.name = user
If you just need the user name only
java -XshowSettings:properties -version 2>&1 | grep user.name | cut -c 16-100
which will print
$java -XshowSettings:properties -version 2>&1 | grep user.name | cut -c 16-100
user
You can't.
What you can do is create a java file to get the information and run with java, here the documentation.
Since you already said that you don't want this, you can grep (filter) the output of
java -XshowSettings:properties -version 2>&1 | grep java.home
java.home = /usr/java/jdk1.8.0_112/jre
If you want to know the system properties of a running jvm, use the jcmd tool
jcmd PID VM.system_properties
I currently have a directory that contains numerous .java files, all with different names. ie. name.java, name2.java, name3.java
I am trying to write a script that loops through all of the files in the directory and changes their class names (inside the file) to match the file name itself.
Currently, all of the .java files contain the class name MyCritter. I want to change all instances of MyCritter in each of the files to the name of the particular java file itself. I wrote a script to try and replace all of the MyCritter terms, however I am getting no change in output. The code is getting frozen after printing the echo line for the first name of the file:
#!/bin/bash
dir1="/Users/path to folder"
subs=`ls $dir1`
for a in $subs;
do
echo a
[ -f "$a" ]
grep -lr "MyCritter" * | xargs sed “s/MyCritter/$a/g”
done
output to terminal: name.java --> then infinite loop/gets stuck
The above code prints the name of the first file in the directory once, but then gets stuck. When I change my second line to this: [ -f "$a" ] || continue it runs through the whole code, but fails to update the files.
I've tried other variations of grep including:
grep -lr -e "MyCritter" * | xargs sed -i “s/MyCritter/$a/g”
grep -lr -e "MyCritter" . | xargs sed -i “s/MyCritter/$a/g”
grep -lr -e "MyCritter" . | xargs sed “s/MyCritter/$a/g”
I was primarily using this site to guide me: http://isaacsukin.com/news/2013/06/command-line-tip-replace-word-all-files-directory
Any push in the right direction would be much appreciated!
If you don't need to have a solution is script form, in a terminal, just cd to the directory containing the *.java files and use the following command:
for f in *.java; do sed -i "s/MyCritter/"${f%.*}"/g" "$f"; done
The "${f%.*}" portion of the sed command returns the filename without the extension and uses it to replace MyCritter.
As others mentioned, there are syntax errors in your code and shellcheck can easily detect those.
One of the alternate ways this can be done is:
#!/bin/bash
dir1="Absolute_path_for_dir"
for f in $dir1/*.java;do
# Extract name from /path/name.java
filename=$(basename "$f" .java)
# Replace ALL occurrences of MyCritter with filename in file $f
sed -i "s/MyCritter/$filename/g" "$f"
done
With GNU awk for inplace editing:
awk -i inplace '{gsub(/MyCritter/,FILENAME)} 1' *.java
or if you want to strip off the ".java":
awk -i inplace 'FNR==1{f=FILENAME; sub(/\.java$/,"",f)} {gsub(/MyCritter/,f)} 1' *.java
I'm quite new to the Bash and would like to ask this:
Is it possible (with a 2-liner ;-) ) to
find all jars in all subdirs and show their folder, name and size?
Just like that:
/jars/ | abc.jar | 123456
/ext/ | def.jar | 1234
/ext/ | ghi.jar | 2345
So it should be sth. like (Pseudo code)
find . -name "*.jar" | while read jar; do echo "$jardir | $jarname | $jarsize";
Thanks
Bernhard
If I understand your question, you could do it with find and -exec; something like,
find . -name "*.jar" -type f -exec du -m {} \;
Which will find all files ending in .jar and call du -m on each found jar file.
Edit based on OP comment below. To get everything separately, you could use something like -
for i in `find . -name "*.jar" -type f`; do
echo $(du -m $i|awk '{print $1}') $(dirname $i) $(basename $i)
done
For the record, this question isn't so much about bash itself as about the command-line tools that are commonly available on systems that provide bash. You can, in fact, perform the heavy lifting mostly with bash itself, but if you want a two- (or in fact a one-)liner then you should meet the sed program. For example:
find . -name '*.jar' -exec ls -l {} \; | sed 's,^\([^ ]\+ \+\)\{4\}\([0-9]\+\) \+.* \([^ ]\+/\)\([^ /]\+\)$,\3 | \4 | \2 ,'
Yes, it's incredibly cryptic. Most of the cryptic part is a regular expression that selects the wanted pieces of a long-form directory listing. But you wanted short, and concision comes with a price.
There's solution using find only:
find -type f -name "*.jar" -printf "| %20h | %10f | %10s |\n"
I have to run the Java application and read the syslog to trigger some other Python based events.
At the same time i also need to dump and store it in /var/tmp/log.log of all java outputs, but because the new Python event controller is added that /var/tmp/log.log i was not able to create. Any idea how can i still make it? . For example: java | python >> java logs as >> log.log for tail -f
BEFORE: (worked)
$ java -cp /var/tmp/Audio.jar Main.Boot >> /var/tmp/log.log &
$ tail -f /var/tmp/log.log
AFTER: (not working)
$ java -cp /var/tmp/Audio.jar Main.Boot | python -u /var/tmp/consumer.py &
$ tail -f ????? how can i have the java syslog still dumped as like my BEFORE ????
/var/tmp/consumer.py
import sys, time, os
while True:
line = sys.stdin.readline()
if line:
sys.stdout.flush()
if "wall:on" in line:
os.system("/var/tmp/me.sh")
else:
time.sleep(1)
/var/tmp/me.sh
#!/bin/bash
export DISPLAY=:0.0
ps aux | grep "/var/tmp/pp.py" | awk '{print $2}' | xargs kill -9;
# System maintain..
python /var/tmp/pp.py &
sleep 3
ps aux | grep "/var/tmp/pp.py" | awk '{print $2}' | xargs kill -9;
Off the top of my head:
$ java -cp /var/tmp/Audio.jar Main.Boot | tee /var/tmp/log.log | python -u /var/tmp/consumer.py &
$ tail -f /var/tmp/log.log
I would also do the awk/grep stuff inside python instead of going through a subprocess.
I'm trying to develop a bash build script for a Java project that will be run on Ubuntu and Fedora. Ubuntu uses the gcj compiler while Fedora uses IcedTea.
Both report their errors and warning in slightly different ways, and I want to ignore the warnings (I know, not generally a good idea, but some of the warnings are simply idiotic).
For gcj, I want to run:
javac *.java 2>&1 | grep -A 4 "error:"
but for IcedTea, I want to run:
javac *.java 2>&1 | grep -A 4 "error:\|errors\|.java:"
I'm still new to bash, so how would I write an if statement that would run one versus the other based upon the javac version?
Assuming your java and javac binaries match, and that icedtea is the special case.
#!/bin/bash
ERROR="error:"
java -version 2>&1 | grep -i icedtea > /dev/null
if [ $? -eq 0 ]; then
ERROR="error:\|errors\|.java:"
fi
javac *.java 2>&1 | grep -A 4 $ERROR
On my system, icedtea and sun have the same output for "javac -version", but not for "java -version".
Writing Java build scripts in bash (or any other shell language) has a number of problems:
scripts tend to be non-portable due to shell differences, different command locations, incompatible command options and so on ... even if you try to make the portable.
scripts cannot cope with dependencies (or at least not easily)
scripts cannot cope with recompiling only stuff that has changed
Instead, I suggest that you write a "build.xml" file and use the Ant build tool. Ant has the advantage of running on any build platform that runs Java, and of taking care of the vast majority of platform differences. It is sort of like a better "Make" designed specifically for building Java.
#!/bin/sh
JAVAC_VERSION="`java -version 2>&1 /dev/null | awk '/IcedTea/ {print $4}' | sed -e 's/[\(0-9]//g'`"
ICEDTEA="IcedTea"
if [ ${JAVAC_VERSION} = ${ICEDTEA} ]; then
javac *.java 2>&1 | grep -A 4 "error:\|errors\|.java:"
else
javac *.java 2>&1 | grep -A 4 "error:"
fi
exit 0
That should do it - if i understood your question correctly. How you get the version - im not quite sure of, but if my javac -version is incorrect just change it accordingly to your needs.