How to avoid java.lang.NoSuchMethodError on earlier java versions - java

I got the following error today:
java.lang.NoSuchMethodError: javax.swing.JList.getSelectedValuesList()Ljava/util/List;
We have some customers still using old java6 versions out there. (Some old posready/embedded-version of windows that doesn't accept installing 1.8 directly..)
Therefore I use compiler compliance level 1.6 in eclipse. However, after an upgrade of our software, some customers reported errors/freezing. I logged in, and found the following error:
java.lang.NoSuchMethodError: getSelectedValuesList()Ljava/util/List;
It appears that this particular JList method in was introduced in 1.7, and I have started using it since getSelectedValues() is deprecated.
But this breaks things on 1.6, since the method does not exist in 1.6.
I have made a workaround, instead of calling getSelectedValuesList() I now call the following method:
public static <E> java.util.List<E> getSelectedValuesList(javax.swing.JList<E> l) {
try {
return l.getSelectedValuesList();
}catch(java.lang.NoSuchMethodError err) {
ArrayList<E> v = new ArrayList<E>();
Object[] oo = l.getSelectedValues();
for (Object o : oo) {
v.add((E)o);
}
return v;
}
}
This seems to work ok. But my question is, since source level is 1.6, how do I detect similar errors? Since I don't even have 1.6 installed, how can I know for sure that all my swing methods that I call actually works in 1.6? I don't want to introduce yet another bug later on. :-)

Related

Java code in different build environments [duplicate]

My project requires Java 1.6 for compilation and running. Now I have a requirement to make it working with Java 1.5 (from the marketing side). I want to replace method body (return type and arguments remain the same) to make it compiling with Java 1.5 without errors.
Details: I have an utility class called OS which encapsulates all OS-specific things. It has a method
public static void openFile(java.io.File file) throws java.io.IOException {
// open the file using java.awt.Desktop
...
}
to open files like with double-click (start Windows command or open Mac OS X command equivalent). Since it cannot be compiled with Java 1.5, I want to exclude it during compilation and replace by another method which calls run32dll for Windows or open for Mac OS X using Runtime.exec.
Question: How can I do that? Can annotations help here?
Note: I use ant, and I can make two java files OS4J5.java and OS4J6.java which will contain the OS class with the desired code for Java 1.5 and 1.6 and copy one of them to OS.java before compiling (or an ugly way - replace the content of OS.java conditionally depending on java version) but I don't want to do that, if there is another way.
Elaborating more: in C I could use ifdef, ifndef, in Python there is no compilation and I could check a feature using hasattr or something else, in Common Lisp I could use #+feature. Is there something similar for Java?
Found this post but it doesn't seem to be helpful.
Any help is greatly appreciated. kh.
Nope there isn't any support for conditional compilation in Java.
The usual plan is to hide the OS specific bits of your app behind an Interface and then detect the OS type at runtime and load the implementation using Class.forName(String).
In your case there no reason why you can't compile the both OS* (and infact your whole app) using Java 1.6 with -source 1.5 -target 1.5 then in a the factory method for getting hold of OS classes (which would now be an interface) detect that java.awt.Desktop
class is available and load the correct version.
Something like:
public interface OS {
void openFile(java.io.File file) throws java.io.IOException;
}
public class OSFactory {
public static OS create(){
try{
Class.forName("java.awt.Desktop");
return new OSJ6();
}catch(Exception e){
//fall back
return new OSJ5();
}
}
}
Hiding two implementation classes behind an interface like Gareth proposed is probably the best way to go.
That said, you can introduce a kind of conditional compilation using the replace task in ant build scripts. The trick is to use comments in your code which are opened/closed by a textual replacement just before compiling the source, like:
/*{{ Block visible when compiling for Java 6: IFDEF6
public static void openFile(java.io.File file) throws java.io.IOException {
// open the file using java.awt.Desktop
...
/*}} end of Java 6 code. */
/*{{ Block visible when compiling for Java 5: IFDEF5
// open the file using alternative methods
...
/*}} end of Java 5 code. */
now in ant, when you compile for Java 6, replace "IFDEF6" with "*/", giving:
/*{{ Block visible when compiling for Java 6: */
public static void openFile(java.io.File file) throws java.io.IOException {
// open the file using java.awt.Desktop
...
/*}} end of Java 6 code. */
/*{{ Block visible when compiling for Java 5, IFDEF5
public static void openFile(java.io.File file) throws java.io.IOException {
// open the file using alternative methods
...
/*}} end of Java 5 code. */
and when compiling for Java 5, replace "IFDEF5". Note that you need to be careful to use // comments inside the /*{{, /*}} blocks.
You can make the calls using reflection and compile the code with Java 5.
e.g.
Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);
You can catch any exceptions and fall back to something which works on Java 5.
The Ant script introduced below gives nice and clean trick.
link: https://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html
in example,
//[ifdef]
public byte[] getBytes(String parameterName)
throws SQLException {
...
}
//[enddef]
with Ant script
<filterset begintoken="//[" endtoken="]">
<filter token="ifdef" value="${ifdef.token}"/>
<filter token="enddef" value="${enddef.token}"/>
</filterset>
please go to link above for more detail.
In java 9 it's possible to create multi-release jar files. Essentially it means that you make multiple versions of the same java file.
When you compile them, you compile each version of the java file with the required jdk version. Next you need to pack them in a structure that looks like this:
+ com
+ mypackage
+ Main.class
+ Utils.class
+ META-INF
+ versions
+ 9
+ com
+ mypackage
+ Utils.class
In the example above, the main part of the code is compiled in java 8, but for java 9 there is an additional (but different) version of the Utils class.
When you run this code on the java 8 JVM it won't even check for classes in the META-INF folder. But in java 9 it will, and will find and use the more recent version of the class.
I'm not such a great Java expert, but it seems that conditional compilation in Java is supported and easy to do. Please read:
http://www.javapractices.com/topic/TopicAction.do?Id=64
Quoting the gist:
The conditional compilation practice is used to optionally remove chunks of code from the compiled version of a class. It uses the fact that compilers will ignore any unreachable branches of code.
To implement conditional compilation,
define a static final boolean value as a non-private member of some class
place code which is to be conditionally compiled in an if block which evaluates the boolean
set the value of the boolean to false to cause the compiler to ignore the if block; otherwise, keep its value as true
Of course this lets us to "compile out" chunks of code inside any method. To remove class members, methods or even entire classes (maybe leaving only a stub) you would still need a pre-processor.
if you don't want conditionally enabled code blocks in your application then a preprocessor is only way, you could take a look at java-comment-preprocessor which can be used for both maven and ant projects
p.s.
also I have made some example how to use preprocessing with Maven to build JEP-238 multi-version JAR without duplication of sources
Java Primitive Specializations Generator supports conditional compilation:
/* if Windows compilingFor */
start();
/* elif Mac compilingFor */
open();
/* endif */
This tool has Maven and Gradle plugins.
hi I have got similar problem when I have shared library between Java SDK abd Android and in both environments are used the graphics so basically my code must to work with both
java.awt.Graphics and android.graphics.Canvas,
but I don't want to duplicate almost any code.
My solution is to use wrapper, so I access to graphisc API indirectl way, and
I can change a couple of imports, to import the wrapper I want to compile the projects.
The projects have some cone shaded and some are separate, but there is no duplicating anything except of couple of wrappers etc.
I think it is the best what I can do.

ClassCastException: java.lang.Object incompatible with com.company.base.BaseDocument

I have a piece of code that has to be JDK 1.4 compliant. And below is a snippet which gets a runtime exception.
BaseDocument baseDocument = new BaseDocument();
baseDocument.setGuid("{somethinghere}");
List document = new ArrayList();
document.add(baseDocument);//runtime error
Exception:
java.lang.ClassCastException: [Ljava.lang.Object; incompatible with [Lcom.company.base.BaseDocument;
I don't understand why we cant cast it to a java Object(since Object is the parent class of all Classes in java).
Im using IBM JDK version 1.7 with Eclipse compiler settings set to JDK 1.4
Please explain what mistake I'm doing here. I know generics is the standard, but it has to be JDK 1.4 compliant :(
Thanks in advance!
Actually, the message says:
[Ljava.lang.Object; incompatible with [Lcom.company.base.BaseDocument;
The [ characters are very important. Apparently, something is attempting cast something of type Object[] to BaseDocument[]; i.e. you are dealing with array types here.
However, I have no idea what is actually causing the problem here, because (on the face of it) there should be no instances of BaseDocument[] in the code you have posted. Furthermore, I don't believe your claim that that exception is thrown at that line. Here is the source code of the ArrayList.add method (Java 6 version):
private transient Object[] elementData;
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
An assignment of a reference into an object array (Object[]) does not entail any runtime type-checks, and cannot throw a ClassCastException.
If you showed us a complete stacktrace, it would be easier to diagnose.

Incoparable Java Types boolean and object

I have this piece of code which worked in Java 6
if(false==sess.getAttribute("admin") || null==sess.getAttribute("admin"))
{
res.sendRedirect("/myapp/login.jsp?errmsg=You must log in as an administrator to manage resources");
return;
}
I want to familiarize myself with the new features of Java 7 and what it offers new in javaee hence I upgraded netbeans to 7.4 and jdk 7u45.
My project opened successfully however the file with this code is marked as a java class with an error by netbeans. Running the project I get the error
java.lang.RuntimeException: Uncompilable source code - incomparable types: boolean and java.lang.Object
What I want to ask is why this worked in java6 and doesn't work in java7
FYI HttpSession sess = req.getSession();
HttpSession.getAttribute returns an Object.
Change the line to (Boolean.FALSE==sess.getAtrribute)
Looks like autoboxing (boolean to Boolean) has changed in Java.
Unfortunately I cannot reproduce your error. I have seen a similar error message in the same Netbeans 7.4/jdk 7u45.
My code:
if (true == evt.getNewValue())
(where evt is a PropertyChangeEvent)
The message in Netbeans (when I hover over the red cross left to the statement) says "incomparable types: boolean and Object". Notice the difference with your message: the missing "java/lang" before Object.
And even stranger: my code compiles without any problem.
Compiler options in Netbeans are: "-version -Xlint:unchecked -Xlint:deprecation"
You should try with
if(false==(Boolean)sess.getAttribute("admin") || null==sess.getAttribute("admin"))
{
res.sendRedirect("/myapp/login.jsp?errmsg=You must log in as an administrator to manage resources");
return;
}
getAttribute probably returns either an Object or a boolean. If it returns a boolean, then null==getAttrubute will not work and if it returns an Object then false==getAttribute will fail.

Error debugging CGLIB FastClass on eclipse

I'm trying to debug an java eclipse project with some problem!
I'm starting using CGLIB to make faster reflection calls using the index metod.
example
FastClass fastClass = FastClass.create(getClass());
int index = fastClass.getIndex("methodName", new Class[] { Object.class });
fastClass.invoke(index, this, new Object[] { obj } );
now when i try to put a breakpoint into a class that is called by fastreflection method this is the eclipse output.
I try to change compiler option on generate line number with no results.
I also upload an eclipse project (built with Juno version) that replicates the problem!!
http://www.filefactory.com/file/4zryz3gjgbyh/n/FastDebug.rar
Thanks!
I "resolved"(understand) the problem, but it is not a problem with Eclipse. When you launch
the program this line: FastClass.create(ReflectionTarget.class); ends up
creating an entirely new version of the compiled class removing all
non-essential stuff from the classfile to make it "fast" - that includes all
the line number / debug infos, which means the breakpoint cannot be set in it.
http://cglib.sourceforge.net/xref/net/sf/cglib/core/package-summary.html
There's no javadoc and you need to read the source but now i understand this is not a
problem but a feature of this method to make fast reflection!

Possible to only load specific lines of code according to Android OS version?

Is there a simple line of code that would allow only loading the code if the OS version meets the requirements?
Lets say I have my target OS as 2.2 but the min sdk is 3 for android 1.5 so even if i have some code in my project that isn't compatable with 1.5 it will still compile since the target OS is 2.2. Anyway, I want to ad a feature that requires code that's not in the 1.5 SDK and will cause a crash if it's loaded on a 1.5 phone. Is there a simple thing like this that I can do? So i dont have to make the entire app not available to 1.5 users?
if (Android OS == >2.1){
//Insert code here that requires 2.1 and up}
else{
//insert code that would appear is OS is <2.1}
Yes, you can do that. In fact there is more than one way. (Note: the only Android specific part of this answer is the way that you find out the platform version.)
Suppose that class X has method void y() in version 2.0 onwards, but not before.
One way to invoke this method with out introducing any compile time dependencies whatsoever is to use reflection to locate the Method and call invoke on it. For example:
X x = ...
if (BUILD.VERSION.RELEASE.compareTo("2.0") >= 0) {
// (exception handling omitted ...)
Method m = c.getClass().getDeclaredMethod("y");
m.invoke(x);
}
Another way is to create a version compatibility adapter API for your application like this:
/** Version compatibility adapter API */
interface Compat {
void doY();
}
/** Adapter class for version 1 */
class CompatV1 {
public void y(X x) {
// do nothing
}
}
/** Adapter class for version 2 */
class CompatV2 {
public void y(X x) {
x.y();
}
}
//
// Code to instantiate the relevant adapter for the current platform.
//
Class<?> compatClass;
// (Exception handling omitted)
if (BUILD.VERSION.RELEASE.compareTo("2.0") < 0) {
compatClass = Class.forName("...CompatV1");
} else {
compatClass = Class.forName("...CompatV2");
}
// (Exception handling omitted)
Compat compat = (Compat) compatClass.newInstance();
// The adapter object can be passed around as a parameter, wrapped
// as a singleton or injected using dependency injection.
// Invoke X.y() as follows:
X x = ...
compat.y(x);
The second version looks a bit heavyweight, but it has the advantages that the dynamic (slow, non-type-safe) code is executed just once, and that the version specific code is isolated from the rest of the code. In real life, you would probably put a number of methods into the adapter interface.
This approach requires a bit more thought, to work out how to design the compatibility API so that it cleanly isolates the version dependencies from the rest of the code. You might also to have to revise the adapter API, and create new adapter classes for each new (incompatible) major release.
Finally, if the platform API changes that you need to adapt to entail using classes or methods in the older version that are removed in the newer version, then you will need to compile your various adapter classes (e.g. the CompatV* classes) using different Android SDKs. This will make your build processes rather more complicated.
For other "takes" on this problem, read the following articles on the Android Blog:
Backward compatibility for Android applications
How to have your (Cup-)cake and eat it too.
Here is another example w/o reflection
http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html
See Backward compatibility for Android (using Reflection).
You can check with Build.VERSION.RELEASE, it gives you the current version of your android system (1.5,1.6,2.1,2.2)
There is more on Build.VERSION

Categories