I'm new to groovy, really like it, but found a compilation problem. I'm using Jetty as a webserver, which is serving .groovy files (groovlets).
Consider two files:
Test1.groovy which contains
println new Test2().property
Test2.groovy which contains:
public class Test2 {
String property = "print this"
}
When calling /Test1.groovy in a browser it prints print this. But when I change the property in something else, it still prints print this, it won't recompile. The only thing I can do is restart jetty.
Note that when all the code is in one file, recompilation does work.
Is there a workaround for this?
I think the reason it's always displaying the default value is because you're newing it inline ... or is that just a problem with the example?
Thanks, but that's not it.
I am getting a little closer, the compiler can be fooled:
When calling /test2.groovy in a browser you get a server error, because test2.groovy only contains class code (and not a main method), but when you add the line
println "" to this file, it actually recompiles when calling it in a browser.
After that, test1.groovy should be recompiled too in order to make it work. It's not a very neat solution, but jetty doesn't need to be restarted this way...
Related
I'm working on a Kotlin multi-platform project, and I need my JS tests to run on Node.js but with custom command line arguments (specifically I need node to run with the --expose-gc flag, because some tests need to trigger garbage collection).
Looking at the documentation for the Gradle Kotlin JS DSL I didn't find any mention of how to do that; does anyone know whether it's at all possible and how?
Unfortunately can not answer your question directly, but there is some suggestion to help you with reverse engineering.
Let's start from some example. We have Gradle tasks to run our project using webpack's dev server such as browserDevelopmentRun, browserProductionRun (not sure if multi-platform projects have it, but JS projects do). We can add:
println(tasks.named("browserProductionRun").get().javaClass)
to build.gradle.kts to find out the exact class used for this task. When we sync Gradle, it outputs:
org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack_Decorated
Now we know the exact class of this task so we can investigate its API. The auto completion or navigating inside of the KotlinWebpack class helps us to find out that it has a helpful nodeArgs property to configure NodeJS arguments for it, so we can set them, for example:
tasks.named("browserProductionRun", org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack::class).get().nodeArgs.add("--trace-deprecation")
Getting back to your question.
In your case I guess you need to investigate the browserTest task. Let's get some info about it by adding:
println(tasks.named("browserTest").get().javaClass)
to build.gradle.kts - a-ha - it seems to be of the org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest_Decorated type. Let's check what's inside. Open KotlinJsTest.kt somehow - for example by typing its name into the window being opened by CMD + Shift + O (make sure to select "All Places" here) or just by typing its name somewhere in build.gradle.kts and navigating inside it.
The only interesting thing I see inside this open class is the following block:
override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec {
val forkOptions = DefaultProcessForkOptions(fileResolver)
forkOptions.workingDir = npmProject.dir
forkOptions.executable = nodeJs.requireConfigured().nodeExecutable
val nodeJsArgs = mutableListOf<String>()
return testFramework!!.createTestExecutionSpec(
task = this,
forkOptions = forkOptions,
nodeJsArgs = nodeJsArgs,
debug = debug
)
}
So maybe it can work out to create your own extension of this class, override its createTestExecutionSpec method and provide nodeJsArgs as you need inside it. After that you'll be needing to declare another Gradle task to launch tests inside build.gradle.kts which will use this new extended class.
So Flow only works correctly the first time I run it, and then I have to restart my computer before it'll work correctly again.
Specifically, the problem I'm seeing is that we are using the Flow language to add type annotations to our JS code. Our linter script is setup to run flow type checking among other things. However, when I fix an issue in my code and then rerun the linter script, it still comes back with the exact same errors... BUT when it shows the piece of code where the error is supposed to be, it actually shows my updated code that's fixed.
So as an example, I had a file I copied into the project, that I didn't think I really needed, but maybe I would. So I copied it in just in case. Well then it came up with a bunch of linter errors, so I decided to just delete the file since I didn't really need it. So then I run "yarn lint --fix" again, but it's still complaining about that file, EVEN THOUGH THE FILE DOESN"T EXIST! Now interestingly, where the linter output is supposed to show the code for those errors it's just blank.
Or another example, let's say I had a couple of functions in my code:
100: function foo() {}
...
150: function bar() {}
And foo has a lot of errors because it was some throw away code I don't need anymore and so I just delete it. So the new code looks like:
100: function bar() {}
Well I rerun the linter and get an error like:
Error ------------------------ function foo has incorrect
something...blah blah
src/.../file.js
100| function bar() {}
I also tested this out on a coworker's machine and they got the same behavior that I did. So it's not something specific to my machine, although it could be specific to our project?
Note: There doesn't appear to be a tag for Flow, but I couldn't post without including at least one tag, so I used flowlang even though that's actually a different language :-( I'm assuming that anyone looking for flow would also use that tag since it's the closest.
The first time you launch Flow it starts up a background process that is then used for subsequent type checking. Unfortunately this background process is extremely slow, and buggy to boot. In linux you can run:
killall flow
To stop the background process. Then if you rerun the flow type checker, it will actually see all your latest changes.
From all the weirdness in our current Xpages project this one currently hits the ceiling:
we have created a few java beans in our current project. Inside Domino Designer they all are stored below Code >> Java, so that it is clear that they are automatically part of the project's classpath. All our beans belong to a package structure de.edcom.* (that's what we have been using forever without any problems). The objects are mostly called from SSJS using the full package names (the aren't registered as managed beans for various reasons) as in
var o = de.edcom.myObject.someMethod();
In none of my previous Xpages projects this caused any problems, it just worked. In the current project, however the XSP runtime all of a sudden started to interpret the package name as a String object giving us this runtime error:
Unknown member 'edcom' in Java class 'java.lang.String'
the ssjs code line in question is looking like this:
return de.edcom.TOC.buildTOC();
We absolutely don't have any clue as to what could be causing this, why only in this project, and why it sometimes IS working, but mostly isn't.
There's one difference between this projects and others before, and that is locallization: users can switch between "english" and "german" locale, and of course we are using codes like
context.setLocaleString("de")
and of course we are having several javascript code fragments looking for local settings as in
if(context.getLocalString()==="de"){...
This morning we in fact have renamed / refactored all java beans to different package names (com.edcom.*), and since then the error hasn't appeared (fingers crossed!).
But then again I think this is just too stupid, there can't really be a connection, or can it?
EDIT:
I tried using importPackage(), in conjunction with an xe:objectData datasource (as recommended by Adrian and Paul in their answers), but I'm still receiving that "unknown member 'edcom' in Java class 'java.lang,String'" message, now only at a different position in the code at my line saying importPackage(de.edcom).
I'll be switching back to the "com.edcom" package and keep looking for a better solution; unfortunately searching for the string "de" inside the entire code yields close to 12.000 matches; now way to find the real reason for this in that haystack
EDIT #2:
looks like we finally found the dreaded "de" variable: it was well hidden in a computed customControl property; I don't have a clue why all the File Searches that I performed over the last few days couldn't find this one.
Anyways it is very good to know that we have to be even more careful when naming our ssjs variables; I never would have thought that a ssjs variable name could ever interfere with TLD parts in Java packages; we probably will make it an internal policy that our variables have to must be named "vDe", "vCom", "vIt" etc. instead of just short lowercase letters...
Probably you used a variable de (which is a String) in an other SSJS script that run before that one faces the problem.
I've seen similar issues that a variable that is not explicitly declared in an script block can inherit values from another script block.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforeRenderResponse><![CDATA[#{javascript:
var ex1 = "Hello World";
var ex2 = "Bye bye"}]]>
</xp:this.beforeRenderResponse>
<xp:this.afterRenderResponse><![CDATA[#{javascript:
print("value ex1: " + ex1);
print("value ex2: " + ex2);}]]>
</xp:this.afterRenderResponse>
</xp:view>
results in:
[1CA8:000C-4354] 10.06.2016 14:33:01 HTTP JVM: value ex1: Hello World
[1CA8:000C-4354] 10.06.2016 14:33:01 HTTP JVM: value ex2: Bye bye
So you should use the importPackage() function to import the references to your java classes or much better, use managed beans or dataContexts.
SSJS requires variables which will be put in a scope. Anything with a dot in it will first go to those variables. It sounds like localization stores the translations in a variable named "de", that would explain your problem.
Maybe importPackage(de.edcom) and then using return TOC.buildTOC(); would resolve the problem. I would consider that better practice, but either way in SSJS you're risking variable name collisions.
Personally, I prefer to back every XPage with a controller Java class (I use Jesse Gallagher's frostillicus framework, and so it's always accessible with the variable pageController), so my SSJS just calls pageController.myMethod(), which then avoids all name collisions and allows Java imports to ensure I map to the right Java class. There are more basic ways of doing it, e.g. with an xe:dataObject at the top of every XPage.
Ive setup a GroovyResourceLoader and it seems to get requests for groovy scripts as necessary. I was just wondering is it specially used anywhere besides class loading ? Is there any benefit in simply wrapping a ClassLoader and loading *.groovy files there as opposed to using a GRL ? Are they just different ways to the same end ?
GroovyResourceLoader (GRL) is used by GroovyClassLoader (GCL) and at least since Groovy 1.8 indirectly through GroovyScriptEngine (GSE). But GSE loads it through a GCL as well.
But what GRL does is to "locate" the scripts and return an URL to the location. What GCL does is use the URL returned by a GRL to get the source and compile it to create the class, which is then available for loading.
GRL is a backend for GCL. So they are not different ways to the same end. True, you still have to do more things to actually execute the script code (unless it is precompiled), but "get the script source, compile it, make a Class out of it and finally execute it" are the steps you always have to do. In our GRL/GCL discussion, GRL does part of the first step, GCL itself does the third step. Step 2 is done by CompilationUnit inside GCL and the last step is yours to be done. There are other ways to complete these steps of course, but that's out of the scope for this discussion.
I just tried to use JCurses from within Groovy, but I always get the following exception:
Caused by: java.lang.NullPointerException at
jcurses.system.Toolkit.getLibraryPath(Toolkit.java:97) at
jcurses.system.Toolkit.<clinit>(Toolkit.java:37)
Toolkit.java:37 :
String url = ClassLoader.getSystemClassLoader()\
.getResource("jcurses/system/Toolkit.class").toString();
Google told me that it could have to do with spaces within the classpath (windows), but moving the library and even using the classes instead of the .jar file was not successful.
It seems to be possible - pleac for groovy references JCurses: http://pleac.sourceforge.net/pleac_groovy/userinterfaces.html
Another way to clear the screen from within a Groovy shell script would also solve my problem. :-)
As jline is bundled with Groovy, can't you use the class jline.ANSIBuffer.ANSICodes (as is shows in the page you linked to)
print jline.ANSIBuffer.ANSICodes.clrscr()
You might also need to do:
print jline.ANSIBuffer.ANSICodes.gotoxy( 1, 1 )
If you want the cursor to go back to the top of the screen
To draw coloured text, you can do:
println new jline.ANSIBuffer().append( 'Some ' )
.red( 'Red' )
.append( ' text' )
.toString()
The root problem is most likely that jcurses.jar was not being found on your classpath, causing ClassLoader.getSystemClassLoader().getResource("jcurses/system/Toolkit.class") to return null.
There's a related problem you can run into if it can't find the C library containing the native code (libjcurses.so or libjcurses64.so on linux). It expects the C libary to be in the same folder where it found jcurses.jar. If it's not there, you'll get:
java.lang.RuntimeException: couldn't find jcurses library
found another trivial way to clear the screen :-)
print "\n"*80