If I put a Java 8 Lambda expression in a REST service, it crashes. If I remove the lambda expression, it works. It does not matter if I use the lambda expression or not. Just the existence the lambda is enough to crash. Everything else Java 8 related seems to work.
Below is my code (simplified):
#Path("finance")
public class FinanceRest {
#GET
#Produces("text/plain")
public String speak() {
return "Hello world.";
}
private void lambdaFunction(Predicate<Account> predicate) {
// Any lambda will cause problems, no matter how simple
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Stream<Integer> onlyOdds = numbers.stream().filter(n -> n%2 != 0);
}
}
As you can see from the code above, just the existence of a lambda expression will cause failure. As soon as I remove the lambda, it works fine. The other Java 8 stuff is fine (for example, the "Predicate" input parameter).
The error message I'm getting is:
java.lang.ArrayIndexOutOfBoundsException: 25980
I've tried this on Tomcat 7 and 8 using Java 8.
I'm using the standard jax-rs stuff from JavaEE 6.... in other words my POM file has this:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
Any help would be appreciated.
Thanks.
The exact error message (on Glassfish 4.0... I've tried both Tomcat and Glassfish) is:
java.lang.ArrayIndexOutOfBoundsException: 52264 at
org.objectweb.asm.ClassReader.readClass(ClassReader.java:2015) at
org.objectweb.asm.ClassReader.accept(ClassReader.java:469) at
org.objectweb.asm.ClassReader.accept(ClassReader.java:425) at
org.glassfish.hk2.classmodel.reflect.Parser$5.on(Parser.java:362) at
com.sun.enterprise.v3.server.ReadableArchiveScannerAdapter.handleEntry(ReadableArchiveScannerAdapter.java:165)
at
com.sun.enterprise.v3.server.ReadableArchiveScannerAdapter.onSelectedEntries(ReadableArchiveScannerAdapter.java:127)
at org.glassfish.hk2.classmodel.reflect.Parser.doJob(Parser.java:347)
at
org.glassfish.hk2.classmodel.reflect.Parser.access$300(Parser.java:67)
at
org.glassfish.hk2.classmodel.reflect.Parser$3.call(Parser.java:306)
at
org.glassfish.hk2.classmodel.reflect.Parser$3.call(Parser.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:266) at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:744)
I found the solution!
I was using Jersey 1.17.1. When I upgraded to 2.7 it worked. My pom file had the following:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.17.1</version>
<scope>compile</scope>
</dependency>
I removed those and added:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.7</version>
</dependency>
And of course I had to modify the web.xml file to have:
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>
Now everything is working well. The question is: Why were lambda expressions still failing when I removed them from the REST class and put them in a non-REST class? Just the fact I was including Jersey 1.x was enough to crash when using lambda expressions (whether or not an actual REST service was involved). But at any rate, I'm pleased the project is working again; I had been wanting to upgrade to the latest version of jax-rs & Jersey anyway so this forced me to do it (costing me several hours of work and need to explain to the "SCRUM master" why my estimate is off (don't get me started on that topic). Now if I can only figure out why Jersey 2 is returning XML when I told it to return JSON I'll be back on track.
Thanks everyone for your help!
Jersey 1.19 is compatible with JDK 1.8.0. Refer to
Jersey 1.19 Release summaryJDK8 support in Jersey 1.19Repackage ASM lib in Jersey 1.19
Please remove asm-3.1.jar since jersey-server-1.19.jar has asm 5.0 repacked in it.
The stacktrace shows that the class org.objectweb.asm.ClassReader.readClass gives an exception. I suppose this is a parser that Glassfish uses internally.
One of the reasons it would crash, is because it is not configured to handle the given input properly. In this case the given input is a lambda expression and it does not know how to handle it.
You will need to look for Java 8 bytecode (lambda) support for Glassfish and Tomcat. If it is not the issue, then it might be a bug in the parser that is internally used.
I had to upgrade spring to 4.3.6.RELEASE and junit to 4.12 before I got rid of this particular error when trying to run a junit test with java 1.8 after I had introduced lamdas.
In addition to all other answers,
On my system this problem is occured on Glassfish 4.0(build 89)
Solution;
I upgraded Glassfish to 4.1(build 13) and it solved that problem.
Related
This question already has answers here:
How do I import the javax.servlet / jakarta.servlet API in my Eclipse project?
(16 answers)
Closed 1 year ago.
I am learning how to create a servlet that takes HTML code and makes a program in my browser.
This code was supposed to output a simple welcome message in my browser via HTML. However, My eclipse IDE does not recognize any of the HTML commands in this code. It says that the imports are not accessible and that the HTTP variables can't be resolved to a type. I'm using tomcat version 10.0.12 and Java 13 for the code (I also have java 16 but eclipse only gave me the option to use 13). I've tried uninstalling and reinstalling Tomcat several times and adding servlet-api.jar but none of those changed a thing. I know there's an extra step I'm missing but I can't figure out what it is?
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.*;
public class WelcomeServlet extends HttpServlet {
#Override
protected void doGet( HttpServletRequest request,
HttpServletResponse response ) throws ServletException, IOException {
response.setContentType( "text/html" );
PrintWriter out = response.getWriter();
out.println( "<title>Welcome to Servlets!</title>" );
{...}
out.println( "</html>" );
out.close(); // close stream to complete the page
}
}
You've messed up your imports. It sounds like you're following a tutorial that's 20+ years old, a lot of what you're doing is extremely outdated. The web is a fairly fast moving environment; I strenously advise against using such old tutorials.
The correct import is import javax.servlet.http.HttpServletRequest;.
Some notes on what you're doing that are bad ideas:
Star imports aren't advised; it's a bit too easy to get confused about where things live or which type(s) you are fetching from where. Your IDE manages your imports for you; let it.
You don't want to write HTML inside strings inside your code. Use a templating engine such as Freemarker, Velocity, Thymeleaf, or Google Closure Templates. Alternatively, write static HTML with a ton of javascript (probably with a client-side javascript-based framework) and write your server as an API that doesn't 'answer' in terms of HTML files; it answers in terms of some structured data format such as JSON. Your static (as in, unchanging; any file-serving HTTP service can provide them) HTML+CSS+Javascript does the job of calling your API to get this structured data, and then your HTML+CSS+JavaScript does the job of rendering it into HTML.
Raw servlets is outdated; the API is extremely old and it shows, for example, you can't even use a simple for-each loop to iterate over all parameter names, because the API returns the obsolete Enumeration instead of the more modern Iterator or even Stream. You neither get the benefit of storing intermediate state in fields (because you aren't guaranteed one instance per invocation), but you also don't get to just dump it all in static fields either (as the spec doesn't guarantee that there'll only ever be one instance) - the worst of both worlds. Look into JAX-RS, Jersey, Dropwizard, sparkjava, or other such frameworks.
tl;dr
Your project lacks a JAR containing the definition of the Jakarta Servlet API (a set of interfaces).
Add jakarta.servlet-api as a dependency to your project, to present to your IDE the needed JAR file during development but omitted from your final build’s WAR file.
Details
Your import statements refer to the Jakarta Servlet API. That API is not built into Java. So you must make a Servlet API JAR file available to your IDE while developing.
Typically in Java development we use a tool such as Maven or Gradle to assist our IDE in obtaining external dependencies such as the Servlet API JAR.
If you are using Maven, edit your POM file to specify a dependency for the Servlet API JAR published as part of the Jakarta.ee project, by the Eclipse Foundation.
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
Notice the scope element, with a value of provided. This means the JAR file should be downloaded, made available to your IDE while developing, but should not be copied into your resulting product when you build.
The output of your build will typically be a WAR file (not JAR). The provided scope means we do not want a copy of the Servlet API JAR within that WAR. The reason is that when developing Jakarta EE web apps, we expect to deploy to a compliant application server that carries its own copy of the Servlet API. Every “profile” of Jakarta EE compliant servers are required to include an implementation of the Servlet API.
So your POM will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>work.basil.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
And here is the source code for a simple servlet.
package work.basil.example.demo;
import java.io.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
#WebServlet ( name = "helloServlet", value = "/hello-servlet" )
public class HelloServlet extends HttpServlet
{
private String message;
public void init ( )
{
message = "Hello World!";
}
public void doGet ( HttpServletRequest request , HttpServletResponse response ) throws IOException
{
response.setContentType( "text/html" );
// Hello
PrintWriter out = response.getWriter();
out.println( "<html><body>" );
out.println( "<h1>" + message + "</h1>" );
out.println( "</body></html>" );
}
public void destroy ( )
{
}
}
You said:
I've tried uninstalling and reinstalling Tomcat several times
Completely unrelated to your problem, I expect.
The installation of Tomcat comes with an implementation of the Servlet API. But your IDE does not “see” Tomcat during development. Your IDE needs to see the interfaces defined in the Servlet API.
Your IDE does not actually execute your servlet, so it does not need an implementation, it needs only the interfaces. When you run your Servlet from the IDE, Tomcat is invoked externally from the IDE. The IDE attaches a debugger connection to Tomcat to facilitate interactive debugging, but the IDE and Tomcat are actually running as separate distinct processes on the host OS. And as I said, Tomcat comes bundled with its own implementation of those interfaces, so your servlet is able to execute.
You said you are using Java 13. Be aware that Java 13 is no longer supported. You should move to either the latest version of Java (17, as of 2021-11), or to one of the Long-Term Support (LTS) versions (8, 11, 17).
If using Java 11 or 17 rather than 8, I would suggest using Tomcat 10.1.x rather than Tomcat 10.0.x. Tomcat 10.1.x supports Jakarta EE 9.1 and 10, which supports Java 11 and later versus Java 8.
See the Tomcat version comparison page.
My application was first downloading the jsp file instead of rendering it on page. I was getting a 200 status code in console but 404 on the page. I looked around and found I was suppose to add the dependencies for tomcat-jasper and tomcat-embed but now am getting a class cast exception saying that jspservlet can’t be cast to javax servlet. Any help appreciated. Thanks!
Getting an ApplicationContextException:
Caused by: javax.servlet.ServletException: Class [org.apache.jasper.servlet.JspServlet] is not a Servlet
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1054) ~[tomcat-embed-core-9.0.48.jar:9.0.48]
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:989) ~[tomcat-embed-core-9.0.48.jar:9.0.48]
at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext.load(TomcatEmbeddedContext.java:82) ~[spring-boot-2.5.2.jar:2.5.2]
... 32 common frames omitted
Caused by: java.lang.ClassCastException: org.apache.jasper.servlet.JspServlet cannot be cast to javax.servlet.Servlet
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1049) ~[tomcat-embed-core-9.0.48.jar:9.0.48]
... 34 common frames omitted
This happens when I have this in my pom:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>10.1.0-M2</version>
<scope>runtime</scope>
</dependency>
When this is taken out of my pom everything works fine, hits the controller and returns the ModelAndView but downloads the jsp instead of actually displaying the page.
TL;DR: Use
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
There are a couple of problems with the dependency you added:
You should use tomcat-embed-jasper instead of tomcat-jasper: it depends on tomcat-embed-core (which is already used by Spring Boot) instead of tomcat-catalina (which is not used by Spring Boot). Including the latter causes problems like in this question.
Jasper 10.1 implements JSP 3.0, which is not compatible with Servlet 4.0 provided by Tomcat 9.0 (cf. this question). Since spring-boot-parent manages these dependencies, you should omit the <version> tag and use the version chosen by Spring Boot (9.0.48 for the version you are using, but it will keep in sync, when you update Spring Boot),
If you deploy your application as WAR file in an external servlet container, you want to use the version of JSP engine provided by the servlet container, instead of your own. Therefore you should set the scope to provided.
So I have this springboot application which I'm migrating from a WAS to a springboot setup. And I have a couple of JSPs which has to be configured. To accomodate these I added the following dependencies:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
The application already came with the following dependency which is being used throughout the application:
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm-jaxrpc-client</artifactId>
<version>6.0</version>
</dependency>
The issue I'm facing is that both these dependencies (jaxrpc-client and tomcat-embed-jasper) have javax.servlet.ServletContext classes in them which is causing the following error:
The method's class, javax.servlet.ServletContext, is available from the following locations:
jar:file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar!/javax/servlet/ServletContext.class
jar:file:/C:/Users/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.30/tomcat-embed-core-9.0.30.jar!/javax/servlet/ServletContext.class
It was loaded from the following location:
file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of javax.servlet.ServletContext
I can't afford to remove any of these dependencies. jaxrpc-client is being referenced in the code already in too many places and I need tomcat-embed-jasper to render my jsp pages. I can't exclude the ServletContext class since its not a dependency(If I'm not wrong about the concept of exclusion). Please help with with a way forward with this issue.
I'm not familiar with IBM's jaxrpc client, but I assume, you have this exception in runtime, when trying to load the application.
In this case consider the following approaches:
Use another jax-rpc client library
Consider Loading the code that uses this library with the different class-loader (you'll have to create one classloader for this) to avoid the clash
Kind of paraphrasing the second option. You can "play" (override the order of loading of specific classes) with spring boot classloader as described in this article
I know, this is too general answer, but hopefully its still helpful.
The first solution is by far the easiest way I can think of.
The second solution is doable, however it pretty much depends on how exactly the code that uses the jax rpc client is loaded and used.
I am trying to implement a completion suggestion for my java application. I've read the documentation but could not find anything on how to implement it using the Java API in Version 5.0.1. (All i found was related to older versions)
this.client.prepareSuggest...
=> does not exist anymore
this.client.prepareSearch... .addSuggestion(csb);
=> does not accept CompletionSuggestionBuilder
This is my maven dependency:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.0.1</version>
</dependency>
Can anyone provide an example?
The correct way of doing it is like this:
CompletionSuggestionBuilder csb = SuggestBuilders.completionSuggestion("foo")
.prefix("prefix");
client().prepareSearch()
.suggest(new SuggestBuilder().addSuggestion("foo", csb))
When parsing my XML files with Simple, I get errors like the following one:
org.simpleframework.xml.core.ValueRequiredException:
Unable to satisfy ... on field 'id' private String Foo.id
for class Foo at line -1
What do I need to change to get the real line number?
[EDIT] Here is a Gist: Demo for SimpleXml line number problem
I'm using simple-xml:2.6.1 and junit:4.9
Depends on where it is run. If you run in JDK 1.5, witout StAX in the classpath this means you are using DOM, so you will always get -1 on an error. If you use JDK 1.5 or above with StAX in the classpath then StAX does not support line numbers (perhaps try a different lib, I recomment Woodstox).
[EDIT] If you want to try Woodstox and you use Maven, add this to your POM:
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
<version>3.1.1</version>
</dependency>
This is the version which uses Apache License 2.0. If you prefer LGPL 2.1, replace -asl with -lgpl.