Trouble accessing Java classes from Firefox extension's Javascript - java

I'm going to develop a Firefox extension which uses some Java classes.
The extension gets the value of <input type="file"> fields, using Javascript.
The Java class I'm going to create is the following:
public class Firefox {
public static String inFileName;
public static void main(String[] args) throws IOException {
inFileName = "";
try {
inFileName = args[0];
} catch (Exception ex) {}
}
On Javascript, I have to use Java reflection in order to access Java classes.
In this manner I can create my Java object:
var fileInput = e.explicitOriginalTarget; // is an object HTMLInputElement, I get this when a file is selected
strArray = java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.net.URL"),3);
classLoader = java.net.URLClassLoader.newInstance(strArray);
parameters = java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.lang.String"),1);
parameters[0]= fileInput.value;
var aClass = java.lang.Class.forName("Firefox", true, classLoader);
var aStaticMethod = aClass.getMethod("main", [parameters.getClass()]); //gets the main(String[] args) method, here I get the exception*
var myJava = aStaticMethod.invoke(null, [parameters]); //invokes the main method
I've been trying this extension on Firefox-3.5b4 and everything goes fine, but when I try it on Firefox-3.0.10 I get the following exception*:
`InternalError: Unable to convert JavaScript value class [Ljava.lang.String; to Java value of type java.lang.Class[]`
For example, putting the following line before the main method invokation:
alert(parameters + " - " + parameters[0]);
on both Firefox-3.0.10 and 3.5b4 I get an alert window which says:
`[Ljava.lang.String;#194ca6c - /root/demo.pdf` //demo.pdf is the selected file
But only on 3.0.10 I get the exception, ONLY on my GNU/Linux machine. On Windows XP instead I have no problems on both Firefox versions!
Note that on both Windows and Linux the Java plugin version is 6 update 13.
Where am I wrong? Is it a possible bug on Firefox-3.0.10 Javascript engine or must I do any other thing before getting the main(...) method?

assuming your complete class name is "your.package.Firefox" you could do:
importPackage("your.package");
args = java.lang.reflect.Array.newInstance(java.lang.String.TYPE, 1);
Firefox.main(args)

you are incorrectly invoiking the method using;
[parameters.getClass()]
which is passing an argument of type java.lang.Class[] into the signature that is expecting a String object. simply pass the parameters object in as it is.

Related

Get .js file value using Java

I have one config.js file:
sOptions = {
enabled: true,
vtest: assign,
stest: remove
}
I want to get value of 'vtest' using Java. I tried below code
ScriptEngine ee = new ScriptEngineManager().getEngineByName("nashorn");
ee.eval(new FileReader("config.js"));
System.out.println("ee: "+ee);
I am not seeing anything is write in log file.
First thing you need valid javascript. When I ran your original js I got exception messsages.
In the fix below I quoted both of your 'assign' and 'remove' values. Also, you need to print out the value that you want.
import javax.script.*;
public class NashornTest{
public static void main(String[] args) throws Exception{
String js = "sOptions = {\n" +
" enabled: true,\n" +
" vtest: 'assign',\n" +
" stest: 'remove'\n" +
"}";
ScriptEngine ee = new ScriptEngineManager().getEngineByName("nashorn");
ee.eval(js);
System.out.println("ee:" + ee);
System.out.println("sOptions: "+ee.get("sOptions"));
}
}
Which shows.
ee:jdk.nashorn.api.scripting.NashornScriptEngine#b3ca52e
sOptions: [object Object]
The next step is to get what you want from the data. Also, if you're just using JSON, you might want to use a JSON library instead of javascript.

How to load 2 versions of the same Jar in 1 class Java

This is a complicated question but I will do my best to describe my problem.
I need to load 2 versions of the same JAR in a top level class (v1.jar and v2.jar) so I have access to both versions of the jar. The reason for this is because I want to test if any feature in v2.jar has regressed from v1.jar
In my top level class, I want to call methods of v1.jar and v2.jar and then validate the output from v1 against v2 output. This way I can be certain nothing got screwed up.
class Common {
// Names of the classes would be the same so not sure how I would invoke the classes from the 2 different jars?
String resultv1 = EngineV1.run("a","b","c");
String resultv2 = EngineV2.run("a","b","c");
Assert.equals(resultv1, resultv2, "Regression has been introduced...");
}
I can't import v1 and v2 jars as maven dependencies since this will create a version conflict in maven and by default maven will use the newest jar. So I thought about creating a common interface and having 2 different implementation classes of that interface. Then in the toplevel I can use class loaders to load v1 and v2 jars, etc. But this way not work since I would have to change production v1.jar to implement the common interface.
Any help or insight will be much appreciated. I'd very much like to see samples if possible. And please don't refer me to other threads
Your test class can set up a ClassLoader for each .jar file. The easiest way to do that, is to use URLClassLoader.
Example:
File jar1 = new File("/path/to/v1.jar");
File jar2 = new File("/path/to/v2.jar");
URLClassLoader v1Loader = URLClassLoader.newInstance(new URL[] { jar1.toURI().toURL() });
URLClassLoader v2Loader = URLClassLoader.newInstance(new URL[] { jar2.toURI().toURL() });
Class<?> engineClass1 = v1Loader.loadClass("org.example.Engine");
Class<?> engineClass2 = v2Loader.loadClass("org.example.Engine");
Method runMethod1 = engineClass1.getMethod("run");
Method runMethod2 = engineClass2.getMethod("run");
Object engine1 = engineClass1.newInstance();
Object engine2 = engineClass2.newInstance();
String result1 = (String) runMethod1.invoke(engine1);
String result2 = (String) runMethod2.invoke(engine2);
Note that since neither .jar file is on the classpath of the test code, the code cannot declare any variables of types from the .jar files. All access from test code must be done using reflection.
UPDATE
You might also need to change the context class loader when making the calls:
String result1, result2;
Thread thread = Thread.currentThread();
ClassLoader myLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(v1Loader);
result1 = (String) runMethod1.invoke(engine1);
thread.setContextClassLoader(v2Loader);
result2 = (String) runMethod2.invoke(engine2);
} finally {
thread.setContextClassLoader(myLoader);
}
// Compare result1 and result2
I found this from a different Stackoverflow question where I needed to load a jar during runtime
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws Exception {
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new Exception(e);
}
}
Works for my purposes

jjs.exe and ServiceLoader

Should a script run by jjs.exe be able to locate services with ServiceLoader just as any Java program could?
I have reduced my case to the following script:
function dump (stream)
{
(new BufferedReader(new InputStreamReader(stream))).lines().forEach(function (x) { print(x); });
}
var BufferedReader = Java.type("java.io.BufferedReader");
var InputStreamReader = Java.type("java.io.InputStreamReader");
var ServiceLoader = Java.type("java.util.ServiceLoader");
var Sts = Java.type("prodist.sts.Sts");
print(Sts);
// A
var stsConfigStream = Sts.class.getResourceAsStream("/META-INF/services/prodist.sts.Sts");
dump(stsConfigStream);
// B
var StsImpl = Java.type("prodist.sts.internal.StsImpl");
print(new StsImpl());
// C
var stsLoader = ServiceLoader.load(Sts.class);
var stsIterator = stsLoader.iterator();
stsIterator.next();
// D
I call jjs.exe setting up the Class-Path on the command line. My script correctly finds and prints the interface name in point A. It locates the service description resource; when I dump the content of the resource, I see the expected content in point B. I make sure the expected implementation class is available in point C.
In point D, the program throws a NoSuchElementException, which I interpret as ServiceLoader not finding any service description resource for the interface.
Is this supposed to work?
Am I missing something?
You need to set the thread context class loader. Refer to any class from your jjs classpath, get it's Class object and then get it's class loader. You then set that loader as thread context class loader. This should be done before you use service loader API:
var StsClass = Java.type("prodist.sts.Sts").class;
java.lang.Thread.currentThread().contextClassLoader = StsClass.classLoader;

Getting info from PsiMethod in Intellij IDEA plugin

I'm writing Intellij IDEA plugin for my project, and I've faced a problem - I cannot get some ingo about method (PsiMethod) from my code.
First, I want to know is this method public.
And second, I want to get fully-qualified names of the parameter classes. Currently I'm doing it like this:
method.getReturnTypeNoResolve().getInternalCanonicalText()
But it doesn't provide full name (with package name) for the standard JVM classes like String and List.
UPDATE First problem solved with the following code:
PsiUtil.getAccessLevel(method.getModifierList()) == PsiUtil.ACCESS_LEVEL_PUBLIC
But I still cannot get fully qualified class name
UPDATE 2 Here is the full listing of my code:
Project currentProject = DataKeys.PROJECT.getData(e.getDataContext());
PsiClass abstractComponentClass = JavaPsiFacade.getInstance(currentProject).findClass("com.mjolnirr.lib.component.AbstractComponent", GlobalSearchScope.allScope(currentProject));
TreeClassChooser result = TreeClassChooserFactory
.getInstance(currentProject)
.createInheritanceClassChooser("Choose the class to generate manifest",
GlobalSearchScope.projectScope(currentProject),
abstractComponentClass,
false,
false,
null);
result.showDialog();
PsiClass classToGenerate = result.getSelected();
List<ManifestMethod> methods = new ArrayList<ManifestMethod>();
for (PsiMethod method : classToGenerate.getAllMethods()) {
// If this method is inherited from the Object class we don't need it
if (isComponentInitialize(method)) {
continue;
}
List<ManifestParameter> parameters = new ArrayList<ManifestParameter>();
for (PsiParameter param : method.getParameterList().getParameters()) {
parameters.add(new ManifestParameter(param.getType().getCanonicalText().replaceAll("\\<.*?\\>", "")));
}
if (method.getReturnType() != null) {
ManifestMethod manifestMethod = new ManifestMethod(method.getName(),
method.getReturnTypeNoResolve().getInternalCanonicalText().replaceAll("\\<.*?\\>", ""),
parameters);
if (!methods.contains(manifestMethod) && isPublic(method)) {
System.out.println("->" + method.getReturnType().getCanonicalText());
methods.add(manifestMethod);
}
}
}
Solved - my test IDEA instance has wrong Java SDK connected.

Eclipse AST variable binding on standalone java application

I'm trying to use Eclipse ASTParser in order to analyse and, if possible, add some code to some classes. One of the information I need requires to have bindings, but because this is a standalone project (the final goal it's a command line tool, independent from eclipse) I can't have them (requireBinding() returns null).
After reading a lot of posts, the far that I can go is using this examples in order to use FileASTRequestor but that's not the way to go since it seems to me that we have to give the KEY to bind before generating the AST tree.
I've found somewhere that we can use ASTParser.setEnvironment method in order to use the bindings in a standalone java application, but I don't think I'm doing it correctly. What's wrong with the code below?
private static final String rootDir = "D:\\workspace\\stateless\\";
private static final String[] classpath = java.lang.System.getProperty( "java.class.path" ).split(";");
private static final String source =
"package de.siemens.tools.stateless.test.examples; " +
"public class ClassWithFinalMemberVariables {" +
"private final int _memberIntVariable = 0;" +
"public void method() {" +
"int localVariable = 0;" +
"System.out.println(_memberIntVariable + localVariable);" +
"}" +
"}";
public static void main(String[] args) throws CoreException {
Document document = new Document(source);
ASTParser parser = ASTParser.newParser(AST.JLS4);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setEnvironment(classpath, new String[] { rootDir },
new String[] { "UTF8" }, true);
parser.setSource(document.get().toCharArray());
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
CompilationUnit unit = (CompilationUnit)parser.createAST(null);
unit.recordModifications();
unit.accept(new ASTVisitor() {
#Override
public void endVisit(VariableDeclarationFragment node) {
IVariableBinding bind = node.resolveBinding();
if(bind == null)
System.out.println("ERROR: bind is null");
super.endVisit(node);
}
Output is always "ERROR: bind is null".
I've already solved it, the code is here:
http://pasteit.com/19433
Even though I prefer the ASTVisitor model, this one gives me every binding available.
And here is the discussion about the problem, for those of you who are curious: https://bugs.eclipse.org/bugs/show_bug.cgi?id=206391
EDIT: I don't have any idea if this is the best solution or not, if you have any suggestion please let me know

Categories