I am new to JavaEE and am trying to create a WAR containing an application that performs a task every 30 minutes (and is started when the app is deployed), and a servlet that will allow a client to connect and get status information, using WildFly.
Is this possible? If so, how do I get WildFly to start the long running process, and how do I get it to inject that object into the servlet?
The long running application is in a class ProcessData() which uses ScheduledExecutorService to spawn a thread on a schedule to perform data management tasks, and has a getStatus() method returning a string about the processing.
This is the servlet:
#WebServlet("/procStat")
public class processorServlet extends HTTPServlet {
#Inject
ProcessData processData;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<p>" + processData.getStatus() + "</p>");
writer.close();
}
}
Why you need to inject it? Can you initialize it and use it with static method or something? Here is my example for that:
public class HelloWorld extends HttpServlet
{
private static final long serialVersionUID = -1L;
ProcessData processData;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// Set response content type
response.setContentType("text/html");
// Actual logic goes here.
PrintWriter out = response.getWriter();
out.println("<h1>" + ProcessData.getStatus() + "</h1>");
}
}
After this the ProcessData class which implements javax.servlet.ServletContextListener
This listener has method which will be executed when web application initialization process is starting.
public class ProcessData implements ServletContextListener
{
private static String message;
private int counter = 0;
Runnable run = new Runnable()
{
#Override
public void run()
{
message = "counter: " + (counter++);
System.out.println(message);
}
};
#Override
public void contextInitialized(ServletContextEvent arg0)
{
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(run, 0, 1, TimeUnit.MINUTES);
}
#Override
public void contextDestroyed(ServletContextEvent arg0)
{
// Empty method
}
public static String getStatus()
{
return message;
}
}
And the web.xml add the servlet and also ServletContextListener like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>TestTask</display-name>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>servlet.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
<listener>
<listener-class>schedule.ProcessData</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
I ask before why you need to inject it because if you have a task which is not connected with specific user (like yours is executed regardless if the user is logon or not) then you end up in situation that many users need access to a single data. You need some kind of singleton and yes you can do it with CDI beans or other Injection technology but you also can make it working without this overhead. In my example I do it with static method and static field.
Related
I can't fathom why this isn't working. Inside a Jersey resource method I make an asynchronous call, but the code blocks and never returns. Testing with curl, my terminal just hangs. From application logs, I can tell the request got intercepted and the code was executed.
Using Jersey 2.8 with maven tomcat7 plugin.
I also tried using an Executor Service instead of creating the thread directly, this is how I'd prefer to do it. Changed to creating a thread directly to be close to the documentation.
Jersey code:
#Path("/trade")
public class TradeEndpoint {
// TODO: make this configurable!!
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
private static MyService myService;
static {
//issues getting Spring working, so get it like this
orderFillService = Bootstrapper.getOrderFillService();
}
#GET
public String getFoo() {
return "Foo";
}
#POST
#Path("limit")
#Consumes({ MediaType.APPLICATION_JSON })
public void limitOrder(#Suspended final AsyncResponse response, Model model) {
System.out.println(model);
new Thread( new Runnable() {
public void run() {
myService.doStuff(model);
}
}).start();
System.out.println("SUBMITTED");
}
Web.xml
<web-app version="2.5" 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/web-app_2_5.xsd">
<servlet>
<servlet-name>web</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.foo.conf.Application</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>web</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
So im trying to create a remote Rest (JSON) service inside an OSGi bundle based in Felix with Maven.
my basic service interface :
#Controller
#RequestMapping("/s/fileService")
public interface RestFileService {
#RequestMapping(value = "/file", method = RequestMethod.POST)
#ResponseBody
public String getFile(Long id);
}
My implementation of the interface
public class RestFileServiceImpl implements RestFileService{
public String getFile(Long id) {
return "test service";
}
}
Normally i would add this to my web.xml
<servlet>
<servlet-name>spring-mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc-dispatcher</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
And this would work fine inside a normal webapp.
But now i want to put this inside an OSGi bundle.
Servlet 3.0 allows you to use #WebServlet to declare a servlet without the web.xml
So i created a RestServlet
#WebServlet(value="/rest", name="rest-servlet")
public class RestServlet implements ServletContextListener {
private static Log sLog = LogFactory.getLog(RestServlet.class);
public void contextInitialized(ServletContextEvent arg0) {
sLog.info("initializing the Rest Servlet");
}
public void contextDestroyed(ServletContextEvent arg0) {
sLog.info("un-initializing the Rest Servlet");
}
}
This is my OSGi activator:
public class Activator implements BundleActivator {
private static Log sLog = LogFactory.getLog(Activator.class);
public void start(BundleContext context) throws Exception {
/*
* Exposing the Servlet
*/
Dictionary properties = new Hashtable();
context.registerService(RestFileService.class.getName(), new RestFileServiceImpl(), properties );
sLog.info("Registered Remote Rest Service");
}
public void stop(BundleContext context) throws Exception {
sLog.info("Unregistered Remote Rest Service");
}
}
I know Felix has its own http implementation with JAX but im trying to do this with spring annotations and as little XML as possible.
Can i force it to register the annotation driven 3.0 servlet ?
What am i doing wrong ? is this even possible ?
If you're looking for an easy way to do REST in OSGi, take a look at some of the web components provided by the Amdatu project. This page pretty much explains how to create a REST service: https://amdatu.org/application/web/ and there is also a video which will talk you through the whole process: https://amdatu.org/generaltop/videolessons/
jsp:
<!DOCTYPE html>
<form action="/{insert your context here}/p/hello" method="post" enctype="multipart/form-data">
<input type="file" name="data">
<button>Go</button>
</form>
Servlet:
#WebServlet
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1;
#Override
protected void doPost( HttpServletRequest request, HttpServletResponse response )
throws IOException, ServletException {
if ( request.getPart( "data" ) != null ) {
response.getWriter().print( "It worked\n\n" );
} else {
response.getWriter().print( "IT IS NOT WORKING!\n\n" );
}
}
}
Filter
#WebFilter( filterName = "hello" )
public class HelloFilter implements Filter {
#Override
public void init( FilterConfig config ) throws ServletException {}
#Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
throws IOException, ServletException {
request
.getRequestDispatcher( "/hello" )
.include( request, response );
request
.getRequestDispatcher( "/hello.jsp" )
.include( request, response );
}
#Override
public void destroy() {}
}
Listener
#WebListener
public class HelloListener implements ServletContextListener {
#Override
public void contextInitialized( ServletContextEvent event ) {
ServletContext context = event.getServletContext();
Dynamic hello = context.addServlet( "hello", HelloServlet.class );
hello.addMapping( "/hello" );
hello.setMultipartConfig( getMultiPartConfig() );
}
#Override
public void contextDestroyed( ServletContextEvent event ) {}
private MultipartConfigElement getMultiPartConfig() {
String location = "";
long maxFileSize = -1;
long maxRequestSize = -1;
int fileSizeThreshold = 0;
return new MultipartConfigElement(
location,
maxFileSize,
maxRequestSize,
fileSizeThreshold
);
}
}
My web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>{insert the context here}</display-name>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</jsp-config>
<filter-mapping>
<filter-name>hello</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
When I submit the form I receive the output "IT IS NOT WORKING!" in the first line of the response. If I change the requested path from /{insert your context here}/p/hello to /{insert your context here}/hello it works, why?
Using:
JBoss EAP 6.1
I was able to reproduce the problem with Tomcat 7.0.30. The same problem was present whether I configured the "HelloServlet" dynamically or statically, using a #MultipartConfig annotation.
I also tested on Jetty version 8.1.13.v20130916, and it worked fine in both cases; /{context}/p/hello and /{context}/hello, without any modifications.
If you were to modify your HelloListener like this:
Dynamic hello = context.addServlet( "hello", HelloServlet.class );
hello.addMapping( "/hello" );
hello.addMapping( "/p/hello" );
hello.setMultipartConfig( getMultiPartConfig() );
it would work, but I suspect that defeats the purpose of what you are trying to do. Another option would be to modify your filter to add new mappings dynamically as required:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// dynamically add mappings
ServletContext context = request.getServletContext();
ServletRegistration registration = context.getServletRegistration("hello");
registration.addMapping("/<dynamic path>/hello");
request.getRequestDispatcher("/hello").include(request, response);
request.getRequestDispatcher("/hello.jsp").include(request, response);
}
It seems like tomcat/jboss is only enabling multipart support for the request based on whether or not the original request path matches one of the servlets configured for multipart support, whereas it should be enabling support based on the path of the request it is currently handling (which might have been "included" by the RequestDispatcher.
It does seem like a tomcat/jboss bug, but you'd have to carefully read the servlet spec to find out, otherwise you could use Jetty instead.
I randomly came across this and what may be the answer to your dilemma at the same time. Given the annotations supplied, the page would be accessed with {insert your context here}/hello and not {insert your context here}/p/hello. Take a look at Multiple folders in Java EE 6 web pages. It is very similar and BalusC provides some great information. Hope that helps.
I use Tomcat 7 and Lo4j for all my server logs and GWT for client (only AJAX calls).
All my unhandled exceptions get logged in my catalina.log.
Now I want to catch all exceptions and add some of the user's specific Tomcat SessionData.
There are several ways:
Try catch over all servlets (there must be a better solution).
http://tomcat.apache.org/tomcat-7.0-doc/aio.html: I would have to change my connecter, and I don't know if I could use the Tomcat Session within the Event Handler (EventType.ERROR).
Better way?
What would be the best way to achieve this?
Out of what I understood from your question, you can try to use at least one of two ways:
Basic Logging Servlet
If you have access to the source code of all of your servlets, you can make a little refactoring using a basic super-servlet that is responsible for the logging/whatever of every request working transparently with AJAX, no error forwards directives, and no global exception handlers. Suppose you use service(ServletRequest,ServletResponse) as the servlet entry point (but you can do the following for every do*() method either), then you can create an abstract super-servlet and simply inherit your servlets from it.
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>stackoverflow.Servlet1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>stackoverflow.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>servlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>servlet2</url-pattern>
</servlet-mapping>
public abstract class BasicServlet extends HttpServlet {
/**
* Won't let it be {#code abstract} - we don't want to force any sub-servlet to implement this method.
*/
protected void doService(ServletRequest request, ServletResponse response) {
}
#Override
public final void service(ServletRequest request, ServletResponse response) {
try {
doService(request, response);
} catch ( Throwable ex ) {
err.println(ex.getMessage());
}
}
}
public final class Servlet1 extends BasicServlet {
#Override
protected void doService(ServletRequest request, ServletResponse response) {
out.println("I'm servlet #1");
}
}
public final class Servlet2 extends BasicServlet {
#Override
protected void doService(ServletRequest request, ServletResponse response) {
out.println("I'm servlet #2");
}
}
An advantage of this method is that you do not need to configure anything else than changing your servlet classes and not depend on the external configuration or context. The disadvantage is that you always must extend BasicServlet.
Filter
I didn't actually test it right now, for more information please see http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html . Filters allow to intercept each request (we use such a filter implementation for our JSPs while debugging and writing the exceptions into the common log file). The disadvantage is that it's not guaranteed that the filter can cover every exception/case, for example if any filter preceded your own filter.
<filter>
<filter-name>exceptionLoggingFilter</filter-name>
<filter-class>stackoverflow.ExceptionLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>exceptionLoggingFilter</filter-name>
<url-pattern>*</url-pattern> <!-- we will process every request -->
</filter-mapping>
public final class ExceptionLoggingFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
try {
filterChain.doFilter(request, response);
} catch ( Throwable ex ) {
err.println(ex);
}
}
#Override
public void destroy() {
}
}
Hope this helps.
Just Overriding the GWT function doUnexpectedFailure worked.
#Override
protected void doUnexpectedFailure(Throwable t) {
ServerLog.error(t.getMessage(), t);
super.doUnexpectedFailure(t);
}
1) You can define error page for your webapp, like this:
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error</location>
</error-page>
Then you can bind another servlet at /error and handle the exception there.
2) You can setUncaughtExceptionHandler for every HTTP connector thread. You could use this technique with servlet filter which would contain reference to the current HttpRequest (say, via a thread local). This won't work with async I/O by the way.
I am developing inherited jsp/java ee application and I would like to introduce Guice IoC container to my application. However, I found some obstacles. I can't translate web.xml entries into guice registrations if there are more then one routing to single servlet using different urls. Problem is with init-parameters.
Here are some extracts from my web.xml:
This one is unrelated to problem, but it is good example how we are using init parameters. Basically it maps users with different roles in systems to appropriate pages.
<!-- LIST INTERNSHIPS SERVLET -->
<servlet>
<servlet-name>ListInternships</servlet-name>
<servlet-class>pl.poznan.put.ims.controllers.ListInternships</servlet-class>
<init-param>
<param-name>CoordinatorPage</param-name>
<param-value>WEB-INF/pages/coordinator/listinternships.jsp</param-value>
</init-param>
<init-param>
<param-name>MentorPage</param-name>
<param-value>WEB-INF/pages/mentor/listinternships.jsp</param-value>
</init-param>
<init-param>
<param-name>AdministratorPage</param-name>
<param-value>WEB-INF/pages/administrator/listinternships.jsp</param-value>
</init-param>
<init-param>
<param-name>AllowedRoles</param-name>
<param-value>Coordinator, Mentor, Administrator</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ListInternships</servlet-name>
<url-pattern>/internships</url-pattern>
</servlet-mapping>
Those two are the troublesome ones:
<!-- CHANGE PASSWORD SERVLET -->
<servlet>
<servlet-name>ChangePassword</servlet-name>
<servlet-class>myapp.controllers.ContextForwarder</servlet-class>
<init-param>
<param-name>SharedPage</param-name>
<param-value>WEB-INF/pages/shared/password.jsp</param-value>
</init-param>
<init-param>
<param-name>AllowedRoles</param-name>
<param-value>*</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ChangePassword</servlet-name>
<url-pattern>/changepassword</url-pattern>
</servlet-mapping>
<!-- HELP SERVLET -->
<servlet>
<servlet-name>Help</servlet-name>
<servlet-class>myapp.controllers.ContextForwarder</servlet-class>
<init-param>
<param-name>SharedPage</param-name>
<param-value>WEB-INF/pages/help/help.jsp</param-value>
</init-param>
<init-param>
<param-name>AllowedRoles</param-name>
<param-value>*</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Help</servlet-name>
<url-pattern>/help</url-pattern>
</servlet-mapping>
Here is my servlet:
#Singleton
public class ContextForwarder extends HttpServlet {
private static final long serialVersionUID = 1L;
private final IUserDao dao;
#Inject
public ContextForwarder(IUserDao dao) {
this.dao = dao;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//trying to get rid of statics, using Ioc
Validator.checkUserLoggedIn (request);
Validator.checkUserAuthorized(this, request);
User currentUser = UserManager.getCurrentUser(request);
//pick matching page for user
String userViewPage = ServletUtils.getUserURL(this, currentUser, "Page");
try {
dao.openSession();
dao.beginTransaction();
currentUser = UserManager.reloadCurrentUser(request, dao);
ServletUtils.forward(request, response, userViewPage);
dao.commit();
}
catch(ServletException e) {
dao.rollback();
throw e;
}
catch(Exception e) {
dao.rollback();
throw new ServletException(e);
}
finally {
dao.closeSession();
}
}
}
public class ServletUtils {
public static void forward(HttpServletRequest request, HttpServletResponse response, String location)
throws ServletException, IOException {
RequestDispatcher view = request
.getRequestDispatcher( response.encodeRedirectURL(location) );
view.forward(request, response);
}
public static String getUserParameter(GenericServlet servlet, User user, String suffix) {
return servlet.getInitParameter( user.getRoles() + suffix );
}
public static String getUserURL(GenericServlet servlet, User user, String suffix)
throws ResourceNotFoundException {
String URL = getUserParameter(servlet, user, suffix);
if(URL == null) {
URL = servlet.getInitParameter("Shared" + suffix);
if(URL == null)
throw new ResourceNotFoundException(suffix);
}
return URL;
}
public static void redirect(HttpServletRequest request, HttpServletResponse response, String location)
throws ServletException, IOException {
response.sendRedirect( response.encodeRedirectURL(location) );
}
}
When i try to translate it into guice (and then register this module):
public class MyServletModule extends ServletModule
{
#Override
protected void configureServlets() {
configureHelp();
configurePassword();
}
private void configureHelp()
{
Map<String, String> params = new HashMap<String, String>();
params.put("SharedPage", "WEB-INF/pages/shared/help.jsp");
params.put("AllowedRoles", "*");
serve("/help").with(ContextForwarder.class, params);
}
private void configurePassword()
{
Map<String, String> params = new HashMap<String, String>();
params.put("SharedPage", "WEB-INF/pages/shared/password.jsp");
params.put("AllowedRoles", "*");
// it's routing correctly to servlet, but it uses params from first registration,
// so that routing to jsp page is incorrect
serve("/changepassword").with(ContextForwarder.class, params);
}
}
The problem is that guice creates ContextForwarder servlet as a singleton with init parameters from the first registered method and then regardless of the request url it has parameters from the first registration. Is there any other solution to route user to different sites considering current user role? Is it possible to configure the same servlet class with two different configurations?
I have found a solution, however I am not fully satisfied with it. I found out that, in solution without guice, web container (tomcat) creates two separate servlets using the same servlet class and injecting different init parameters. Guice by default restricts servlets to be singletons, so to copy default behaviour from web.xml solution I needed to find a way to create two instance of the same class and register it twice with different parameters. I solved this by creating two sub-classes to my servlet class, both with empty body, then I registered them with different parameters.
This solution works, but it involve creating empty-body sub-classes which I am not satisfied with. It's not a problem when I got two sub-classes, but with more of them code is becoming cumbersome.