MapTool - Development - Java 6 vs Java 7 coding

Progress reports and musings from the developers on the current gaming tools.

Moderators: dorpond, trevor, Azhrei

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

Ed Note: If this should be in a different section of the Forum the I apologize and request that admin move it to the correct section

I am trying to write a modification to MapTool (for my own purpose and anyone else that likes the mod) which is based on Java 7's nio function. However, for users who are not using Java 7, I can provide similar (but slightly watered down) functionality using pre Java 7 io functions.

It was suggested to me to Class to try to call the Java 7 Class and if this fails resort to using the Java 6 version. I found the following existing example in MapTool.java:

Code: Select all

try {
	Class<?> appClass = Class.forName("com.apple.eawt.Application");
	Method getApplication = appClass.getDeclaredMethod("getApplication", (Class[]) null);
	Object appl = getApplication.invoke(null, (Object[]) null);
	Method setDockIconImage = appl.getClass().getDeclaredMethod("setDockIconImage", new Class[] { java.awt.Image.class });
	// If we couldn't grab the image for some reason, don't set the dock bar icon!  Duh!
	if (img != null)
		setDockIconImage.invoke(appl, new Object[] { img });
		if (MapToolUtil.isDebugEnabled()) {
		// For some reason Mac users don't like the dock badge icon.  But from a development standpoint I like seeing the
		// version number in the dock bar.  So we'll only include it when running with MAPTOOL_DEV on the command line.
		Method setDockIconBadge = appl.getClass().getDeclaredMethod("setDockIconBadge", new Class[] { java.lang.String.class });
		String vers = getVersion();
		vers = vers.substring(vers.length() - 2);
		vers = vers.replaceAll("[^0-9]", "0"); // Convert all non-digits to zeroes
		setDockIconBadge.invoke(appl, new Object[] { vers });
	}
} catch (Exception e) {
	log.info("Cannot find/invoke methods on com.apple.eawt.Application; use -X command line options to set dock bar attributes", e);
}
As far as I can tell the first part tries to call the getApplication class in com.apple.eawt.Application and then does some things with it if successful or drops to the log.info if the loading and/or invoking of the class is unsuccessful.

If I am correct the relevant part to invoking the class (and thus determining if it is available) can be summarized with:

Code: Select all

try {
	Class<?> appClass = Class.forName("[i]file_in_which_the_class_is_defined[/i]");
	Method getApplication = appClass.getDeclaredMethod("[i]class_name[/i]", (Class[]) null);
	Object appl = getApplication.invoke(null, (Object[]) null);
} catch (Exception e) {
	// Class was not loaded
}
For my solution, I wrote two classes: one Java 6 class and one Java 7 class. I use the above to try to invoke the Java 7 class first and the if that fails I try the Java 6 class. The corresponding code looks like this:

Code: Select all

// Clean up the cache
try 
{
	Class<?> java7CacheClass = Class.forName("net.rptools.maptool.client.CacheCleanerJava7");
	Method getJava7CacheApplication = java7CacheClass.getDeclaredMethod("CheckCacheSize", (Class[]) null);
	getJava7CacheApplication.invoke(null, (Object[]) null);
}
catch(Exception x)
{
	try
	{
		Class<?> java6CacheClass = Class.forName("net.rptools.maptool.client.CacheCleanerJava6");
		Method getJava6CacheApplication = java6CacheClass.getDeclaredMethod("CheckCacheSize", (Class[]) null);
		getJava6CacheApplication.invoke(null, (Object[]) null);
	}
	catch(Exception x2)
	{
		MapTool.showWarning("Unable To Load Java6- or Java7+ CacheCleaner Implementation.");
	}
}
As far as I can tell this should try to load the Java 7 class, if that fails try to load the Java 6 class and if that fails displays the warning.
When I try this on my Win8 machine using Java 7 it correctly runs the Java 7 branch of the code. However, when I try the same code on my Ubuntu machine running Java 6 then the program cores before it gets to the Java 6 branch. I believe that my Ubuntu Java 6 machine is set up correctly because it can run an unmodified version of MapTool without any problems.

As far as I know the Java 7 Class uses nio and thus is not pre Java 7 compatible but the Java 6 Class code does not use any Java 7 functions and thus should be Java 6 compatible.

I am enclosing a Diff Path for my modifications which includes the source code for the two (Java 6 and Java 7) new classes.
CacheCleaner_Rev5_Patch_For_MapTool-1.3.b89.txt
(17.14 KiB) Downloaded 151 times
Can anyone point me in the right direction as to why my Java 6 / Java 7 compatibility does not seem to be working?
Last edited by Azhrei on Mon Sep 09, 2013 1:38 pm, edited 1 time in total.
Reason: moved to Developer Notes
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Azhrei »

Close. When you call getDeclaredMethod() you're not passing a class name, but the name of the method within the class that you want to invoke. The class name has already been specified when you called forName() (the parameter isn't a file name, but a fully qualified class name).

You're probably getting a null reference back from the getDeclaredMethod() and that's causing the crash.

You might be able to get some more info by Google'ing that method name. You can look at documentation for JDBC since its drivers are often loaded using forName() and there will be a LOT of examples based around it. (Well, unless the example has been updated to use JNDI, but many haven't been because that's a lot more configuration for simple use cases.)

username
Dragon
Posts: 277
Joined: Sun Sep 04, 2011 7:01 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by username »

I suggest you use

Code: Select all

System.getProperty("java.version")
instead of relying on exceptions. BTW, Azhrei's explanation of your problem doesn't explain a core dump. Are you sure you saw a core dump?

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

Azhrei wrote:Close. When you call getDeclaredMethod() you're not passing a class name, but the name of the method within the class that you want to invoke. The class name has already been specified when you called forName() (the parameter isn't a file name, but a fully qualified class name).
You are right. However, as far as I can tell that is what my code does (I just explained it incorrectly).

In my code the Class is CacheCleanerJava7 and the public Method to call inside that class is CheckCacheSize.
Azhrei wrote:You're probably getting a null reference back from the getDeclaredMethod() and that's causing the crash.

You might be able to get some more info by Google'ing that method name. You can look at documentation for JDBC since its drivers are often loaded using forName() and there will be a LOT of examples based around it. (Well, unless the example has been updated to use JNDI, but many haven't been because that's a lot more configuration for simple use cases.)
You lost me a little here. I can't possible google the CheckCacheSize method (of CacheCleanerJava7) because I wrote it. Are you suggesting that I google the forName() of Class?
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

username wrote:I suggest you use

Code: Select all

System.getProperty("java.version")
instead of relying on exceptions. BTW, Azhrei's explanation of your problem doesn't explain a core dump. Are you sure you saw a core dump?
As far as I know, I can't use that method (there is even a note about it in the MapTool source code) because if you don't use the Class method of invoking the class at runtime, the class gets compiled into the code (even if the logic never runs it) and this will prevent the executable from running on machines which can not run that class (i.e. machines that do not have Java 7)...which is exactly what I am trying to avoid.

Unless you are suggesting to still use the runtime Class invoking but use the above code to determine which to use. Hmm...I could work. I'll give that a try.
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

username wrote:I suggest you use

Code: Select all

System.getProperty("java.version")
instead of relying on exceptions. BTW, Azhrei's explanation of your problem doesn't explain a core dump. Are you sure you saw a core dump?
MapTool detects the JAVA version and makes it available via:

Code: Select all

MapTool.JAVA_VERSION
Just to see how this is determined, I look at the source code and it seems that it uses:

Code: Select all

System.getProperty("java.specification.version");
Spoiler
"specification" is inserted between "java" and "version"
Was that a typo on your part or does java.version and java.specification.version get different properties?
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

Okay...I took everyone's suggestion and applied the following changes:

1. I am using MapTool.JAVA_VERSION to determine if I should use the Java 7 or Java 6 Class
2. I have made the two classes use different methods (so that I could...)
3. I only invoke the Java 7 class using the runtime Class method. I have now added the Java 6 class as a normal compile time class.

The modified code looks like:

Code: Select all

// Clean up the cache
if(MapTool.JAVA_VERSION>=1.7)
{
	try
	{
		Class<?> java7CacheClass = Class.forName("net.rptools.maptool.client.CacheCleanerJava7");
		Method getJava7CacheApplication = java7CacheClass.getDeclaredMethod("CheckCacheSizeViaJava7", (Class[]) null);
		getJava7CacheApplication.invoke(null, (Object[]) null);
	}
	catch(Exception x2)
	{
		MapTool.showWarning("Unable To Load Java7+ CacheCleaner Implementation.");
		CacheCleanerJava6.CheckCacheSizeViaJava6();
	}
}
else
{
	CacheCleanerJava6.CheckCacheSizeViaJava6();
}
However when I run this on my Ubuntu Java 6 VM, I still get the same error:
lordashes@lordashes-VirtualBox:~/Documents/MapTool_CGE_3$ java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
lordashes@lordashes-VirtualBox:~/Documents/MapTool_CGE_3$ ./LaunchMapTool.sh
Executing maptool-1.3.b89.CGEwS.v3.2.jar ...
Exception in thread "main" java.lang.UnsupportedClassVersionError: net/rptools/maptool/client/LaunchInstructions : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
Could not find the main class: net.rptools.maptool.client.LaunchInstructions. Program will exit.
I checked LaunchInstructions.java and all it seems to do is setup the memory and run the main program. I tried increasing the amount of memory to MapTool (in the .SH file) but got the same results. I am confused as to what the major.minor version seems to be referring to. It can't be the Java version because that does not have 51.0 in it at all.

As I said, normally I would conclude that my Java installation is incorrect or not compatible but I am able to run the unmodified MapTool on it, so either my modification still uses some Java 7 code somewhere or something specific that I added is not implemented in the Java 6 implementation that I am using.
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

I googled the problem and some people say that this problem is causes when you compile the application using a JDK that is a newer version than the JRE you are using to run it on.

Since my development machine is using Java 7 (and has only Java 7 installed) but I am trying to get it running on Java 6 this would be, in my case, true.

It seems that in Eclipse you can select the compile compatibility level but you need the respective level JDK/JRE installed. I will see if I can get a Java 6 JDK/JRE installed and see if that resolves my issue.
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

Lee
Dragon
Posts: 958
Joined: Wed Oct 19, 2011 2:07 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lee »

If you're using Eclipse for Java EE, you can convert your project to faceted form and change the Java compiler compliance to Java 6. This is if you want to avoid installing multiple JDKs and JREs on your system. Just go into your project properties and on the left pane, you'll find the list item for "Project Facets". It should be straightforward from there. I hope this helps.

username
Dragon
Posts: 277
Joined: Sun Sep 04, 2011 7:01 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by username »

java.version gves you the exact version (including patch level) while java.specification.version gives you the specification the impleentation complies with. I don't know, whether OpenJDK uses a different java.version prefix. So, yes, the specification version is probably better.

With the exception you saw, Lee's suggestion is probably best. You can achieve the same thing with a javac switch that forces it to generate output for a 1.6 JVM. Note that you still need to do the checks, because the imports you rely on are available during compile time and not during runtime. But you can avoid the reflection API (and probably the try/catch). Just invoke the method directly.

Code: Select all

// Clean up the cache
if(MapTool.JAVA_VERSION>=1.7)
{
   CacheCleanerJava7.checkCacheSizeViaJava7();
}
else
{
   CacheCleanerJava6.checkCacheSizeViaJava6();
}
(Note that I have lowercased the method names according to convention.) Maybe you can even combine this into one class?

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

Lee wrote:If you're using Eclipse for Java EE, you can convert your project to faceted form and change the Java compiler compliance to Java 6. This is if you want to avoid installing multiple JDKs and JREs on your system. Just go into your project properties and on the left pane, you'll find the list item for "Project Facets". It should be straightforward from there. I hope this helps.
Thanks for the suggestion...I will give it a try.

Edit:

I checked and it seems that I am running Eclipse for Java Development and not Eclipse for Java EE Development.

I think the easiest solution would be for me to get a Java 6 JDK for the VM that I am testing my Java 6 stuff on. This way I don't need to touch my Java 7 development workstation, I can keep the Eclipse version that I have now, but still compile to 1.6 (Java 6) compatibility.

I guess the alternative would be to download Eclipse for Java EE and use that instead of my current version.
Last edited by Lord.Ashes on Tue Sep 10, 2013 10:47 pm, edited 2 times in total.
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

username wrote:java.version gves you the exact version (including patch level) while java.specification.version gives you the specification the impleentation complies with. I don't know, whether OpenJDK uses a different java.version prefix. So, yes, the specification version is probably better.
Good to know.
username wrote:With the exception you saw, Lee's suggestion is probably best. You can achieve the same thing with a javac switch that forces it to generate output for a 1.6 JVM. Note that you still need to do the checks, because the imports you rely on are available during compile time and not during runtime. But you can avoid the reflection API (and probably the try/catch). Just invoke the method directly.

Code: Select all

// Clean up the cache
if(MapTool.JAVA_VERSION>=1.7)
{
   CacheCleanerJava7.checkCacheSizeViaJava7();
}
else
{
   CacheCleanerJava6.checkCacheSizeViaJava6();
}
Maybe I am misunderstanding but I was under the impression that as soon as you include a class that uses Java 7 in an application, the application will refuse to run on a Java 6 machine regardless if the application execution actually reaches a point that instances the class. This is what I understood from the comments in the MapTool.java source code file. This was the reason why Azhrei suggested using the Class<?> method invoking (as is used in MapTool.java for a slightly different purpose) in the first place.
username wrote:(Note that I have lower cased the method names according to convention.) Maybe you can even combine this into one class?
Thanks...I should try more to be consistent with conventions...
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

Lee
Dragon
Posts: 958
Joined: Wed Oct 19, 2011 2:07 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lee »

Lord.Ashes wrote: Edit:

I checked and it seems that I am running Eclipse for Java Development and not Eclipse for Java EE Development...

...I guess the alternative would be to download Eclipse for Java EE and use that instead of my current version.
You should still try to see if your Eclipse already has this feature. I'm going with what I know from way back when I started using Eclipse. For all I know, they might have gone ahead in the interim and put the feature in all Eclipse versions for Java, not just EE.

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Azhrei »

Lord.Ashes wrote:Maybe I am misunderstanding but I was under the impression that as soon as you include a class that uses Java 7 in an application, the application will refuse to run on a Java 6 machine regardless if the application execution actually reaches a point that instances the class.
Yep, you're correct. Unfortunately.

Your use of a method name that starts with a capital letter threw me off -- standard convention in Java is class names are capitalized but method names are camelback and start with a lowercase letter. :)

It looks like your code should work. When you single-step it, which line does it die on? If the JVM crashes with a core dump, there's clearly a problem inside the JVM. Likely a bug in the C implementation of the various functions.

User avatar
Lord.Ashes
Dragon
Posts: 350
Joined: Wed Jul 03, 2013 5:58 am

Re: MapTool - Development - Java 6 vs Java 7 coding

Post by Lord.Ashes »

Okay. I got Eclipse installed in my Ubuntu Java 6 setup along with a Java 6 JDE.

I compiled the unmodified MapTool source code no problem and was able to run it.

However, when I added the code modification I was no longer able to compile and/or run it because it contained the Java 7 code.

So I am stuck...

If I compile it in Java 7 then it won't run under a Java 6 JRE (even if I avoid the Java 7 code by checking version or using exceptions).
If I compile it using Java 6 then I can not include any Java 7 code.

I am starting to think that the easiest solution to to compile the Java 6 version with the java 6 code and the Java 7 code with the Java 7 compiler and then create a script that will use the appropriate version.

I was really starting to warm up to Java until this compatibility issue came to light. I guess if you either stick to writing all Java 6 or all Java 7 code then you don't have this issue (assuming you compile with the corresponding compiler version) but I find it difficult to believe that it isn't a more common question how to leverage some Java 7 functionality if the user has it but still be able to run it on Java 6.
"We often compare ourselves to the U.S. and often they come out the best,
but they only have the right to bare arms, while we have the right to bare breasts"
The Right To Bare Breasts by Bowser & Blue

Post Reply

Return to “Developer Notes”