java


Monkey Problems 2

Recently, we tried to user monkeyrunner on an Ubuntu 18.04 LTS. We installed Android Studio through snap and we setup the Android SDK in ~/Andoid. monkeyrunner was installed in ~/Android/Sdk/tools/bin. To our immeasurable disappointment we found out that when we tried to execute monkeyrunner, it would give the following error:

$ ./monkeyrunner
-Djava.ext.dirs=/home/tux/Android/Sdk/tools/lib:/home/tux/Android/Sdk/tools/lib/x86_64 is not supported. Use -classpath instead.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

At first, we had no idea what that meant, so we used file command on monkeyrunner and we found out that monkeyrunner is a bash script.

$ file monkeyrunner
monkeyrunner: POSIX shell script, ASCII text executable

After reading the code, we read the following at the last lines:

#need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
#might need more memory, e.g. -Xmx128M
exec java -Xmx128M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir:$swtpath" -Djava.library.path="$libdir" -Dcom.android.monkeyrunner.bindir="$progdir" -jar "$jarpath" "$@"

First thing we did was to find the java version that was used, since the command that was giving the problem was that. We found out that we had version 11.

$ java -version
openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1)
OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1, mixed mode, sharing)

After reading a bit, we found out that after Java version 8 the command line directive -Djava.ext.dirs it was deprecated and the recommendation to use -classpath was added. In the past, java.ext.dirs was used to instruct the JRE from where to load additional class and jar files. Since we had Java version 11 we had to try the recommendation to remove the -Djava.ext.dirs directive and use -classpath instead. So, we edited the file monkeyrunner and changed the last line as follows:

exec java -Xmx128M $os_opts $java_debug -classpath "$frameworkdir/*:$swtpath" -Djava.library.path="$libdir" -Dcom.android.monkeyrunner.bindir="$progdir" -jar "$jarpath" "$@"

We tried executing the newly updated monkeyrunner again, only to hit another wall!

$ ./monkeyrunner
Exception in thread "main" java.lang.NoClassDefFoundError: com/android/chimpchat/ChimpChat
at com.android.monkeyrunner.MonkeyRunnerStarter.(MonkeyRunnerStarter.java:60)
at com.android.monkeyrunner.MonkeyRunnerStarter.main(MonkeyRunnerStarter.java:188)
Caused by: java.lang.ClassNotFoundException: com.android.chimpchat.ChimpChat
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
… 2 more

It turns out that when you use the -jar directive, JRE will ignore the -classpath directive and so it will again not be able to load any external class or jar files…

Solution

Instead of reinventing the wheel (We tried, we failed, it was painful. Still worth the shot!) we decided to install Java version 8 on Ubuntu 18.04 LTS side by side with Java version 11 and just used that.

First, we checked the list of installed jvm on our machine using the following command:

update-java-alternatives --list;

Where we got the following:

$ update-java-alternatives --list
java-1.11.0-openjdk-amd64 1111 /usr/lib/jvm/java-1.11.0-openjdk-amd64
java-1.8.0-openjdk-amd64 1081 /usr/lib/jvm/java-1.8.0-openjdk-amd64

It turned out we already had Java version 8 but if we didn’t we would install it as follows:

sudo apt install openjdk-8-jdk;

and then it would again appear in the list mentioned above.

Then, we switched to the Java version 8 using the following command and selecting the appropriate option:

sudo update-alternatives --config java;

$ sudo update-alternatives --config java
[sudo] password for tux:
There are 2 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 auto mode
1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode
2 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode
Press to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode

After switching Java to Version 8 monkeyrunner was working as expected!!


Android get GSF ID (Google Services Framework Identifier)

The Google Services Framework Identifier (GSF ID) is a unique 16 character hexadecimal number that your device automatically requests from Google as soon as you log to your Google Account for the first time. For a specific device, the GSF ID will change only after a factory reset. To get this information programmatically, you need to do two steps.

A) Add the following to your manifest


<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

B) Execute the following code


private static final Uri sUri = Uri.parse("content://com.google.android.gsf.gservices");
public static String getGSFID(Context context) {
 try {
  Cursor query = context.getContentResolver().query(sUri, null, null, new String[] { "android_id" }, null);
  if (query == null) {
   return "Not found";
  }
  if (!query.moveToFirst() || query.getColumnCount() < 2) {
   query.close();
   return "Not found";
  }
  final String toHexString = Long.toHexString(Long.parseLong(query.getString(1)));
  query.close();
  return toHexString.toUpperCase().trim();
 } catch (SecurityException e) {
  e.printStackTrace();
  return null;
 } catch (Exception e2) {
  e2.printStackTrace();
  return null;
 }
}


JavaFX: Could not execute Jar build from artifact, while we could execute from IntelliJ IDEA

One of the applications we recently build on IntelliJ IDEA was using JavaFX.

When executing the application from inside IntelliJ, it would work as expected and load the GUI.
On the other hand, when executing the jar file from the terminal, we would get the following error:

$ java -jar hello.jar 
Graphics Device initialization failed for :  es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
	at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:221)
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:205)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:209)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:337)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
	at java.lang.Thread.run(Thread.java:745)
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: No toolkit found
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:217)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:209)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:337)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	... 5 more

As the above information was not very helpful, we executed the jar again using the -Dprism.verbose=true directive to get more information from the error.

$ java -Dprism.verbose=true -jar hello.jar 
Prism pipeline init order: es2 sw 
Using java-based Pisces rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Prism pipeline name = com.sun.prism.es2.ES2Pipeline
Loading ES2 native library ... prism_es2
GraphicsPipeline.createPipeline failed for com.sun.prism.es2.ES2Pipeline
java.lang.UnsatisfiedLinkError: Can't load library: /home/user/Projects/Java/hello/out/artifacts/amd64/libprism_es2.so
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1827)
	at java.lang.Runtime.load0(Runtime.java:809)
	at java.lang.System.load(System.java:1086)
	at com.sun.glass.utils.NativeLibLoader.loadLibraryFullPath(NativeLibLoader.java:201)
	at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:94)
	at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:39)
	at com.sun.prism.es2.ES2Pipeline.lambda$static$0(ES2Pipeline.java:68)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.prism.es2.ES2Pipeline.(ES2Pipeline.java:50)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:187)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:91)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
	at java.lang.Thread.run(Thread.java:745)
*** Fallback to Prism SW pipeline
Prism pipeline name = com.sun.prism.sw.SWPipeline
GraphicsPipeline.createPipeline failed for com.sun.prism.sw.SWPipeline
java.lang.UnsatisfiedLinkError: Can't load library: /home/user/Projects/Java/hello/out/artifacts/amd64/libprism_sw.so
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1827)
	at java.lang.Runtime.load0(Runtime.java:809)
	at java.lang.System.load(System.java:1086)
	at com.sun.glass.utils.NativeLibLoader.loadLibraryFullPath(NativeLibLoader.java:201)
	at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:94)
	at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:39)
	at com.sun.prism.sw.SWPipeline.lambda$static$0(SWPipeline.java:42)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.prism.sw.SWPipeline.(SWPipeline.java:41)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:187)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:91)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
	at java.lang.Thread.run(Thread.java:745)
Graphics Device initialization failed for :  es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
	at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:221)
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:205)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:209)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:337)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
	at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
	at java.lang.Thread.run(Thread.java:745)
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: No toolkit found
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:217)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:209)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:337)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	... 5 more

From the new log, we could see that the application was trying to load some .so (libprism_es2.so and libprism_sw.so) files that were not available in LD_LIBRARY_PATH.
We used find, to locate them:


find / -name "libprism_es2.so" 2>/dev/null;

Then we updated the LD_LIBRARY_PATH variable to include the new path


export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/idea-IC/jre64/lib/amd64/;

java -jar hello.jar;

Note: Here we used the copies that were available in our IntelliJ IDEA installation.
It is not recommended as you need to deploy them per machine, but in our case it was OK as the development machine was the actual machine to use the tool as well.


Creating Jar with Intellij IDEA 2017.3 – no main manifest attribute, in .jar 1

Recently we tried to create a Jar file from IntelliJ IDEA 2017.3 using Maven.

After building the artifact, we got the error no main manifest attribute, in $FILE.jar while executing the jar file created by IntelliJ.
After extracting the jar we observed that there was a manifest file but not the one that was specified while creating the artifact.

After some serious google-fu, we got to the following bug ticket.
Apparently, IntelliJ does not read the manifest file that is located in the src/main/java folder!
Working Solution: As suggested in the ticket, we moved the META-INF folder to src/main/resources and rebuild the artifact.