I want to wrap underlaying RuntimeExceptions to a custom json format , making the servlet container won't dump the stacktrace to client.
I follow this question : JAX-RS (Jersey) custom exception with XML or JSON .
When calling :
try {
doSomething(parameters);
}
catch(RuntimeException e) {
throw new MyCustomException(500 , e.getMessage() , Status.INTERNAL_SERVER_ERROR);
}
When I intentionally feed wrong parameters (and trigger RuntimeException thrown by doSomething() ) , I didn't see MyCustomExceptionMapper working. Instead , the servlet container dumps :
The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
api.MyCustomException: (underlaying msgs)
The MyCustomExceptionMapper is indeed registered in the javax.ws.rs.core.Application :
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(other classes);
set.add(MyCustomExceptionMapper.class);
return set;
}
What did I miss ?
Thanks a lot !
Environment : JAX-RS , jersey-server 1.5
classes spec :
class MyCustomException extends RuntimeException
#Provider
class MyCustomExceptionMapper implements ExceptionMapper<MyCustomException>
updated :
I suspect that Application.getClasses() is never called , so I add some println messages :
#Override
public Set<Class<?>> getClasses()
{
System.out.println("\n\n\n\n ApiConfig getClasses");
}
And in deed , it's never shown !
I am sure this ApiConfig is in the web.xml :
<context-param>
<param-name>javax.ws.rs.core.Application</param-name>
<param-value>destiny.web.api.ApiConfig</param-value>
</context-param>
But why it seems Jersey never calls it ?
I found the solution.
All I have to do is annotate MyCustomExceptionMapper with Spring's #Repository.
And remove the section in web.xml (not needed)
<context-param>
<param-name>javax.ws.rs.core.Application</param-name>
<param-value>destiny.web.api.ApiConfig</param-value>
</context-param>
Because Spring will lookup all #Repository and find a #Provider , and Jersey will make use of it.
I think (on the basis of my experiments) that exception providers are looked up by exact class match, rather than by inheritance match, so an exception provider that handles RuntimeException will only fire if the app throws a raw RuntimeException; that's not the case with the class you've showed us. I have some theories about how to fix this (e.g., with a custom filter handler, or possibly some use of AOP) but nothing final yet.
In relation to the second half of your question, I just don't know. What I do know is that Apache CXF (the JAX-RS implementation I've worked with) has/had some failings in this area, and that I thus stick to registering all my #Providers by hand in the app's Spring config. I offer that as experience…
Your web.xml had an incorrect param-name in web.xml so that setting was being ignored.
The correct param name is javax.ws.rs.Application (not javax.ws.rs.core.Application which is the class you're extending).
See for example:
docs.oracle.com/cd/E24329_01/web.1211/e24983/configure.htm#RESTF179
You just need to configure the servlet in web.xml. You don't need to add #Repository to your ExceptionMapper.
<servlet>
<servlet-name>rest-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.base.package.to.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
When the application is deploy you can see the following lines in log file:
INFO: Scanning for root resource and provider classes in the packages:
your.base.package.to.rest
INFO: Root resource classes found:
class your.base.package.to.rest.resources.FooResource
INFO: Provider classes found:
class your.base.package.to.rest.providers.NotFoundMapper
Tested with:
Jersey v1.11 12/09/2011 10:27 AM
Spring v3.1.1
GlassFish Server Open Source Edition 3.1.2 (build 23)
I faced the same problem and the change in web.xml, in particular in tag solved the issue.
Just make sure that the for the includes your package for the exception and exception mapper classes not only the packages containing the models and resources
jersey.config.server.provider.packages
basepackage
ex. if package for models/entities is pkg.entity and for exception is pkg.exception param-value (basepackage) will be pkg. If basepackage is set pkg.entity it doesn't work...that is how i solved the issue.
Related
Quite a while ago, I had worked on a REST service that was implemented using Apache CXF. Earlier this year, I ported it to Jersey, but there is an incompatibility that I didn't notice at the time.
Apache CXF provides a convenient way to do "extension mapping". The clients of the original service were written to expect this. We're now discovering that using extensions in the client with the ported service isn't working, because extension mapping isn't configured in the service. After looking around for a while, I see that Jersey doesn't provide an obvious way to do this.
Short of changing the clients to not provide the extension, what are some possible strategies for "fixing" this in the service? I'm fairly certain that there are no clients that will be using XML.
Assuming you're using Jersey 2.x, there actually is a convenient way to handle this. It is with the ServerProperties.MEDIA_TYPE_MAPPINGS property. If you are using a ResourceConfig subclass for your configuration you can just do
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("com.example");
Map<String, MediaType> mappings = new HashMap<>();
mappings.put("json", MediaType.APPLICATION_JSON_TYPE);
mappings.put("xml", MediaType.APPLICATION_XML_TYPE);
property(ServerProperties.MEDIA_TYPE_MAPPINGS, mappings);
}
}
If you are using a web.xml, you can do
<servlet>
<servlet-name>JerseyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.example</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.mediaTypeMappings</param-name>
<param-value>xml:application/xml, json:application/json</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I've been trying to setup a Spring MVC controller but when I try to make a GET request, I get a 404 error.
I created a working test example here: https://github.com/Jardo-51/zk-spring-mvc-test
When I run the application on Tomcat and try to make a GET request to: http://localhost:8080/zk-spring-mvc-test/api/v0/foo, I get a 404 error and the logs say:
WARNING: No mapping found for HTTP request with URI [/zk-spring-mvc-test/api/v0/foo] in DispatcherServlet with name 'dispatcher-api'`
I've been trying to fix it according to this answer, and found out that the controller is mapped correctly because the logs on startup say:
INFO: Mapped "{[/zk-spring-mvc-test/api/v0/foo],methods=[GET]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.jardoapps.zkspringmvctest.controllers.FooController.method()
The app uses ZK framework which needs its own servlets so maybe there is a conflict with the DispatcherServlet. Please see my example app for more details (it contains only the necessary code).
Here is the web.xlm (Spring context and MVC config are at the top).
Here is the controller class.
Simply replace #RequestMapping("zk-spring-mvc-test/api/v0/foo") with #RequestMapping("/v0/foo") in your FooController class.
The reason is that the path that you specify into the #RequestMapping annotation is the part of the request's URL beyond the part that called the servlet.
You defined DispatcherServlet's mapping as:
<servlet-mapping>
<servlet-name>dispatcher-api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
So we have zk-spring-mvc-test that is the context root (this is deploy dependent), /api/ that calls the Spring DispatcherServlet, and finally /v0/foo that should be mapped by your controller:
#RestController
#RequestMapping("/v0/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> method() {
return ResponseEntity.ok().body("OK");
}
}
You can see Spring MVC configure url-pattern for further information.
According to the documentation here it says:
If you have a modified web.xml template then you will need to migrate this to Spring as Grails 3.x does not use a web.xml (although it is still possible to have on in src/main/webapp/WEB-INF/web.xml).
which I interpret to mean that if I'm incorporating a 3rd party proprietary library that has a web.xml, then I can put it in src/main/webapp/WEB-INF unaltered (along with everything else that they put in their tomcat webapp directory) and grails will load it. Is this interpretation correct? That's what seems to be implied by this answer.
I started a grails 3 app with the react profile (I tried the web profile too) and a webpage with calls their servlet. However, while an html file in webapp can be found, the servlet call itself is returning 404 and I can't figure out why. If I build a war file and deploy on a standalone tomcat, the servlet call works, but when I run like this:
./gradlew server:bootRun --debug
then it doesn't, and I don't see anything interesting printed to the console.
Is there some URL mapping I need to manipulate or something in application.yml?
In the web.xml, the servlet that's being called looks like this (this is a small piece of it, does it am):
<servlet>
<servlet-name>DataSourceLoader</servlet-name>
<servlet-class>com.isomorphic.servlet.DataSourceLoader</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DataSourceLoader</servlet-name>
<url-pattern>/isomorphic/DataSourceLoader</url-pattern>
</servlet-mapping>
I realize the alternative is to rewrite web.xml using Beans and put stuff in resources.groovy, but I'd prefer an approach that requires as little of my coding as possible.
[update]
I've been able to update my grails-app/conf/spring/resources.groovy with this:
import org.springframework.boot.web.servlet.ServletRegistrationBean
// Place your Spring DSL code here
beans = {
DataSourceLoader(ServletRegistrationBean) { bean ->
servlet = new com.isomorphic.servlet.DataSourceLoader()
urlMappings = ['/isomorphic/DataSourceLoader']
}
}
and it seems to be working... Nevertheless, I am still interested in ways to only use web.xml, if possible, which is my original question.
As part of an upgrade from Grails 2.x to Grails 3.3, I had been previously using web.xml to define 3rd party servlets.
The approach I took in order to make these servlets available(and load at startup) was through a custom class. So you can define a custom class, in src/java as such:
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ServletRegistrations {
#Bean
public ServletRegistrationBean fileServlet(){
ServletRegistrationBean registration = new ServletRegistrationBean(new FileServlet(), "/files/*");
// to load add startup uncomment the line below
//registration.setLoadOnStartup(1);
// define init param
registration.addInitParameter("basePath","/WEB-INF/resources");
return registration;
}
}
So although you don't define everything in one XML file, you can still define all your servlets in this one single class, so it's not a big change and I now I've gone through this once, I prefer to be able to do the definitions in code rather than xml. Hope that helps!
I have Login.xhtml and Home.xhtml. I configured the url pattern in web.xml as follows
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>Login.xhtml</welcome-file>
</welcome-file-list>
When I run the whole project, the login page URL is like this http://localhost:8080/fran/Login.xhtml , here fran is my project name..
However, I would like it to be http://localhost:8080/fran/Login/ instead of http://localhost:8080/fran/Login.xhtml.
How can I achieve this? Is it possible to customize the <url-pattern> for every page to get rid of the .xhtml extension?
If your sole reason is to get rid of the .xhtml extension, then there are various ways depending on the JSF version you're using.
JSF 2.3+
JSF 2.3 offers a new API to collect all views: the ViewHandler#getViews(). Combine this with ServletRegistration#addMapping() in a ServletContextListener as below.
#FacesConfig
#WebListener
public class ApplicationConfig implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
addExtensionLessMappings(event.getServletContext(), FacesContext.getCurrentInstance());
}
private void addExtensionLessMappings(ServletContext servletContext, FacesContext facesContext) {
servletContext
.getServletRegistrations().values().stream()
.filter(servlet -> servlet.getClassName().equals(FacesServlet.class.getName()))
.findAny()
.ifPresent(facesServlet -> facesContext
.getApplication()
.getViewHandler()
.getViews(facesContext, "/", ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME)
.forEach(view -> facesServlet.addMapping(view))
);
}
}
Effectively, this is an oneliner. Source: Arjan Tijms' Blog and The Definitive Guide to JSF.
If you're using MyFaces as JSF 2.3 implementation, then this can be transparently activated by solely the following web.xml context parameter:
<context-param>
<param-name>org.apache.myfaces.AUTOMATIC_EXTENSIONLESS_MAPPING</param-name>
<param-value>true</param-value>
</context-param>
Mojarra does not have an equivalent yet.
JSF 2.2-
Use OmniFaces FacesViews. It offers a zero-configuration way to achieve that by placing the view files in /WEB-INF/faces-views/ folder. Otherwise, if you intend to not modify your project structure and want to keep your view files at the usual place and still benefit of extensionless URLs, then it's a matter of adding the following context parameter:
<context-param>
<param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
<param-value>/*.xhtml</param-value>
</context-param>
In case you don't want to use OmniFaces, but rather want to homegrow your own, just look at source code of OmniFaces. It's open source under Apache 2.0 License. It's only not an oneliner.
Take a look at prettyfaces: Pretty URLs for JavaServer Faces ,
Look at the 2. Create pretty-config.xml example in the main page
And take a look at the Chapter 2. Get Started
Just a little complement to mr. #balusc excelent answer about MyFaces on JSF 2.3... (Edit: not really a complement as one can not complement what's complete, but just a workaround for tomcat/tomee users to deal with this tomcat bug).
Using MyFaces 2.3.6, I received a Exception talking about servlet specification and ServletContextListener:
java.lang.UnsupportedOperationException: Section 4.4 of the Servlet 3.0 specification does not permit this method to be called from a ServletContextListener that was not defined in web.xml, a web-fragment.xml file nor annotated with #WebListener
Following the stack i saw this line:
at org.apache.myfaces.webapp.StartupServletContextListener.contextInitialized(StartupServletContextListener.java:103)
And after adding that listener to web.xml all worked fine:
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
I am back with working in Springs. I used to work in Springs but blindly, didn't understand much. I used to get a lot of errors, very basic ones, and I am getting them again.
My problem is that, I don't know how the configuration of the Spring-MVC work.
What happens when I run the project from my STS?
I am working on the spring template project in STS.
I am getting this when I run the project.
WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/common/] in DispatcherServlet with name 'appServlet'
I am totally fed up and broken.
Just 2 months of break from work, I am back at the starting block.
I don't want to post my code and make the question specific.
I want an answer that explains the way in which the server executes a spring project. Right from the running of an application(basic hello world application) to the display of the home page.
This will be helpful for all the beginners.
I tried searching for such an explanation in the net but I didn't get any proper explanation, but got a lot of basic samples. Those samples are easy to understand but are not explaining the way in which the server goes about.
Note: I am looking for an answer that explains the Springs concept. From the running of an application to the display of a home page. What all happens in this process? Where does the server start with? How does it go about?
Here is the flow initially servlet container loads the web.xml file.In web.xml we will specify that all the requests are handled by the spring FrontController that is DispatcherServlet.
We include it by adding the following code
<servlet>
<servlet-name>dispatcher</servlet-name>
<servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Here it indicate if the url request is of *.htm it is handled by dispatcherServlet then dispatcherServlet load dispatcher-servlet.xml . Where we need to mention the mapping to controller by writing the specific url request such as
<bean name="/insert.htm" class="com.controller.MyController"></bean>
So in bean we mention that for request of /insert.htm it tells the servlet to look in the mentioned class.You need use the Annotation of #RequestMapping above the method for ex
#RequestMapping("/insert.htm")
public ModelAndView insert(HttpServletRequest req,Student student)
{
String name=req.getParameter("name");
int id=Integer.parseInt(req.getParameter("id"));
student.setId(id);
return new ModelAndView("display","Student",student);//It returns a view named display with modelclass name as `Student` and model object student
}
So when a Request url of /insert.htm appears it executes the above method it returns a ModelAndView object nothing but an view.It again goes to dispatcher-servlet.xml and looks for view Resolver the normal code that is to be added is
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
So from this it gets the logical view name and appends the prefix and suffix to it .Finally it displays the content in the view.so it looks for display in view resolver prefixes and suffixes the things and finally returns /WEB-INF/jsp/display.jsp .Which displays the jsp content
You are mapping your Spring servlet only for requests that end with .htm. The request for the root of your application does not end with .htm and so, it does not get picked up by Spring. Edit your web.xml as follows, in order to use Spring for all requests:
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Then, use this as the controller:
package com.mkyong.common;
#Controller
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView helloWorld() {
ModelAndView model = new ModelAndView("index");
model.addObject("msg", "hello world");
return model;
}
}
The controller intercepts the requests for the context root of the application, adds the msg attribute to the model and redirects to the index view.
So, you need to add the index.jsp file in the /WEB-INF/views/ directory. Inside your jsp, you will be able to use the value of the msg attribute.
From what every you have posted you do no have a request mapping for the url /common/.
You will have to create another request mapping function like the one below in your controller class and create a view file also.
#RequestMapping(value = "/common/", method = RequestMethod.GET)
public ModelAndView common(HttpServletRequest request,
HttpServletResponse response) {
ModelAndView model = new ModelAndView("common");
model.addObject("msg", "hello world");
return model;
}