Implementing a REST api which switches between XML and JSON in Servlets - java

Twitter's REST api allows you to append a JSON or XML at the end of the URI.
e.g.
http://twitter.com/statuses/public_timeline.json
http://twitter.com/statuses/public_timeline.xml
In the context of servlet, statuses will be the webapp context name & public_timeline.json is mapped to some servlet. I want to maintain one servlet to do the dispatch. Can this be done in servlets ?

The URL mapping in Servlet only supports extension matching, not prefix matching. So this doesn't work,
<url-pattern>/public_timeline.*</servlet-class>
Here is my suggestion,
<servlet-mapping>
<url-pattern>/*</servlet-class>
<servlet-name>YourServlet</servlet-class>
</servlet-mapping>
In the servlet, you can do this,
String path = request.getPathInfo();
if (path == null) return;
int index = path.indexOf('/');
String api;
if (index < 0)
api = path;
else
api = path.substring(index+1);
if (api.equals("public_timeline.json"))
// Process it as JSON
if (api.equals("public_timeline.xml"))
// Process it as XML
Most APIs use a format parameter to indicate response format. I think that's the better way.

In Java, if you are using JAXRS for your rest services, annotate your REST service method like:
#Path("/statuses/public_timeline{fileExt}")
Then add the following to you method argument:
#PathParam(value="fileExt") String fileExt
The the fileExt variable would be your extension. I think that would be a standard REST way to do it using JAXRS

Yes, it is possible.
Take a look at the web.xml file you may use a pattern like:
<servlet-mapping>
<url-pattern>public_timeline.*</servlet-class>
<servlet-name>myservlet</servlet-class>
</servlet-mapping>
I think you can even use public_timeline.[json|xml]

You can do simple glob-matching in url-pattern:
<servlet>
<servlet-name>public_timeline</servlet-name>
<servlet-class>your.package.PublicTimelineServlet</servlet-class>
</servlet>
<servlet-mapping>
<url-pattern>/public_timeline.*</servlet-class>
<servlet-name>public_timeline</servlet-class>
</servlet-mapping>

Related

Redirect the servlet request to another servlet

In our app for all the notification we trigger through mail.
All the templates have non sso link
>/Userlogin?param1=param2value&param2=param2value">Link to access app
I need to modify this link in all templates to
>/Userloginsso?param1=param2value&param2=param2value">Link to access app
Since there are many templates and takes lot of manual effort, is there any way we can redirect the request of Userlogin to Userloginsso. Any configuration that we can do in web.xml ?
Considering you have a mapping for Userlogin in web.xml as below:
<web-app>
<servlet>
<servlet-name>Userlogin</servlet-name>
<servlet-path>com.something.Userlogin</servlet-path>
</servlet>
<servlet-mapping>
<servlet-name>Userlogin</servlet-name>
<url-pattern>/Userlogin</url-pattern>
</servlet-mapping>
</web-app>
Modify existing mapping to :
<web-app>
<servlet>
<servlet-name>Userloginsso</servlet-name>
<servlet-path>com.something.Userloginsso</servlet-path>
</servlet>
<servlet-mapping>
<servlet-name>Userloginsso</servlet-name>
<url-pattern>/Userlogin</url-pattern>
</servlet-mapping>
</web-app>
Now all calls to Userlogin will be redirected to Userloginsso servlet.
You could do a simple redirect in your UserLogin servlet with the following:
public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException {
String param1 = request.getParameter ("param1");
String param2 = request.getParameter ("param2");
// other parameters
// Build the new url: if too much parameters, prefer using a StringBuilder over String concatenation for better performances
String baseUrl = request.getContextPath () + "/Userloginsso?param1=" + param1 + "&param2=" + param2;
String encodedUrl = response.encodeRedirectURL (baseUrl);
response.sendRedirect (encodedUrl);
}
If I understand your question correctly you could use a filter example here get the url and foward it somplace else in your app. Or and url rewrite library such us this one
If you still want a servlet you could use a ProxyServlet. There are already many good implementations.
Examples:
Complex proxy servlet with all features
Simple proxy servlet, limited features

About Welcome Page and Accessing Web Service Methods

I have a problem with configuration ( or basic understanding how things work at background). I create a JAVAEE project by checking Web application and ReSt api checkbox ( in intellij with glassfish 5.0). I have sample code below which web methods work but welcome page does not work. My web.xml and sample web service methods are below.
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>test</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 Web Application</servlet-name>
**<url-pattern>/ *</url-pattern>**
</servlet-mapping>
#Path("/RestTest")
public class TestString {
#Context
ServletContext context;
#GET
#Path("insertdb/{param1}/{param2}")
#Produces(MediaType.APPLICATION_JSON)
public Object writeToDb( #PathParam("param1") String param1
,#PathParam("param2") String param2){
try{
String password= context.getInitParameter("DbPassword");
Class.forName("org.mariadb.jdbc.Driver");
Connection dbCon = DriverManager.getConnection(
"jdbc:mariadb://xxx/testdb", "root", password);
PreparedStatement stmt=dbCon.prepareStatement(
"INSERT INTO TestTable VALUES(?,?)");
stmt.setString(1,param1);
stmt.setString(2,param2);
stmt.executeUpdate();
dbCon.close();
return "Success";
}catch(SQLException | ClassNotFoundException ex){
return ex.toString();
}
}
#GET
#Path("sum/{sum1}/{sum2}")
#Produces(MediaType.TEXT_HTML)
public String calculateSum(#PathParam("sum1") int param1
,#PathParam("sum2") int param2){
return ""+(param1 + param2);
}
If i change this line url-pattern "/*" to "/"
then welcome page is accessible but not methods.
Thus what i want is, having a welcome page which i will use for documentation for my web services(i dont want SOAP) and web methods must work by adding / to base url. How can i achieve that and what is difference between /* and /
See here for explanation of differences:
What is url-pattern in web.xml and how to configure servlet
Generally for a rest api it is best to use a path specific to all rest calls, for instance http://localhost/mywebapp/rest/...
Something like:
<servlet-mapping>
<servlet-name>jersey-servlet/servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You only want jersey serving particular URLs when it is bundled in a WAR that also includes html pages.
To answer your question, difference between "/" and "/*"
A mapping that contains the pattern "/" matches a request if no other pattern matches. This is the default mapping. The servlet mapped to this pattern is called the default servlet. The default mapping is often directed to the first page of an application. Example :
Both requests will display same contents from index.jsp
http://myhost.com/index.jsp
and
http://myhost.com/
Now, a mapping that contains "/*" overrides all other servlets, including all servlets provided by the servlet container such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets.
Possible solution for your issue
Change the URL pattern to specific instead of default pattern.
<servlet>
<servlet-name>webservice</servlet-name> //servlet name
<servlet-class>com.rest.MyRestServlet</servlet-class> //servlet class
</servlet>
<servlet-mapping>
<servlet-name>webservice</servlet-name> //servlet name
<url-pattern>/RestTest/*</url-pattern> //all webservice request
</servlet-mapping>
All the web service request are accessible through
http://myhost.com/RestTest/
You may also be interested to look
What is URL-pattern in web.xml and how to configure servlet
Basics of Java Servlet
Servlet configuration and url-pattern
As you highlighted, your problem revolves around those four lines:
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
As Jim Weaver mentioned, it's a matter of url-pattern.
Solution(s)
You may consider three solutions (from the most-preferred to least-preferred):
dedicated REST URL: the easiest way is to have a dedicated url-pattern such as /rest/* for your web services. You can benefit some assets such as url hierarchy or you can easily implement a security framework over it.
URL rewriting may be an option and this answer suggests some library. I haven't tested those libraries myself
Page redirection can be an option to go around REST filtering but in the specific case of having the url-pattern at /*, I have to say I'm not sure if it's working for the reason I'll explain in next section
now a bit of explanation of what happened
Why setting the url-pattern at /* prevent from accessing the welcome page?
Actually, it's not only the welcome page that is not accessible: it's all the resources under the defined url-pattern. Whatever get in touch with REST stays with REST... Here is the schema taken from JSR 339 Appendix C:
With a GlassFish 5.0, I guess you're using JAX-RS 2.1 (JSR 370) but the sections I'm quoting have the same content
Without entering into detail, it is visible that only ContainerRequest Filters are executed in the process. Especially, it's worthy to notice that after Request Matching, requests are not forwarded to any servlet in a sense that standard resources are not reachable, unless specified by the REST method.
It's also worthy to highlight the fact the servlet filters are executed beforehand (leveraging this point is absolutely vital for managing security for example). I did not find back the source proving this point but I know it's somewhere on the web ^^.
Request matching is defined at section 3.7.2. In a nutshell, it is in three steps where the first one is the most important for your question, especially at step D:
Identify a set of candidate root resource classes matching the request
c. ...
d. If [the set of classes whose path matches the request URL] is empty then no matching resource can be found, the algorithm terminates and an implementation MUST generate a NotFoundException (404 status) and no entity
e. ...
highlights are mine.
The two last steps are
Obtain a set of candidate resource methods for the request
Identify the method that will handle the request
TL;DR
What happened when you set <url-pattern>/*<url-pattern> and tries to access to your welcome page (or any page actually):
Your server receives the GET request
If there are filters (such those from a security framework), there are executed
REST enters the scene
Pre Match filters are executed (none if your case)
Fetch your root resources classes (in your example, all classes in the test package)
Find if one of those class match the request URL
None are found, a 404 status is returned
To avoid unnecessary URL conflicts, the best options would be having a dedicated URL for your REST methods
If you mention in web xml like following all the request receive by 'Jersey Web Application' servlet. so request to index.jsp also redirect to 'Jersey Web Application' servlet
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
To avoid this add some prefix to the url to separate rest request like following
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>rs/*</url-pattern>
</servlet-mapping>

How to use a URL like a parameter

I want a simple servlet that can be used without parameters. Something like :
http://servername:8080/do/view/username/address
and use it like a parameter :
http://servername:8080/do?action=view&login=username&page=address
Both urls will have the same behaviour. I prefer don't use any framework, only servlets and filters.
How can I obtain the url name from the servlet? What's the best solution?
Response:
Based on the reply made by #BalusC i have created the following servlet that do all i want:
#WebServlet("/do/*")
public class ActionTestCasesServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
String pathInfo = request.getPathInfo();
String[] parts = pathInfo.substring(1).split("/");
RequestDispatcher destination = getServletContext()
.getRequestDispatcher("/" + parts[0] + ".jsp");
if (parts.length > 1) {
request.setAttribute("username", parts[1]);
}
if (parts.length > 2) {
request.setAttribute("page", parts[2]);
}
destination.forward(request, response);
}
}
This code call the "view.jsp" passing the attributes "username" and "page".
Just map the servlet on /do/* instead of /do.
#WebServlet("/do/*")
This way you can obtain the path information using HttpServletRequest#getPathInfo().
String pathInfo = request.getPathInfo(); // /view/username/address
You can use the usual String methods like String#split() to split it in fragments.
String[] parts = pathInfo.substring(1).split("/"); // view, username, address
See also:
Design Patterns web based applications - for the case you intend to homegrow MVC (note: I don't recommend this, it's fun if it's for learning/hobby purposes, but if it's for real job, rather pick an existing MVC framework so that you can ensure that every pitfall is covered)
You say you'd prefer not to use "any framework, only servlets and filters", but have you considered the tuckey.org UrlRewriteFilter? This is a single filter that you can register in web.xml and then declare rules such as
<rule>
<from>/do/(.+)/(.+)/(.+)</from>
<to>/do?action=$1&login=$2&page=$3</to>
</rule>
in an XML file. Then you just write your servlets to expect the query parameters as normal and let the filter deal with the "pretty" URLs.
Spring MVC do it very very good. But if you do not want to use third-party frameworks all you can is handle request.getRequestURI(), split this string and do what you want. For example you can use pattern /entity/action.
I think previous threads reply are the perfect solution. I played with web.xml file to see what I can do for and here is my results. I was able to change the web.xml to get a url similar to what you want "http://localhost:8080/do/myServlet.do". Here is web.xml file content which pertains to the servlet-name and url-pattern.
<servlet>
<servlet-name>do</servlet-name>
<servlet-class>ControlServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>do</servlet-name>
<url-pattern>/myServlet.do</url-pattern>
</servlet-mapping>
</web-app>

Trouble with web.xml url pattern

In my web.xml I have URL pattern for servlet like this:
<url-pattern>/*/myservice</url-pattern>
So I want to call it using blablabla/myservice also as anyWord/myservice.
But it doesn't work. It work only if I call it using this URL: /*/myservice (with asterisk in URL).
You can't do that.
According to the Servlet 2.5 Specification (and things aren't that different in other levels of the specification), chapter SRV.11.2:
A string beginning with a / character and ending with a /* suffix
is used for path mapping.
A string beginning with a *. prefix is used as an extension mapping.
A string containing only the / character indicates the "default" servlet of the application. In this case the servlet path is the request URI minus the context path and
the path info is null.
All other strings are used for exact matches only.
Your case falls under the 4th bullet, so exact mapping is used.
To circumvent that, use a mapping of / (third case). Map all requests to go to a particular servlet, and have that servlet re-route requests to handlers of some sort (either other servlets, or some custom classes).
For example:
<url-pattern>/</url-pattern>
<servlet-name>MyServlet</servlet-name>
And then, within MyServlet's code, inspect the URL that you received in the request (using request.getPathInfo()) and use the value to forward the request to other handlers.
You could use a filter while your url pattern is /* and inside the filter decide which redirection you required.
<filter>
<display-name>MyFilter</display-name>
<filter-name>MyFilter</filter-name>
<filter-class>com.MyfilterClass</filter-class>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</filter>
What about two ULR-mapping sections?
<servlet-mapping>
<servlet-name>ModifyMemberSVL</servlet-name>
<url-pattern>/ModifyMember</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ModifyMemberSVL</servlet-name>
<url-pattern>/Administration/Add_Member/ModifyMember</url-pattern>
</servlet-mapping>

URL Pattern for servlet mapping in web.xml

I need a workaround with this URL mapping in web.xml to create URLs with a letter, followed by a "_" followed by any combination of alphanumeric characters.
I want to map a servlet to something like this:
/something_*
Instead of:
/something/*
Using different "somethings" for different JSP's. Example:
/search_Something-I-searched-for
I tried using:
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/something_*</url-pattern>
</servlet-mapping>
But this doesn't seem to work.
This answer tells me I can't do this within web.xml, so maybe there's some workaround.
I don't know if this information is important, but I'm using JBoss and Struts2 in my project.
Map a servlet to the containing directory. Inside that servlet, take apart the URL path and forward to the appropriate named servlet.
Why not try Spring MVC Framework. Spring can offer that url mapping you want.
#RequestMapping(value="/something_{name}", method=RequestMethod.GET)
public String demo(#PathVariable(value="name") String name, ModelMap map) {
String something = name;
// Do manipulation
return "something"; // Forward to something.jsp
}
Watch this Spring MVC Framework Tutorial

Categories