I've tried a few methods and this is the closest yet - how can the maths library be called from a jruby script?
require 'java'
require 'commons-math3-3.2.jar'
import org.apache.commons.math3.random.RandomGenerator
myClass = RandomGenerator.new
puts "hello!"
puts myClass.nextBoolean()
The error that is returned is NoMethodError: undefined method `nextBoolean' for #<#:0x7816fcc4>
OK. Got it working.
require 'java'
require 'commons-math3-3.2.jar'
Randy = org.apache.commons.math3.random.RandomDataGenerator
jp = Randy.new
puts jp.nextInt(10,20)
Related
How can I call a Java code snippet from JRuby code? My code snippet is really short, actually it's just a set of a few Java statements.
Explained here on how to call existing Java code from JRuby. The most basic usage:
require 'java'
java.lang.System.out.println("Hello, world!")
As a bit more complex example, if you want to import your arbitrary package (say, 'foo.bar.baz') from a JAR, you can do this:
require 'java'
require 'foobarbaz.jar'
def foo
Java::Foo
end
shiny_thingy = foo.bar.baz.Thingy.new("Shiny")
shiny_thingy.shine()
If you want to evaluate a string as if it was Java, you would need to compile it first; you can use the techniques in this question, but Java generally frowns on autogenerated code, and it is not trivial to do it. Or you can translate it into JRuby, calling Java classes as described above, and skip the compilation issue.
We might be able to help better if we knew what your snippet consisted of.
EDIT: Here is the adaptation of the linked code that will instantiate an arbitrary class. Be aware that it will create .class files, which is AFAIK inevitable when a compilation step is involved. The code assumes a subdirectory named tmp exists; adapt to your use case.
shiny_source = <<-EOF
package foo.bar.baz;
public class Shiny {
public Shiny() {
System.out.println("I'm shiny!");
}
}
EOF
require 'java'
java_import javax.tools.SimpleJavaFileObject
java_import java.net.URI
class JavaSourceFromString < SimpleJavaFileObject
def initialize(name, code)
uri = "string:///" + name.gsub('.', '/') + Kind::SOURCE.extension
super URI.create(uri), Kind::SOURCE
#code = code
end
def getCharContent(ignore_encoding_errors)
#code
end
end
java_import javax.tools.ToolProvider
java_import java.io.StringWriter
java_import java.net.URL
java_import java.net.URLClassLoader
compilation_path = java.nio.file.Paths.get('tmp').to_absolute_path.to_s
jc = ToolProvider.get_system_java_compiler
raise "Compiler unavailable" unless jc
jsfs = JavaSourceFromString.new('foo.bar.baz.Shiny', shiny_source)
file_objects = [jsfs]
ccl = java.lang.Thread.current_thread.get_context_class_loader
classpath = ccl.getURLs.to_a.join(java.io.File::pathSeparator)
options = ['-d', compilation_path, '-classpath', classpath]
output = StringWriter.new
success = jc.get_task(output, nil, nil, options, nil, file_objects).call
raise output unless success
url = URL.new("file:" + compilation_path + "/")
ucl = URLClassLoader.new_instance([url].to_java(URL))
shiny_class = ucl.load_class('foo.bar.baz.Shiny')
shiny_class.new_instance
We are developing a Java project that is able to instrument (change) class files at build time. We defined a Gradle task that invokes a java based Ant task which takes an inputDir (e.g. build/classes) and an outputDir (e.g. build/classes-instrumented) and possible other parameters. The task gets invoked separately for main and test class files after compilation. Since the "normal" java sourceSet is not a good fit, our first thought was to implement our own sourceSet but couldn't find an easy way. A reasonable alternative, similar to ANTLR etc, seemed to be extra variables. Since I needed several, I went for a Map.
sourceSets.all { ext.instrumentation = [:] }
sourceSets.all {
instrumentation.inputDir = null
instrumentation.outputDir = null
instrumentation.classPath = null
}
def postfix = '-instrumented'
Below you see how we initialize the variables.
sourceSets {
main {
instrumentation.inputDir = sourceSets.main.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
instrumentation.classPath = sourceSets.main.output + configurations.compile
}
test {
instrumentation.inputDir = sourceSets.test.output.classesDir
instrumentation.outputDir = instrumentation.inputDir + postfix
}
}
However it fails with "Could not find method main() for arguments [build_f2cvmoa3v4hnjefifhpuk6ira$_run_closure5_closure23#12a14b74] on root
project 'Continuations'."
We are using Gradle 2.1
I have the following questions:
any idea why the first one fails?
Is the extra variable a reasonable solution to approach the problem?
Thanks a lot for your help
solution: install last version.
I had the same problem, I read gradle documentation of gradle 3, but gradle 2.7 was installed.
checked gradle version 2.7
then read gradle 2.7 doc https://docs.gradle.org/2.7/userguide/tutorial_java_projects.html#N103CD , but found no info about sourceSet in java plugin for that version
installed gradle 3 --> problem solved
I'm building a Python UI using Tkinter. For the needs of the program, I've to connect Python with Java to do some stuff, so I'm using a simple Jython script as a linker. I cant use Tkinter with Jython because it's not supported.
Python (ui.py) -> Jython (linker.py) -> Java (compiled in jars)
To call the Jython function in Python I use subprocess as follows:
ui.py:
cmd = 'jython linker.py"'
my_env = os.environ
my_env["JYTHONPATH"] = tons_of_jars
subprocess.Popen(cmd, shell=True, env=my_env)
Then, in the Jython file, linker.py, I import the Java classes already added on the JYTHONPATH, and I create an object with the name m and call to some functions of the Java class.
linker.py:
import handler.handler
m = handler.handler(foo, moo, bar)
m.schedule(moo)
m.getFullCalendar()
m.printgantt()
The thing is that I've created a m object, that will be destroyed after the execution of jython linker.py ends.
So the question is: Is possible to save that m object somewhere so I can call it from ui.py whenever I want? If it's not possible, is there any other way to do this?
Thanks in advance.
I finally solved it by using ObjectOutputStream.
from java import io
def saveObject(x, fname="object.bin"):
outs = io.ObjectOutputStream(io.FileOutputStream(fname))
outs.writeObject(x)
outs.close()
def loadObject(fname="object.bin"):
ins = io.ObjectInputStream(io.FileInputStream(fname))
x=ins.readObject()
ins.close()
return x
I am starting to switch from a well-known Java build system to Gradle to build all my projects, and after barely two hours into it I have already been able to publish a new version of one of my projects without a problem -- a breeze.
But now I encounter a difficulty. In short, I need to replicate the functionality of this Maven plugin which generates the necessary files for a ServiceLoader-enabled service.
In short: given a base class foo.bar.MyClass, it generates a file named META-INF/services/foo.bar.MyClass whose content is a set of classes in the current project which implement that interface/extend that base class. Such a file would look like:
com.mycompany.MyClassImpl
org.othercompany.MyClassImpl
In order to do this, it uses I don't know what as a classloader, loads the Class objects for com.myCompany.MyClassImpl or whatever and checks whether this class implements the wanted interface.
I am trying to do the same in Gradle. Hours of googling led me to this plugin, but after discussing with its author a little, it appears this plugin is able to merge such files, not create them. So, I have to do that myself...
And I am a real beginner both with Gradle and Groovy, which does not help! Here is my current code, link to the full build.gradle here; output (which I managed to get somehow; doesn't work from a clean dir) shown below (and please bear with me... I do Java, and I am final happy; Groovy is totally new to me):
/*
* TEST CODE
*/
final int CLASS_SUFFIX = ".class".length();
final URLClassLoader classLoader = this.class.classLoader;
// Where the classes are: OK
final File classesDir = sourceSets.main.output.classesDir;
final String basePath = classesDir.getCanonicalPath();
// Add them to the classloader: OK
classLoader.addURL(classesDir.toURI().toURL())
// Recurse over each file
classesDir.eachFileRecurse {
// You "return" from a closure, you do not "continue"...
if (!isPotentialClass(it))
return;
// Transform into a class name
final String path = it.getAbsolutePath();
final String name = path.substring(basePath.length() + 1);
final String className = name.substring(0, name.length() - CLASS_SUFFIX)
.replace('/', '.');
// Try and load it
try {
classLoader.loadClass(className);
println(className);
} catch (NoClassDefFoundError ignored) {
println("failed to load " + className + ": " + ignored);
}
}
boolean isPotentialClass(final File file)
{
return file.isFile() && file.name.endsWith(".class")
}
The output:
com.github.fge.msgsimple.InternalBundle
failed to load com.github.fge.msgsimple.bundle.MessageBundle: java.lang.NoClassDefFoundError: com/github/fge/Frozen
failed to load com.github.fge.msgsimple.bundle.MessageBundleBuilder: java.lang.NoClassDefFoundError: com/github/fge/Thawed
com.github.fge.msgsimple.bundle.PropertiesBundle$1
com.github.fge.msgsimple.bundle.PropertiesBundle
com.github.fge.msgsimple.provider.MessageSourceProvider
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$1
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$2
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$3
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.LoadingMessageSourceProvider
com.github.fge.msgsimple.provider.MessageSourceLoader
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$Builder
com.github.fge.msgsimple.provider.StaticMessageSourceProvider$1
com.github.fge.msgsimple.provider.StaticMessageSourceProvider
com.github.fge.msgsimple.source.MessageSource
com.github.fge.msgsimple.source.MapMessageSource$Builder
com.github.fge.msgsimple.source.MapMessageSource$1
com.github.fge.msgsimple.source.MapMessageSource
com.github.fge.msgsimple.source.PropertiesMessageSource
com.github.fge.msgsimple.locale.LocaleUtils
com.github.fge.msgsimple.serviceloader.MessageBundleFactory
com.github.fge.msgsimple.serviceloader.MessageBundleProvider
:compileJava UP-TO-DATE
The problem is in the two first lines: Frozen and Thawed are in a different project, which is in the compile classpath but not in the classpath I managed to grab so far... As such, these classes cannot even load.
How do I modify that code so as to have the full compile classpath availabe? Is my first question. Second question: how do I plug that code, when it works, into the build process?
Here are some hints:
Create a new URLClassLoader, rather than reusing an existing one.
Initialize the class loader with sourceSets.main.compileClasspath (which is an Iterable<File>) rather than classesDir.
Turn the code into a Gradle task class. For more information, see "Writing a simple task class" in the Gradle User Guide.
Ideally, you'd use a library like ASM to analyze the code, rather than using a class loader. To avoid the case where you cannot load a class because it internally references a class that's not on the compile class path, you may want to initialize the class loader with sourceSets.main.runtimeClasspath instead.
I just started using JRuby and I create a small test file:
require 'java'
java_import java.io.File
f = File.new ARGV[0]
When I run the program like so: jruby test.rb file.txt
I get the following warning:
/Library/Frameworks/JRuby.framework/Versions/1.6.5/lib/ruby/site_ruby/shared/builtin/javasupport/core_ext/object.rb:99 warning: already initialized constant File
The class of f is in fact the java File class, but I still get the warning, any help??
I found out this is related to the following JRuby ticket by looking in object.rb:
http://jira.codehaus.org/browse/JRUBY-3453
Seems like a reasonable warning to me, since Ruby already has a File class (i.e. the constant "File" was already initialized to refer to the Ruby File class).
Myself, I would probably skip the import and just do
require 'java'
f = java.io.File.new ARGV[0]
which should work and would eliminate name clashes.
You can also do
require 'java'
java_file = java.io.File
f = java_file.new ARGV[0]
or
module JavaIO
include_package "java.io"
end
f = JavaIO::File.new ARGV[0]