I got a little project where I have to compute a list. The computation depends on serveal factors.
The point is that these factors change from time to time and the user should be allowed to change this by it's self.
Up to now, the factors are hard-coded and no changes can be done without recompiling the code.
At the moment the code looks like this:
if (someStatement.equals("someString")) {
computedList.remove("something");
}
My idea is to make an editable and human readable textfile, configfile, etc. which is loaded at runtime/ at startup? This file should hold the java code from above.
Any ideas how to do that? Please note: The targeted PCs do not have the JDK installed, only an JRE.
An effective way of going about this is using a static initializer. Static Block in Java A good and concise explanation can be found under this link.
One option here that would allow this would be to use User Input Dialogs from the swing API - then you could store the users answer's in variables and export them to a text file/config file, or just use them right in the program without saving them. You would just have the input dialogs pop up at the very beginning of the program before anything else happens, and then the program would run based off those responses.
You could use Javascript for the configuration file language, instead of java. Java 7 SE and later includes a javascript interpreter that you can call from Java. it's not difficult to use, and you can inject java objects into the javascript environment.
Basically, you'd create a Javascript environment, insert the java objects into it which the config file is expected to configure, and then run the config file as javascript.
Okay, here we go... I found an quite simple solution for my problem.
I am using Janino by Codehaus (Link). This library has an integrated Java compiler and seems to work like the JavaCompiler class in Java 7.
BUT without having the JDK to be installed.
Through Janino you can load and compile *.java files(which are human readable) at runtime, which was exactly what I needed.
I think the examples and code-snippets on their homepage are just painful, so here's my own implementation:
Step one is to implement an interface with the same methods your Java file has which is loaded at runtime:
public interface ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList);}
Then you call the Janino classloader when you need the class:
File janinoSourceDir = new File(PATH_TO_JAVAFILE);
File[] srcDir = new File[] { janinoSourceDir };
String encoding = null;
ClassLoader parentClassLoader = this.getClass().getClassLoader();
ClassLoader cl = new JavaSourceClassLoader(parentClassLoader, srcDir,
encoding);
And create an new instance
ZuordnungsInterface myZuordnung = (ZuordnungInterface) cl.loadClass("zuordnung")
.newInstance();
Note: The class which is loaded is named zuordnung.java, so there is no extension needed in the call cl.loadClass("zuordnung").
And finaly the class I want to load and compile at runtime of my program, which can be located wherever you want it to be (PATH_TO_JAVAFILE):
public class zuordnung implements ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList){
ArrayList<String> computedList = (ArrayList<String>) rawList.clone();
if (Model.getSomeString().equals("Some other string")) {
computedList.add("Yeah, I loaded an external Java class");
}
return computedList;
}}
That's it. Hope it helps others with similar problems!
Related
Little background for context:
The application I support allows third parties to develop plugins that can leverage some of our functionality. We hand them our "externalAPI.jar"; they put it in their project, implement some interfaces, and build their own APK. We find the would-be plugin by asking the package manager for all installed applications and see if each has a "pluginclass.xml" in the assets directory. If it has that XML file, we anticipate its contents being the canonical path of a class that implements our ExternalPluginVX interface, and using a new PathClassLoader(ApplicationInfo.sourceDir, this.getClass().getClassLoader()), we load the class, create a new instance, and start using it.
The problem:
Sometimes third parties will put
compile files ("./libs/externalAPI.jar")
in their gradle files instead of the correct syntax:
provided files ("./libs/externalAPI.jar")
The result of course being things don't work properly. Sometimes they almost work, but then have unpredictability in their behavior - usually involving vicious crashes. Notably, since their APK is well-formed in its own right, and the XML file is there, we'll see the plugin, load the target class successfully, instantiate it successfully, and things go haywire from there when they try and reference back to us.
The question:
Is there a way for my application to check at runtime if the other application compiled our API classes into their APK instead of using provided files like they should have?
A viable solution is to use a DexFile.
Since I already have the ApplicationInfo.sourceDir, I can just construct a DexFile and iterate through its contents.
//this variable's value assigned by iterating through context.getPackageManager().getInstalledApplications(0)
ApplicationInfo pkg;
String interfaceTheyShouldntHave = ExternalPluginVX.class.getCanonicalName(); //"com.project.external.ExternalPluginVX"
DexFile dexFile = new DexFile(pkg.sourceDir);
Enumeration<String> entries = dexFile.entries();
while(entries.hasMoreElements()){
String entry = entries.nextElement();
if(entry.equals(interfaceTheyShouldntHave)){
Toast.makeText(ctxt, "Plugin \"" + pluginName + "\" could not be loaded. Please use 'provided files' instead of 'compile files'", Toast.LENGTH_LONG).show();
return;
}
}
I'm working on a project where there's the need to call some methods from dll files.
These two dlls are
EasySign.dll
EasySignJNI
EasySignJNI depends on EasySign.
I wrote the class to load EasySignJNI as follows:
package easysign;
class EasySign {
EasySign(){}
public native String EasyHashFile(String filename);
public native int EasySign(String pkcs11_driver,String pin, int type, String file_data, int out_format, String signed_file, String cert_out, int cert_format);
public native int EasyVerify(String cert_user, String file_data, String signed_file, String crl_file, String ca_file, String out_document);
static {
System.loadLibrary("EasySignJNI");
}
}
Now I would call these method from my main method like this:
public class Test {
public static void main(String[] args) throws IOException{
EasySign es = new EasySign();
System.out.println("EasyHashFile : " + es.EasyHashFile("test.txt"));
}
}
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located? It is possible to call native method in this way?
I'm using NetBeans for completeness.
EDIT:
I have noticed that the third party dll provided to me (the JNI dll in particular) defines method names without any package, so I'm forced to put the class that loads the dll in the default package. Is there any way to change only the dll method names including my own package name?
EDIT 2:
What I mean is that both EasySign.dll and EasySignJNI.dll are provided me as they are and I can't modify them (I have not the source code). The EasySignJNI is the JNI portion but inspecting it I have noticed thath the method sign is in the form: _java_EasySign_MethodName. When I load the dll in Java from my Easysign class (this class must reside in the "mypackage" package), I receive the jni unsatisfiedlinkerror because, if I understood right, I'm calling the "_java_mypackage_EasySign_MethodName" method, i.e the sign is different from the dll's one. So the only way to make it work is to rewrite the JNI part and build it to have the correct sign of the JNI method?
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located?
Correct, the operating system will locate the dependent EasySign.dll for you as long as it is available where the OS expects it to be.
It is possible to call native method in this way? I'm using NetBeans for completeness.
I read through your edit and you have successfully lost me. What default package are you referring to? (Remember that none of us know what EasySign.dll is) So, I am going to provide some info about how I do what you originally described and hopefully it will help.
First start by compiling EasySign if you have the src. Do not build a DLL or shared object, instead build a static library. If you do not have the src code for EasySign, or a prebuilt static library, you will be stuck with the dll and can continue to the next step.
Now you are ready to compile the jni portion. All of your JNI C code should basically translate your Java input/output to their JVM/Native types and call the appropriate functions in the DLL library. You want to keep this layer and thin and simple as possible because it is incredibly difficult to debug. Your C++ package names shouldn't really matter here and you can use what ever package name you want for your Java classes. You should be able to compile the JNI code and preferably static link to the EasySign.dll file so you don't need to worry about distributing it. If you must dynamically link, make sure EasySign.dll gets installed to a location that is on the DLL PATH / LDPATH because the OS will need to locate and load that file right after the JVM loads the JNI DLL.
At this point you should just be able to point -Djava.library.path at your JNI DLL's path and all should work.
My code is failing with a ClassNotFoundException.
I can see that the jar file containing the class is definitely on the classpath from the command prompt execution.
Is there a way to dump the list of classes on the classpath from the JVM? (Ideally some Java code).
(I don't want to see the classes in a directory, I want to see a list of what is loaded into the JVM).
You can programatically display the classpath by looking at the classloaders and dumping the URLs they are loading from.
Something like this:
import java.net.URLClassLoader;
import java.util.Arrays;
public class ClasspathDumper
{
public static void main(String... args)
{
dumpClasspath(ClasspathDumper.class.getClassLoader());
}
public static void dumpClasspath(ClassLoader loader)
{
System.out.println("Classloader " + loader + ":");
if (loader instanceof URLClassLoader)
{
URLClassLoader ucl = (URLClassLoader)loader;
System.out.println("\t" + Arrays.toString(ucl.getURLs()));
}
else
System.out.println("\t(cannot display components as not a URLClassLoader)");
if (loader.getParent() != null)
dumpClasspath(loader.getParent());
}
}
it would produce output similar to:
Classloader sun.misc.Launcher$AppClassLoader#2a340e:
[file:/C:/Java/workspaces/myproject/bin/]
Classloader sun.misc.Launcher$ExtClassLoader#bfbdb0:
[file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/dnsns.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/localedata.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/sunec.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/sunjce_provider.jar, ...]
..? (Ideally some Java code)
If you were looking only to resolve a Class-Not-Found bug, then adding a dump code within the app can add complexity to turn it off later. Perhaps it would be better to use -verbose:class JVM argument which would output all classes loaded at runtime. Its easy to turn off and output of the console can be easily redirected to a log.
That's actually not what you want to see if you're getting a CNFE, since it's not found. Plus not all available classes will be loaded at any given time.
Start by going through this list. But in general, if it's not found, it's actually not found.
Well, you could create a memory dump (e.g. via jmap) and view it (e.g. via jhat).
Alternatively, IIRC jconsole can show the loaded classes, so you could just view them. I'm not entirely sure though and I don't have a running jconsole right now.
A third alternative (for Java 5+) would be VisualVM which is part of the Java6+ distribution.
However, most certainly your jar file is not on the classpath or you are using some custom classloaders. Could you elaborate on how you put that jar on the classpath?
Is there a way in using externally stored sourcecode and loading it into a Java programm, so that it can be used by it?
I would like to have a program that can be altered without editing the complete source code and that this is even possible without compiling this every time. Another advantage is, that I can change parts of the code like I want.
Of course I have to have interfaces so that it is possible to send data into this and getting it back into the fixed source program again.
And of course it should be faster than a pure interpreting system.
So is there a way in doing this like an additional compiling of these external source code parts and a start of the programm after this is done?
Thank you in advance, Andreas :)
You need the javax.tools API for this. Thus, you need to have at least the JDK installed to get it to work (and let your IDE point to it instead of the JRE). Here's a basic kickoff example (without proper exception and encoding handling just to make the basic example less opaque, cough):
public static void main(String... args) throws Exception {
String source = "public class Test { static { System.out.println(\"test\"); } }";
File root = new File("/test");
File sourceFile = new File(root, "Test.java");
Writer writer = new FileWriter(sourceFile);
writer.write(source);
writer.close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("Test", true, classLoader);
}
This should print test in stdout, as done by the static initializer in the test source code. Further use would be more easy if those classes implements a certain interface which is already in the classpath. Otherwise you need to involve the Reflection API to access and invoke the methods/fields.
In Java 6 or later, you can get access to the compiler through the javax.tools package. ToolProvider.getSystemJavaCompiler() will get you a javax.tools.JavaCompiler, which you can configure to compile your source. If you are using earlier versions of Java, you can still get at it through the internal com.sun.tools.javac.Main interface, although it's a lot less flexible.
Java6 has a scripting API. I've used it with Javascript, but I believe you can have it compile external Java code as well.
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/
Edit: Here is a more relevant link:
"Dynamic source" code in Java applications
I want to launch a java subprocess, with the same java classpath and dynamically loaded classes as the current java process. The following is not enough, because it doesn't include any dynamically loaded classes:
String classpath = System.getProperty("java.class.path");
Currently I'm searching for each needed class with the code below. However, on some machines this fails for some classes/libs, the source variable is null. Is there a more reliable and simpler way to get the location of libs that are used by the current jvm process?
String stax = ClassFinder.classPath("javax.xml.stream.Location");
public static String classPath(String qualifiedClassName) throws NotFoundException {
try {
Class qc = Class.forName( qualifiedClassName );
CodeSource source = qc.getProtectionDomain().getCodeSource();
if ( source != null ) {
URL location = source.getLocation();
String f = location.getPath();
f = URLDecoder.decode(f, "UTF-8"); // decode URL to avoid spaces being replaced by %20
return f.substring(1);
} else {
throw new ClassFinder().new NotFoundException(qualifiedClassName+" (unknown source, likely rt.jar)");
}
} catch ( Exception e ) {
throw new ClassFinder().new NotFoundException(qualifiedClassName);
}
}
See my previous question which covers getting the classpath as well as how to launch a sub-process.
I want to launch a java subprocess, with the same java classpath and dynamically loaded classes as the current java process.
You mean invoke a new JVM?
Given that...
it is possible to plug in all sorts of agents and instrumentation into a JVM that can transform classes at load time
it is possible to take a byte array and turn it into a class
it is possible to have complex class loader hierarchies with varying visibility between classes and have the same classes loaded multiple times
...there is no general, magic, catch-all and foolproof way to do this. You should design your application and its class loading mechanisms to achieve this goal. If you allow 3rd party plug-ins, you'll have to document how this works and how they have to register their libraries.
If you look at the javadoc for Class.getClassLoader, you'll see that the "bootstrap" classloader is typically represented as the null. "String.class.getClassLoader()" will return null on the normal sun jvm implementations. i think this implementation detail carries over into the CodeSource stuff. As such, I wouldn't imagine you would need to worry about any class which comes from the bootstrap classloader as long as your sub-process uses the same jvm impl as the current process.