I have written an application in Java and succesfully compiled it using gcj. It worked surprisingly well, but I've run into a hurdle: I can only run the executable through a shell script, because I have to specify the library paths.
The libraries I need are SWT, Xerces and GNU-crypto.
Is there a way to statically link the libraries when compiling in gcj, or is this not a good idea? Alternatively, can I specify the (relative) library path while compiling?
Presently, my shell script looks like this:
#!/bin/sh
export LD_LIBRARY_PATH=./libs/:$LD_LIBRARY_PATH
exec ./MyJavaApp $*
The idea is to make the static field "sys_paths" null so that it would construct the paths from the changed value.
See the post here (Post#223 by AjaySingh516) http://forums.sun.com/thread.jspa?messageID=3744346#3744346
Class clazz = ClassLoader.class;
Field field = clazz.getDeclaredField("sys_paths");
boolean accessible = field.isAccessible();
if (!accessible)
field.setAccessible(true);
Object original = field.get(clazz);
// Reset it to null so that whenever "System.loadLibrary" is called, it
// will be reconstructed with the changed value.
field.set(clazz, null);
try {
// Change the value and load the library.
System.setProperty("java.library.path", "./libs/");
System.loadLibrary("mylibapr");
} finally {
// Revert back the changes.
field.set(clazz, original);
field.setAccessible(accessible);
}
.
gcj System Properties (See: Standard properties supported by libgcj)
http://gcc.gnu.org/onlinedocs/gcj/System-properties.html
.
Solution#2
: Set System environment variable at compile time
http://linux.die.net/man/1/gcj
For this you have to use parameter -Djava.library.path=./libs/ with gcj
From gcj manual (above link):
--main= CLASSNAME
This option is used when linking to specify the name of the class whose "main" method should be invoked when the resulting executable is run.
-Dname[=value]
This option can only be used with "--main". It defines a system property named name with value value. If value is not specified then it defaults to the empty string. These system properties are initialized at the program's startup and can be retrieved at runtime using the "java.lang.System.getProperty" method.
I have never worked with gcj but as per docs these system properties can be retrieved at runtime, hence it will be portable to other systems as well.
Also see: http://gcc.gnu.org/wiki/Statically_linking_libgcj?action=show&redirect=Statically+linking+libgcj
To answer the first part of your question -
From the gcj man page:
"Static linking of libgcj may cause essential parts of libgcj to be omitted. Some parts of libgcj use reflection to load classes at runtime. Since the linker does not see these references at link time, it can omit the referred to classes. The result is usually (but not always) a "ClassNotFoundException" being thrown at runtime. Caution must be used when using this option."
For the static linking of the other libraries, I'm not sure. I haven't had a reason to do that.
Linux executables are different than Windows. Normally you have a "launcher" or some such depending on which exact windowing system you are using. You set the icon in that, not on the executable itself. Usually, launch scripts are used to set any environment that you need for running the executable. Again, this all depends on your exact desktop window system.
Why are you using an AOT? I would suggest reading the following article. One of the drawbacks that it mentions for AOTs is the following...
Dynamic applications. Classes that the application loads dynamically at runtime may be unavailable to the application developer. These can be third-party plug-ins, dynamic proxies and other classes generated at runtime and so on. So the runtime system has to include a Java bytecode interpreter and/or a JIT compiler.
Related
I have created a maven project L and written a Java extension (i.e. an optional package) implementing (i.e. extending) the (abstract) service providers that implement (i.e. extend) LocaleServiceProvider, to support a dialect (let's call it xy) that isn't normally supported by the JRE. (I do not want to use the CLDR extension that came with Java 8, even though I'm running 8.141.)
The project compiles, and produces a jar with a META-INF/services folder that contains the provider-configuration files in UTF-8 with the qualified provider class names being on a line that ends with a line feed (\n).
I have then declared a maven dependency in my project P on the locale project L, and I thought that that would work, because the tutorial states
The extension framework makes use of the class-loading delegation
mechanism. When the runtime environment needs to load a new class for
an application, it looks for the class in the following locations, in
order:
[...]
The class path: classes, including classes in JAR files,
on paths specified by the system property java.class.path. If a JAR
file on the class path has a manifest with the Class-Path attribute,
JAR files specified by the Class-Path attribute will be searched also.
By default, the java.class.path property's value is ., the current
directory. You can change the value by using the -classpath or -cp
command-line options, or setting the CLASSPATH environment variable.
The command-line options override the setting of the CLASSPATH
environment variable.
Maven puts all dependencies on the classpath, I believe.
Yet when I run my unit test in P (in IntelliJ; L is on the classpath), it fails:
#Test
public void xyLocalePresent() {
Locale xy = new Locale("xy");
assertEquals("P not on classpath", xy, com.example.l.Locales.XY); // access constant in my Locale project L; should be equals to locale defined here
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, xy);
assertEquals("dd/MM/yy", df.toPattern()); // fails; L specifies the short date pattern as dd/MM/yy
}
I have to start it with -Djava.locale.providers=SPI,JRE -Djava.ext.dirs=/path/to/project/L/target. If I do that, it works, indicating that L's service providers were loaded successfully (indicating the jar's structure is ok).
NB: the Java 8 technotes say that the order SPI,JRE is the default.
Why, oh why does it not work when I just put L on the classpath? Why do I have to point to it explicitly?
Update: After going through the JavaDoc again, I just saw this (emphasis mine):
Implementations of these locale sensitive services are packaged using
the Java Extension Mechanism as installed extensions.
That explains things. :(
Is there any way to make this work by just putting L on the classpath when P runs, i.e. without having to install L (or having to use -D system properties)? (P uses maven, Struts2 and Spring, if that helps...)
In more complex applications, such as web servers (e.g. Tomcat), there are multiple ClassLoaders, so each WebApp served by the web server can be kept independent.
The extension mechanism is for extending the core Java functionality, i.e. features available globally within the running JVM (the web server). As such, they must be loaded by the System ClassLoader.
The standard way to add an extension to the code Java runtime, is to either
add the Jar file to the JRE_HOME/lib/ext folder
add extra folders to be searched by specifying the java.ext.dirs system property
You could also just add it to the Bootstrap ClassPath yourself, but that might cause problems if the Security Manager is activated. Not sure about that part. So it's best to do it the official way.
Note that the classpath defined by the CLASSPATH environment variable, or the -cp command-line option, does not define the Bootstrap ClassPath.
To learn more, read the Java documentation "How Classes are Found".
The class BasicLabelUI in javax/swing/plaf/basic is affected by a confirmed bug.
In my application I need functionality provided by the fixed version (filed for v9).
Due to both legal and technical reasons, I'm still bound to the affected JDK version.
My approach was to create a package javax/swing/plaf/basic inside my project, containing the fixed version.
How can I force my project to favor my included version of the class over the defective class in the installed JDK?
This has to be somewhat portable as the fixed class also has to be working on customer side and the defective class in the JDK installation has to be disregarded. Therefore, I dont want to modify the JDK, but rather bypass this particular class.
As mentioned by the other answers, you could in theory of course unzip your JVM's rt.jar file and replace the file with a compatible bugfixed version.
Any classes of the Java Class library such as those of Swing are loaded by the bootstrap class loader which looks up its classes from this rt.jar. You can generally not prepend classes to this classpath without adding them to this file. There is a (non-standard) VM option
-Xbootclasspath/jarWithPatchedClass.jar:path
where you would prepend a jar file that includes the patched version, but this does not necessarily work on any Java virtual machine. Also, it is illegal to deploy an application that changes this hehavior! As it is stated in the official documentation:
Do not deploy applications that use this option to override a class in
rt.jar because this violates the Java Runtime Environment binary code
license.
If you however appended a class to the bootstrap class loader (what is possible without using non-standard APIs by using the instrumentation API), the runtime would still load the original class as the bootstrap class loader in this case searches the rt.jar first. It is therefore impossible to "shadow" the broken class without modifying this file.
Finally, it is always illegal to distribute a VM with a patched file, i.e. putting it into a production system for a customer. The license agreement states clearly that you need to
[...] distribute the [Java runtime] complete and unmodified and only bundled as part of your applets and applications
Changing the VM that you distribute is therefore not recommended as you might face legal consequences when this is ever uncovered.
Of course, you can in theory build your own version of the OpenJDK but you could not call the binary Java anymore when you distribute it and I assume that your customer would not allow for this by what you suggest in your answer. By experience, many secure environments compute hashes of binaries before execution what would prohibit both approaches of tweaking the executing VM.
The easiest solution for you would probably be the creation of a Java agent that you you add to your VM process on startup. In the end, this is very similar to adding a library as a class path dependency:
java -javaagent:bugFixAgent.jar -jar myApp.jar
A Java agent is capable of replacing a class's binary representation when the application is started and can therefore change the implementation of the buggy method.
In your case, an agent would look something like the following where you need to include the patched class file as a ressource:
public static class BugFixAgent {
public static void premain(String args, Instrumentation inst) {
inst.addClassFileTransformer(new ClassFileTransformer() {
#Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
if (className.equals("javax/swing/plaf/basic/BasicLabelUI")) {
return patchedClassFile; // as found in the repository
// Consider removing the transformer for future class loading
} else {
return null; // skips instrumentation for other classes
}
}
});
}
}
The javadoc java.lang.instrumentation package offers a detail description of how to build and implement a Java agent. Using this approach, you can use the fixed version of the class in question without breaking the license agreement.
From experience, Java agents are a great way for fixing temporary bugs in third party libraries and in the Java Class Library without needing to deploy changes in your code or even being required to deploy a new version for a customer. As a matter of fact, this is a typical use case for using a Java agent.
How can I force my project to favor my included version of the class over the defective class in the installed JDK?
Simple answer - you can't. At least, not while strictly obeying the constraint that you should use the affected Java version.
Assuming that you can identify an appropriate version in the OpenJDK source repos, it would be possible to build your own flavor of the Java libraries with a bug patched. However, that won't be real Java. Certainly, it won't qualify as "the affected Java version" that you are constrained to use. (And besides, you are committing yourself to an endless cycle of reapplying your patch to each new patch release of the current version of Java ...)
It is also possible in theory to put a modified version of some Java standard library class into a JAR and prepend it to the JVM's bootstrap classpath using the -Xbootclasspath command line option. But that is tantamount to changing "the affected Java version" too.
Doing it by using a Java agent to use a patched version of the class is breaking the rules too. And it is more complicated. (If you are going to break your rules, do it the easy way ...)
If you and your customers do decide that tweaking the JVM is an acceptable solution, then doing it via the bootstrap classpath is probably the simplest and cleanest approach. And it is DEFINITELY legal1.
However, I'd recommend that you find a workaround for the bug until a version of Java 9 with your fix is released.
1 - Actually, even the build-from-modified-source approach is legal, because the Oracle Binary license does not apply to that. The Binary license is about distributing a modified version of an Oracle binary. The other possible issue is that you may be violating the terms for using the Java trademark(s) if you distribute a version that is incompatible with "true" Java, and call your distro "Java". The solution to that is ... don't call it "Java"!
However, don't just follow my advice. Ask a lawyer. Better yet, don't do it at all. It is unnecessarily complicated.
Eclipse has an option in Java Compiler tab:
Store information about method parameters (usable via reflection)
If I checked on the option. I am able to get method parameter via Parameter API. It is good.
If I turn on the option, Compiler will stored method parameter information in All my compiling classes. This may make the class/jar file bigger. Eclipse turn it off by default.
My question is: Is there any way to turn on the option for some class I want? Is there any compiler directive that I can add it into my java class for this purpose?
Thank you!
Yes in a way. This is an option to javac (see -parameters) and javac can be run on whatever set of files you would like. However, there is not any option to selectively apply -parameters to certain classes when running javac on multiple files, so you would have to run multiple javac's most likely. This could be done through a build file most likely with a build language (for instance Ant or Gradle).
I am using junit's TemporaryFolder to test part of my application that deals with the filesystem. Is it possible to set the junit variable java.io.tmpdir to an Environment variable in the run configuration?
Based on our long-lasting discussion I now understand that you ask this:
How to configure JUnit so it uses a specific folder for creating the temporary files.
(If I get your question properly, please edit it accordingly and change the title.)
JUnit 4.11+
The easiest way is to use the new constructor TemporaryFolder(File parentFolder). This is the preferred way.
JUnit < 4.11
You are aware that the org.junit.rules.TemporaryFolder uses the the system property java.io.tmpdir. Actually, when you look at the source code, it uses internally the File.createTempFile(prefix, suffix) method which uses this system property.
You will find exhaustive information here: Environment variable to control java.io.tmpdir?.
More generally about system properties and environment variables: Java system properties and environment variables
However, you should notice this sentence from the Java-doc:
A different value may be given to this system property when the Java
virtual machine is invoked, but programmatic changes to this property
are not guaranteed to have any effect upon the temporary directory
used by this method.
If you want to be sure you have the temporary folder always under your control, you may create your own version of org.junit.rules.TemporaryFolder - it's not so difficult, you just use the File.createTempFile(prefix, suffix, directory) with explicitly assigned the 3rd parameter directory.
MATLAB is configured to search its static java class path before searching the user-modifiable dynamic path. Unfortunately, the static path contains quite a number of very old public libraries, so if you are trying to use a new version you may end up loading the wrong implementation and get errors.
For instance, the static path contains an old copy of the google-collections.jar, which has long been supplanted by Google's guava library and which has some of the same class names (e.g. com.google.common.base.Objects). As a result, if you invoke a Guava method that uses a newer method of one of such a class, you will end up getting surprising NoSuchMethodErrors because the google-collections jar is found first.
As of R2012b, MATLAB lets you specify additional jars to add to the static path by putting a javaclasspath.txt file in your preferences folder, but that adds jars to the end of the path, and doesn't let you override jars that are built into MATLAB.
So what is the best way around this?
I got an official response from Mathworks:
As of MATLAB R2013a (also in R2012b), classes can be added to the front of the static Java class path by including the following line in javaclasspath.txt:
<before>
Any directory that is after this line in javaclasspath.txt will be added to the front of the static Java class path. This is an undocumented use of javaclasspath.txt as of R2013a.
But overall in MATLAB, the ability to add classes to the front of the static Java classpath is not available through javaclasspath.txt in MATLAB 8.0 (R2012b).
MATLAB searches for classpath.txt in the following order:
In the startup directory. As of MATLAB 8.0 (R2012b) a warning will be shown if the file is found there and it will be ignored.
In the first directory on the MATLABPATH environment variable. (This environment variable is used in the bin/matlab shell script on Linux and in general is not used by the end-user).
In the toolbox/local directory.
Although the MATLABPATH environment variable of point 2 is normally not used by end-users we can use it in a workaround to allow reading a custom classpath.txt outside of the toolbox/local directory.
On Windows:
You will need to create the MATLABPATH environment variable. The first directory on it should be your directory with the custom classpath.txt AND you will also need to add the toolbox\local directory as second option. So from a cmd prompt you could do:
set MATLABPATH=c:\Users\user\Documents\myMATLABClasspath;c:\Program Files\MATLAB\R2012b
\toolbox\local
matlab.exe
One hack that appears to work is to add the jar to the top of the classpath.txt file that can be found in your MATLAB installations toolbox/local folder. Unfortunately, this is automatically generated and may get rewritten at some unspecified time, such as when you install new toolboxes, so this approach would require you to have some way to notice when this happens and reapply the hack.
If you're distributing a jar that's intended to be used with matlab, it may be better to use proguard as described at http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava.
If you specify that all of your classes and their (public) fields and methods are to be preserved and include guava as a program jar (not a library), then it will rename all of guava's methods and update your compiled bytecode to reference the new names.
It seems a bit hackish, but depending on the audience, it may be significantly easier than teaching your users about static vs. dynamic classpath, and it won't break any matlab code that depends on the old behavior.
Instead of obfuscating the package as suggested by #user2443532, I have found it easier to "shade" the conflicting package instead of obfuscating it - unless you actually need obfuscation. One easy way to do this is to build your package using Maven and use the maven-shade-plugin. Internal calls are modified automatically, so you don't need to modify any of the Java code.
Direct calls from Matlab will need to be modified - for example, calls to com.opensource.Class become shaded.com.opensource.Class.
For more info on shading, see What is the maven-shade-plugin used for, and why would you want to relocate Java packages?