I created a jar file containing all my compiled stuff. Additionally my ant build script copies the required libs into a subfolder "libs". The structure looks like this:
MyProgram.jar
libs/
So when I try to run my program now I get the following error:
java -cp ".:/home/user/java/MyProgram/jar/libs" -jar MyProgram.jar
java.lang.ClassNotFoundException: org.postgresql.Driver
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at database.PostgresQL.getConnection(PostgresQL.java:38)
at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:19)
at main.Main.calculateCorrelationMatrix(Main.java:51)
at main.Main.main(Main.java:28)
java.lang.NullPointerException
at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:25)
at main.Main.calculateCorrelationMatrix(Main.java:51)
at main.Main.main(Main.java:28)
Why does this happen?
You use either -jar or -cp, you can't combine the two. If you want to put additional JARs on the classpath then you should either put them in the main JAR's manifest and then use java -jar or you put the full classpath (including the main JAR and its dependencies) in -cp and name the main class explicitly on the command line
java -cp 'MyProgram.jar:libs/*' main.Main
(I'm using the dir/* syntax that tells the java command to add all .jar files from a particular directory to the classpath. Note that the * must be protected from expansion by the shell, which is why I've used single quotes.)
You mention that you're using Ant so for the alternative manifest approach, you can use ant's <manifestclasspath> task after copying the dependencies but before building the JAR.
<manifestclasspath property="myprogram.manifest.classpath" jarfile="MyProgram.jar">
<classpath>
<fileset dir="libs" includes="*.jar" />
</classpath>
</manifestclasspath>
<jar destfile="MyProgram.jar" basedir="classes">
<manifest>
<attribute name="Main-Class" value="main.Main" />
<attribute name="Class-Path" value="${myprogram.manifest.classpath}" />
</manifest>
</jar>
With this in place, java -jar MyProgram.jar will work correctly, and will include all the libs JAR files on the classpath as well.
When the -jar option is used the -cp option is ignored. The only way to set the classpath is using manifest file in the jar.
It is easier to just use the -cp option, add your jar file to that, then explicitly call the main class.
Also, assuming the /home/user/java/MyProgram/jar/libs folder contains jar files (as opposed to class files) this won't work. You cannot specify a folder of jar file but must specify each jar file individually in the classpath (it is worth writing a simple shell script to do this for you if there are a significant number of jars).
It is a bit tricky. The following script is an attempt to get the classpath from the manifest of the jar and then allow to add extra classpath entries. I had mixed results with this but want to share the script nevertheless so it might be made fully functional here.
The script has two names
showmanifest
calljar
by hardlinking the two file together
with
ln calljar showmanifest
with calljar -h you can see the usage.
#!/bin/bash
#set -x
# show the manifest of a jar file
# 2012-07-18
# author WF
#
# show usage
#
usage() {
echo "usage: showmanifest (jarfile | directory jarfile) " 1>&2
echo "usage: calljar directory jarfile classpath pattern arguments" 1>&2
echo " -h|--help " 1>&2
echo " show this help and exit" 1>&2
echo " -m|--mainclass javaclass" 1>&2
echo " mainclass to use (otherwise manifest is inspected)" 1>&2
exit 1
}
#
# show the manifest of the given jar file
#
show() {
dir="$1"
jar="$2"
fulljar=`find "$dir" -name "$jar"`
cd /tmp
mkdir show$$
cd show$$
jar xvf $fulljar META-INF/MANIFEST.MF
cat META-INF/MANIFEST.MF
cd /tmp
rm -rf show$$
}
#
# show the classpath of the manifest
#
calljar() {
dir="$1"
jar="$2"
classpath="$3"
pattern="$4"
arguments="$5"
cmd=`show "$dir" "$jar" | awk -v extracp="$classpath" -v dir="$dir" -v pattern="$pattern" -v jar="$jar" -v mainclass="$mainclass" -v args="$arguments" '
/Main-Class: / { if (mainclass=="") mainclass=$2 }
/^Class-Path:/ {
incp=1;
cp=$0;
gsub("Class-Path: ","",cp)
next
}
/^ .*$/ && incp {
line=substr($0,2)
# remove carriage return (if any)
cp=cp line
}
END {
# we do not like carriage returns
gsub("\\r","",cp)
gsub("\\r","",mainclass)
# we do not like blanks ...
gsub(" ","",cp)
gsub(pattern,":"dir"/"pattern,cp)
print "java -cp " extracp cp ":"dir"/"jar " " mainclass " " args
}
'`
#echo $cmd
$cmd
}
# echo $# arguments found: $*
# parse command line options
while true; do
# echo "option $1"
case "$1" in
# options without arguments
-h|--help) usage;;
# for options with required arguments, an additional shift is required
-m|--mainclass) mainclass=$2; shift;;
(--) shift; break;;
(-*) echo "$0: error - unrecognized option $1" 1>&2; usage;;
(*) dir=$1;shift;break;;
esac
shift
done
#echo "argcount=$#"
case $# in
0) dir=`dirname "$dir"`
jar=`basename "$dir"`
show "$dir" "$jar";;
1) jar="$1"
show "$dir" "$jar";;
2) usage;;
3) usage;;
*) jar="$1"; shift;
classpath="$1"; shift;
pattern="$1"; shift;
arguments="$#";
#echo "mainclass=${mainclass}"
#echo "classpath=${classpath}"
#echo calljar "${dir}" "${jar}" "${classpath}" "$pattern" "$arguments"
calljar "$dir" "$jar" "$classpath" "$pattern" "$arguments"
;;
esac
For quick, one-off tests of an app, you can simply symlink the needed dependency JAR files into the directory containing the main app JAR file.
Example (for an app app.jar which uses the Eclipse SWT library, which in my case was installed in /usr/share/java):
$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
Related
I am using Windows CMD, and for some reason, I am getting this error message relating to my .java files. For example, I am typing:
javac FirstProgram.java
However, this error message occurs:
javac: file not found: FirstProgram.java
Usage: javac <options> <source files>
use -help for a list of possible options
I typed in javac -version, and I am currently using javac 1.8.0_144. Someone on in another Stack Overflow question suggested to change the System Variables. I used JAVA_HOME as the variable name, and I copied the path to my JDK folder, but thus far, I haven't had much luck. I still receive the same error message.
There are several problems here ... including why
PROBLEM 1:
"...I am not even allowed to save my FirstProgram.java in the Java
folder in Documents." <= ?!?
PROBLEM 2:
"...I got the error message about a false flag" <= This is probably a space " " in the path name
STRONG SUGGESTION:
Download Eclipse and try compiling and running your program from Eclipse. In other words, using an IDE, instead of the command line.
You can download Eclipse here:
http://eclipse.org
There is a good "starters tutorial" here:
Creating your first Java project
Run the dir or dir/p command to see your directory contents on your command prompt
c:\path\to\your program directory\dir
See if FirstProgram.java is listed or not? If not then you are in a wrong directory.
Now you have two options
You change to the correct directory using cd command or
You use the absolute path for your FirstProgram.java file
Navigate to that specific folder containing that program then [ Shift + L_Click ], click open cmd, then run it again to ensure it is being ran in that folder.
You should try to use absolute path while using the command if it says the file isn't found.
javac /some/directory/path/to/the/file/FirstProgram.java
Note: On command line, most of the shells would anyway not let you complete the path if the file doesn't exist there. And the other way if you're copying the path from an explorer/finder, it shall be guaranteed to be existing.
Edit: The absolute path as pointed out in comments would be using forward slashes in Windows, e.g :
javac \some\directory\path\to\the\file\FirstProgram.java
This is my a simplified DOSJavaIDE environment for simple test applications if Eclipse is too much of a hassle. I can point compiling and running an application to a specific JVM version. Study this script to see how folder structure and paths are given in each of the commands.
Folders and files
c:\projects\test1\classes\
c:\projects\test1\lib\
c:\projects\test1\lib\somelib1.jar
c:\projects\test1\lib\somelib2.jar
c:\projects\test1\src\
c:\projects\test1\src\test\GameLoop2.java
c:\projects\test1\src\META-INF\MANIFEST.MF
c:\projects\test1\javaenv.bat
javaenv.bat
#REM Standalone JavaDosEnvironment
#set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_112
#"%JAVA_HOME%\bin\java" -version
#set cmd=%1
#if "%cmd%"=="" (
#echo Please specify command to run ^(1..n or empty to exit^)
#echo 1=Compile, 2=Jar, 12=CompileJar
#echo 3=Run-test1 GameLoop2 with vsync
#SET /p cmd="1..n: "
)
#IF /I "%cmd%"=="1" set cmd=compile
#IF /I "%cmd%"=="compile" call :COMPILE
#IF /I "%cmd%"=="2" set cmd=jar
#IF /I "%cmd%"=="jar" call :JAR
#IF /I "%cmd%"=="12" set cmd=compilejar
#IF /I "%cmd%"=="compilejar" (
call :COMPILE
call :JAR
)
#IF /I "%cmd%"=="3" set cmd=run-test1
#IF /I "%cmd%"=="run-test1" call :RUN-test1
#goto :END
:COMPILE
xcopy /Y .\src\META-INF\*.* .\classes\META-INF\
set cp=./lib/somelib1.jar;./lib/somelib2.jar
"%JAVA_HOME%\bin\javac" -classpath "%cp%" -sourcepath ./src -d ./classes ./src/test/*.java
#goto :eof
:JAR
xcopy /Y .\src\META-INF\*.* .\classes\META-INF\
SET MF=./classes/META-INF/MANIFEST.MF
"%JAVA_HOME%\bin\jar" cvfm ./lib/test.jar %MF% -C ./classes .
#goto :eof
:RUN-test1
"%JAVA_HOME%\bin\java" -cp "./lib/*" test.GameLoop2 "fullscreen=false" fps=60 vsync=true
#goto :eof
:END
#pause
MANIFEST.MF
Implementation-Title: testapp
Implementation-Version: 1.0.0 (2017-07-21)
Implementation-Vendor: myname
Implementation-URL: http://my.homepage.com/
Run this script in a command-line such as javaenv.bat compile, javaenv.bat jar, javaenv.bat run-test1 or run without arguments to prompt for selection list.
See a customized manifest where you may write anything you want and is included in a ./lib/test.jar file. Compile target has few 3rd party dependency libraries in a classpath.
I want to compile a basic java project, https://sourceforge.net/projects/pdfformfiller2 its installation instructions are quite short:
Make sure that iText library, itext-xtra-5.x.0.jar and itextpdf-5.x.0.jar, are accessible to JAVA,
e.g. these are placed in the "lib" subfolder of the current folder.
Get latest ones from: https://sourceforge.net/projects/itext/files/iText/
Compile PdfFormFiller.java
Then from the command line you give command (to see usage help):
java -jar pdfformfiller.jar
I never compiled jars before, and I'm having hard time trying to compile PdfFormFiller correctly. Here's where I've get:
wget -O pdfformfiller.zip https://sourceforge.net/projects/pdfformfiller2/files/latest/download
# author mentions 5.2.0, which is not available anymore, so we go for the latest 5.x:
wget http://kent.dl.sourceforge.net/project/itext/5.5.10/itext5-5.5.10.zip
unzip pdfformfiller.zip
unzip itext5-5.5.10.zip -d pdfformfiller/lib
cd pdfformfiller
javac -cp "lib/*" PdfFormFiller.java
mkdir META-INF
echo -e 'Manifest-Version: 1.0\nClass-Path: pdfformfiller.jar\nMain-Class: PdfFormFiller' > META-INF/MANIFEST.MF
jar -cvfm pdfformfiller.jar META-INF/MANIFEST.MF lib PdfFormFiller.class
Which succeeds without an error, but still doesn't run:
$ java -jar pdfformfiller.jar
Error: Could not find or load main class PdfFormFiller
I guess I'm missing something trivial?
Edit
Complete automation:
iText5=5.5.10
wget -O pdfformfiller.zip https://sourceforge.net/projects/pdfformfiller2/files/latest/download
wget http://kent.dl.sourceforge.net/project/itext/${iText5}/itext5-${iText5}.zip
unzip pdfformfiller.zip
unzip itext5-${iText5}.zip -d pdfformfiller/lib
cd pdfformfiller
mkdir classes
javac -cp "lib/*" -d ./classes/ PdfFormFiller.java
mkdir META-INF
echo 'Manifest-Version: 1.0' > META-INF/MANIFEST.MF
echo "Class-Path: ./lib/itextpdf-${iText5}.jar ./lib/itext-xtra-${iText5}.jar ./lib/itext-pdfa-${iText5}.jar" >> META-INF/MANIFEST.MF
echo 'Main-Class: PdfFormFiller.PdfFormFiller' >> META-INF/MANIFEST.MF
jar -cvfm pdfformfiller.jar ./META-INF/MANIFEST.MF ./lib -C ./classes/ PdfFormFiller
Edit 2
It seems to be the only way to fill pdf form from CLI reliably:
# list fields in a file:
$ java -jar pdfformfiller.jar input.pdf -l
myfield
# prepare field data:
$ echo 'myfield αβγ' > fields
# specify font, fill the fields, flatten the form:
$ java -jar pdfformfiller.jar input.pdf -f fields -font Times_New_Roman.ttf -flatten output.pdf
Works like a charm!
Here are the steps I followed to get it working.
First of all, just for the sake of clarity, let's create a dedicated folder for your compiled classes. It's not mandatory, but just an example of good development practice. I'm omitting the steps of creating folders, changing dirs etc. because it's quite obvious. All commands are run from the project's root directory
javac -cp "lib/*" -d ./classes/ PdfFormFiller.java
Fixing the two main things that were missed:
a) the reference for required lib folder and
b) package name:
echo -e 'Manifest-Version: 1.0\nClass-Path: ./lib/itextpdf-5.5.4.jar ./lib/itext-xtra-5.5.4.jar ./lib/itext-pdfa-5.5.4.jar\nMain-Class: PdfFormFiller.PdfFormFiller' > META-INF/MANIFEST.MF
Assembling jar (please note that additional option: -C is being used here):
jar -cvfm pdfformfiller.jar ./META-INF/MANIFEST.MF ./lib -C ./classes/ PdfFormFiller
This is the final output from executing the resulting jar file:
$ java -jar pdfformfiller.jar
USAGE: pdfformfiller document.pdf [ -l ] [ -v ] [ -f fields_filename ] [ -font font_file ] [ -flatten] [ output.pdf ]
document.pdf - name of source pdf file (required).
-l - only list available fields in document.pdf.
-v - verbose. Use to debug the fields_filename file.
-f fields_filename - name of file with the list of fields values to apply to document.pdf.
if ommited, stdin is used.
-font font_file - font to use. Needed UTF-8 support, e.g. cyrillic and non-latin alphabets.
-flatten - Flatten pdf forms (convert them to text disabling editing in PDF Reader).
output.pdf - name of output file. If omitted, the output if sent to stdout.
fields_filename file can be in UTF-8 as is of the following format:
On each line, one entry consists of 'field name' followed by value of that field without any quotes.
Any number of whitespaces allowed before 'field name', and one space separates 'field name' and its value.
In value, newline characters should be encoded as "\n",
'U+2029 utf-8 E280A9 : PARAGRAPH SEPARATOR PS' should be encoded as "\p",
and '\' characters should be escaped as "\\".
For checkboxes, values are 'Yes'/'Off'.
Based on the Belgian iText library v. 5.2.0, http://www.itextpdf.com/
I have a jython application that uses some libraries from org.apache....
I've noticed that when I set the CLASSPATH=/some/path/*.jar, the script fails with an ImportError, whereas if i set the CLASSPATH=/some/path/*, the script will work fine.
unset CLASSPATH
jython /path/to/script.py
This fails with an ImportError: No module named apache
export CLASSPATH=/path/to/jars/*.jar
echo $CLASSPATH # shows all the jars in the folder
jython /path/to/script.py
This fails with the same ImportError
export CLASSPATH=/path/to/jars/*
echo $CLASSPATH # shows an identical list of jars as the previous classpath
jython /path/to/script.py
This succeeds.
Is there something I'm missing about either the CLASSPATH environment variable or about jython? For regular java, sometimes I will set -cp /path/to/jars/*.jar and that works. For regular java, I haven't used the CLASSPATH environment variable.
Edit:
If I diff the echo $CLASSPATH for both of the ways I set the classpath, there is no difference
export CLASSPATH=/path/to/jars/*.jar
echo $CLASSPATH >> first.txt
export CLASSPATH=/path/to/jars/*
echo $CLASSPATH >> second.txt
diff first.txt second.txt
The result of this grep command shows nothing:
ls -1 /path/to/jars/ | grep -v \.jar$
I have a java project, and need to write makefile to let it run on Linux. My project includes external jar files and resource package(.txt resourses).I am really a newbie for Linux, and just learn how to write makefile.
I refer to some materials and write a makefile like this:
# Set the file name of your jar package:
JAR_PKG = ADBproject1.jar
# Set your entry point of your java app:
ENTRY_POINT = adb/Bing_WebResults/Run.java
# Need resource directory
RES_DIR = yes
SOURCE_FILES = \
adb/jsonModels/Metadata.java \
adb/jsonModels/Result.java \
adb/jsonModels/Data.java \
adb/jsonModels/DataContainer.java \
adb/models/Weight_ID.java \
adb/models/Pair.java \
adb/models/Document.java \
adb/models/Collections.java \
adb/Bing_WebResults/Bing_Search.java\
adb/Bing_WebResults/Run.java \
JAVAC = javac
JFLAGS = -encoding UTF-8
vpath %.class bin
vpath %.java src
# show help message by default
Default:
#echo "make new: new project, create src, bin, res dirs."
#echo "make build: build project."
#echo "make clean: clear classes generated."
#echo "make rebuild: rebuild project."
#echo "make run: run your app."
#echo "make jar: package your project into a executable jar."
build: $(SOURCE_FILES:.java=.class)
# pattern rule
%.class: %.java
$(JAVAC) -cp bin -d bin $(JFLAGS) $<
rebuild: clean build
.PHONY: new clean run jar
new:
ifeq ($(RES_DIR),yes)
mkdir -pv src bin res
else
mkdir -pv src bin
endif
clean:
rm -frv bin/*
run:
java -cp bin $(ENTRY_POINT)
jar:
ifeq ($(RES_DIR),yes)
jar cvfe $(JAR_PKG) $(ENTRY_POINT) -C bin . res
else
jar cvfe $(JAR_PKG) $(ENTRY_POINT) -C bin .
endif
But I don't know how to add those two external .jar files (gson.jar, commons.jar) into makefile. And I'm not quite sure, whether the file paths I wrote are correct.
javac has a -cp and -classpath argument:
-classpath <path> Specify where to find user class files and
annotation processors
-cp <path> Specify where to find user class files and
annotation processors
They seem to be equivalent as far as the documentation is concerned.
I solve the problem by adding all *.jar files to a new folder "lib".
Then
javac -sourcepath src/ -classpath lib/*.jar
will solve the external jar file problem.
I'm new to Scala and don't know Java. I want to create a jar file out of a simple Scala file. So I have my HelloWorld.scala, generate a HelloWorld.jar.
Manifest.mf:
Main-Class: HelloWorld
In the console I run:
fsc HelloWorld.scala
jar -cvfm HelloWorld.jar Manifest.mf HelloWorld\$.class HelloWorld.class
java -jar HelloWorld.jar
=> "Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld/jar"
java -cp HelloWorld.jar HelloWorld
=> Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:675)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:316)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:280)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)
at hoppity.main(HelloWorld.scala)
Sample directory structure:
X:\scala\bin
X:\scala\build.bat
X:\scala\MANIFEST.MF
X:\scala\src
X:\scala\src\foo
X:\scala\src\foo\HelloWorld.scala
HelloWorld.scala:
//file: foo/HelloWorld.scala
package foo {
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
}
MANIFEST.MF:
Main-Class: foo.HelloWorld
Class-Path: scala-library.jar
build.bat:
#ECHO OFF
IF EXIST hellow.jar DEL hellow.jar
IF NOT EXIST scala-library.jar COPY %SCALA_HOME%\lib\scala-library.jar .
CALL scalac -sourcepath src -d bin src\foo\HelloWorld.scala
CD bin
jar -cfm ..\hellow.jar ..\MANIFEST.MF *.*
CD ..
java -jar hellow.jar
In order to successfully use the -jar switch, you need two entries in the META-INF/MANIFEST.MF file: the main class; relative URLs to any dependencies. The documentation notes:
-jar
Execute a program encapsulated in a
JAR file. The first argument is the
name of a JAR file instead of a
startup class name. In order for this
option to work, the manifest of the
JAR file must contain a line of the
form Main-Class: classname. Here,
classname identifies the class having
the public static void main(String[]
args) method that serves as your
application's starting point. See the
Jar tool reference page and the Jar
trail of the Java Tutorial for
information about working with Jar
files and Jar-file manifests.
When you use this option, the JAR file
is the source of all user classes,
and other user class path settings are ignored.
java command line usage
manifest spec
(Notes: JAR files can be inspected with most ZIP applications; I probably neglect handling spaces in directory names in the batch script; Scala code runner version 2.7.4.final .)
For completeness, an equivalent bash script:
#!/bin/bash
if [ ! $SCALA_HOME ]
then
echo ERROR: set a SCALA_HOME environment variable
exit
fi
if [ ! -f scala-library.jar ]
then
cp $SCALA_HOME/lib/scala-library.jar .
fi
scalac -sourcepath src -d bin src/foo/HelloWorld.scala
cd bin
jar -cfm ../hellow.jar ../MANIFEST.MF *
cd ..
java -jar hellow.jar
Because Scala scripts require the Scala libraries to be installed, you will have to include the Scala runtime along with your JAR.
There are many strategies for doing this, such as jar jar, but ultimately the issue you're seeing is that the Java process you've started can't find the Scala JARs.
For a simple stand-alone script, I'd recommend using jar jar, otherwise you should start looking at a dependency management tool, or require users to install Scala in the JDK.
I ended up using sbt assembly, it is really simple to use. I added a file called assembly.sbt into the project/ directory at the root of the project with a one liner (Note your version might need to be changed).
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Then just run the assembly task in sbt:
> assembly
Or just 'sbt assembly' in project root directory
$ sbt assembly
It will first run your tests and then it will generate the new jar into the target/ directory (given that my build.sbt already lists all my dependencies).
In my case, I just make that .jar file executable, rename to remove the extension and it is ready to ship!
Also, if you are doing a command line tool, don't forget to add a man page (I hate scripts without proper manpages or with multi-page plain text documentation that is not even piped into a pager for you).
You can also use maven and the maven-scala-plugin. Once you set up maven, you can just do mvn package and it will create your jar for you.
I tried to reproduce MyDowell's method. Finally I could make it work. However I find that the answer though correct a bit too complicated for a novice ( in particular the directory structure is unnecessarily complicated ).
I can reproduce this result with very simplistic means. To start with there is only one directory which contains three files:
helloworld.scala
MANIFEST.MF
scala-library.jar
helloworld.scala
object HelloWorld
{
def main(args: Array[String])
{
println("Hello, world!")
}
}
MANIFEST.MF:
Main-Class: HelloWorld
Class-Path: scala-library.jar
first compile helloworld.scala:
scalac helloworld.scala
then create the jar:
\progra~1\java\jdk18~1.0_4\bin\jar -cfm helloworld.jar MANIFEST.MF .
now you can run it with:
java -jar helloworld.jar
I found this simple solution because the original one did not work. Later I found out that not because it is wrong, but because of a trivial error: if I don't close the second line in MANIFEST.MF with a newline, then this line will be ignored. This took me an hour to find out and I tried all other things before, in the process finding this very simple solution.
I don't want to write why's and how's rather just show the solution which worked in my case (via Linux Ubuntu command line):
1)
mkdir scala-jar-example
cd scala-jar-example
2)
nano Hello.scala
object Hello extends App { println("Hello, world") }
3)
nano build.sbt
import AssemblyKeys._
assemblySettings
name := "MyProject"
version := "1.0"
scalaVersion := "2.11.0"
3)
mkdir project
cd project
nano plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.1")
4)
cd ../
sbt assembly
5)
java -jar target/target/scala-2.11/MyProject-assembly-1.0.jar
>> Hello, world
I modified the bash script adding some intelligence including auto-manifest generation.
This script assumes that the main object is named the same as the file it is in (case sensitive). Also, either the current directory name must equal to the main object name or the main object name should be provided as a command line parameter. Launch this script from the root directory of your project. Modify the variables at the top as required.
Be aware that the script will generate the bin and dist folders and will ERASE any existing contents in bin.
#!/bin/bash
SC_DIST_PATH=dist
SC_SRC_PATH=src
SC_BIN_PATH=bin
SC_INCLUDE_LIB_JAR=scala-library.jar
SC_MANIFEST_PATH=MANIFEST.MF
SC_STARTING_PATH=$(pwd)
if [[ ! $SCALA_HOME ]] ; then
echo "ERROR: set a SCALA_HOME environment variable"
exit 1
fi
if [[ ! -f $SCALA_HOME/lib/$SC_INCLUDE_LIB_JAR ]] ; then
echo "ERROR: Cannot find Scala Libraries!"
exit 1
fi
if [[ -z "$1" ]] ; then
SC_APP=$(basename $SC_STARTING_PATH)
else
SC_APP=$1
fi
[[ ! -d $SC_DIST_PATH ]] && mkdir $SC_DIST_PATH
if [[ ! -d $SC_BIN_PATH ]] ; then
mkdir "$SC_BIN_PATH"
else
rm -r "$SC_BIN_PATH"
if [[ -d $SC_BIN_PATH ]] ; then
echo "ERROR: Cannot remove temp compile directory: $SC_BIN_PATH"
exit 1
fi
mkdir "$SC_BIN_PATH"
fi
if [[ ! -d $SC_SRC_PATH ]] || [[ ! -d $SC_DIST_PATH ]] || [[ ! -d $SC_BIN_PATH ]] ; then
echo "ERROR: Directory not found!: $SC_SRC_PATH or $SC_DIST_PATH or $SC_BIN_PATH"
exit 1
fi
if [[ ! -f $SC_DIST_PATH/$SC_INCLUDE_LIB_JAR ]] ; then
cp "$SCALA_HOME/lib/$SC_INCLUDE_LIB_JAR" "$SC_DIST_PATH"
fi
SCALA_MAIN=$(find ./$SC_SRC_PATH -name "$SC_APP.scala")
COMPILE_STATUS=$?
SCALA_MAIN_COUNT=$(echo "$SCALA_MAIN" | wc -l)
if [[ $SCALA_MAIN_COUNT != "1" ]] || [[ ! $COMPILE_STATUS == 0 ]] ; then
echo "Main source file not found or too many exist!: $SC_APP.scala"
exit 1
fi
if [[ -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
rm "$SC_DIST_PATH/$SC_APP.jar"
if [[ -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
echo "Unable to remove existing distribution!: $SC_DIST_PATH/$SC_APP.jar"
exit 1
fi
fi
if [[ ! -f $SC_MANIFEST_PATH ]] ; then
LEN_BASE=$(echo $(( $(echo "./$SC_SRC_PATH" |wc -c) - 0 )))
SC_MAIN_CLASS=$(echo $SCALA_MAIN |cut --complement -c1-$LEN_BASE)
SC_MAIN_CLASS=${SC_MAIN_CLASS%%.*}
SC_MAIN_CLASS=$(echo $SC_MAIN_CLASS |awk '{gsub( "/", "'"."'"); print}')
echo $(echo "Main-Class: "$SC_MAIN_CLASS) > $SC_MANIFEST_PATH
echo $(echo "Class-Path: "$SC_INCLUDE_LIB_JAR) >> $SC_MANIFEST_PATH
fi
scalac -sourcepath $SC_SRC_PATH -d $SC_BIN_PATH $SCALA_MAIN
COMPILE_STATUS=$?
if [[ $COMPILE_STATUS != "0" ]] ; then
echo "Compile Failed!"
exit 1
fi
cd "$SC_BIN_PATH"
jar -cfm ../$SC_DIST_PATH/$SC_APP.jar ../$SC_MANIFEST_PATH *
COMPILE_STATUS=$?
cd "$SC_STARTING_PATH"
if [[ $COMPILE_STATUS != "0" ]] || [[ ! -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
echo "JAR Build Failed!"
exit 1
fi
echo " "
echo "BUILD COMPLETE!... TO LAUNCH: java -jar $SC_DIST_PATH/$SC_APP.jar"
echo " "
One thing which may cause a similar problem (although it's not the problem in the initial question above) is that the Java vm seems to demand that the main method returns void. In Scala we can write something like (observe the =-sign in the definition of main):
object MainProgram {
def main(args: Array[String]) = {
new GUI(args)
}
}
where main actually returns a GUI-object (i.e. it's not void), but the program will run nicely when we start it using the scala command.
If we package this code into a jar-file, with MainProgram as the Main-Class, the Java vm will complain that there's no main function, since the return type of our main is not void (I find this complaint somewhat strange, since the return type is not part of the signature).
We would have no problems if we left out the =-sign in the header of main, or if we explicitly declared it as Unit.
If you do not wish to use sbt facilities I recommend the use of a makefile.
Here is an example where foo package is replaced by foo.bar.myApp for completeness.
makefile
NAME=HelloWorld
JARNAME=helloworld
PACKAGE=foo.bar.myApp
PATHPACK=$(subst .,/,$(PACKAGE))
.DUMMY: default
default: $(NAME)
.DUMMY: help
help:
#echo "make [$(NAME)]"
#echo "make [jar|runJar]"
#echo "make [clean|distClean|cleanAllJars|cleanScalaJar|cleanAppJar]"
.PRECIOUS: bin/$(PATHPACK)/%.class
bin/$(PATHPACK)/%.class: src/$(PATHPACK)/%.scala
scalac -sourcepath src -d bin $<
scala-library.jar:
cp $(SCALA_HOME)/lib/scala-library.jar .
.DUMMY: runjar
runJar: jar
java -jar $(JARNAME).jar
.DUMMY: jar
jar: $(JARNAME).jar
MANIFEST.MF:
#echo "Main-Class: $(PACKAGE).$(NAME)" > $#
#echo "Class-Path: scala-library.jar" >> $#
$(JARNAME).jar: scala-library.jar bin/$(PATHPACK)/$(NAME).class \
MANIFEST.MF
(cd bin && jar -cfm ../$(JARNAME).jar ../MANIFEST.MF *)
%: bin/$(PATHPACK)/%.class
scala -cp bin $(PACKAGE).$#
.DUMMY: clean
clean:
rm -R -f bin/* MANIFEST.MF
cleanAppJar:
rm -f $(JARNAME).jar
cleanScalaJar:
rm -f scala-library.jar
cleanAllJars: cleanAppJar cleanScalaJar
distClean cleanDist: clean cleanAllJars