I deploy a Spring MVC webapp on Tomcat 8.5 with the following controller:
import java.util.concurrent.Callable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class AppController {
#RequestMapping(value="getOkSync", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String getOkSync() {
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "ok";
}
#RequestMapping(value="getOkAsync", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody Callable<String> getOkAsync() {
return new Callable<String>() {
#Override
public String call() {
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "ok";
}
};
}
}
while the first method returns after 60 sec the correct result, the second method returns after approximately 30 seconds with HTTP response code 500 and the corresponding Spring class logs
Could not complete async processing due to timeout or network error.
(if the delay is set on the other hand to 20 seconds both metods return "ok" after 20 seconds as expected).
Is the timeout controlled by Spring MVC or by Tomcat? What is the property which controls the timeout?
Well, the following works (i.e. both methods return now "ok" after 60 seconds), though there is an interaction between Spring and Tomcat which I do not understand completely at the moment (in any case it appears that if I don't set the property via Spring, the timeout will be that of Tomcat, though I don't know how to set the latter)
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="my.base.package")
public class AppConfig extends WebMvcConfigurerAdapter implements WebApplicationInitializer {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// TODO Auto-generated method stub
configurer.setDefaultTimeout(120000);
super.configureAsyncSupport(configurer);
}
...
Related
I am trying to call asynchronous method that returns Future Object, I suppose that it will print YYY and then XXX since the XXX is in a method that is 1 sec long. however, after deploying the code, it did not work properly, I tried the same thing with 10 objects and they printed sequentially. where is the error
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package testfuture;
import java.net.InetAddress;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.ejb.Asynchronous;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
public class TestFuture {
#Schedule(minute = "*/1", hour = "*", persistent = false)
public void start() {
try{
Future<String> r = inparallelMethod(5) ;
System.out.print("YYY");
r.get();
}
catch ( InterruptedException ie )
{
System.out.print(ie.getMessage());
}
catch (ExecutionException e)
{
System.out.print(e.getMessage());
}
}
#Asynchronous
public Future<String> inparallelMethod(int i) throws InterruptedException
{
Thread.sleep(1000);
System.out.print("XXX");
return null;
}
}
Because you call inparallelMethod inside the instanced class "bybassing" container managmenet of calling an async menthod.
You have to define the async method in another bean, #Inject that bean and call the method.
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
public class TestFuture {
#Inject
AsyncService service;
#Schedule(minute = "*/1", hour = "*", persistent = false)
public void start() {
try{
Future<String> r = service.inparallelMethod(5) ;
System.out.print("YYY");
r.get();
}
catch ( InterruptedException ie )
{
System.out.print(ie.getMessage());
}
catch (ExecutionException e)
{
System.out.print(e.getMessage());
}
}
}
#Stateless
public class AsyncService {
#Asynchronous
public Future<String> inparallelMethod(int i) throws InterruptedException
{
Thread.sleep(1000);
System.out.print("XXX");
return null;
}
}
I post the code to make work the async but your code in very poor to test an async scenario, Thread.sleep inside a java-ee is a very bad practice because thread are managed by container and you can't know wich thread you are really sleeping!
I created a custom error page to replace the default whitelabel based on this tutorial. It worked fine but I need to pass other attributes to the page so I changed my code to intercept the error endpoint based on the geoand's answer here.
Here is my final code:
#Controller
public class ErroHandlerController implements ErrorController {
#Value("${terena.midas.location}")
private String midasLocation;
#RequestMapping("/error")
public String handleError( Model model ) {
model.addAttribute( "midasLocation", midasLocation );
return "error";
}
#Override
public String getErrorPath() {
return "/error";
}
}
Well the code worked sending my variable midasLocation but I lost the error details like path, status,message, etc... How can I bring them back again?
You need to use the ErrorAttributes which "provides access to error attributes which can be logged or presented to the user".
Take a look:
at how the default Spring Error Controller does it: BasicErrorController.java
LogicBig -
Spring Boot - Using ErrorAttributes in our custom ErrorController
Basic functionality:
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.context.request.WebRequest;
#Controller
public class ErrorHandler implements ErrorController {
private final ErrorAttributes errorAttributes;
public ErrorHandler(ErrorAttributes errorAttributes) {
this.errorAttributes = errorAttributes;
}
#GetMapping("/error")
public String handleError(Model model, WebRequest webRequest) {
model.addAttribute("midasLocation", "xxx");
final Throwable error = errorAttributes.getError(webRequest);
model.addAttribute("exception", error);
model.addAttribute("message", error == null ? "" : error.getMessage());
return "error";
}
#Override public String getErrorPath() {
return "/error";
}
#GetMapping("/throwErrorForTest")
public String throwError() {
throw new RuntimeException("my exception");
}
}
Working with spring, I am new to rabbitmq, i want to know where i am wrong.
I have written a rabbitmq connection factory, and a listener container containing a listener. I have also provided the listener container with an error handler but it doesnt seems to work.
My spring beans:
<rabbit:connection-factory id="RabbitMQConnectionFactory" virtual-host="${rabbitmq.vhost}" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"/>
<rabbit:listener-container missing-queues-fatal="false" declaration-retries="0" error-handler="errorHandlinginRabbitMQ" recovery-interval="10000" auto-startup="${rabbitmq.apc.autostartup}" max-concurrency="1" prefetch="1" concurrency="1" connection-factory="RabbitMQConnectionFactory" acknowledge="manual">
<rabbit:listener ref="apcRabbitMQListener" queue-names="${queue.tpg.rabbitmq.destination.apc}" exclusive="true" />
</rabbit:listener-container>
<bean id="errorHandlinginRabbitMQ" class="RabbitMQErrorHandler"/>
This is my RabbitMQErrorHandler class:
public class RabbitMQErrorHandler implements ErrorHandler
{
#Override
public void handleError(final Throwable exception)
{
System.out.println("error occurred in message listener and handled in error handler" + exception.toString());
}
}
What i assume is, if i provide invalid credentials to the connection factory, handleError method of the RabbitMQErrorHandler class should execute, and the server should start properly, however, when i try to run the server, the method does not executes(the exception is thrown in console) and the server is not able to start. Where am i missing something and what that might be?
The error handler is for handling errors during message delivery; since you haven't connected yet, there is no message for which to handle an error.
To get connection exceptions, you should implement ApplicationListener<ListenerContainerConsumerFailedEvent> and you will receive the failure as an event if you add it as a bean to the application context.
You will get other events (consumer started, consumer stopped etc) if you implement ApplicationListener<AmqpEvent>.
EDIT
<rabbit:listener-container auto-startup="false">
<rabbit:listener id="fooContainer" ref="foo" method="handleMessage"
queue-names="si.test.queue" />
</rabbit:listener-container>
<bean id="foo" class="com.example.Foo" />
Foo:
public class Foo {
public final CountDownLatch latch = new CountDownLatch(1);
public void handleMessage(String foo) {
System.out.println(foo);
this.latch.countDown();
}
}
App:
#SpringBootApplication
#ImportResource("context.xml")
public class So43208940Application implements CommandLineRunner {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So43208940Application.class, args);
context.close();
}
#Autowired
private SimpleMessageListenerContainer fooContainer;
#Autowired
private CachingConnectionFactory connectionFactory;
#Autowired
private RabbitTemplate template;
#Autowired
private Foo foo;
#Override
public void run(String... args) throws Exception {
this.connectionFactory.setUsername("junk");
try {
this.fooContainer.start();
}
catch (Exception e) {
e.printStackTrace();
}
Thread.sleep(5000);
this.connectionFactory.setUsername("guest");
this.fooContainer.start();
System.out.println("Container started");
this.template.convertAndSend("si.test.queue", "foo");
foo.latch.await(10, TimeUnit.SECONDS);
}
}
I have a POJO that was instantiated from a servlet. I need to make a lookup of an EJB within this POJO, either CDI or JNDI. My JEE container is TomEE 1.6.0.
My question is this: need the EJB have remote interface? Because if I instantiate it directly from the servlet by #EJB the remote interface does not need...
Just see this simple example which always throws NameNotFoundException.
#Stateless
public class MyEJB
{
public String sayHello()
{
return "Hello";
}
}
The next servlet try yo lookup MyEJB:
#WebServlet("/myServlet")
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID=1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException
{
try
{
Context ctx = new InitialContext();
MyEJB ejb = (MyEJB) ctx.lookup("MyEJB");
System.out.println(ejb.sayHello());
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
}
The line: MyEJB ejb = (MyEJB) ctx.lookup("MyEJB"); always throws NameNotFoundException. But if I use #EJB it work fine. But I need do the lookup in JNDI mode because finally I will instantiate this EJB within a POJO.
So, why fails this lookup ?
This has nothing to do with remote interfaces.
When you declare MyEJB in servlet using #EJB it works, because servlet is container-managed - your TomEE server instantiates servlet object. However declaration of MyEJB in MyPojo (also using #EJB) won't work, because MyPojo is not container-managed - it is created using new MyPojo(), not by TomEE server.
You could for example make MyPojo another EJB (using #Stateless) and inject it using #EJB to the servlet - not by creating new MyPojo().
I can solve this problem by myself by instrospecting the JNDI tree with this simple class:
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
public class JndiInstrospector
{
public static void print()
{
try
{
Context ctx = new InitialContext();
String n = ctx.getNameInNamespace();
_print(n);
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException();
}
}
private static void _print(String name) throws Exception
{
try
{
System.out.println("Name in manespace: "+name);
Context ctx = new InitialContext();
NamingEnumeration<Binding> list = ctx.listBindings(name);
while( list.hasMoreElements() )
{
Binding b = list.nextElement();
String s = b.getName();
_print(name+"/"+s);
}
}
catch(Exception ex)
{
// ignore
}
}
}
Finally, the EJB That I was espected to lookup was found at:
"java:/openejb/local/FacadeBeanLocalBean", where FacadadeBean is the name of my EJB (stateless session bean).
For my unit-tests I use a simple test-server based on Jetty:
package eu.kostia.textanalysis.webservices.jetty;
import java.awt.Desktop;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class TestServer {
static private final String CONTEXT_PATH = "/webservice";
static private final String PROJECT_HOME = System.getenv("MY_WORKSPACE_HOME") + "/WebServices";
static public final int PORT = 8080;
private Server server;
private Exception startException;
private static class SingletonHolder {
private static final TestServer INSTANCE = new TestServer();
}
/**
* Returns the singleton instance of the test server.
*
* #return the singleton instance of the test server.
*/
public static TestServer getInstance() {
return SingletonHolder.INSTANCE;
}
private TestServer() {
server = new Server(PORT);
WebAppContext context = new WebAppContext();
context.setDescriptor(PROJECT_HOME + "/web/WEB-INF/web.xml");
context.setResourceBase(PROJECT_HOME + "/web");
context.setContextPath(CONTEXT_PATH);
context.setParentLoaderPriority(true);
server.setHandler(context);
}
/**
* Start the test server. This method returns only when the server is
* complete started. There is no effect when you invoke this method and the
* server is already running.
*/
public void start() {
if (!server.isRunning()) {
startException = null;
new Thread("TestServer") {
public void run() {
try {
server.start();
server.join();
} catch (Exception exc) {
startException = exc;
}
}
}.start();
while (true) {
if (startException != null) {
throw new Error(startException);
}
// Block this method call until the server is started
if (server.isStarted()) {
return;
}
}
}
}
/**
* Stop the test server.
*/
public void stop() {
try {
if (server.isRunning()) {
server.stop();
}
} catch (Exception e) {
throw new Error(e);
}
}
/**
* Returns {#code true} is the server is running.
*
* #return {#code true} is the server is running.
*/
public boolean isRunning() {
return server.isRunning();
}
public static void main(String[] args) throws Exception {
TestServer.getInstance().start();
Desktop.getDesktop().browse(new URI("http://localhost:8080/webservice/"));
}
}
It works very well for servlet configured in web.xml but I'd now like to use the new annotation syntax introduced by the Servlet Specification 3.0, for example:
#WebServlet(urlPatterns = {"/hello"})
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.print("<h1>HttpServlet using Servlet 3.0</h1>");
}
}
How shoud I configure Jetty in my TestServer class to process also annotation-based servlets?
Add to your code
context.setConfigurations(new Configuration[] {
new AnnotationConfiguration(), new WebXmlConfiguration(),
new WebInfConfiguration(), new TagLibConfiguration(),
new PlusConfiguration(), new MetaInfConfiguration(),
new FragmentConfiguration(), new EnvConfiguration() });
You only need to set the AnnotationConfiguration to get the auto-discovery of annotated classes to work. The rest of the configurations are so you can enable other aspects of the container. Supposedly you should be able to do this from the commandline, using OPTIONS=annotations,jsp,(etc...), but I never got that working. At least this way it should pick up your annotated classes properly in the embedded environment.
Also as a side note it appears the Eclipse jetty project has annotation turned off by default, whereas riptide claims to have them turned on by default. I'm guessing this is a difference in the configuration files.
Answering yet another year later.
In the current version of Jetty (8.1) you can accomplish exactly what you want with the command line:
java -jar start.jar OPTIONS=annotations,plus etc/jetty-plus.xml
invoked from the jetty home directory.
Jetty 8 is implementing the servlet 3.0 specification but it's still experimental.
You could also use the embedded glassfish 3 plugin to run your tests. See the below links for some info:
http://wikis.sun.com/display/GlassFish/3.1EmbeddedOnePager
http://ocpsoft.com/java/using-embedded-glassfish-with-maven/
http://embedded-glassfish.java.net/
I realise as I write this that there are no authoritative resource for using the Glassfish plugin in the manner Jetty is often used. However it does work in a similar way.
I hope this helps at least a bit.