#Override for interface methods causes JSP compilation to fail - java

For some reason, putting #Override on methods overriding interface methods causes JSP compilation to fail in weblogic. Everything is definitely running off of JDK 1.6.0_14, but this Java 5'ism still persists.
Oddly, JSP compilation occasionally fails with a stacktrace pointing at code not necessarily obviously used by the JSP itself.
What's going on here?

The #Override is supposed to only be retained in source so shouldn't come up in byte code. There might an angle you can play in ensuring those classes are complied separately from the JSPs and simply available in the classpath -- rather than the source path.
If that's already the case then it might be a different issue than what is immediately showing.
Used to be the JSP complier was a separate library shipped with the server and not tied to the vm the server is running in. WLS used to use Javelin. Seems like they switched that in 10 to use the Java Compiler API. So it should work fine as long as you have Sun vm Java 1.6. But if there's 'javelin' anything in your stacktrace, definitely check that angle.

I've seen this a lot myself. In Java 6, it is (supposedly) permissible to use #Override on interface implementation methods. In Java 5, this is an error. However in my Java 6 code, sometimes #Override is accepted on interface implementation methods, and sometimes it is not.
To make things weirder, some IDEs (e.g. NetBeans) are fine with it, while IntelliJ IDEA is sometimes ok and sometimes not. I have found, however, that compiling the code in either IDE will ignore the alleged errors being reported by the IDE.
In other words, is the problem manifesting in your IDE? If so, compile the code directly (use the command-line if necessary) and see what happens. The IDE may be reporting spurious errors.

A possible workaround might be to precompile JSP using appc. This could at least allow to circumvent the issue.
Regarding the "real" question, my understanding is that you did upgrade domains, so maybe have a look at the following resources:
Web Applications, JSPs, and Servlets
Backwards Compatibility Flags
Procedure for Upgrading a WebLogic Domain
Select Upgrade Options (related to domain upgrade)
Backward Compatibility Flags
JSPCompilerBackwardsCompatible - Specifies whether to allow JSPs that do not comply with the JSP 2.0 specification
This is a wild guess but maybe some backward compatibility flag is activated and WebLogic keeps using the "old" approach.

I agree with your instinct and the other answers that WLS is using Java 5 somehere. The items below seem like useful tidbits from Oracle/WebLogic resources. I don't have a WebLogic Server 10.3 installation to confirm these:
Weblogic Server 10.3
According to this, at least Weblogic Server 10.3 is required to use Java 6, but I can't see anything the confirms this as authoritative info:
http://forums.oracle.com/forums/thread.jspa?threadID=884263
Re: WebLogic 10.0 supports Java 6?
Posted: Apr 9, 2009 12:26 PM in response to: user8324142
Hi,
Weblogic 10 will not support JDK6.
Please upgrade to Weblogic 10.3 to work with JDK 6.
Checking the Java version
http://download.oracle.com/docs/cd/E12840_01/common/docs103/install/postins.html#wp1090736
Determining Which JDK Version You Are Using
You can determine which version of the JDK you are using by issuing a command, as follows:
Open a command prompt window and go to the appropriate directory:
BEA_HOME\WL_HOME\server\bin (Windows)
BEA_HOME/WL_HOME/server/bin (UNIX)
In both pathnames, BEA_HOME represents the directory in which you have installed your software and WL_HOME represents the wlserver_< version >.
Make sure that your environment is set up properly by entering the following command at the prompt:
setWLSenv.cmd (Windows)
setWLSenv.sh (UNIX)
Enter the following command at the prompt:
java -version
Configuring Java version:
http://download.oracle.com/docs/cd/E12840_01/wls/docs103/perform/WLSTuning.html
Setting Java Parameters for Starting WebLogic Server
... Java parameters must be specified whenever you start WebLogic Server. ... Oracle recommends that you incorporate the command into a [startup] script ...
If you used the Configuration Wizard to create your domain, the WebLogic startup scripts are located in the domain-name directory where you specified your domain. By default, this directory is BEA_HOME\user_projects\domain\domain-name, where BEA_HOME is the directory that contains the product installation, and domain-name is the name of the domain directory defined by the selected configuration template. ...
Change the value of the variable JAVA_HOME to the location of your JDK. For example:
set JAVA_HOME=C:\bea\jdk150_03
...

In my mind, #Override makes sense on methods that are overriding methods, not implementing methods.
So if you have an interface:
public interface MyInterface {
public void doSomething();
}
A class that implements that interface is below (MyClassA):
public MyClassA implements MyInterface {
public void doSomething() {
System.out.println("This is from MyClassA");
}
}
Then, the below class extends MyClassA and overrides doSomething, and so then I'd add the #Override annotation.
public MyClassB extends MyClassA implements MyInterface {
#Override
public void doSomething() {
System.out.println("This is from MyClassB");
}
}
I wouldn't do the below (whether or not it is permissable) in any case, since it breaks the idea about overriding something - you're implementing, not overriding the interface:
public MyClassA implements MyInterface {
#Override
public void doSomething() {
System.out.println("This is from MyClassA");
}
}

Related

Replacing a class with another using Eclipse Transforms: can't fix ClassFormatError

I'm using Equinox Transforms to replace one class with a custom version of it on the classpath. The transforms mechanism seems to be working properly, and I return an InputStream of the custom class from public InputStream getInputStream(InputStream inputStream, URL transformerUrl) of my transformer class.
But the loading of that class then results in a ClassFormatError with message Truncated class file. The only thing I can imagine causing this is a mismatch between Java versions, but I've done everything I know how to do to eliminate that as a source of the problem, to no avail.
I removed all but one JDK from Eclipse in the Installed JREs preference. I made sure all the plug-ins involved have the same Java version for their execution environment and the JRE on the build path is specified with the same execution environment. Target and product execution environments are defined the same also. I did a clean all in the workspace and re-launched eclipse and my runtime workbench many times, with -clean. After all this, I still get the error.
What else could it be if not Java versions causing this error? The Equinox Transforms page says it can be used to transform class resources. Has anyone actually done that?
there is a more standardized way to replace a class in osgi, through a WeavingHook. This hook should be registered as a service, and have the possibility to dynamically enhance a class.
See the WeavingHook Javadoc or a concrete example with Aries Proxy, which dynamically create proxies with the help of asm (WovenProxyGenerator).

Replace a class within the Java class library with a custom version

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.

JDBC incompatibility error

I have a legacy code written in older version of java. I am trying to compile the code and generate an .ear file using ant. The java version i am using is jdk7/jdk6 as java home .
On executing the ant script, I am getting following error
Compiling 20 source files to C:\views\kroger\kroger\of_platform\build\oneforce\classes\ra
warning: [options] bootstrap class path not set in conjunction with -source 1.4
C:\views\kroger\kroger\of_platform\sdk\src\java\ra\com\workscape\connector\wedb\
JdbcConnectionImpl.java:81: error: JdbcConnectionImpl is not abstract and does not override abstract method getNetworkTimeout() in Connection
public class JdbcConnectionImpl implements JdbcConnection {
C:\views\kroger\kroger\of_platform\sdk\src\java\ra\com\workscape\connector\wedb\
JdbcDataSource.java:78: error: JdbcDataSource is not abstract and does not override abstract method getParentLogger() in CommonDataSource
public class JdbcDataSource
I am assuming this error is because of certain jars not compatible with newer jdk version.
This works good when I use jdk1.4/1.5 .. Pls let me know if i need to take any latest version of jar.. The database used is oracle and i am using ojdbc14 for compilation of the database java code.
Its kind of important as we have to decide which version of java we need to use.
Thanks
Java 1.7 has introduced few new methods in CommonDataSource & Connection interfaces. The ones for which you are seeing the error are:
getParentLogger()
getNetworkTimeout()
Also, keep in mind that there are more new methods in Java 1.7 than the above two stated ones.
If you want to use Java 1.7, then your custom classes, JdbcConnectionImpl & JdbcDataSource (or their super classes) should implement the new methods defined by Connection & CommonDataSource, respectively.

How to create a *standalone* linux executable from java code

I know that java intent is for gui and multi platform, but the problem I'm facing it how to release a java application into a linux servers, which I don't have control on, i.e. I dont know what java vm is installed if at all.
So, how do i compile this into a true standalone linux exe, do not assume any pre installed package on the target linux.
public class MyTest {
public static void main(String[] args) {
System.out.println("You passed in: " + args[0]);
}
}
You need to specify as a requirement an installed JRE.
Otherwise you would need to deliver a JRE yourself as part of the deliverable application
The GNU COmpiler for Java does exactly this. Keep in mind that it will work properly only for small programs, either way you'll need a JVM.
There's also Avian, which has another approach and allows to deploy a lightweight JVM with jour application, but it still hasn't all the features of a full JRE.
Creating one binary from java may not be that good. You may consider tools like http://launch4j.sourceforge.net/ for creating a full installation along with appropriate jre.

Why is javac failing on #Override annotation

Eclipse is adding #Override annotations when I implement methods of an interface. Eclipse seems to have no problem with this. And our automated build process from Cruise Control seems to have no problem with this. But when I build from the command-line, with ant running javac, I get this error:
[javac] C:\path\project\src\com\us\MyClass.java:70: method does not override a method from its superclass
[javac] #Override
[javac] ^
[javac] 1 error
Eclipse is running under Java 1.6. Cruise Control is running Java 1.5. My ant build fails regardless of which version of Java I use.
The #Override annotation spec changed in Java 1.6. In Java 1.5, the compiler did not allow the #Override annotation on implemented interface methods, but in 1.6 it does. First search result I found is a blog post here.. It was not well documented, but it did change.
Eclipse is adding it because your Eclipse is set for 1.6 compliance. You should try to keep your build and eclipse environments on the same version of Java. It's unclear to me by your specifying Cruise Control is running Java 5 on whether or not it is compiling using a separate JDK6 or not.
Separate from the above 1.5 vs 1.6 #Override annotation rules, remember that Eclipse has its own compiler implementation (not javac) and will occasionally have different behavior. Whenever something compiles in Eclipse, but not Ant or Maven, you will need to find a way to make both compilers happy.
I can't really explain the problem you're seeing but it seems to be related to the fact that JDK 5 will not allow #Override on implemented methods of an interface, only on overridden methods present in a super class.
JDK 6 will allow #Override on any of them.
If your ant build fails it may be passing a source parameter to javac, asking for JDK 5 compliance.
The direct answer to the question "Why" an error is raised by javac when #Override is used in the context of a method implementation is actually in the java specifications:
"The rationale for this is that a concrete class that implements an interface will necessarily override all the interface's methods irrespective of the #Override annotation, and so it would be confusing to have the semantics of this annotation interact with the rules for implementing interfaces."
See http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.6.1.4
But apparently someone changed his mind for java 1.6 and 1.5 u21...
#Override tags for implemented methods are new to Java 1.6. In Java 1.5 #Override is only correct when overriding a method in a base class. Read more here and here.
A lot of people, including me, got busted by this. See here for a bigger SO discussion
Eclipse would be pointing to 1.6 version of Java rather than 1.5.
See here for configuring java version in eclipse.
Ensure that there is only one definition of that interface.
Example:
HttpServletRequest
This is an interface with different specs depending on provider.
Compare pax-web-jetty and apache-felix-jetty. They have different methods.
I have had the same problem when building a project with ANT. The solution to the problem was to change the following property inside the build.properties file:
javac.compiler=org.eclipse.jdt.core.JDTCompilerAdapter
to:
javac.compiler=modern
That solved the problem and the project got compiled and deployed successfully.

Categories