How to add directory to Clojure's classpath? - java

I have installed the libraries with Maven to the ~/.m2/repository/ directory. I would like to add that path to the default Clojure classpath. I could not find the documentation how to do that.
Any hints?
Cheers!
clj
Clojure 1.4.0
user=> (require '[clojure.java.jmx :as jmx])
FileNotFoundException Could not locate clojure/java/jmx__init.class or clojure/java/jmx.clj on classpath: clojure.lang.RT.load (RT.java:432)
The class path by default is:
user=> (println (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader))))
(#<URL file:/Users/myuser/cljmx/> #<URL file:/usr/local/Cellar/clojure/1.4.0/clojure-1.4.0.jar> #<URL file:/Users/myuser/cljmx/>)
nil

Leiningen really makes this process a lot less painful by keeping the setting of the classpath associated with the project, and more importantly leads to a repeatable build process. where you can come back to the project years later and still get a repl. A general outline of using leiningen in these cases:
lein new projectname
add the library you need to your project.clj file with a name you choose
run lein deps to print out the command to use to add the jar to your local repo
add the jar
run lein deps again (you can skip this step if using leiningen2)
run lein repl
enjoy
this is assuming that the library you are using is not already part of or available from a package in a maven repo, which many are.

The non-painful, popular method is to not mess with maven and classpaths and the JRE directly and use leiningen: https://github.com/technomancy/leiningen/
Otherwise, you can modify whatever is in clj and add/set the classpath in whatever ways java likes. See for example Setting multiple jars in java classpath

It should be noted that you also have the option of adding classpaths at runtime with the library pomegranate https://github.com/cemerick/pomegranate
This lets you do like:
(require '[cemerick.pomegranate :as pom])
(pom/add-classpath "/home/user/~.m2/....")

I assume that clj is a script to start Clojure REPL. Take a look into this script and find line similar to this:
java -cp /path/to/clojure.jar clojure.main
Here you start class clojure.main having "clojure.jar" on your classpath. To add more jars just add them to the end of -cp option values. E.g. on Linux:
java -cp /path/to/clojure.jar:/path/to/mylib.jar clojure.main
(use ; instead of : on Windows)
However, very soon you'll get tired of this way and will look for project management tool. So it makes sense to start using it right now. Take a look at Leiningen - it manages dependencies for you based on Maven (so it will be extremely easy to add new jar) and has REPL.

Another option is to create a deps.edn file in the folder where you plan to run the REPL or where you keep your project files.
This file is used to inform Clojure about dependencies, source files, execution profiles, etc… It's loaded when you run a REPL (but there are other use cases) and it's supported by the core of Clojure and it's officially documented on https://clojure.org/reference/deps_and_cli
In your case you may just want to put something like the following, to declare what dependencies you want to download and put on the Java classpath.
{
:deps {
the.dependency/you-want {:mvn/version "1.0.0"}
}
}
In deps.edn you can specify:
third party dependencies (eg JARs) that can be already saved locally, or hosted on a Maven repository, or on Git repository…
source paths, where your source code resides, if any
Note that the dependencies will be downloaded and cached in .cpcache/ folder, beside the deps.edn itself. I'm not sure if you can instruct it to use the global ~/.m2 instead.
You may find the dependency coordinates (name and latest version) on clojars.org
deps.edn is "lighter", part of the core Clojure, if less powerful than leiningen; so maybe suited for setting up an environment for casual/exploratory coding at the REPL or CLI.
You can also have a global deps.edn in ~/.clojure/deps.edn where you may want to define common configurations, dependencies, etc. to be used across different projects. Specific configurations can be invoked/overridden using options on the command line.

Related

Decompile JAR, Modify it, Recompile again?

I have an older Jar file which I have to modify in order to get it to work.
I have already decompiled it with jd-gui and changed the parts that needed change. I would now like to know what I have to do in order to get a jar back?
I guess that I have to add the libraries, but there is no extra lib directory.
How do I put everything together?
#Stephen C
"Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected "
How exactly is this relevant? I only want the function of the Jar file and not a 1:1 copy of it.
The Jar file is unsigned. What exactly would be the reason to sign a Jar file anyway? If I start it from another program?
If you are missing dependencies after decompiling the entire jar, that could mean the jar did not include them. When you create a jar, you can choose not to include dependencies in it, but this assumes that they will be available on the classpath at runtime. Check if there are any relationships with other jar files on the classpath when you run the original jar. See this answer for details on how to achieve this. Once you have identified all the dependencies, you can easily compile the jar from an IDE such as IntelliJ/Eclipse or from command line.
As a side note, if the changes you would like to make to the jar are minor or isolated I recommend editing the bytecode (much easier for small edits). I prefer this bytecode editor.
If decompilation fails on some parts of the code, the functionality of the jar will not be restored upon recompilation. In this case, instead of going through all available decompilers and hoping that you can decompile that code, I suggest you identify the set of classes which you are trying to edit, modify the rest of the classes such that they compile and do not have any side effects on the classes which are of interest to you (do not delete anything that is referenced by these) and compile the jar (even if this isn't the jar you want). You can then extract only the class files which you wanted to modify from the jar, and overwrite them in the original jar. This will only work if your changes didn't have any side effects.
If you are asking how to create a JAR:
You can do it using a build tool such as Maven, Gradle, Ant and so on.
You can do it using a Java IDE
You can do it using the command line jar tool; see How to create a JAR file from the official Oracle Java tutorials.
But it there is no guarantee that what you are doing will actually work. Here are a couple of the problems.
If the original JAR file was signed, you won't be able to re-sign the new JAR ... unless you have the private key that was used when signing.
Decompilation is often inaccurate. There is no guarantee that the Java code that it produces is a correct representation of the original class. Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected / unwanted differences relative to the original.
I guess that I have to add the libraries, but there is no extra lib directory.
I'm not sure what you mean by that. The (new) dependencies of the JAR don't necessarily need to be in the JAR itself. It depends on what kind of JAR it is; e.g. is it a so-called "executable" JAR that you launch using java -jar my.jar ....
Use jar plugins for gradle or maven for example, or build it with intelliJ, here's an answer in another post: How to build jars from IntelliJ properly?
Since I had trouble getting the .jar file completely recompiled and working with my application, I wanted to share here the steps that were missing in the previous answers.
In my specific case, to make things as easy as possible, I wanted to only modify one .java file in the result of the decompilation and update it directly in the .jar file.
I started from the answer from Paulo Ebermann on a similar question, and I ended up with:
javac -source 8 -target 8 -d classdir -cp \
"\
path/to/dep1.jar; \
path/to/dep2.jar; \
path/to/myoriginal.jar \
" path/to/my/java/class.java
cd classdir
jar -uf path/to/myoriginal.jar path/to/my/java/class.class
Notes:
the path/to/myoriginal.jar which is in the path of the dependencies, this forces the java compiler to look for missing classses in the original .jar file rather than in the local decompiled code (which would require recompilation and more dependencies) and therefore I have intentionally left out the . path in the list of class paths
the -source 8 -target 8 which were required to force the class to compile for the appropriate version of the runtime machine.
the -uf which indicates that the jar tool should only update the file with the provided class

Clojure custom Java Interop

I'm trying to find the best documentation and information on integrating custom Java files into a Clojure project. I've reviewed the project Enlight and see that the files are all .java files under the /src/main/java directory. Unfortunately it doesn't use Leiningen (what I'm using) so I can't see how it is called together the java files.
Suppose I want to use a big Java project from Clojure, like MALLET, which is abstracted into oblivion that a standard, major, main entry point like public static void main () cannot be found. Do I just dump every .java file into my classpath and hope for the best?
To include your own .java files in Leiningen project:
(defproject my-project "0.0.1-SNAPSHOT"
; ...
:java-source-paths ["src/main/java" "src_other/java"]) ; It's up to you how to structure paths
In this setup your .java files compilation will be managed by Leiningen.
To include existing Java project which is available in some of Maven repositories, just add dependency. For MALLET it will look like:
(defproject my-project "0.0.1-SNAPSHOT"
; ...
:dependencies [[cc.mallet/mallet "2.0.7"]])
Finally, if the goal is to include private jar file - the best option is to create local Maven repository.
In all of these cases you will be able to do normal Java <-> Clojure interop.

GWT project and Eclipse linked resources

I have to use a Javascript file in my GWT Project. This Javascript is in a common library project and I deploy it together with my GWT Project using ANT.
So, I have no problem in production environment: but I cannot test it in development phase.
I tried to create an Eclipse link to Javascript resource but seem that GWT "can't see it".
Some behavior with other kind of resources (images, css etc.).
Is it a bug or is there another way to do?
I'm using Eclipse Juno, GWT 2.5.0 and Debian 7.0.
Thank you.
Can you just use script tag in your project's .html file?
If you're using some kind of source control, it usually has a way to make a link to a dependent project or file. Simply include that link in your GWT Project, and reference the file through there.
If you can't or don't want to do it through your source control, do it through your OS. Since you're using Linux, simply make a symbolic link to the common file/folder using ln -s (if you were using Windows, you'd need to run mklink from the command line), and reference the file that way.
In either case - source control or OS - you'll be able to see the file(s) when you refresh your project in Eclipse, and modifying one will modify the other in its own directory.
Edit - information on symbolic links in CVS
I haven't played with CVS in quite some time, so can't speak much about its capabilities for symbolic links. A bit of googling said it's not supported, though there are workarounds. One workaround is to add script files that run during checkout. That sounds like it may still be tough to make OS-agnostic. I did find one site that mentioned using module aliases to get the same result. Maybe that will give what you need. An excerpt from the site follows:
One common way to handle situations like this in CVS is to alias the
collection in a modules file rule. -Checkout the "CVSROOT" module and
you'll find the "modules" file; simply change it and check it in like
anything else, with the exception that when you check in CVSROOT files
they "activate" at the same time. The example below may look a little
kludgy, and it is because AFAIK you can't redefine a directory and alias
it at the same time, sadly. I'll use a typical Java situation as its
package system lends itself well to this kind of thing:
Real module directories are "a", "b", and "common"
Directory alias for all common srouce
_common_src_all -d src/com/mycompany/common common/src/com/mycompany/common
Full "A" project including common
a_all &a &_common_src_all
Full "B" project including common
b_all &b &_common_src_all

Compiling Java package throws errors for external Jars

Pretty basic problem here. So I have a Java package that I have created that has three classes (one has the main method). I am trying to use a few Apache Jars, and have added these to my build path in Eclipse. However Eclipse wont let me build and run it properly, so I am trying the command line. I have added the env var CLASSPATH and pointed it to my lib directory which hold the Apache Jars. However, when I try to use javac I get a bunch of errors:
package org.apache.xmlrpc does not exist
import org.apache.xmlrpc.client.XmlRpcClient;
I was reading the man page for javac and it said that:
If neither CLASSPATH, -cp nor -classpath is specified, the user class path consists of the current directory.
So I tried copying the Jars to the same location as my three source files, but no change.
Can someone please tell me what I'm doing wrong?
Thanks.
Classpath variable (or command line option of javac) must contain all jars explicitly. It cannot go through jar files stored in specified directory.
You can compile this by specifying the option -cp on the command line:
javac -cp foo.jar:bar.jar foo/bar/Baz.java
You then run it with the same option:
java -cp foo.jar:bar.jar foo.bar.Baz
It sounds like you've just set the classpath to the directory containing the jar files. You need to set it to the individual jar files, or use java.ext.dirs to set an "extension" directory containing jar files. I'd recommend using the specific jar files. Something like:
// Assuming Windows...
CLASSPATH = c:\libs\foo.jar;c:\libs\bar.jar
I'd also personally recommend specifying the classpath on the command line instead of using an environment variable - the latter will work, but it ends up being a bit more fiddly if you want to compile different projects against different libraries.
However, I'd actually recommend getting Eclipse working first, rather than retreating to the command line. It should be fine - if you could give us more information about what's failing in Eclipse, we may be able to help you with that instead.
The jar files in the current directory are not automatically included; that only refers to .class files in normal package/directory hierarchy. Jar files must be added either explicitly, or via a wildcard like javac -cp ./* (Assuming JDK6+)
(Some OSes may require an escape of the * to avoid globbing; OSX does not.)
I agree with previous answers, but I would also recommend to use proper java build tool - like ant (perceived easier to use, but not necessary) or maven ( perceived more difficult to use, but really worth learning )

How should I set CLASSPATH?

I did this before:
CLASSPATH=".:/home/phoenies/jdk1.6.0_17/lib/tools.jar:/home/phoenies/jdk1.6.0_17/lib/dt.jar"
But today an article says I should do this:
CLASSPATH=".:/home/phoenies/jdk1.6.0_17/lib"
If I do so, will it search all the jar files in lib? So it's probably a shorter way?
Since you are using JDK6, you can use classpath wildcards: CLASSPATH=".:/home/phoenies/jdk1.6.0_17/lib/*" will match all JARS inside lib/
Check out http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html there's a section called "Understanding class path wildcards"
I think having a CLASSPATH environment variable is wrong for all but the easiest of "Hello, World" tutorials.
The right way is to set the CLASSPATH for every project when you compile and run. Every project is likely to be different, so this makes perfect sense.
IDEs ignore CLASSPATH environment settings; so do all Java EE app servers. It's a relic of Java 1.0. I don't have CLASSPATH set on any machine that I work on.
Learn to script it for the command line. Or use Ant. You'll be glad you did.
Yes, it will search all jar files in lib if you do it the second way. It's pretty odd to see class path being set as specifically as in the first one. I suppose on a server where you wanted to be sure what jars were being loaded, that might be one way to restrict them, but you might run into issues with how long it can be if you had several jars.
Jar files need to be specified by name in the Classpath variable. One thing to note is that the commandline -classpath param is more versatile than the environment variable, as it allows you to set a classpath per application.
In Java 1.6+ you can set the classpath to a directory followed by /* to load all JAR files in that directory. Not just the directory name though - that's for loading class files in that directory and subdirectories.

Categories