How do I include jars in a groovy script? [duplicate] - java

This question already has answers here:
How do I auto load a database jar in Groovy without using the -cp switch?
(5 answers)
Closed 6 years ago.
I have a groovy script that needs a library in a jar. How do I add that to the classpath? I want the script to be executable so I'm using #!/usr/bin/env groovy at the top of my script.

Starting a groovy script with #!/usr/bin/env groovy has a very important limitation - No additional arguments can be added. No classpath can be configured, no running groovy with defines or in debug. This is not a groovy issue, but a limitation in how the shebang (#!) works - all additional arguments are treated as single argument so #!/usr/bin/env groovy -d is telling /usr/bin/env to run the command groovy -d rathen then groovy with an argument of d.
There is a workaround for the issue, which involves bootstrapping groovy with bash in the groovy script.
#!/bin/bash
//usr/bin/env groovy -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $#; exit $?
import org.springframework.class.from.jar
//other groovy code
println 'Hello'
All the magic happens in the first two lines. The first line tells us that this is a bash script. bash starts running and sees the first line. In bash # is for comments and // is collapsed to / which is the root directory. So bash will run /usr/bin/env groovy -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $# which starts groovy with all our desired arguments. The "$0" is the path to our script, and $# are the arguments. Now groovy runs and it ignores the first two lines and sees our groovy script and then exits back to bash. bash then exits (exit $?1) with status code from groovy.

If you really have to you can also load a JAR at runtime with:
this.getClass().classLoader.rootLoader.addURL(new File("file.jar").toURL())

My Favorite way to do this is with Groovy Grapes. These access the Maven Central Repository, download the referenced jar, and then put it on the classpath. Then you can use the library like any other library. The syntax is really simple:
#Grab(group='com.google.collections', module='google-collections', version='1.0')
You can read more details here. One major advantage here is that you don't need to distribute your dependencies when you distribute your script. The only drawback to this method is that the Jar has to be in the Maven repository.

You can add the jars to $HOME/.groovy/lib

You can also try out Groovy Grape. It lets you use annotations to modify the classpath. Its experimental right now, but pretty cool. See docs.groovy-lang.org/.../grape

The same as you would in Java.
This is an example of running a MySQL status monitoring script. mysql.jar contains the MySQL connector that I call from script status.groovy.
groovy -cp mysql.jar status.groovy ct1

Below is a combination of Patrick's solution, Maarteen Boekhold's solution, and foozbar's comment that works with both Linux and Cygwin:
#!/bin/bash
// 2>/dev/null; SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
// 2>/dev/null; OPTS="-cp $SCRIPT_DIR/lib/extra.jar:$SCRIPT_DIR/lib/spring.jar"
// 2>/dev/null; OPTS="$OPTS -d"
// 2>/dev/null; OPTS="$OPTS -Dlog4j.configuration=file:/etc/myapp/log4j.xml"
// 2>/dev/null; exec groovy $OPTS "$0" "$#"; exit $?
import org.springframework.class.from.jar
//other groovy code
println 'Hello'
How it works:
// is a valid groovy comment, so all of the bash commands are ignored by Groovy.
// will return an error, but the error output is redirected to /dev/null and is therefore not displayed.
bash executes commands following a semicolon even though the previous command failed.
exec replaces the current program in the current process without forking a new process. Thus, groovy runs within the original script process (ps shows the process as the script rather than the groovy executable)
The exit $? statement following exec groovy prevents bash from trying to interpret the rest of the script as a bash script, and also preserves the return code from the groovy script.
The above bash trick is more convenient in some cases than the RootLoader trick because you can use regular import statements within the script. Using the RootLoader trick forces you to load all of the classes using reflection. This is fine in some situations (such as when you need to load a JDBC driver), but inconvenient in others.
If you know your script will never be executed on Cygwin, then using Patrick's or Maarteen's solution will likely result in slightly better performance because they avoid the overhead of generating and throwing away an error.

Adding to #Patrick his answer, which helped me a lot, I recently discovered another trick.
If you add lots of jars to the classpath all on one line, things can become quite unreadable. But you can do the following!
#!/bin/bash
//bin/true && OPTS="-cp blah.jar -Dmyopt=value"
//bin/true && OPTS="$OPTS -Dmoreopts=value2"
//usr/bin/env groovy $OPTS "$0" $#; exit $?
println "inside my groovy script"
Let your imagination run wild on how complex a command line you can break down this way into manageable pieces
Maarten

If you want to use it right away before the import declarations it is possible like this :) :
// printEmployees.groovy
this.class.classLoader.rootLoader.addURL(
new URL("file:///C:/app/Dustin/product/11.1.0/db_1/jdbc/lib/ojdbc6.jar"))
import groovy.sql.Sql
sql = Sql.newInstance("jdbc:oracle:thin:#localhost:1521:orcl", "hr", "hr",
"oracle.jdbc.pool.OracleDataSource")
sql.eachRow("SELECT employee_id, last_name, first_name FROM employees")
{
println "The employee's name is ${it.first_name} ${it.last_name}."
}
Taken from this javaworld.com article.

Related

Using parameters from shell script in my Java code

I have to write two scripts, one to compile my code and another one to run it. I manage to compile the code with this script:
#!/bin/bash
javac SimilaridadeP.java
And I was able to run it using this script:
#!/bin/bash
java SimilaridadeP
The problem is that I need to execute my second shell script following this command:
./execute.sh input output
Where input and output are the names of the files that I am going to use in my Java code. The problem that I am facing right now is that I don't know how I can use these both names in my Java code.
So far I managed to get both names using this code:
#!/bin/bash
in=$1
out=$2
java SimilaridadeP
But since "SimilaridadeP" is the name of my java file and it can't be called like a method with parameters I am kind of lost.
If your java code is already reading the command-line parameters, it's just a matter of changing the bash this way:
#!/bin/bash
java SimilaridadeP "$1" "$2"
This works with arbitrary number of arguments:
#!/bin/bash
java SimilaridadeP "$#"

Launch Java with a specific argument each time

I have a program which allows me to define the java executable (/usr/bin/java), but does not allow me to add specific arguments to the executable.
I want to be able to run Java with a specific argument each time in order to enable Security Manager.
So far, I have tried to add the argument after /usr/bin/java, so it looks like
java=/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy
That did not work as the program probably checks to see if a file exists. Another way I tried was to make a bash script called java which contained:
/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy $*
I then set the java path to /home/java (Location of my script). That however did not work either. Is there some sort of way I can do this?
Thanks.
Put your java call in a shell script java.sh:
#!/bin/bash
/usr/bin/java -Djava.security.manager -Djava.security.policy=/home/java.policy $#
Change permissions with chmod u+x java.sh, then call your program with java=./java.sh (adapt path for your script as needed).
Notes about executable bit and shebang line
Both the shebang line (#!/bin/bash) and execute permission are important here. Without them, system calls of the exec* family will fail because the kernel does not know what to do with the file or because execution is rejected due to the missing executable bit.
This is different when run directly from a shell (./java.sh), because most shells have some compatibility feature for that case so they will run a script in a shell if exec* fails. The execute permission must be set though.
The only case where neither is needed is if you give your script as argument to the shell: bash java.sh.
Your second approach would be ok, but your problem is likely to be that your Multicraft application is not finding your script. More so, it's the environment of your web server (Apache?) that may need to have PATH altered to be able to find your java wrapper script.

running nashorn using Shebang does not accept -cp option

I am trying to use a class into a jar using a Nashorn Shebang script with the -cp option (java version "1.8.0_31"). However it does not works. I have perform some test. The following shebang line works:
#!/usr/bin/jjs -scripting
#!/usr/bin/jjs -fv (returns nashorn full version 1.8.0_31-b13)
while the following not:
#!/usr/bin/jjs -cp ./some/lib/lib.jar will return the following error message: "-cp ./some/lib/lib.jar" is not a recognized option.
#!/usr/bin/jjs -scripting -fv will return the error message: "-scripting -fv" is not a recognized option. Use "-h" or "-help" to see a list of all supported options"
All options are theoretically valid. The classpath option should also works as seen on http://www.adam-bien.com/roller/abien/entry/setting_the_classpath_for_nashorn .
More info about nashorn and Shebang: http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html#CHDEGHJJ
You ran into an issue which doesn’t have anything to do with Nashorn nor Java. According to this answer the command line argument handling with shebang never was clearly specified and it seems to be a common behavior to treat everything encountered after the first white-space as one single argument.
So one solution would be to write a shell script containing an invocation of jjs with the actual arguments and use that shell script as the interpreter in the shebang line of your Nashorn script.
You can use -Dnashorn.args=-cp in a shebang script. See also https://bugs.openjdk.java.net/browse/JDK-8072138
As it appears you want a way to automatically add JARs to your classpath, I'll highlight a little wrapper I've written which allows you to define Maven co-ordinate dependencies (including transitives) to be added to the classpath of your script, so you can write a script using the "# dep" lines:
#!/usr/bin/env jjs-with-deps
#
# The line below is parsed by the jjs-with-deps script to build a new
# classloader in which the script is really executed, including logback
# and its transitive dependencies.
#
# dep:ch.qos.logback:logback-classic:1.1.2
var log = org.slf4j.LoggerFactory.getLogger("com.example.app.Logger");
log.info("Hello World!");
It does require Maven to be installed somewhere on your PATH, and it does slightly increase startup time (but then again, you're already starting up a JVM ;). First invocation of a given script will be much slower while any dependencies are downloaded to the local M2 repository.
Link is https://github.com/stevestorey/jjs-with-deps

Groovyc *.groovy strange behavior

I have a handfull of groovy files that I compile into java class files with groovyc (because it's a good bit quicker to start java than starting up groovy just to run a short script.) All the scripts are in the default package but they share some common functionality defined in a ".support" package.
I was just reworking it and found an amazingly strange problem--when I use:
cd /D c:\scripts
groovyc -d c:\destPath *.groovy
cd support
groovyc -d c:\destPath *.groovy
Everything worked fine.
But when I compile the way I'd expect to:
groovyc -d c:\destPath c:\scripts\*.groovy
It would only compile one of the scripts (I believe it was the last one alphabetically).
Anyone have an explanation for this behavior?
This seems due to the wrapper batch script that executes the groovyc executable.
On Windows, the groovyc command will start a batch script called startGroovy.bat:
"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.tools.FileSystemCompiler %*
I'm no expert in batch files but If you take a close look at startGroovy.bat, you can easily notice that there is some kind of a hack that is being done on the * wildcard that is passed as an argument to the script (e.g. c:\scripts\*.groovy):
rem escape minus (-d), quotes (-q), star (-s).
set _ARGS=%*
if not defined _ARGS goto execute
set _ARGS=%_ARGS:-=-d%
set _ARGS=%_ARGS:"=-q%
rem Windowz will try to match * with files so we escape it here
rem but it is also a meta char for env var string substitution
rem so it can't be first char here, hack just for common cases.
rem If in doubt use a space or bracket before * if using -e.
set _ARGS=%_ARGS: *= -s%
set _ARGS=%_ARGS:)*=)-s%
set _ARGS=%_ARGS:0*=0-s%
set _ARGS=%_ARGS:1*=1-s%
set _ARGS=%_ARGS:2*=2-s%
set _ARGS=%_ARGS:3*=3-s%
set _ARGS=%_ARGS:4*=4-s%
set _ARGS=%_ARGS:5*=5-s%
set _ARGS=%_ARGS:6*=6-s%
set _ARGS=%_ARGS:7*=7-s%
set _ARGS=%_ARGS:8*=8-s%
set _ARGS=%_ARGS:9*=9-s%
If you turn echoing on by executing set DEBUG=on in the command line, and then execute the groovyc command again, you will obtain the output of the batch script where you can see how the wildcard is being processed. Below is a sample log with a scenario where we have 3 files named File1.groovy, File2.groovy and File3.groovy.
groovyc -d c:\destPath c:\scripts\*.groovy
Sample output:
C:\scripts>set _ARG=C:\scripts\File3.groovy
C:\scripts>set _ARG=C:\scripts\File3.groovy
C:\scripts>set _ARG=C:\scripts\File3.groovy
C:\scripts>set CMD_LINE_ARGS= C:\scripts\File3.groovy
C:\scripts>set _ARG=
It seems the script is ignoring the first two files and eventually only the last one is passed to the org.codehaus.groovy.tools.FileSystemCompiler class (the actual compiler). This behavior does not exist when the * wildcard is the first character in the path to the source files.

convert a java program into a linux command

I have created one java program on my Linux system which indents and formats the given file. I want to make that program work like a command in Linux which will take file names and other options as arguments and then will produce the output. I can do this with a C program by just copying the compiled executable in /bin folder but I don't know how to do it with java.
Sample script that can might further help-
#!/bin/bash
#Set whatever number of arguments you expect for the Java jar you have
ARGS_EXPECTED=3
if [ $# -ne $ARGS_EXPECTED ]
then
echo "[$HOSTNAME]: Usage: `basename $0` filename arg1 arg2"
exit 1
fi
java -cp yourfile.jar com.yourpkg.Driver $1 $2 $3
Save the above content to a file, say test.sh
and use the command to give an executable permission chmod +x test.sh
run like ./test.sh filename arg1 arg2 from current directory where test.sh is
I thing this can be useful for your case: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/binfmt_misc.txt?id=HEAD
You can simply run a Java class file or jar file with "java" command from command line. Example:
java -jar yourprogram.jar argument1 argument2
If you save this line in a text file saved for example as "script.sh" and then give it the permission for execution you can run it double clicking or from terminal typing ./script.sh in the same folder containing the file script.sh.
You can also produce scripts that use arguments with $1 $2 etc. avoiding the need of editing file.
http://www.linuxquestions.org/questions/linux-newbie-8/how-to-pass-command-line-parameter-to-shell-script-254396/
You can use named parameters, too.
You can also produce a C program for a new command like you suggested that run the "java" command. In this case you can introduce arguments directly from terminal and pass them to java command in the C source.
As others have pointed out it is probably best to use a small shell script to run the Java application. There are several open source products that will help you wrap your Java code to produce a runnable (set of) .jar(s).
If you have correctly separated your business logic from your interface (as you should) then it is probably best if your Java application parses the parameters given on the command line interface. To do this create a separate class for parsing such parameters and calling the classes making up the business logic. Of course this will lead quickly - if not immediately - in writing a parser for Linux like CLI parameters. When this happens you may wish to consider the Apache Commons CLI project.
If you don't want to use any wrapping application/runtime, my method is generally pointing to all the class file containers in the classpath and directly pointing to the class containing the static main method:
java -cp "path_to_jar;path_to_class_folder;etc" "nl.owlstead.stackoverflow.LinuxMain"

Categories