I'm working on creating a security library that will be used by several RESTful clients. I'm using Java EE 5, Jersey 1.17 and Maven. The clients will use my library to call a third party app using a token. The third party app will then return all the information it has on that token, like expiration, scope and userId.
My idea is to make a filter that will check if there is an Authorization header, and if that's so, it calls the third party app. If the third party app validates the token and returns the token's info, I need to return that information, stored in a TokenInformation object, back to the resources. In a previous post, someone said that I could do this:
public class MyFilter implements Filter{
#Override
public void doFilter(final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String header = req.getHeader("Authorization");
TokenInformation info = new TokenInformation();
info = buildInfo(info);
if (true) {
request.setAttribute("auth", info);
chain.doFilter(request, response);
} else {
handleError(response);
}
}
}
So, by sending the TokenInformation object to the request as an additional attribute, I would be able to retrieve it later in the resource classes. The thing is that I'm using Java EE 5, and I didn't realize that I couldn't use the #Context annotation to inject the ServletRequest object. How can I access the ServletRequest object again from a resource class, so that I can access the TokenInformation object in, for example, the DAO?
The way I'm using jersey is by doing this in my web.xml:
<servlet>
<servlet-name>Security API</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.ni.apps.engineering.securitylibrary.resources.SecurityResource</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Security API</servlet-name>
<url-pattern>/1/*</url-pattern>
</servlet-mapping>
The SecurityResource class has this:
public class SecurityResource extends Application{
public static final String SUPPORTED_REPRESENTATIONS = MediaType.APPLICATION_XML
+ "," + MediaType.APPLICATION_JSON;
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(Security.class);
return set;
}
}
The Security class has this:
#Path("")
public class Security implements ISecurity{
#Override
public Response get(String upId) {
String test = "";
try{
TokenInformation tI = (TokenInformation) HttpServletRequestWrapper.
test = "test1";
}catch(Exception e){
System.out.println(e.getMessage());
}
return null;
}
}
You don't have to access ServletRequest at Dao layer.
In Servlet you can get ServletRequest object and you can pass the value to Dao layer.
If you really want to access then pass ServletRequest object to Dao layer by reference.
<servlet-name>Security API</servlet-name>
<servlet-class>com.packagename.MyServlet</servlet-class>
public MyServlet extends com.sun.jersey.spi.container.servlet.ServletContainer{}
You can extend jersey servlet and you can initialize Servlet with Application Class by programatically.
In MyServlet you can reach the request object.
Servlet Information :
https://jersey.java.net/apidocs/1.17/jersey/com/sun/jersey/spi/container/servlet/ServletContainer.html
Related
I have a web application implementing REST API using Jersey. The web container id Tomcat
Here is a summary of the API:
/rest/patients
Gets a list of patients' metadata.
/rest/patients/{id}
Gets detailed data about a specific patient.
/rest/patients/{id}/visits
Gets a list of visits` metadata for a specific patient.
/rest/patients/{id}/visits/{visitId}
Gets detailed data about a specific visit of a specific patient.
My problem is that I can't get the sub-sub resources. For example, when I request /rest/patients/1 the detailed data of patient #1 is received correctly.
But when I request /rest/patients/1/visits I get 404 error, and the flow doesn't even enter the getVisits() method.
It looks like that when a request for a specific patient id received (patients/{id}), Jersey is directing it correctly from PatientsMetadataResource to PatientsResource.
But when a visits sub-sub-resource is being requested (patients/{id}/visits), Jersey doesn't direct it into the PatientsResource.
So how can I direct a sub resource along with all of its sub-sub resources into the same class?
Code for PatientsMetadataResource (The name is a bit vague, and I need to change it):
#Path("/patients")
public class PatientsMetadataResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getPatients(#QueryParam("page") int pageIndex) {
//.... Loads, Builds and returns the patients' metadata list correctly
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/{uid:\\d+}")
public PatientResource getPatient(#PathParam("uid") int uid) {
return new PatientResource(uid);
}
}
Code for PatientResource:
public class PatientResource {
private final int uid;
public PatientResource(int uid) {
this.uid = uid;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getPatient() {
//Returns the patient correctly
System.out.println("A Patient was asked");
Patient patient = PersistentDataProvider.loadPatientByUid(uid);
return Response.ok(patient).build();
}
#GET
#Path("/visits")
#Produces(MediaType.APPLICATION_JSON)
public VisitsResource getVisits(#PathParam("uid") int patientUid) {
//The flow doesn't even enter here. A 404 is being returned instead.
System.out.println("Visits were asked");
return new VisitsResource(patientUid);
}
}
Code for Jersey part in web.xml:
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>il.co.site_building.dvardy.resources</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Sub-Resource Locators aren't supposed to have HTTP method annotations
// #GET <--- Remove this
// #Produces(MediaType.APPLICATION_JSON)
#Path("/{uid:\\d+}")
public PatientResource getPatient(#PathParam("uid") int uid) {
return new PatientResource(uid);
}
Their main purpose is simply to forward request to the sub-resource class, not GET/POST/etc anything. When Jersey sees that HTTP method annotation, it no longer gets treated as a sub-resource locator.
Also you don't need need to pass the id. It will get passed accordingly
#Path("parent")
class ParentResource {
#Path("{id}")
public ChildResource getChild() {
return new ChildResource();
}
}
class ChildResource {
#GET
public Response get(#PathParam("id") long id) {}
#GET
#Path("something")
public Response something(#PathParam("id") long id) {}
}
Here GET 'parent/1' goes to ChildResource.get, passing the path param and GET parent/1/something goes to ChilsResource.something, passing the path param
My application is visited from different countries and I am using a common servlet Filter (MyFilter.java) to control all requests. Is it possible to redirect to other Servlets based on country's visitor?
Currently my web.xml configuration is below
<filter>
<filter-name>myfilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Suppose if user visits from USA I want to redirect it to US_Filter.java , or if user visits from Australia I want to redirect it to AU_Filter.java, or if User visits from UK I want to redirect it to UK_Filter.java. Is this possible from web.xml?
I am thinking of making Country wise configuration in web.xml like
country=US (US_Filter)
country=AU (AU_Filter)
country=UK (UK_Filter)
country=None (MyFilter)
But I don't know how?
I require this because of different behaviour we perform based on countries for example their mobile no validation, managing users subscription services, etc.
Please provide me suggestions.
Thanks,
I think it's not possible to use your web.xml for this. However, you can accomplish what you want by coding your MyFilter class carefully, so that you don't need to modify it when adding a new country.
I see you're using Spring. That's good news, since MyFilter is actually a bean managed by Spring. This means that other beans might be injected to it. My suggestion is that you have one bean per country and one main filter that would be in charged of delegating to the right country bean.
Firstly, let your web.xml as it is now:
<filter>
<filter-name>myfilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
This will let the bean named myfilter be invoked for every request.
Then, implement MyFilter in such a way that it's country-agnostic, so that it doesn't need to be modified when a country is added or removed:
#Component("myfilter")
public class MyFilter implements Filter {
public static final String DEFAULT = "default";
public static final String SUFFIX = "_Filter";
// Autowire all beans that implement CountryFilter, mapped by bean name
#Autowired
private Map<String, CountryFilter> filters;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Get country code from request
Locale locale = request.getLocale();
String countryCode = locale.getCountry().toUpperCase();
// Format key to gey country-specific filter
String key = countryCode + SUFFIX;
// If bean doesn't exist for request country...
if (!this.filters.containsKey(key)) {
// ..fallback to default filter
key = DEFAULT + SUFFIX;
}
// Get filter for country
CountryFilter filter = this.filters.get(key);
// Delegate to actual country (or default) filter
boolean countinueChain = filter.doFilterForCountry(request, response);
if (continueChain) {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig config) throws ServletException {
}
#Override
public void destroy() {
}
}
This class is generic enough. You wouldn't need to change it when adding or removing a country. The trick is to use Spring autowiring behavior for collections. If you autowire a Map<String, T>, then Spring will populate this map with all instances of beans of class T, being keys equal to the bean names and values the corresponding bean instances.
Then, you'd need the CountryFilter interface:
public interface CountryFilter {
boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
You'd need to implement the CountryFilter for each country, letting it be a Spring bean whose name matches the pattern CC_Filter, where CC stands for the 2-digit ISO country code. For example, for US you might have:
#Component("US" + MyFilter.SUFFIX)
public class UsFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle US specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to US page
// ((HttpServletResponse) response).sendRedirect("US-url");
// return false;
// ONLY ONE of the options!
}
}
And for the UK:
#Component("UK" + MyFilter.SUFFIX)
public class UkFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle UK specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to UK page
// ((HttpServletResponse) response).sendRedirect("UK-url");
// return false;
// ONLY ONE of the options!
}
}
Same for other countries.
Finally, it might happen that you have no implementation for a given country. In this case, you might want to have a default filter as a fallback case:
#Component(MyFilter.DEFAULT + MyFilter.SUFFIX)
public class DefaultFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle DEFAULT specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to DEFAULT page
// ((HttpServletResponse) response).sendRedirect("DEFAULT-url");
// return false;
// ONLY ONE of the options!
}
}
Hope this helps you solve your problem. I believe it's a quite flexible approach, it even has a fallback case. To add a new country, all you'd need to do is implement a new CountryFilter.
http://examples.javacodegeeks.com/core-java/util/locale/java-util-locale-example/
please go through this site!!
Using that site i was suggesting you to add another filter so that it check for locale & redirect to desired servlet.
So, let's have this simple controller:
#Controller
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
#RequestMapping(value="/entities", method = RequestMethod.GET)
public #ResponseBody ResultPojo getSomething() {
logger.info("getSometing");
return new ResultPojo();
}
}
...and the following context fragment:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Which basically means I want to be able to return nothing but json representations of the result bean, otherwise return 406.
If I send a GET request with accept=application/json, everything works fine, a json representation is returned in the http response with the 200 Ok status.
If I send a GET request with accept=application/xml, 406 is returned.
My problem in the second case is that even though 406 is returned eventually, the getSomething() method is still called (which I can see in the log). While this is no big deal for GET methods, it can cause confusion for POST methods (the resource is altered, but 406 is returned).
Is there a simple way to tell SpringMVC to check the accept header and return 406 before invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?
Is there a simple way to tell SpringMVC to check the accept header and return 406 before
invoking the controller method? Or do I have to develop a custom http SpringMVC interceptor?
the problem is I would have to put the produces clause to every
#RequestMapping in every controller. I'd like to set this on an
application level.
as far as I know there is no simpler method with SpringMVC. However, using standard JEE filters this is not very hard to do either. Just do something like:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class YourFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
if (request.getRequestHeader("Accept").contains("application/json")) {
chain.doFilter(req, res);
} else {
((HttpServletResponse)response).setStatus(SC_NOT_ACCEPTABLE);
}
}
public void init(FilterConfig config) throws ServletException {
// any startup stuff here if needed
}
public void destroy() {
// add code to release any resource
}
}
and:
<filter>
<filter-name>YourFilter</filter-name>
<filter-class>
path.to.YourFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>YourFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(didn't test the code, but it should be about right)
Maybe this is what you want:
#RequestMapping(value = "/entities", method = RequestMethod.GET, headers = {"content-type=application/json"})
methodName() {
...
}
First, a bit of context:
web.xml:
I match all /app/* requests to my dispatcher:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
My question, why does calling /app/import/view work when returning String:
Controller:
public class ImportController extends MultiActionController {
public String view(HttpServletRequest request, HttpServletResponse response) throws Exception
return "importer.home";
}
but NOT when returning ModelAndView:
public class ImportController extends MultiActionController {
public ModelAndView view(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("importer.home");
mav.addObject(new ExlFile());
return mav;
}
Is there a different naming convention if we use ModelAndView?
You're using MultiActionController, which has very strict constraints on method naming conventions and signatures.
However, you shouldn't be using MultiActionController at all, it's obsolete and deprecated. Write controllers as described in the Spring manual, i.e. using #Controller and #RequestMapping, then you get much more flexible method signatures.
If you are using spring 3.0, then i will prefer to use annotation.
#Controller
#RequestMapping(value = "/app")
public class ImportController {
#RequestMapping(value="/importer/home")
public ModelAndView view(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("importer.home");
mav.addObject(new ExlFile());
return mav;
}
Can we write an argument constructor in a Servlet? If yes, how can you call?
Can we write an argument constructor in a Servlet?
Yes, you can but it is useless since the servlet container won't invoke it.
The proper way to do it is to use the init() method:
#Override
public void init() throws ServletException {
String foo = getInitParameter("foo");
String bar = getServletContext().getInitParameter("bar");
// ...
}
In this example, getInitParameter("foo") returns the value of the <init-param> of the specific <servlet> entry in web.xml, and getServletContext().getInitParameter("bar") returns the value of the independent <context-param> in web.xml.
The problem can be state more generically:
"According to the servlets (2.3)
specification, the servlets are
instantiated by the servlet engine by
invoking the no-arg constructor. How
can I initialize a servlet properly
given that correct initialization
depends on the
central/global/unique/application
configuration?"
Actually, you can use serlvets with constructor and/or initialize them as you like. However, it requires a little bit of plumbing.
Assuming you have a servlet with a constructor having arguments:
package org.gawi.example.servlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SampleServlet extends HttpServlet
{
private final String mMessage;
public SampleServlet(String message)
{
mMessage = message;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/plain");
response.getWriter().write(mMessage);
}
}
The first thing you need is a unique servlet whithin your application, let's call it InitializationServlet, to create all the required instances. Those instances must then be exported in the servlet context to be retrieve by another servlet (explained later). The InitializationServlet may look like this:
package org.gawi.example.servlets;
import javax.servlet.*;
import javax.servlet.http.*;
public class InitializationServlet extends HttpServlet
{
public void init() throws ServletException
{
SampleServlet servletA = new SampleServlet("this is servlet A");
SampleServlet servletB = new SampleServlet("this is servlet B");
SampleServlet servletC = new SampleServlet("this is servlet C");
getServletContext().setAttribute("servletA", servletA);
getServletContext().setAttribute("servletB", servletB);
getServletContext().setAttribute("servletC", servletC);
}
}
You see that only the init() method has been provided. This servlet is not servicing any HTTP request. Its only purpose is to store the servlet in the ServletContext. Note that you could have also use this servlet to load your application configuration. So this can act as the web-application entry point, like the main(String[] args) method of a program. This might remind you of the ContextLoaderServlet of SpringSource.
The last piece is the DelegateServlet that will effectively be instantiated by the servlet container, only this servlet will forward all the pertinent method calls to the wrapped servlet instance:
package org.gawi.example.servlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class DelegateHttpServlet extends HttpServlet
{
private static final String SERVLET_CONTEXT_KEY_INIT_PARAMETER = "servletContextKey";
private HttpServlet mServlet;
public void init(ServletConfig servletConfig) throws ServletException
{
super.init(servletConfig);
locateServlet(servletConfig);
mServlet.init(servletConfig);
}
private void locateServlet(ServletConfig servletConfig) throws ServletException
{
String servletContextAttributeName = servletConfig.getInitParameter(SERVLET_CONTEXT_KEY_INIT_PARAMETER);
if (servletContextAttributeName == null)
{
throw new ServletException("Unable to find init parameter '" + SERVLET_CONTEXT_KEY_INIT_PARAMETER + "'");
}
Object object = servletConfig.getServletContext().getAttribute(servletContextAttributeName);
if (object == null)
{
throw new ServletException("Unable to find " + servletContextAttributeName + " in servlet context.");
}
if (!(object instanceof HttpServlet))
{
throw new ServletException("Object is not an instance of"
+ HttpServlet.class.getName()
+ ". Class is "
+ object.getClass().getName()
+ ".");
}
mServlet = (HttpServlet) object;
}
public void destroy()
{
mServlet.destroy();
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
mServlet.service(req, res);
}
}
During its initialization, the DelegateServlet will look-up the target servlet in the servlet context using the servletContextKey servlet initial argument.
The web.xml for such an application might look like that:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Example</display-name>
<description>Example web showing handling of servlets w/ constructors.</description>
<servlet>
<servlet-name>Initialization</servlet-name>
<servlet-class>org.gawi.example.servlets.InitializationServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>A</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletA</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>B</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletB</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>C</servlet-name>
<servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>
<init-param>
<param-name>servletContextKey</param-name>
<param-value>servletC</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>A</servlet-name>
<url-pattern>/servlet/a</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>B</servlet-name>
<url-pattern>/servlet/b</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>C</servlet-name>
<url-pattern>/servlet/c</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>5</session-timeout>
</session-config>
</web-app>
Be sure to load the InitializationServlet first, using a low <load-on-startup> value.
The benefit of this approach is that HttpServlet objects can be handled like any other regular Java object or bean. Hence, it provides a better control over initialization: no more tricky stuff to do in the init() method, nor messy servlet init-arg handling.
You can't. Servlet is instantiated reflectively by container. If servlet spec have allowed arguments in constructor, you would have to have some complicated deployment descriptor like,
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
<servlet-argument id="1" type="string">Test<servlet-argument>
<servlet-argument id="2" type="int">10</servlet-argument>
<load-on-startup>1</load-on-startup>
</servlet>
I guess no one wants that.
Constructors are objects managed by the application server.
For initialization, see the init() method.
Update:
Can I use a constructor in my servlet?
A: A servlet is a normal Java class, so when there are no custom
constructors, there is an implicit
default constructor with no arguments.
Servlet containers typically use the
Class.newInstance() method to load
servlets, so you must be careful to
add an explicit default constructor if
you add non-default constructors.
source: http://www.codestyle.org/java/servlets/FAQ.shtml