How to access protected method of Java jar file's class - jruby

I am using a Java command-line application (which is open-source) as a jar file for my jrubyonrails project. The main application is like following
public class Decoder extends Annotator {
public Decoder() {
super();
}
public static void main(String[] args) {
... // Do something that I don't want
myDesiredMethod();
... // And some other thing
}
...
}
There are many steps which I want to skip, I only want myDesiredMethod function. And it is a protected method from the parent Annotator class.
public class Annotator extends Helper {
...
protected SomeClass myDesiredMethod(boolean reMap) throws Exception { ... }
...
}
Annotator class does not have any public constructor so that I cannot:
ann = Annotator.new
It raises this error: TypeError: no public constructors for Annotator.
Then I try to implement another class which inherits Annotator in order to access myDesiredMethod. This is the jruby code I have tried so far
require 'java'
require 'decoder.jar'
java_import java.util.ArrayList
java_import java.lang.StringBuilder
module MyModule
class RuDecoder < Annotator
include_package 'com.decoder'
def self.my_method
myDesiredMethod
end
end
It returns the error: NoMethodError: undefined method 'myDesiredMethod' for MyModule::RuDecoder:Class. Seems jruby does not look for the method of the parent class.
Is there any solution in my case, I don't want to rebuild the java library to jar and manually put it into my program every time it has an update.

Turns out that I made thing over-complicated. I can call the default constructor of Annotator as following:
constructors = Annotator.java_class.declared_constructors.first
constructors.accessible = true
annotator = constructors.new_instance.to_java
And use simple call myDesiredMethod: annotator.myDesiredMethod

Related

Does Mockito support #Any #Inject Instance<> interface?

I have a init() method that use injected private instance<>. How can I test this class using JUnit or Mockito? I tried to make some fake class and add them to a list and set this list to my private field but I have this error
java.lang.IllegalArgumentException: Can not set javax.enterprise.inject.Instance field ......
MyClass is:
#Singleton
#Startup
public class HandlerManager {
#Any
#Inject
private Instance<RollbackHandler<RollbackData>> handlers;
private RollbackHandler<RollbackData> rollbackHandler;
#PostConstruct
public void init() {
for (RollbackHandler<RollbackData> bean : handlers) {
//do something
}
}
}
Any annotation is not processed by frameworks, unless you use custom work. you will have to define all those dependencies as mocks in your test using #Mock and call injectMocks() from before test methods such as setup(). It is a multi part problem.
Use constructor injection, field injection is evil. you still will be able to annotate your constructor with #Inject.
when(provider.iterator()).thenReturn(list.iterator);
works for me.
You can create a temporary list with concrete implementations of the RollbackHandler, and mock the iterator() method of your Instance<RollbackHandler<RollbackData>> object so that it returns the iterator of the temporary list.
Example:
private void mockIterator() {
Instance<RollbackHandler<RollbackData>> handlers = mock(Instance.class);
List<RollbackHandler<RollbackData>> handlersList = Collections.singletonList(new RollbackHandlerImpl<>());
when(handlers.iterator()).thenReturn(handlersList.iterator());
}

How to run jul-to-slf4j bridge once per JVM?

I'd like to run Surefire in parallel mode (multiple JVMs) where each JVM must run:
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
exactly once before the first test. How can this be done?
There are various ways to make some code run at the beginning of a test suite.
Here are 4 (I'm sure there are more):
JUnit via RunWith Suite with Suite.SuiteClasses and BeforeClass (adapted from examples in SuiteTest):
#RunWith(Suite.class)
#SuiteClasses({FirstTest.class, SecondTest.class/*, ...*/, LastTest.class})
public static class AllWithSLF4JBridgeHandler {
#BeforeClass
public static void registerRootLoggerHandlers() {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
TestNG with BeforeSuite:
/**
* Base class for each test class (i.e. every test class should extend this class).
*/
public abstract class BaseTest {
#BeforeSuite
public void registerRootLoggerHandlers() {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
TestNG with Guice:
/**
* Test module. Each test class should be annotated with `#Guice(TestModule.class)`.
*/
public class TestModule implements Module {
#Override
public void configure(Binder binder) {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
Static initialization blocks (test-framework independent):
/**
* Base class for each test class (i.e. every test class should extend this class).
*/
public abstract class BaseTest {
static {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
I'm not sure how all of these methods work with Surefire's parallel mode. Methods 1 and 2 may not work there but I believe methods 3 and 4 should.
Another option would be to not use the programmatic installation of the SLF4JBridgeHandler but to use a java.util.logging.config file or class (see LogManager):
"java.util.logging.config.file":
logging.properties file:
// register SLF4JBridgeHandler as handler for the j.u.l. root logger
handlers = org.slf4j.bridge.SLF4JBridgeHandler
System property assignment:
java -Djava.util.logging.config.file=/path/to/logging.properties ...
This works well if you know the path to your logging file beforehand.
"java.util.logging.config.class":
Using a file may not be a good option if you're deploying a WAR and don't know where the file will be, etc. so alternatively you can create a logging config class:
public class SLF4JBridgeHandlerInitializer {
public SLF4JBridgeHandlerInitializer() throws IOException {
String loggingConfigurationString = "handlers = " + SLF4JBridgeHandler.class.getName();
InputStream inputStream = new ByteArrayInputStream(loggingConfigurationString.getBytes());
LogManager.getLogManager().readConfiguration(inputStream);
}
}
System property assignment:
java -Djava.util.logging.config.class=package.SLF4JBridgeHandlerInitializer ...
I've done this before and it has worked well for me (SLF4JBridgeHandler.Initializer by mfulton26 · Pull Request #57 · qos-ch/slf4j).
These final two options should initialize each JVM instance as long as the appropriate system property is set.

JRuby class & Java baseclass: making private java methods available to child

I have a JRuby child class with a Java parent class. I need to override the Java function so that when it is called my JRuby method implementation is called first. The problem is that the java method is private. Any ideas?*
// Java:
public class JavaClass {
private void check(String what) {
System.out.println(what);
}
}
# JRuby:
class RubyClass < JavaClass
def check() # => private above. any way to force it public
super("RubyClass.check was called first")
end
end
*I am aware that this is not generally a good idea. I'm trying to get FXMLLoader to work in JRuby without wholesale reimplementation.
you can call the private method (using Java's reflection) ... but you won't be able to make it public on the original Java class (just on the JRuby side) and thus you can not call it using super ... here's a sample :
>> big_int = Java::JavaMath::BigInteger.new '42'
=> #<Java::JavaMath::BigInteger:0x787faefa>
>> big_int.signInt
NoMethodError: undefined method `signInt' for #<Java::JavaMath::BigInteger:0x787faefa>
from (irb):6:in `evaluate'
now let's do some Java reflection to invoke the (Java) private signInt method :
class Java::JavaMath::BigInteger
def sign_int
signInt = java_class.declared_method :signInt
signInt.accessible = true
signInt.invoke(self)
end
end
and try again :
>> big_int.sign_int
=> 0

Using Jackson JSON Views without annotating original bean class

Is there any way that I can use Jackson JSON Views or something like it, without having to annotate the original bean class? I'm looking for some kind of runtime/dynamic configuration to let me do something similar.
My bean is an #Entity packaged in a JAR that may be shared by multiple projects. I'm trying to avoid touching and re-packaging the shared JAR because of UI changes in the consuming projects.
Ideally I'd like to do something like
jsonViewBuilder = createViewBuilder(View.class);
jsonViewBuilder.addProperty("property1");
jsonViewBuilder.addProperty("property2");
to replace
Bean {
#JsonView(View.class)
String property1;
#JsonView(View.class)
String property2;
}
Any ideas?
Underlying environment: Spring 3.0, Spring MVC and Glassfish 3.1.1.
How about using the Mix-In feature?
http://wiki.fasterxml.com/JacksonMixInAnnotations
http://www.cowtowncoder.com/blog/archives/2009/08/entry_305.html
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonView;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY)
.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
mapper.getSerializationConfig().addMixInAnnotations(Bar.class, BarMixIn.class);
mapper.setSerializationConfig(mapper.getSerializationConfig().withView(Expose.class));
System.out.println(mapper.writeValueAsString(new Bar()));
// output: {"b":"B"}
}
}
class Bar
{
String a = "A";
String b = "B";
}
abstract class BarMixIn
{
#JsonView(Expose.class)
String b;
}
// Used only as JsonView marker.
// Could use any existing class, like Object, instead.
class Expose {}

Struts2 JSON Plugin With Annotations

I have a Struts2 Action Class configured via annotations. All of the "normal" methods that are annotated with #Action work fine.
However, I need to add a method into the action that returns JSON.
Here is a trimmed down version of my class (dao autowired with Spring):
#Namespace("featureClass")
// define success and input actions for class here
public class FeatureClassAction extends ActionSupport {
FeatureClassDao featureClassDao;
#Autowired
public setFeatureClassDao(FeatureClassDeao featureClassDao) {
this.featureClassDao = featureClassDao;
}
List<FeatureClass> featureClasses;
// snip normal actions
#Action("/featureClassesJSON")
#JSON
public String getFeatureClassesJSON() throws Exception {
featureClasses = featureClassDao.getAll();
return SUCCESS;
}
}
Can anyone assist? If I have to go the struts.xml route, that means moving all of my other actions (which work fine) into it.
I figured I would share the answer, since anyone else with the same problem would likely also face the silence.
I created two actions: FeatureClassAction and FeatureClassJsonAction. FeatureClassAction was annotated as such:
#ParentPackage("struts-default")
#Namespace("/featureClass")
public class FeatureClassAction extends ActionSupport {
FeatureClassJsonAction is annotated like this:
#ParentPackage("json-default")
#Namespace("/featureClass")
public class FeatureClassJsonAction extends ActionSupport {
The method in the JSON Action was annotated like this:
#Action(value="featureClassesJson", results = {
#Result(name="success", type="json")
})
public String getFeatureClassesJSON() throws Exception {
Hope it helps someone.