I want to build a real simple example for invoking a asynchronous servlet in an embedded Jetty server. When I try to start a Runnable from the AsyncContext of the Request, I get a NullPointerException.
This is the server code:
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
ServletHolder holder = handler.addServletWithMapping(AsyncServlet.class, "/*");
holder.setAsyncSupported(true);
server.start();
server.join();
}
This is the servlet code:
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
final AsyncContext ctxt = req.startAsync();
ctxt.start(() -> {
ctxt.complete();
});
}
And this is the error with stack trace:
java.lang.NullPointerException
at org.eclipse.jetty.server.AsyncContextState$2.run(AsyncContextState.java:168)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:620)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:540)
at java.lang.Thread.run(Thread.java:745)
Maybe the server needs any additional configuration. Any suggestions?
Addition: The same servlet runs without any change in an embedded tomcat.
Don't use ServletHandler directly, that's an internal class that ServletContextHandler creates / uses / manages.
Here's a modification of your example, that sets up the servlet context properly.
package jetty;
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class EmbeddedAsyncServer
{
public static class EmbeddedAsyncServlet extends HttpServlet
{
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
final AsyncContext ctxt = req.startAsync();
ctxt.start(new Runnable()
{
#Override
public void run()
{
System.err.println("In AsyncContext / Start / Runnable / run");
ctxt.complete();
}
});
}
}
public static void main(String[] args) throws Exception
{
Server server = new Server(9090);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
ServletHolder asyncHolder = context.addServlet(EmbeddedAsyncServlet.class,"/async");
asyncHolder.setAsyncSupported(true);
server.setHandler(context);
server.start();
server.join();
}
}
Access http://localhost:9090/async and you'll see the following output
2014-12-29 13:18:57.075:INFO::main: Logging initialized #69ms
2014-12-29 13:18:57.131:INFO:oejs.Server:main: jetty-9.2.6.v20141205
2014-12-29 13:18:57.156:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler#65123c41{/,null,AVAILABLE}
2014-12-29 13:18:57.169:INFO:oejs.ServerConnector:main: Started ServerConnector#722b302{HTTP/1.1}{0.0.0.0:9090}
2014-12-29 13:18:57.169:INFO:oejs.Server:main: Started #166ms
In AsyncContext / Start / Runnable / run
Related
I am trying to do long polling in a struts web application. I start an AsyncContext inside an ActionSupport action method, do some time-consuming work async, and then would like to send the SUCCESS response to struts.
I know that I can do PrintWriter pw = asyncContext.getResponse().getWriter(); and write a raw response, but I would like to somehow signal struts to proceed with the predefined result in struts.xml. Is this possible?
<action name="myAction" method="action1" class="myActionClass">
<result name="success" type="redirectAction">
/pages/myPage.jsp <!-- I want to run this from async --->
</result>
</action>
In non-async action I can simply return SUCCESS and struts takes care of everything, but I am having trouble with achieving a similar effect with async action. This is what I have so far:
public void action1() {
HttpServletRequest req = ServletActionContext.getRequest();
HttpServletResponse res = ServletActionContext.getResponse();
final AsyncContext asyncContext = req.startAsync(req, res);
asyncContext.start(new Runnable() {
public void run() {
// Some time-consuming polling task is done here
asyncContext.complete();
// Can I somehow proceed to predefined struts result from here?
}
});
}
Currently it seems cannot be done clearly. I am working if I can import this support to Struts but for now, I have a hack which works. I extended StrutsExecuteFilter as below:
package me.zamani.yasser.ww_convention.utils;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.dispatcher.filter.StrutsExecuteFilter;
import org.apache.struts2.dispatcher.filter.StrutsPrepareFilter;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* Created by user on 8/31/2017.
*/
public class MYStrutsAsyncExecuteFilter extends StrutsExecuteFilter {
public final int REQUEST_TIMEOUT = 240000;//set your desired timeout here
private ExecutorService exe;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
int size = 41;//set your desired pool size here
exe = Executors.newFixedThreadPool(
size,
new ThreadFactory() {
public Thread newThread(Runnable r) {
return new Thread(r, "My Struts Async Processor");
}
}
);
super.init(filterConfig);
}
#Override
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
if (excludeUrl(request)) {
chain.doFilter(request, response);
return;
}
// This is necessary since we need the dispatcher instance, which was created by the prepare filter
if (execute == null) {
lazyInit();
}
final ActionMapping mapping = prepare.findActionMapping(request, response);
//if recursion counter is > 1, it means we are in a "forward", in that case a mapping will still be
//in the request, if we handle it, it will lead to an infinite loop, see WW-3077
final Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);
if (mapping == null || recursionCounter > 1) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
/* I ADDED THESE */
final AsyncContext context = req.startAsync();
context.setTimeout(REQUEST_TIMEOUT);
context.addListener(new AsyncListener() {
public void onComplete(AsyncEvent asyncEvent) throws IOException {
}
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
context
.getResponse()
.getWriter().write("Request Timeout");
}
public void onError(AsyncEvent asyncEvent) throws IOException {
context
.getResponse()
.getWriter().write("Processing Error");
}
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
}
});
exe.execute(new ContextExecution(context, mapping));
}
}
private boolean excludeUrl(HttpServletRequest request) {
return request.getAttribute(StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING") != null;
}
#Override
public void destroy() {
exe.shutdown();
super.destroy();
}
class ContextExecution implements Runnable {
final AsyncContext context;
ActionMapping mapping;
public ContextExecution(AsyncContext context, ActionMapping mapping) {
this.context = context;
this.mapping=mapping;
}
public void run() {
try {
execute.executeAction((HttpServletRequest) context.getRequest(),
(HttpServletResponse) context.getResponse(), mapping);
context.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
then
<filter>
<filter-name>struts2asyncexecute</filter-name>
<filter-class>me.zamani.yasser.ww_convention.utils.MYStrutsAsyncExecuteFilter</filter-class>
<async-supported>true</async-supported>
</filter>
then put your desired async actions in a specific package and exclude them from Strut's original filter but map them to above filter in your web.xml.
I'm working to improve this to be more configurable and clear then import to Struts.
Could you please test in your app? and please feel free to let me know any idea.
i trying to open a pdf right after i created.
I want to see the result in my browser, but i dosen't.
By the way, the programm runs on a server.
If i try to open the pdf there, no problem - all works fine.
Just the client can't see the pdf if he clicks, for example, on a button.
Here's my code:
#WebServlet("/GeneratePdfCustomer")
public class GeneratePdfCustomer extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
final int id = Integer.parseInt(request.getParameter("id"));
makePdf(request, response, "POST",id);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void makePdf(HttpServletRequest request,
HttpServletResponse response, String methodGetPost, int id)
throws ServletException, IOException {
response.setContentType("application/pdf");
try {
Document document = new Document();
PdfWriter.getInstance(document, response.getOutputStream());
document.open();
document.add(new Paragraph("Test"));
document.close();
} catch (Exception e) {
System.out.println("Error" + e);
}
}
}
I using the iText library.
EDIT:
Now i have change my servlet code, but still not working for me.
I can't see the pdf in my browser. Firefox and IE both are show nothing.
package de.WorldCheckCustomer.model;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;
#WebServlet("/GeneratePdfCustomer")
public class GeneratePdfCustomer extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
makePdf(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
makePdf(request, response);
}
public void makePdf(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Set content type to application / pdf
// browser will open the document only if this is set
response.setContentType("application/pdf");
// Get the output stream for writing PDF object
OutputStream out = response.getOutputStream();
try {
Document document = new Document();
/* Basic PDF Creation inside servlet */
PdfWriter.getInstance(document, out);
document.open();
document.add(new Paragraph("Tutorial to Generate PDF using Servlet"));
document.add(new Paragraph(
"PDF Created Using Servlet, iText Example Works"));
document.close();
} catch (DocumentException exc) {
throw new IOException(exc.getMessage());
} finally {
out.close();
}
}
private static final long serialVersionUID = 6067021675155015602L;
}
I am new to EJB. I tried an example from java_for_web_with_servlets_jsp_and_ejb book. The following code creates an session been called Adder which adds two integers:
Adder.java:
package com.brainysoftware.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface Adder extends EJBObject{
public int add(int a,int b) throws RemoteException;
}
AdderBean.java
package com.brainysoftware.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class AdderBean implements SessionBean{
public int add(int a, int b){
System.out.println("From AdderBean");
return (a+b);
}
#Override
public void ejbActivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
#Override
public void ejbPassivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
#Override
public void ejbRemove() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
#Override
public void setSessionContext(SessionContext arg0) throws EJBException,
RemoteException {
// TODO Auto-generated method stub
}
}
AdderHome.java
package com.brainysoftware.ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface AdderHome extends EJBHome{
Adder create() throws RemoteException, CreateException;
}
The deployment descriptor is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<description>Your first EJB application</description>
<display-name>Adder Application</display-name>
<enterprise-beans>
<session>
<ejb-name>Adder</ejb-name>
<home>com.brainysoftware.ejb.AdderHome</home>
<remote>com.brainysoftware.ejb.Adder</remote>
<ejb-class>com.brainysoftware.ejb.AdderBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
I created a jar file of this project and placed the jar in the tomcat's lib.
Now, for the client I created a dynamic web project having the class BeanClient.java that uses the Adder bean:
import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.brainysoftware.ejb.Adder;
import com.brainysoftware.ejb.AdderHome;
import javax.rmi.PortableRemoteObject;
public class BeanClient {
public static void main(String[] args){
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
props.put(Context.PROVIDER_URL, "localhost:1099");
try{
InitialContext jnic = new InitialContext(props);
System.out.println("Get context");
Object ref = jnic.lookup("Adder");
System.out.println("Got reference");
AdderHome home = (AdderHome) PortableRemoteObject.narrow(ref,AdderHome.class);
Adder adder = home.create();
System.out.println("Adding 2 and 5:"+adder.add(2,5));
} catch(NamingException e){
System.out.println(e.toString());
} catch(CreateException e){
System.out.println(e.toString());
} catch (RemoteException e) {
System.out.println(e.toString());
}
}
}
On executing this class in eclipse, I get the following error:
Get context
javax.naming.CommunicationException: Could not obtain connection to any of these urls: localhost:1099 and discovery failed with error: javax.naming.CommunicationException: Receive timed out [Root exception is java.net.SocketTimeoutException: Receive timed out] [Root exception is javax.naming.CommunicationException: Failed to connect to server localhost:1099 [Root exception is javax.naming.ServiceUnavailableException: Failed to connect to server localhost:1099 [Root exception is java.net.ConnectException: Connection refused: connect]]]
Could you please help me out with this?
to look up and ejb firstbale :
you must have an interface and it's implementation.
you have to annotate your interface (#Local ou #Remote)
The implementation must be annotated #statless ou #statful
You have to add on you project a jndi.properties.
your ejb project must be deployed on your server to be easy to look up it.
i have for you an example with jboss here :
https://www.youtube.com/watch?v=mBwgHuBg96g
i hope that my answer is useful pour you
this wierd problem is very interrupting me for a long time. I have a class name Connector inside dynamic web application in eclipse, with these code:
public class Connector {
private static final String dbURL = "jdbc:mysql://localhost:3306/";
private Connection con;
public Connector(String userName, String password) {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
con = DriverManager.getConnection(dbURL, userName, password);
} catch (SQLException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (Exception e) {
System.err.print("Unidentified exception has acurred!");
e.printStackTrace();
}
}
when I'm using it from different Class in the same package, named portal, it works fine, but when I'm trying to use it from servlet in package servlets, named LoginHandle.java, I get ClassNotFoundException.
The Class is in the build path of all classes, and I checked it by trying to import it from the servlet, but when I create new instance, it is not being recognized. I tried to move the servlet to the package of the connector, and vise versa, and it didn't affect. Here is the servlet's code:
package servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import portal.Connector;
import portal.UserTableAnalyzer;
#WebServlet("/LoginHandle")
public class LoginHandle extends HttpServlet {
private static final long serialVersionUID = 1L;
public LoginHandle() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String referer = request.getHeader("Referer");
String pageName = referer.substring(referer.lastIndexOf('/') + 1);
if(pageName.equals("Login.jsp"))
{
HttpSession session = request.getSession(false);
Connector c = new Connector("root", "16180339887");
c.executeUpdate("USE Main");
String id = request.getParameter("id"), password = request.getParameter("password");
String query = "SELECT FROM Users WHERE id ='" + id + "' AND password = '" + password + "'";
String[][] result = c.executeQuery(query);
UserTableAnalyzer uta = new UserTableAnalyzer(result);
if(result.length > 0)
{
session.setAttribute("userID", uta.getID(0));
session.setAttribute("role", uta.getRole(0));
response.sendRedirect("Main.jsp");
}
else
{
request.setAttribute("wrongDetails", new Boolean(true));
response.sendRedirect("Login.jsp");
}
}
else
response.getWriter().print(pageName);
}
}
sorry if my english is bad, or if details are missing
Your driver is not in the server classpath, build path has nothing to do with that.
You didn't write which application server are you using. You also should use DataSource in servlets, rather than DriverManager. Here is sample configuration for Tomcat.
UPDATE
For Tomcat 7 you need to do the following:
Put mysql jar in the $CATALINA_HOME/lib
Configure Datasource in context
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="user" password="pass"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/databaseName"/>
Use Datasource in servlet (pseudo code)
#WebServlet("/LoginHandle")
public class LoginHandle extends HttpServlet {
private static final long serialVersionUID = 1L;
#Resource(lookup="jdbc/testDB")
private DataSource ds;
...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Connection c = ds.getConnection();
...
I am working on sample application called bookstore where I have used Dependency Injection. Very simple application. I am using JavaEE 6, GlassFish 3.1.2, Static data in a class (no db), Eclipse Juno.
I can provide more info if needed.
Error I get:
WARNING: StandardWrapperValve[com.bookstore.web.BookListServlet]: PWC1406: Servlet.service() for servlet com.bookstore.web.BookListServlet threw exception
java.lang.NullPointerException
at com.bookstore.web.BookListServlet.doGet(BookListServlet.java:29)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
This I know is Dependency is not injected. Class instance is not created to use it. If I remove #Inject and created an instance of a class than the page is loading fine.
Servlet code generating error: See bold comments
package com.bookstore.web;
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.bookstore.BookRepositoryImp;
#WebServlet("/book/")
public class BookListServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Inject
private BookRepositoryImp bookRepo;
public BookListServlet() {
super();
}
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException {
**//bookRepo = new BookRepositoryImp(); If uncommect this code and remove #Inject than page working fine**
req.setAttribute("books", bookRepo.listBooks());
String path = "/WEB-INF/pages/book-list.jsp";
getServletContext().getRequestDispatcher(path).forward(req, res);
}
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException {
}
}
Project structure:
Have you tried moving your beans.xml file into the WEB-INF folder where your web.xml lives, maybe for some reason your beans.xml file is not copied properly to the resulting war file.