Let me set the scene briefly. I have full visibility of, and total control over my development environment; happy days. However, when it comes to UAT and Production environments I'm flying completely blind and at times I feel like I'm performing keyhole surgery with only a sledge hammer and a forklift, oh, and no keyhole.
I spent some time putting together a stack that I could use as a template, a reusable pattern where, from application to application I could recycle the initial pipework and pretty much only change the data model. What I needed was something completely stateless, all I really need was CRUD, so I opted for a REST based approach; and for me that meant reference implementation, Jersey. The other thing was that I strived to make it as simple to deploy as possible; essentially a WAR to be dropped into the application server that could then update the persistence layer when the application server started up.
Everything went swimmingly. Well, that's a bit of a lie, I found that some browsers still do not support PUT and DELETE.
RESTful Browser Verb Check
Browser | GET | POST | PUT | DELETE |
FF7 | Yes | Yes | Yes | Yes |
Chrome14 | Yes | Yes | Yes | Yes |
IE8 | Yes | Yes | Converts to GET | Converts to GET |
Anyhoo, I readied myself to deploy to the UAT environment. This consisted of listing everything component and detailing every step of configuration. As this was the bedding-in phase I needed the entire environment configured, which included the installation of the JRE, application server, database and the application. This could have been smoother if I had access to the boxes, but nevertheless we got there in the end.
So now I have my environment set up and my application deployed, let's kick the tyres. To my horror I got a 500 back from the service. Dang. I didn't have access to the logs and the only person with access was on leave that day. So I waited.
After a bit more waiting, I finally got a hold of the logs. And found the culprit, the little blighter was sticking out like a strawberry in a bowl of peas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SEVERE: Allocate exception for servlet Jersey REST Service | |
java.lang.UnsupportedClassVersionError: OBFUSCATED_PACKAGE_NAME : Unsupported major.minor version 51.0 (unable to load class OBFUSCATED_CLASS_NAME) | |
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1851) | |
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:890) | |
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1354) | |
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233) | |
at java.lang.Class.forName0(Native Method) | |
at java.lang.Class.forName(Class.java:247) | |
at com.sun.jersey.core.reflection.ReflectionHelper.classForNameWithException(ReflectionHelper.java:236) | |
at com.sun.jersey.spi.scanning.AnnotationScannerListener$AnnotatedClassVisitor.getClassForName(AnnotationScannerListener.java:214) | |
at com.sun.jersey.spi.scanning.AnnotationScannerListener$AnnotatedClassVisitor.visitEnd(AnnotationScannerListener.java:183) | |
at org.objectweb.asm.ClassReader.accept(Unknown Source) | |
at org.objectweb.asm.ClassReader.accept(Unknown Source) | |
at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133) | |
at com.sun.jersey.core.spi.scanning.uri.FileSchemeScanner$1.f(FileSchemeScanner.java:86) | |
at com.sun.jersey.core.util.Closing.f(Closing.java:71) | |
at com.sun.jersey.core.spi.scanning.uri.FileSchemeScanner.scanDirectory(FileSchemeScanner.java:83) | |
at com.sun.jersey.core.spi.scanning.uri.FileSchemeScanner.scan(FileSchemeScanner.java:71) | |
at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:223) | |
at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:139) | |
at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80) | |
at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104) | |
at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78) | |
at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89) | |
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:700) | |
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678) | |
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203) | |
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373) | |
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556) | |
at javax.servlet.GenericServlet.init(GenericServlet.java:212) | |
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1172) | |
at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:808) | |
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:129) | |
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) | |
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) | |
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) | |
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) | |
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286) | |
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845) | |
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) | |
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) | |
at java.lang.Thread.run(Thread.java:662) |
I've seen you before, I thought, and fired off a mail to the box admin (who was WFH; I'd ideally walk round and discuss face-to-face) to verify the JRE running on the UAT box. Some time later it was confirmed UAT was running JDK-Y. I was using JDK-X, so the UAT environment was running an earlier version than that which I build the WAR with - makes perfect sense and matches exactly to the exception. Fine:
- Download/Install JDK-Y
- Change JDK-X to JDK-X (Preferences -> Java -> Compiler)
- Clean project
- Rebuild
- Redeploy locally
- Redeploy UAT
- Let's change the Change JRE System Libraries (Right-click project -> Properties -> Java Build Path -> JRE System Library -- double-click and change to JDK-Y)
- Clean project
- Rebuild
- Redeploy locally
- Redeploy UAT
I Googled it. Every post said the same thing about the runtime and compile time JDKs being different, but I know that and I've told Eclipse. I checked that the Jersey binaries - built with Java SE Y. I've told Eclipse I want to use JDK-Y. How many times do I have to tell it? OK, I thought, everywhere I see JDK-X, I'll change it to JDK-Y, even if it seems irrelevant.
- Change Target Platform (Window -> Preferences -> Plug-in Development -> Target Platform -- double-click and change JRE name under the Environment tab
- Change Project Facet (Right-click project -> Properties -> Project Facets -> Java -> Select JDK-Y from drop down
- Clean project
- Rebuild
- Redeploy locally
- Redeploy UAT
SUCCESS
The moral of the story is, well I don't really know. What I do know is, it is annoying that you have to tell Eclipse four times in four totally different locations that you want to use a different JRE, and it's difficult to debug an application in an environment when you don't have access to said environment.