Problem
With Groovy's default imports any script has access to java.{lang,util,io,net}.*, groovy.{lang,util}.*, java.math.BigInteger, and java.math.BigDecimal without importing anything.
What if I want none of that?
Is it possible to create a GroovyShell that would not have any default import?
Example demonstrating the issue
def importCustomizer = new ImportCustomizer()
// importCustomizer.clearImports() <-- I wish that would exist
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(importCustomizer)
def binding = new Binding("Date", "today")
def shell = new GroovyShell(binding, configuration)
println shell.evaluate("Date")
It prints class java.util.Date.
I'd like to get today instead, using Date from the binding.
(It's just an example, inspired by this recent question).
What I have tried so far
With CompilerConfiguration and ImportCustomizer, I can only add more imports, I don't know how to start from a clean slate.
I've also seen this answer (for which I just fixed the dead link), which pointed me to where those default imports are defined, but I don't see a way to remove imports, again it's only about adding more.
It looks like I have to hook "Semantic Analysis" phase:
This includes resolving classes, static imports and scope of variables.
Using groovyConsole confirms this: if I type x=Date in there, then use "Script" menu → "Inspect AST", the tool shows:
after "Conversion" I still have x=Date
after "Semantic Analysis" I see x = java.util.Date
And at that phase the ResolveVisitor does its thing (using default imports) and I have not found a way to get around that.
Related
Below is the command cmd that is going to get executed and is working fine.
def process = cmd.execute(['PATH=D:/Project/Node_Project/build/nodejs/node-v10.10.0-win-x64'],null)
"D:/Project/Node_Project" is my project root folder.
I am trying something like below to make the path generic but it didn't work. ${project.buildDir} also didn't work.
def process = cmd.execute(['PATH=${project.projectDir}/build/nodejs/node-v10.10.0-win-x64'],null)
I want to know what is the correct format to make it generic.
https://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/String.html#execute(java.lang.String[],%20java.io.File)
The above link contains the documentation for the syntax only but no examples to solve my problem.
Executing the gradle application plugin's installDist task creates a directory build/install/my-application-name/bin that contains wrapper scripts, my-application-name and my-application-name.bat. Running either of these scripts runs the application, and arguments passed to these scripts are passed to the underlying application.
In UNIX shell scripts you can access the name that was used to execute the program as $0. In fact, the UNIX version of the gradle-generated startup script uses $0 several times.
How can I configure the gradle application plugin such that these scripts will pass the value of $0 (and whatever the Windows equivalent is on Windows) into the underlying application, perhaps as a Java system property?
Since parameter for obtaining the name of the script being run is referenced differently in Linux($0) and in Windows(%0), the most straightforward way to generate custom scripts would be to use separate custom templates for the respective start script generators:
startScripts {
unixStartScriptGenerator.template = resources.text.fromFile('unixStartScript.txt')
windowsStartScriptGenerator.template = resources.text.fromFile('windowsStartScript.txt')
}
The default templates are easy to obtain invoking e.g. unixStartScriptGenerator.template.asString()
Documentation on customizing the start scripts can be found here.
This is what I ended up doing, based on jihor's answer. I'm posting it here just so that there's a working answer for anyone else interested:
startScripts {
def gen = unixStartScriptGenerator
gen.template = resources.text.fromString(
gen.template.asString().replaceFirst('(?=\nDEFAULT_JVM_OPTS=.*?\n)') {
'\nJAVA_OPTS="\\$JAVA_OPTS "\'"-Dprogname=\\$0"\''
})
// TODO: do something similar for windowsStartScriptGenerator
}
This uses replaceFirst is instead of replace so we can match a pattern. This is a little less brittle, and also lets us use lookahead so we don't have to actually replace what we're looking for. (This is groovy's variant of replaceFirst that takes a closure, by the way. This requires far less escaping than the version that takes a replacement string in this case.)
Also, instead of:
JAVA_OPTS="$JAVA_OPTS -Dprogname=$0"
we actually need something like:
JAVA_OPTS="$JAVA_OPTS "'"-Dprogname=$0"'
This is because $0 may contains special character (like spaces), and the startup script removes one level of quoting in the value of $JAVA_OPTS using eval set --.
(If anyone knows how to make this work on Windows, pleas feel free to update this answer.)
I took an alternative approach. According to the documentation, as far back as Gradle 2.4 and all the way through Gradle 4.8, we should be able to set the following properties within the startScripts task:
applicationName
optsEnvironmentVar
exitEnvironmentVar
mainClassName
executableDir
defaultJvmOpts
appNameSystemProperty
appHomeRelativePath
classpath
Unfortunately, this is not true for the following properties, which seem to have never been exposed:
appNameSystemProperty
appHomeRelativePath
If appNameSystemProperty were exposed as the documentation describes, then we should be able to simply do the following:
startScripts {
applicationName = 'foo'
appNameSystemProperty = 'appName'
}
This would then result in the addition of -DappName=foo to the Java command constructed within both of the start scripts.
Since this is not the case, I took the following approach, which is a bit more verbose than the earlier solution to this question, but is perhaps less brittle because it does not rely on tweaking the out-of-box templates. Instead, it results in the documented behavior.
startScripts {
mainClassName = '...'
applicationName = 'foo'
unixStartScriptGenerator =
new CustomStartScriptGenerator(generator: unixStartScriptGenerator)
windowsStartScriptGenerator =
new CustomStartScriptGenerator(generator: windowsStartScriptGenerator)
}
class CustomStartScriptGenerator implements ScriptGenerator {
#Delegate
ScriptGenerator generator
void generateScript(JavaAppStartScriptGenerationDetails details,
Writer destination) {
details = new CustomDetails(details: details)
this.generator.generateScript(details, destination)
}
static class CustomDetails implements JavaAppStartScriptGenerationDetails {
#Delegate
JavaAppStartScriptGenerationDetails details
#Override
String getAppNameSystemProperty() { 'appName' }
}
}
I just started learning about groovy and trying to transpose my java code to groovy scripts. Usually java allows you have a class with only methods that you can call from other classes. I wanted to translate that to groovy. I have in one file - lets call it File1- a method like this:
def retrieveData(String name){
// do something
}
and in the second file, File2, I call File1 like this:
def file1Class = this.class.classLoader.parseClass(new File("../File1.groovy"))
and then try to call the method in File1 like this:
def data = file1Class.retrieveData("String")
but it keeps giving me this error - MissingMethodException:
groovy.lang.MissingMethodException: No signature of method: static File1.retrieveData() is applicable for argument types: (java.lang.String) values: [String] Possible solutions: retrieveData(java.lang.String)
so it does recognize that I am sending in the correct number of parameters and even the correct object, but it isn't running the method as it should?
Is there something I am missing? I tried to remove the object definition from the method - in other words - like this:
def retrieveData(name){
// do something
}
but that didn't work either. I am clueless about what the next step would be. Can anyone please help push me in the right direction? I would greatly appreciate it.
See the answer provided in this StackOverflow reponse.
Use the GroovyScriptEngine class. What does the GroovyScriptEngine do? From the docs:
Specific script engine able to reload modified scripts as well as
dealing properly with dependent scripts.
See the example below.
def script = new GroovyScriptEngine( '.' ).with {
loadScriptByName( '..\File1.groovy' )
}
this.metaClass.mixin script
retrieveData()
Note how we use the loadScriptByNamemethod to
Get the class of the scriptName in question, so that you can
instantiate Groovy objects with caching and reloading.
This will allow you to access Groovy objects from files however you please.
Using Vim Syntastic with an android project. (e.g. com.myproject.project) It's not aware of classes declared within my project but outside of the current file. e.g. the following flags errors:
import com.myproject.project.SomeClass;
...
SomeClass someclass = new SomeClass();
Saw this post Configure syntastic to work fine with Android projects which solve the problem:
Method 1:
Inside vim editor
:SyntasticJavacEditClasspath
Then add the following to the buffer window
/path-to-your-app/bin/classes
/path-to-your-android-sdk/platforms/android-19/*.jar
Method 2:
Add the following to the .vimrc:
let g:syntastic_java_javac_classpath = "/<path-to-your-app>/bin/classes:/<path-to-your-android-sdk>/platforms/android-19/*.jar"
Here is a summary of the various methods which worked for me in linux vim7.4 and Syntastic3.7.0-224 with credit to each.
Method 1 - manual creation of .syntastic_javac_config
1. Edit .vimrc and use this syntax:
let g:syntastic_java_javac_config_file_enabled = 1
2. Where you edit your vim files, add this to a file named .syntastic_javac_config
let g:syntastic_java_javac_classpath = '/home/davis/progs/princeton-algos/week1/libs/algs4.jar'
Method 2 - advantage no matter where you edit the class path is known.
1. Edit .vimrc and use this syntax:
let g:syntastic_java_javac_classpath = "/home/davis/progs/princeton-algos/week1/libs/algs4.jar"
This adds the jar and
Method 3 - Automatic generation of .syntastic_javac_config file
1. Edit .vimrc and use this syntax:
let g:syntastic_java_javac_config_file_enabled = 1
2. Edit a java file with vim
3. :SyntasticJavacEditClasspath
When the edit window opens, add the class path without quotes and a newline after each entry the class path. In my case, this is the entry
for the setting includes the current folder as well:
/home/davis/progs/princeton-algos/week1/libs/algs4.jar
.
4 :wq the edit setting window
5. Now the class path is set for syntastic when editing files from that location. If you edit from a new directory, you will need to repeat the process.
Besides the comments above, this post also helped.
I'm trying to use boilerpipe from JRuby. I've seen the guide for calling Java from JRuby, and have used it successfully with another Java package, but can't figure out why the same thing isn't working with boilerpipe.
I'm trying to basically do the equivalent of this Java from JRuby:
URL url = new URL("http://www.example.com/some-location/index.html");
String text = ArticleExtractor.INSTANCE.getText(url);
Tried this in JRuby:
require 'java'
url = java.net.URL.new("http://www.example.com/some-location/index.html")
text = Java::DeL3sBoilerpipeExtractors::ArticleExtractor.INSTANCE.getText(url)
This is based on the API Javadocs for boilerpipe. Here's the error:
jruby-1.6.0 :042 > Java::DeL3sBoilerpipeExtractors::ArticleExtractor
NameError: cannot load Java class deL3sBoilerpipeExtractors.ArticleExtractor
from org/jruby/javasupport/JavaClass.java:1195:in `for_name'
from org/jruby/javasupport/JavaUtilities.java:34:in `get_proxy_class'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/site_ruby/shared/builtin/javasupport/java.rb:45:in `const_missing'
from (irb):42:in `evaluate'
from org/jruby/RubyKernel.java:1087:in `eval'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:158:in `eval_input'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:271:in `signal_status'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:270:in `signal_status'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:155:in `eval_input'
from org/jruby/RubyKernel.java:1417:in `loop'
from org/jruby/RubyKernel.java:1190:in `catch'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:154:in `eval_input'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:71:in `start'
from org/jruby/RubyKernel.java:1190:in `catch'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:70:in `start'
from /usr/local/rvm/rubies/jruby-1.6.0/bin/irb:17:in `(root)'
Looks like it didn't parse the camelcase into the appropriate Java package name. What am I doing wrong? I believe I've set up my classpath alright (last 3 entries), though there may be some conflict with xerces possibly being included twice:
$ echo $CLASSPATH
:/jellly/Maui1.2:/jellly/Maui1.2/src:/jellly/Maui1.2/bin:/jellly/Maui1.2/lib/commons-io-1.4.jar:/jellly/Maui1.2/lib/commons-logging.jar:/jellly/Maui1.2/lib/icu4j_3_4.jar:/jellly/Maui1.2/lib/iri.jar:/jellly/Maui1.2/lib/jena.jar:/jellly/Maui1.2/lib/maxent-2.4.0.jar:/jellly/Maui1.2/lib/mysql-connector-java-3.1.13-bin.jar:/jellly/Maui1.2/lib/opennlp-tools-1.3.0.jar:/jellly/Maui1.2/lib/snowball.jar:/jellly/Maui1.2/lib/trove.jar:/jellly/Maui1.2/lib/weka.jar:/jellly/Maui1.2/lib/wikipediaminer1.1.jar:/jellly/Maui1.2/lib/xercesImpl.jar:/jellly/boilerpipe-1.1.0/boilerpipe-1.1.0.jar:/jellly/boilerpipe-1.1.0/lib/nekohtml-1.9.13.jar:/jellly/boilerpipe-1.1.0/lib/xerces-2.9.1.jar
I'd recommend against trying to guess the module name we put under Java::, since for unusual packages it can get mangled pretty badly. Use java_import 'your.weird.package.ArticleExtractor' or if all the package components are compatible with Ruby method naming, you can also do Java::your.weird.package.ArticleExtractor.
Also, since you might run into this... you'll want to reference the INSTANCE variable as ArticleExtractor::INSTANCE, since we map it as a Ruby constant.
Have fun!
You can also use the nice Jruby Boilerpipe Gem which wraps the Java code
Or the pure ruby implementation of Boilerpipe Ruby Boilerpipe Gem
require 'boilerpipe'
Boilerpipe::Extractors::ArticleExtractor.text("https://github.com/jruby/jruby/wiki/AboutJRuby")