jruby + no public constructor - java

Using JRuby 1.6.0RC1
I've got a java file like
package com.foo.bar
public class Foo
{
Foo(String baz){}
}
If, in jruby, I do
com.foo.bar.Foo.new "foo"
then I get
TypeError: no public constructors for Java::ComFooBar::Foo
Reading http://jira.codehaus.org/browse/JRUBY-5009 makes me thing this is WAD, but how do I get around the problem without altering the java file?
Subclassing Foo and then instantiating I get a different error:
ArgumentError: Constructor
invocation failed: tried to access
method
com.foo.bar.Foo.(Ljava/lang/String;)V
from class
org.jruby.proxy.com.foo.bar.Foo$Proxy0
EDIT:
Got it to work through help from Headius on IRC. The following works, but could possibly be more intelligent:
def package_local_constructor klass,*values
constructors = klass.java_class.declared_constructors
constructors.each do |c|
c.accessible = true
begin
return c.new_instance(*values).to_java
rescue TypeError
false
end
end
raise TypeError,"found no matching constructor for " + klass.to_s + "(" + value.class + ")"
end

There indeed is no public constructor for that. The constructor is package level.
How do other Java classes outside the package com.foo.bar acquire objects of this type? It may be there is already a factory in that package that produces this class by calling the package-scoped constructor, and that you could call from JRuby.
If not, you could make a public factory class in that package, possibly in Java, possibly in Ruby, and call this constructor from there.
You might also be able to monkey-patch to add a ruby-accessible constructor or factory method, without having to modify the Java source.

That's because the constructor is has package level access.
You could try to define your ruby class in the same package as the foo class.
See: Assigning a Java package to a JRuby class

In Java you can use the reflection API to do something like:
Constructor constructor = MyClass.class.getConstructor(Class ... paramTypes);
constructor.setAccessible(true);
MyClass myClass = (MyClass)constructor.newInstance(Object ... args);
Not sure you can do that in JRuby, but I'd imagine you could.
There's an oracle guide to this: http://download.oracle.com/javase/tutorial/reflect/member/ctorInstance.html

Guess the only fixes are the one you proposed, or "remove your initializer from the ruby class" (which may be a bug in jruby--shouldn't it call its ancestor no matter what?) or "make the java class initializer protected access" [I'm not sure why jruby disdains package level so much].
http://betterlogic.com/roger/2011/05/javajavamirah-woe/comment-page-1/#comment-5034

Related

Calling java class methods in Rhino script

I have one interface method as follows and saved in Order.java file
and rhino script.
In rhino script I am trying to call the interface methods.
rhino:
var x = Packages.com.data.Order.X;
print(x);
java:
package com.data;
public interface Order
{
String X = "Hello, World!";
void invoke();
}
but it is not printing "Hello world".
Instead it is printing
uncaught JavaScript runtime exception: TypeError: Cannot call
property invoke in object [JavaPackage com.data.Order]. It is not a
function, it is "object". "
problem Statement: How to call java interface method from rhino script.
That type of error usually means that the Rhino engine is not seeing the class you are attempting to use.
If you are definitely seeing the jar get into the classpath, next, I'd double check that the jar contains your class exactly as referenced.
You can verify whether the JS step is seeing your class properly by Alerting it like this:
Alert(com.foo.bar.MyClass);
If the Alert indicates it is a JavaClass then it found your class. Otherwise it will say it is a JavaPackage.

nameof equivalent in Java

C# 6.0 introduced the nameof() operator, that returns a string representing the name of any class / function / method / local-variable / property identifier put inside it.
If I have a class like this:
class MyClass
{
public SomeOtherClass MyProperty { get; set; }
public void MyMethod()
{
var aLocalVariable = 12;
}
}
I can use the operator like this:
// with class name:
var s = nameof(MyClass); // s == "MyClass"
// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"
// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"
// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".
This is useful since the correct string is checked at compile time. If I misspell the name of some property/method/variable, the compiler returns an error. Also, if I refactor, all the strings are automatically updated. See for example this documentation for real use cases.
Is there any equivalent of that operator in Java? Otherwise, how can I achieve the same result (or similar)?
It can be done using runtime byte code instrumentation, for instance using Byte Buddy library.
See this library: https://github.com/strangeway-org/nameof
The approach is described here: http://in.relation.to/2016/04/14/emulating-property-literals-with-java-8-method-references/
Usage example:
public class NameOfTest {
#Test
public void direct() {
assertEquals("name", $$(Person.class, Person::getName));
}
#Test
public void properties() {
assertEquals("summary", Person.$(Person::getSummary));
}
}
Sadly, there is nothing like this. I had been looking for this functionality a while back and the answer seemed to be that generally speaking, this stuff does not exist.
See Get name of a field
You could, of course, annotate your field with a "Named" annotation to essentially accomplish this goal for your own classes. There's a large variety of frameworks that depend upon similar concepts, actually. Even so, this isn't automatic.
You can't.
You can get a Method or Field using reflection, but you'd have to hardcode the method name as a String, which eliminates the whole purpose.
The concept of properties is not built into java like it is in C#. Getters and setters are just regular methods. You cannot even reference a method as easily as you do in your question. You could try around with reflection to get a handle to a getter method and then cut off the get to get the name of the "property" it resembles, but that's ugly and not the same.
As for local variables, it's not possible at all.
You can't.
If you compile with debug symbols then the .class file will contain a table of variable names (which is how debuggers map variables back to your source code), but there's no guarantee this will be there and it's not exposed in the runtime.
I was also annoyed that there is nothing comparable in Java, so I implemented it myself: https://github.com/mobiuscode-de/nameof
You can simply use it like this:
Name.of(MyClass.class, MyClass::getProperty)
which would just return the String
"property"
It's also on , so you can add it to your project like this:
<dependency>
<groupId>de.mobiuscode.nameof</groupId>
<artifactId>nameof</artifactId>
<version>1.0</version>
</dependency>
or for Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'
I realize that it is quite similar to the library from strangeway, but I thought it might be better not to introduce the strange $/$$ notation and enhanced byte code engineering. My library just uses a proxy class on which the getter is called on to determine the name of the passed method. This allows to simply extract the property name.
I also created a blog post about the library with more details.
Lombok has an experimental feature #FieldNameConstants
After adding annotation you get inner type Fields with field names.
#FieldNameConstants
class MyClass {
String myProperty;
}
...
String s = MyClass.Fields.myProperty; // s == "myProperty"

How can I call a method defined on a Scala package object from Java? (Scala 2.10.x)

Given a method defined on a (3rd party, so I can't just move it) Scala package object, like so:
package foo
package object bar {
def doSomething(s: String): String = ???
}
I need to call doSomething from Java code. I know that in general, I can get at a Scala companion object's methods from Java using ScalaObject$.method(). However, the example above compiles to foo.bar.package$.class, and of course Java screams about package being a reserved word.
Is there a way to call this from Java directly?
The best I can come up with (works, but ugly) is to wrap doSomething in Scala code that's not in a package object, and then call the wrapper from my Java code.
object BarUtil {
def wrapper(s: String) = foo.bar.doSomething(s)
}
and in Java
public String doIt(String s) {
return BarUtil$.wrapper(s);
}
You can access the package object as foo.bar.package$.MODULE$ (note the dollar signs, do not remove them):
foo.bar.package$.MODULE$.doSomething("hello")

How to invoke methods of a class (in java) when class name and method name are stored in two different strings

Well, not sure if the question sounds a little weird but let me try to put forth the clarification :
I have a JSP page. On this JSP page, I am calling a java class defined in one of my packages under my projects. This class connects to database and access a table which has got fields namely - functionname, function class. Now I am able to retrieve in my JSP the two strings, lets say -
String funName = "ComFunctions";
String className = "funLog");
Now, I want to invoke this function using this class name i.e. basically something like - className.funName
Is it possible in Java? Actually, these functions and class names will be retrieved in a for loop, so I can't directly call using real classname but have to use strings.
Kindly suggest if there is a way or worl around or if the question is still unclear.
I tried the following approach so far but no luck -
Class c = Class.forName(className);
Object o = c.newInstance();
Method m = c.getMethod(funName, String.class); // Not sure what is supposed to be second parameter here i.e. after funName
Error - the above code gives " No class found error". And i made sure that class is there under the package. Even adding package name i.e. packge.classname didnt help and it says "Symbol not found" for package name.
Any pointers please?
Example class that I am trying to invoke -
package mypackage;
public class ComFunctions extends WDriverInitialize{
public static void main(String[] args){
}
public static void funLog(String username){
System.out.println(userName);
}
}
You need to make sure the compiled class is in the webapp's classpath (ie, WEB-INF/classes) and use the FQN (ie, add the package name). You could also make a JAR file of your classes and add that to the WEB-INF/lib folder.
Also, the extra parameter in getMethod is to fetch a method with the matching parameters (ie, in your example, one that takes a String
You're missing one piece of the puzzle, and that's the method arguments. Without it, you can't really be sure what method funName is referring to, and what arguments to pass to it.
And of course, the class needs to be in the classpath.

JRuby calls the wrong method

I got a strange problem with a call to a Java method from JRuby.
In my Java class these methods are defined twice, and it appears JRuby calls the wrong one.
So I tried to use java_method, but I always got a:
TypeError: cannot convert instance of class org.jruby.RubyModule to class java.lang.Class
Here's my Java code:
public class Renderer {
...
public void addRenderer(IElementRenderer r) {
System.out.println("Added element render: " + r.getClass().toString());
basicRenderers.add(r);
rendererMap.put(r.elementClass(), r);
}
public void addRenderer(IBasicRenderer r) {
System.out.println("SHOULD NOT GO THERE !!");
basicRenderers.add(r);
}
}
and my JRuby code:
add_renderer = renderer.java_method :add_renderer, [Java::dragon.render.IElementRenderer]
add_renderer.call TextRenderer.new
I also tried with java_send but I got the same error:
renderer.java_send(:add_renderer, [Java::dragon.render.IElementRenderer], TextRenderer.new)
Next, I tried with:
renderer.add_renderer(TextRenderer.new.to_java(IElementRenderer))
This time no errors but the wrong method is called ...
How can I fix this problem?
You can fix that cannot convert instance of class org.jruby.RubyModule to class java.lang.Class using java.lang.Class.for_name
In your case, it is
add_renderer = renderer.java_method :add_renderer, [java.lang.Class.for_name("dragon.render.IElementRenderer")]
This is because java interfaces become Ruby Modules by default and the second argument to :java_method expects an array of Class objects.
You can print the matched method to see it is matching the intended method.
For example, I see below code is matching the println(String) on System.out.
>>java.lang.System.out.java_method "println", [java.lang.Class.for_name("java.lang.String")]
#<Method: Java::JavaIo::PrintStream#(java.lang.String)>
I've had problems like this before. It was many versions ago and I think JRuby's method matching algorithm has improvedd over time. Are you using the latest JRuby?
If nothing else works, you may need to add another method, or a wrapper class. Something that distinguishes your methods by name or number of parameters, not just parameter type.

Categories

Resources