This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-05
Channels
- # aws (12)
- # babashka (30)
- # beginners (294)
- # calva (98)
- # clj-on-windows (3)
- # clojure (48)
- # clojure-europe (31)
- # clojure-italy (8)
- # clojure-nl (2)
- # clojure-uk (11)
- # clojurescript (58)
- # conjure (1)
- # events (1)
- # fulcro (35)
- # graalvm-mobile (2)
- # jobs (8)
- # lsp (11)
- # malli (25)
- # off-topic (33)
- # pathom (24)
- # pedestal (1)
- # polylith (15)
- # re-frame (7)
- # reitit (10)
- # releases (8)
- # remote-jobs (2)
- # sci (3)
- # shadow-cljs (79)
- # spacemacs (10)
- # sql (17)
- # tools-deps (17)
- # vim (4)
- # xtdb (11)
I tried to follow the instruction from https://github.com/juxt/edge/tree/master/examples/tutorial.vent
(I modified cd edge/tutorial.vent
to cd edge/examples/tutorial.vent
because the path in the doc is wrong)
When I run $ ../bin/rebel --cljs -A:dev:build:dev/build
, everything is fine and it showed a REPL.
But when I enter (go)
in the REPL, I got this error. How could I fix this.
Did you load the file up into the REPL?
@https://app.slack.com/team/U07SWUQTH no. First, I switch to the path edge/examples/tutorial.vent
in terminal. And execute ../bin/rebel --cljs -A:dev:build:dev/build
, which will bring up a REPL, then I enter (go)
I'm not familiar with Edge and this particular tutorial you're following. However, for the REPL to do anything useful like starting a project, you need to evaluate some code inside of it. Are you connected to the REPL through your code editor? If so, navigate to the file where the go
function is defined and evaluate that file. Then the go
function will be accessible inside your REPL.
I read some about Edge, have you seen this particular section in the docs? https://www.juxt.land/edge/docs/dev-guide.html#_starting_your_system
@U07SWUQTH yeah.I tried terminal repl and emacs cider. Both have the same error. Below is the error with using cider. It looks like the figweel things have some problems.
Hi team, how can I create Java local date time in clojure for the given time? #object[java.time.LocalDateTime 0x7af2002a "2021-07-05T15:49:30.051"]
@vnazaryan OK you want to produce a LocalDateTime - what is the input you are getting to produce it from?
eg.
user=> (java.time.LocalDateTime/of 2020 7 5 9 1)
#object[java.time.LocalDateTime 0x737edcfa "2020-07-05T09:01"]
if you have all the individual values as numbers
anyway, your solution probably exists somewhere in the javadoc for LocalDateTime, it took me a minute and a half to figure that out from the page
@vnazaryan if you are unfamiliar with Java interop, I recommend checking this guide: https://clojure.org/reference/java_interop.
I have deps.edn with :paths ["src" "resources" "../reportgenerator/build/web/WEB-INF/classes"] . In the classes folder I have itils folder and in it I have several classes that I want to import into clojure project. I import it like this (:import (utils Result SalXmlDocument Sample)) , but that gives me errors :clojure.error/class java.lang.ClassNotFoundException, :clojure.error/cause "java.lang.SalXmlDocument"},
@ruby.object Having :paths
outside your project like that is deprecated (and will be disallowed), so that is not a solution.
Any reason why the change? We also often have mixed Clojure/Java projects where some Java code-gen runs as part of the Java build and puts generated classes in some folder that are needed for the REPL.
I'll defer to @U064X3EF3 to explain with :paths
outside a project was deprecated.
Having generated classes in a folder is fine, it should just be a folder inside the project
Accessing arbitrary paths outside the project root is a potential security issue and the Clojure CLI will currently print a warning if you do this (and eventually we plan to error in this case)
What's the security issue? If someone can modify my deps.edn, they can as easily just put a class inside the same folder.
In my case, the classes are generated outside the project folder. Because they're actually part of a dependency. The Java build is setup to generate classes, and build multiple projects and dependencies, and then it grabs the union of it all, and puts it in the workspace folder, inside the workspace folder are multiple projects. So now inside one of those projects I want to start a REPL, and so I need to point it to the classes folder of the workspace.
if they're a dependency, then use a :local/root dep
we are definitionally saying that a project's paths are descendants of the project root only
if you pick up a lib with external :paths as a git dep, this will point to some random point on the dependee's drive - this is not meaningful across users
if you create a deps.edn pointing to that dir in :paths, yes
how is that at odds with a Java build system?
And some of them are code-gen packages, they don't even have Java source, but XML that generates Java client code
are these Java deps you control? if so, you can add a deps.edn
and you just get folders of classes? not jars?
:local/root can point to a .jar file
Yes, though there might be a corresponding Jar as well. I would need to verify. The code-gen package is the one I'm not sure if it would.
Currently I use lein and a plugin that lets me add my own paths to the lein classpath. I was hoping to move to deps.edn at some point. That's why I ask
I imagine there are potentially many ways to accomplish a workaround, but you're out of normal scope for a deps project
So the envisioned development flow if say you work on a Java package and a Clojure package where the Clojure package depends on the Java one is that you'd build the Java one locally to a Jar and use local/root in the Clojure one?
I would probably colocate them so I could work on the Java and Clojure together, which solves a lot of these problems, but then I'm a fan of monorepos anyway, even multi-lingual ones.
Then you can have the classes
folder that the Java builds into be a :paths
entry within the (Clojure) projects, since they are both in the same tree.
What if :paths
didn't allow paths outside the folder, but it was allowed for :extra-paths
?
I understand as the main :paths
its weird that a project could depend on things that are environment dependent. But extra-paths will always be used in an alias or other "profile" like things, used for development.
:extra-paths
is just added to :paths
so I think the same rules should apply. If something is external to your project, it's a dependency not a path.
In my case, I have to work with what we have and it's setup to not be a monorepo. So each package is its own repo. In the past I used symlinks, but those require a lot of manual labor to maintain. And every new hire that joined the team you'd have to set it up for them as well. With relative paths, a simple convention works, you just say clone all your repos in the same folder, and it'll all just work with relative paths
As a dependency it works too, but local/root only supports Jar dependencies for Java. So maybe it could also support Java class folders?
Though I can also see an issue with resource folders if treated purely as a dependency.
If each project is separate, each project should produce a reusable artifact. A directory full of .class
files is not an "artifact".
That's a matter of perspective 😂. The build system is quite sophisticated. So actually when it pull down dependencies it doesn't pull down Jars, but .class files into a global local folder under versioned paths. If you have many packages that all depends on the same version of the class it'll all symlink things so the dependency closure of each package is pointing to the correct place. And it does the closure at a workspace level based on the "main" package as the start entry.
I might experiment with seeing if I can set things up with deps.edn without using :paths and I'll report back. Maybe local/root with Jars can work
That's a very strange definition of "sophisticated" in my view... 🙂
I don't know, how deps is encouraging git deps, you could argue in Java Jars are complicated and a build folder of classes with a launch script in it would have been simpler
I think the reason it does that is because it supports more than just Java. The artifact repository has artifacts for all programming languages
So for example I could take a dependency on a package that has an executable script, and it would pull it down as well
tools.deps as a library is extensible to whatever custom "dep" type you'd like to plugin via the extension points (but you're at that point outside what the Clojure CLI currently supports)
Hum, I just remembered that with lein I even need to custom generate some paths each time it runs dynamically.
If I extend a customer dep type, can I plug my extension into deps.edn in a way that tools will transparently work with it?
Like say I need a custom deps type, and I define things with it in my deps.edn. And now I want Cursive to start a repl with deps.edn but for it to work with my custom deps type?
the Clojure CLI does not yet have a way to define and load custom extensions
Cursive can use tools.deps directly but also does not have any place to do that afaik
but if you use tools.deps as a library, it's just a matter of loading a namespace that extends the multimethods
I've been delaying doing anything with the CLI until someone actually cared :)
Fair fair. For now I'm happy with lein, the point is mostly to get tools to play nice with our internal package manager. And Lein with some plugins did the trick. Once I experiment with trying to switch to tools.deps instead I'll report my blockers if any.
i'm not sure myself, does the :local path key support that?
there might be a very good reason, i just haven't run into it or i'm confused, why are your class files not in a jar?
I have a legacy netbeans app and I was thinking about using it's classes to import to clojure and create a jar file from clojure code to be used by the netbeans app
is it possible to gradually refactor the old app into clojure? or should I quit the job?
if you can produce a jar from the netbeans app, then i believe deps should be able to pick it up and use the code. Then you can recompile. But I would wait tell someone with more experience doing this answers. Your question was interesting because i hadn't ever considered the situation.
https://github.com/bigos/JavaApplication3/blob/master/clojure/responder/src/jac/responder.clj
It sounds like you are really more interested in calling into Clojure from the legacy NetBeans app?
(and ultimately replacing the Java code with Clojure, working from the ground up)
There are two or three possible approaches but the one I prefer -- because it's how we refactored our legacy app to use Clojure (and then, ultimately, replaced the legacy app completely with Clojure) is to leverage Clojure's Java API https://clojure.org/reference/java_interop#_calling_clojure_from_java
You can start by adding Clojure as a regular dependency on your legacy app -- Clojure is "just" a library, like anything else you're already using in the legacy code. And gradually, as you write more Clojure to replace the Java code, start adding more Clojure libraries as dependencies.
You would also need your Clojure project's src
folder added to the classpath of your legacy app (so the legacy project could "see" your Clojure code when it is referenced via the Java API).
@ruby.object How does that sound?
that is what I had in mind. to some extent you confirm my idea. But I am stuck at the clojure being a library of my legacy app. For some hope of success that clojure library has to work and finding the examples is not easy. to have a woking library for the legacy app I need to be able to import some classes.
I had success with trivial app https://github.com/bigos/JavaApplication3 also at some point I was able to import legacy java classes to my clojure app, but for some reason it stopped working.
I know nothing about NetBeans, but the first step will be to add org.clojure/clojure
as a dependency to your Java project (with version "1.10.3") and in your existing Java code, try out what I linked to above: referencing some clojure.core
functions.
public static String passTheData(SalXmlDocument info) { // use the new class for more talking to clojure hmm.report.foo("hello"); hmm.report.passdata(info);
Did you read the page I linked, explaining Clojure's Java API?
You can avoid all the gen-class pain.
https://clojure.github.io/clojure/javadoc/clojure/lang/IFn.html mentions only Objects, how do I pass a hash, or instance of an object
Everything in Java is an Object. You can pass in anything.
And the Clojure code is just regular Clojure code.
:methods [#^{:static true} [foo [String] void] #^{:static true} [ldata [SalXmlDocument] void] #^{:static true} [tryme [Object] void]]
You don't need any of that if you call into Clojure from Java via Clojure's Java API that I linked to.
https://stackoverflow.com/questions/46436476/how-to-invoke-clojure-function-directly-from-java
No, you don't want deps.edn
for this: just add Clojure to your existing Java project. You'll use whatever tool your Java project uses to build the Java code already.
You know how to build the existing NetBeans Java project, yes @ruby.object?
And you have a list of its current dependencies in some NetBeans config?
Excellent! So you can add Clojure to that.
Your project.xml
has sections like this?
<groupId>javax.media</groupId>
<artifactId>jmf</artifactId>
<version>2.1.1e</version>
Clojure is "just" a dependency like:
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<version>1.10.3</version>
And you should be able to rebuild your Java project and it will add Clojure to it.
<library dirs="200"> <file>${file.reference.mockito-all-1.10.16.jar}</file> <path-in-war>WEB-INF/lib</path-in-war> </library>
The NetBeans UI probably has an option somewhere to "add dependency" if you're used to doing it that way...
(like I say, I have never used NetBeans...)
https://stackoverflow.com/questions/6819317/adding-dependencies-in-maven-netbeans ?
Well, something somewhere in the project has to have the list of dependencies. When you find that, you can add Clojure to it.
That will not work. You need to specify Clojure as a dependency not a downloaded library.
Clojure itself depends on two other libraries. Downloading the JAR will not give you those libraries so your code will fail (in ways you will find hard to debug).
Please figure out how to add dependencies to your Java project and add Clojure that way.
As I keep saying, I know nothing about NetBeans. Somehow, somewhere, your existing Java project must have a way of declaring/specifying its existing dependencies. Those are being fetched from Maven. Clojure is on Maven. So you can add Clojure as a dependency to the existing Java project somehow and that's all you need.
NetBeans itself definitely lets you add new dependencies to a project (not JAR files, not class libraries, actual dependencies). You need something that understands Maven-style dependencies to fetch Clojure for you, so that transitive dependencies are also fetched.
Who built this project in the first place? Can you ask them about adding a dependency?
(since you said "or should I quit the job?" I assume you have colleagues you can ask about this project)
Well, then you're probably not going to be able to mix Clojure and Java if you can't find out or figure out how to add Maven-style dependencies to that existing project, sorry.
Once they hire a Java programmer, there will be someone who can set that up for you.
I'm sure there are a lot of legacy projects that would benefit from clojure, but why is is so hard, are people so fed up with the old projects so that they rewrite them from scratch?
It isn't hard. It just assumes some knowledge about the Java ecosystem which you don't have, unfortunately.
Sorry I can't really help you with this 😞
Haha... OK, fair enough.
I have emailed my boss saying i am making progress. i will have some explaining tomorrow
If it was publicly accessible, I could probably dig into it some more, but right now I don't even know what to suggest you look for in the project (since I gather it doesn't have pom.xml
which would be the telltale sign of Maven?).
Is the build.xml
file just running javac
with no dependency management stuff in place at all?
(I can't imagine the code uses no external libraries at all but I suppose that is technically possible)
$ tree ./nbproject/ ./nbproject/ ├── ant-deploy.xml ├── build-impl.xml ├── build-impl.xml~ ├── genfiles.properties ├── private │ ├── private.properties │ └── private.xml ├── project.properties ├── project.xml └── rest-build.xml
jacekp@EID6043:~/code/AAA-java/reportgenerator$ tree ./lib/ ./lib/ ├── bcmail-jdk15on-1.67.jar ├── bcpg-jdk15on-1.67.jar ├── bcpkix-jdk15on-1.67.jar ├── bcprov-jdk15on-1.67.jar ├── cds.astro.jar ├── clojure-1.10.2.jar ├── commons-lang-2.6.jar ├── core-renderer-R8.jar ├── javacsv.jar ├── javax.mail-1.6.2.jar ├── lidlcsvreport.jar ├── mockito-all-1.10.16.jar ├── multipart.jar ├── opencsv-5.1.jar ├── openpdf-1.3.23.jar └── unbescape-1.1.6-SNAPSHOT.jar
Out of curiosity, what version of Java is it running with?
$ tree -d ./src/ ./src/ ├── conf └── java ├── emailing ├── exceptions ├── sal │ ├── calculations │ ├── reports │ │ ├── animal_feed │ │ ├── content_producers │ │ ├── micro │ │ └── tools │ ├── stamps │ └── user_options ├── servlets ├── utils └── xml └── regulatory_methods
$ java -version java version "1.8.0_162" Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
At least you're on Java 8 🙂
OK, so since you've gotten as far as manually downloading Clojure 1.10.2, here are the other libs you'll need to download to at least move forward:
org.clojure/clojure 1.10.2
. org.clojure/spec.alpha 0.2.194
. org.clojure/core.specs.alpha 0.2.56
If you download those manually from Maven, like you did for Clojure, and put them in lib
, you should at least be able to try the Java -> Clojure calls into clojure.core
per the example linked somewhere above.
It'll be painful to add any new Clojure libraries or update Clojure etc but it should at least get you to a working state for something basic.
Then, in some Java code in the project, you should be able to import clojure.java.api.Clojure;
and import clojure.lang.IFn;
and then do this:
IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);
As for writing your own Clojure code and being able to require and invoke that, you'll need your (Clojure) src
folder to be on the project's classpath but I'm sure where to suggest putting that or how to configure the app to add it...
In the Java code you could call System.getProperty( "java.class.path" );
to see what is on the current classpath and maybe there's an "obvious" safe place to put your src
folder in some directory on that classpath...?
I expect you'd have to log the result or print it -- however the current project writes stuff to a console log...
That getProperty()
call is just going to return a string.
OK, well, good luck tomorrow. We'll be here 🙂
ant -f /home/jacekp/code/AAA-java/reportgenerator -Djavac.includes=servlets/GeneratorServlet.java -Dnb.internal.action.name=debug.single -Ddirectory.deployment.supported=false -DforceRedeploy=false -Dbrowser.context=/home/jacekp/code/AAA-java/reportgenerator/src/java/servlets/GeneratorServlet.java -Ddebug.class=servlets.GeneratorServlet debug-single-main init: deps-module-jar: deps-ear-jar: deps-jar: compile-single: Generating Report - ReportID : 1388907 - Report Type : +44 - UserName : jacekp - Location : Concept Loading KeyStore File URL: http://localhost:10000/xml/report/generate/1388907Converting stream to string Starting loading groups from XML group = Pesticide Screen (GC CSOPP611 & LC CSOPP603 Reg) group = Fosetyl-Al (sum fosetyl + phosphorous acid and their salts, express as fosetyl) group = Miscellaneous group = Miscellaneous group = Pesticide Screen (GC CSOPP611 & LC CSOPP603 Reg) Finished Loading Groups from XML Report Unit : 1' : kg Report Unit : 3' : mg for 5 : null Parsing new report : before trying clojure after trying clojur
public static String passTheHash(SalXmlDocument info) { System.out.println("before trying clojure"); IFn plus = Clojure.var("clojure.core", "+"); plus.invoke(1, 2); System.out.println("after trying clojure");
System.out.println( plus.invoke(1, 2) );
Then at least you should see the result printed 🙂
Well, you have to figure out how to add a src
folder to the app's classpath somehow.
Then you just write your Clojure code in that folder, and use var
/`require` to make it accessible to Java.
IFn require = Clojure.var("clojure.core", "require");
require.invoke("your.namespace");
IFn yourFunc = Clojure.var("your.namespace","your-func");
yourFunc.invoke(...);
lidlcsvreport$ tree . ├── CHANGELOG.md ├── classes │ └── sal │ └── reports │ └── LIDL_Pesticide_Report.class ├── copy-java-classes ├── deps.edn ├── doc │ └── intro.md ├── LICENSE ├── lidlcsvreport.jar ├── pom.xml ├── README.md ├── http://README.org ├── resources ├── src │ └── sgs │ └── lidlcsvreport.clj └── test └── sgs └── lidlcsvreport_test.clj
Start small. Start by just adding a src
folder and some simple code and make sure you can call it.
Remember that this is a Java project: the deps.edn
etc is irrelevant.
You're not building Clojure to a JAR or compiling it to classes.
You're using Clojure source code inside the Java project, and relying on the require
invocation to load and compile it.
I don't think so. For a Java project, that's just where to find the Java source code -- it's not about the classpath.
System.out.println( System.getProperty( "java.class.path" ) );
^ that in your Java code will print out what the class path is when the Java code runs.i have created clojure-src folder at the root of java project and uses the properties/sources dialog to add it to the project
I don't think that will work because I don't think that will affect the classpath.
What does System.out.println( System.getProperty( "java.class.path" ) );
print?
before classpath /home/jacekp/code/AAA-java/reportgenerator/build/web/WEB-INF/classes:/home/jacekp/code/AAA-java/reportgenerator/lib/bcmail-jdk15on-1.67.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/bcpg-jdk15on-1.67.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/bcpkix-jdk15on-1.67.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/bcprov-jdk15on-1.67.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/cds.astro.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/commons-lang-2.6.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/core-renderer-R8.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/javacsv.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/javax.mail-1.6.2.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/mockito-all-1.10.16.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/multipart.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/opencsv-5.1.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/openpdf-1.3.23.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/unbescape-1.1.6-SNAPSHOT.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/clojure-1.10.2.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/core.specs.alpha-0.2.56.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/spec.alpha-0.2.194.jar:/home/jacekp/netbeans-8.2rc/enterprise/modules/ext/jaxrs-2.0/javax.ws.rs-api-2.0.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jboss-javaee.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/activation.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/jaxb-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/jbossws-native-jaxrpc.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/jbossws-native-jaxws-ext.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/jbossws-native-jaxws.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/jbossws-native-saaj.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/resolver.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/serializer.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/stax-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/xalan.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/endorsed/xercesImpl.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/concurrent.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/dom4j.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/getopt.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/javassist.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jaxb-impl.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jaxb-xjc.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-asintegration-core.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-asintegration-jmx.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-asintegration-mc.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-deployers.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-jboss5.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop-mc-int.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-aop.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-bootstrap.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-classloader.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-classloading-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-classloading-vfs.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-classloading.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-common-core.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-dependency.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-client-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-client.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-core-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-core.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-impl.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-structure-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-vfs-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-deployers-vfs.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-j2se.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-jmx.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-kernel.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-logbridge.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-logging-jdk.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-logging-log4j.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-logging-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-logmanager.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-main.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-managed.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-mbeans.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-mdr.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-metatype.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-profileservice-spi.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-reflect.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-system-jmx.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-system.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-vfs.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/jboss-xml-binding.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/log4j-boot.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/osgi.core.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/trove.jar:/home/jacekp/apps/jboss-5.1.0.GA/lib/wstx.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jbossws-native-jaxws-ext.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/ejb3-persistence.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/mail.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/el-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jbossws-native-jaxrpc.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jbossws-native-saaj.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jbossws-native-jaxws.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/servlet-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/hibernate-entitymanager.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jsp-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/common/lib/jboss-jsr77.jar:/home/jacekp/apps/jboss-5.1.0.GA/server/default/deployers/ejb3.deployer/jboss-ejb3-deployer.jar:/home/jacekp/apps/jboss-5.1.0.GA/server/default/deployers/ejb3.deployer/jboss-ejb3-iiop.jar:/home/jacekp/apps/jboss-5.1.0.GA/server/default/deploy/jbossweb.sar/jsf-libs/jsf-api.jar:/home/jacekp/apps/jboss-5.1.0.GA/server/default/deploy/jbossweb.sar/jsf-libs/jsf-impl.jar:/home/jacekp/apps/jboss-5.1.0.GA/server/default/deploy/jbossweb.sar/jstl.jar after classpat
Thanks. Just a sec...
Hmm, so /home/jacekp/code/AAA-java/reportgenerator/build/web/WEB-INF/classes
is the only actual folder on the classpath...
Yeah, expected. I was hoping for some other folder where you could safely put Clojure source code 🙂
When we were adding Clojure to our legacy app, we edited the app's config to add an additional folder to the classpath at startup (our Clojure src
folder).
Do you always run the project via ant
like that? Or does it normally run as a web app?
OK, so ant
is just for testing it?
It looked like you were using ant
to actually run the code for testing that the Clojure invocation worked.
public static void main(String[] args) throws ServletException, FileNotFoundException, IOException, MissingValuesException, NoSuchAlgorithmException, CertificateException, KeyStoreException { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); // insert parameters here // when(request.getParameter("reportid")).thenReturn("1366796"); when(request.getParameter("reportid")).thenReturn("1388907")
Right, and that's the "single-debug-main"?
I wouldn't bother trying to follow something so old.
If you have a small, simple Clojure project, you can ask Clojure to compile a namespace to .class
files in a specific target directory from the REPL:
user=> (binding [*compile-path* "/home/jacekp/code/AAA-java/reportgenerator/build/web/WEB-INF/classes"]
(compile "your.namespace"))
That would let you write Clojure in a separate folder, and compile it into that classes
folder so it would be accessible to the Java code (via the var
/`invoke` calls).What I'm trying to help you avoid is having to write all the gen-class stuff since that's so painful and also it requires the compiled Java classes to be on your Clojure classpath -- which would mean your Clojure project would have to live in that same tree so it could be on your :paths
. Code compiled per above, into the WEB-INF/classes
folder, would then be on the classpath when the Java code runs so that your Java code could require it -- but I worry that classes
folder gets cleaned out each time to build the Java code (which would erase your Clojure code).
You could also build an uberjar of your Clojure project -- without AOT -- and put that in lib
(you wouldn't need the Clojure JARs in lib
then because Clojure would be in your uberjar) and, again, access the Clojure code via require
as above.
.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/clojure-1.10.2.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/core.specs.alpha-0.2.56.jar:/home/jacekp/code/AAA-java/reportgenerator/lib/spec.alpha-0.2.194.jar:/home/jacekp/code/AAA-java/reportgenerator/src/clojure:/home/jacekp/netbeans-8.2r
Yay! :thumbsup::skin-tone-2:
So now you can put example.clj
unto that src/clojure
folder with (ns example)
and a function in it, and try requiring/invoking it from the Java code.
(baby steps, to verify each new piece works!)
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'example/requi
// any other namespace than clojure.core needs to be "required" IFn require = Clojure.var("example", "require"); require.invoke(Clojure.read("example")); IFn foo = Clojure.var("", "foo"); System.out.println(foo.invoke());
we never agreed on paying you evening rate so I am not expecting to give you lots of money
IFn require = Clojure.var("clojure.core", "require"); // this is a core function
Then
require.invoke(Clojure.read("example"));
IFn foo = Clojure.var("example", "foo"); // needs the ns for example/foo
I got it working 🙂 thank you. I am sure I will ask more questions on slack, but I will always remain grateful for your help.
Hey team, I am playing with the slack api. To use their thing, I need to reify the https://github.com/slackapi/java-slack-sdk/blob/ce13f06e99d3b2c9234d871bbb2ebeedb9fdf282/bolt/src/main/java/com/slack/api/bolt/handler/builtin/SlashCommandHandler.java#L9 To do that, I wrote this:
(let [config ;;
app (App. config)
server (SlackAppServer. app)]
(.command app "/hello"
(reify SlashCommandHandler
(apply [this req ctx]
(.ack ctx ":wave Hello!"))))
(.start server))
The error I get is:
[qtp740546585-42] ERROR com.slack.api.bolt.servlet.SlackAppServlet - Failed to handle a request - class java.lang.String cannot be cast to class com.google.gson.JsonElement (java
.lang.String is in module java.base of loader 'bootstrap'; com.google.gson.JsonElement is in unnamed module of loader 'app')
java.lang.ClassCastException: class java.lang.String cannot be cast to class com.google.gson.JsonElement (java.lang.String is in module java.base of loader 'bootstrap'; com.googl
e.gson.JsonElement is in unnamed module of loader 'app')
at consistent_slack.core$_main$reify__1646.apply(form-init998480002803844197.clj:15)
My understanding of reify, is a bit wonky. Any guidance on how I can think about what is happening here, much appreciated!ack
is here https://github.com/slackapi/java-slack-sdk/blob/master/bolt/src/main/java/com/slack/api/bolt/context/Context.java#L92 and expects JsonElement
but you passed a String
-- which is what the error says.
Thanks for the resp Sean! Based on the SlashCommandHandler
interface, I thought the context was this: https://github.com/slackapi/java-slack-sdk/blob/ce13f06e99d3b2c9234d871bbb2ebeedb9fdf282/bolt/src/main/java/com/slack/api/bolt/context/builtin/SlashCommandContext.java#L51-L53
which has a ack(String
implementation. Am I misunderstanding how above is functioning?
what does clash command response.builder do
i mean , imagine it tries to build a json from a string
I think the issue here is that those types are available via generics to the Java compiler -- but generic types don't exist in compiled code -- they are erased -- so the information that Clojure has available only has the raw types.
(generics are compile-time polymorphism, effectively, but not actual dynamic polymorphism)
Most of slack's APIs are just http calls that receive and return json. I would probably ignore their Java library and just interface with their http API
Yes, that's how we interact with Slack from Clojure. Far easier than their ridiculously convoluted Java SDK.
The addition of generics was about when I quit Java. Having spent years on the C++ Standards Committee, working on the specification of templates, I didn't want to deal with a watered down version of that in Java!
Thanks team! > I think the issue here is that those types are available via generics to the Java compiler -- but generic types don’t exist in compiled code -- they are erased -- so the information that Clojure has available only has the raw types. Appreciate the depth Sean. Would love to understand this more. Which type is being erased here? (i.e I am not sure what you mean by “this type is available via generics to the Java compiler”, but not to our compiled code via Clojure) (indeed http api may be best, but am trying to smack a few things together as demo, and ideally would like to get this guy to work. Though yeah — oof — very darn convoluted)
I think of you pass a valid Json string I think it might work. I don't think :hello is valid
@stopachka Java generics only exist in the (Java) compiler. They don't exist at runtime -- they're not in the .class
files -- which is what Clojure has to work with.
@U0DJ4T5U1 I’m not quite sure I understand:
(.command app "/hello"
(reify SlashCommandHandler
(apply [this req ctx]
(.ack ctx ":wave Hello!"))))
Do you mean wrap ":wave Hello!"
like this ? "\":wave Hello!\""
@seancorfield — wanted to confirm — in this context is the generic “JsonElement”?
The <CTX extends Content> stuff is the generics.
(wait, maybe not — apologies for the noob questions — looking now at where this is written)
I think I understand what you mean, but am not sure where this is happening. https://github.com/slackapi/java-slack-sdk/blob/ce13f06e99d3b2c9234d871bbb2ebeedb9fdf282/bolt/src/main/java/com/slack/api/bolt/context/builtin/SlashCommandContext.java#L24 ^ this says CTX extends Context, but why is this a generic? (I thought this was inheritence)
I was thinking "1", instead of ":hello..."
Generics are compile-time only for Java. They do not exist in the bytecode so no other languages see any of that generic stuff when compiling against Java .class
files. I don't know how to say it any differently.
Thanks for bearing with me Sean. What I don’t understand is the following: I don’t see where a generic is written in the source code above.
The <CTX extends Content> stuff is the generics.
Where is this stuff?So if I understand correctly:
• The above interface is only understood by java, as it uses generics
• From clojure side, it doesn’t know the class
of CTX
when our SlashCommandHandler
runs, and defaults to the base Context
type
Okay,
(.command app "/hello"
(reify SlashCommandHandler
(apply [this req ctx]
(let [^SlashCommandContext x ctx]
(.ack x "hello!")
)
)))
The following makes it work!
Thanks for bearing with me and for the time @seancorfield @U0DJ4T5U1Okay, one more esoteric java noob q:
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
import com.slack.api.model.event.ReactionAddedEvent;
app.event(ReactionAddedEvent.class, (payload, ctx) -> {
If I wanted to convert this to clojure, how would I convert passing in ReactionAddedEvent.class
?
I am trying:
(.event app
(class MessageEvent)
(reify BoltEventHandler
(apply [this evt ctx]
(log/info "ctx")
(log/info ctx))))
But am getting:
{:type java.lang.IllegalArgumentException
:message Unexpectedly failed to register the handler
:at [ event App.java 564]}]
Which makes me think I am not correctly converting MessageEvent.class
https://github.com/slackapi/java-slack-sdk/blob/416ab0415983c42c88ff3b266858462edebf8c91/bolt/src/main/java/com/slack/api/bolt/App.java@stopachka (class MessageEvent)
should be just returns Class
, you actually want MessageEvent
which is the clojure equivalent of java's MessageEvent.class
eg.
(ins)user=> (class Number)
java.lang.Class
(ins)user=> Number
java.lang.Number
it's a good example of boilerplate elimination I think, what would Number be other than the class Number?
I'm wondering now whether there's any value other than nil
that returns something other than Class
from (comp class class)
probably nothing
user=> (into #{} (map (comp class class))
[nil 1 Number "" String Double/NaN 'foo])
#{nil java.lang.Class}
ah, makes a loot of sense! Thanks @noisesmith, and clojure ftw!!
Hi everyone. Brand new to Clojure and can't stop reading for the last two weeks. Interested to start a SaaS (learning) project with Clojure and was wondering if it is considered OK to use Vue or React without going the SPA road. A bit like adding some React or Vue components only in the html templates (using Semler). I guess it is possible but would like to hear about your experiences.
I don't see a problem with that. I've also made something for pages that need a tiny bit of CLJS, for quickly setting up something, without going through the hoops of setting up a CLJS project first. https://borkdude.github.io/scittle/