Spring and returning protected resources - java

I have a pretty standard Spring 3.0.7 web app
The structure is like this
WebContent/
resources/
myStaticConent/
WEB-INF/
views/
myProtectedContent/
I am using the <mvc:resources> configuration for the static content and my controllers get views using the InternalViewResolver from WEB-INF/views
Now I have a requirement to return non-JSP content ( JPGs,PNGs,HTML,etc ) from a protected directory in WEB-INF
So a user might enter a URL like http:myWebApp/myProtectedContent and hit my protected content controller.
#Controller
public class HelloWorldController {
#RequestMapping(value="/myProtectedContent")
public String index() {
return "myjpg.jpg";
}
}
Essentially I want to conditionally serve a file just like I would a view. Anyone know how this can be done ?
I looked at some of the other methods here, Streaming using Inputstream seems overkill for files that are essentially static. Can I register another "view" type ? I need this to appear l( from the web browser side ) like a standard http request response ( like the current view implementation).
I would really like to avoid inventing my own file handling methods unless there is some reason why using the file access methods are better then Springs "other" view resolvers like ResourceBundleResolver
So the requirement is
Conditionally respond to a http request with variable file type (jpg,png,html) from inside WEB-INF without wrapping in a jsp or having the file interpreted by the JSTL view. The names of the files are known and static. The controller will determine the file name based on its own business logic.

You can reproduce the behavior of the underlying implementation of <mvc:resources/> which is org.springframework.web.servlet.resource.ResourceHttpRequestHandler, which essentially streams out the content of the static files - You can like ResourceHttpRequestHandler, extend from org.springframework.web.servlet.support.WebContentGenerator which has extensive support for sending last-modified and caching related headers, and finally to stream the content also there is a utility that Spring provides:
org.springframework.util.FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
Updated:
#Controller
public class HelloWorldController implements ApplicationContextAware {
ApplicatonContext ctx = ...;
#RequestMapping(value="/myProtectedContent")
public void index(HttpServletRequest req, HttpServletResponse res) {
Resource resource = ctx.getResource("classpath:staticpath/myjpg.jpg");
FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
}
}

Something you can do is to map a new servlet to the path you want to be protected and handle the request the way you want.
For example, in web.xml:
<servlet>
<servlet-name>protServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/protServlet-context.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>protServlet</servlet-name>
<url-pattern>/myProtectedContent</url-pattern>
</servlet-mapping>
This way, you map a new servlet (DispatcherServlet) for URLs that are protected content.
The load-on-startup value equals 2 is due if you already have a DispatcherServlet with this field value equals 1.

Related

Spring Controller Request Mapping Does Not Work Correctly

I have a simple request mapping that opens up an html file residing in static folder. It works correctly when the request mapping is a one part request like '/town' but does not works (returns 404) on urls like '/visual/town'. When I debug, the program falls to the desired place though. It just cannot recognise the file that it points to when the mapping constitutes of two or more parts. This doesn't makes sense to me at all.
This one works :
#Controller("VisualHomeController")
public class HomeController {
#RequestMapping(value = "/town")
public String emergentTown() {
return "static/visual/emergent_town.html";
}
}
This one does not :
#Controller("VisualHomeController")
public class HomeController {
#RequestMapping(value = "/visual/town")
public String emergentTown() {
return "static/visual/emergent_town.html";
}
}
And here is my servlet mapping, It seems that the problem arises here :
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
So, I figured out that the problem is with the servlet mapping.
I needed to add another servlet mapping for the first part of my additional controller which is /visual as :
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/visual/*</url-pattern>
</servlet-mapping>
Then it worked. So whenever you want to have requests like /.../new_page you need to define the ... as servlet mappings. Note that changing the main servlet mapping to /* is not enough for this purpose.
Note : It still doesn't make sense that why the program falls into the return ... when debugging. I mean, if one more servlet mapping is needed, how front controller can route the request to that method? And while doing so why it can not resolve the view?

Tomcat use of static variables

Is it possible to use static variables in my project to store data for all Servlets (they are in one .war file) and different requests? (It's not data that belongs to a distinct session)
data for all Servlets
You can use ServletContext for this.
Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.
There is one context per "web application" per Java Virtual Machine. (A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace such as /catalog and possibly installed via a .war file.)
For example: in web.xml
<context-param>
<param-name>param</param-name>
<param-value>Myname is web xml</param-value>
</context-param>
In your servlet
public class ParameterServlet extends HttpServlet {
---
public void init(ServletConfig config) throws ServletException {
super.init(config);
ServletContext context = getServletContext();
name= context.getInitParameter("param");
}
A complete example here.
For Objects
setting
getServletContext().setAttribute("myObj", obj);
getting
MyObj attribute = (MyObj)getServletContext().getAttribute("myObj");
you can access those objects across servlets.
Yes you can do that.
However it is better to define these constants in your web.xml using the <context-param> tag.
Servlets can then retrieve constants defined using the <context-param> tag using the call:
context.getInitParameter()
Example of name-value pairs in web.xml:
<context-param>
<param-name>name</param-name>
<param-value>Joe</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>password</param-value>
</context-param>
in web.xml you can define
<context-param>
<description>My variable</description>
<param-name>variable.name</param-name>
<param-value>value</param-value>
</context-param>
And then access it from Servlet code:
String variable = getServletContext().getInitParameter("variable.name");
Yes you can, the static variable will be accessible for all the servlet threads. But about using the static variable, you should make a proper decision depending on the factors like the life time of the data you want to store and the amount of data you are going to store.
And since it is used in servlets context, make sure that its thread-safe.

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>

Spring MVC request mapping conflict

I'm having an issue regarding #RequestMapping on classes. Say I have these two controllers:
#Controller
#RequestMapping(value="/controller1")
public class Controller1 {
#RequestMapping(value="/method11.do")
public #ResponseBody method11(){
//...
}
#RequestMapping(value="/method12.do")
public ModelAndView method12(){
//This method redirects me to another jsp where I'll call Controller2 methods
return new ModelAndView("test");
}
}
#Controller
#RequestMapping(value="/controller2")
public class Controller2 {
#RequestMapping(value="/method21.do")
public #ResponseBody method21(){
//...
}
}
When I first call via AJAX method11, it works fine, the url generated is http://mydomain/myapp/controller1/method11.do
Then, I call method12 and get redirected to test.jsp, and from there, I call to method21, and here is the problem, the url generated is not the expected http://mydomain/myapp/controller2/method21.do, but something else, depending on how I make the AJAX call:
url:'controller2/method21' --> http://mydomain/myapp/controller1/controller2/method21.do
url:'/controller2/method21' --> http://mydomain/controller2/method21.do
So, in what way should I make the calls so that they always start at http://mydomain/myapp/...?
I believe I could just use url:'/myapp/controller2/method21.do', but I guess there should be a more generic way in which I don't have to use 'myapp' on every call.
This is my web.xml:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
You should make the client aware of the proper URL by retrieving the context root within your script using JSP EL.
In JSP
<script>var ctx = "${pageContext.request.contextPath}"</script>
You can then use ctx as a prefix to the URLs constructed via Javascript.
var url = ctx + "/rest_of_url"
On the server side, you can use:
${pageContext.request.contextPath} or JSTL has a tag, <c:url> which will append your context root.

Handling URLs in a Java Serlvet

I am fairly new to servlets and the way I am handling repsonses from the client is using if uri matches a certain path such as:
public class EntryServlet extends HttpServlet
{
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws IOException
{
String uri = request.getRequestURI ();
if (uri.matches ("/1")) {
do something....
} else if (uri.matches ("/2")) {
do something else.....
} else {
throw error message...
}
}
}
This works only if my web service have a few pages. But say i have 50 xml documents and I want to map exmaple.com/1 to a xml ...to example.com/50 to another xml. Now it would be bad design to have 50 if else statements to handle each of the resource. What is a better way to implement this? I am trying to do this purely in java with no third party apps/plugins/frameworks
Update:
I am working with only one servlet with a background thread. Essentially a client request to process a file and the background thread handles the processing and stores xml docs in a db. And I want have each of the xml docs to be a resource on the web service.
You gave the answer "I want to map", so lets use a Map
you have 2 options (that i see now)
1: you put the "/1" as a key and put a String as value that map's to your xyz.xml
2: you put the "/1" as a key and put an object that implements an "Executor" interface...
in both cases you lookup the value for the key and then use the value object to return something useful.
Put this in the init of your Servlet and fill it with your objects:
Map<String,String> myActions = new HashMap<String,String>();
myActions.put("/1","one.xml");
.....
Now you can do this:
String uri = request.getRequestURI ();
if (myActions.containsKey(uri)) {
String value = myActions.get(uri);
do something with value
} else {
throw error message...
}
you can easily change this to option 2 by using object that implement this:
public interface Action {
String execute(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException;
}
The answer may depend on what you want to do with the information in the URI. When writing a REST style application, the URI is one of the primary ways that information is passed to your application. So if you may want to re-consider your aversion to additional packages since the REST APIs are very popular for good reason.
Sticking with plain Servlets, lets say we have 50 xml documents that we want to serve based on which id number is passed through the URL.
We could store a table in a database that indicates the correspondence between id number and document. Or we could store this data into a CSV file. Either way, for such a relatively small table, I would just load the whole table into memory when the Servlet starts up. You can place code in an init() method for loading data into your application.
Then we have something like this:
static HashMap<Integer, String> fileMap;
public void init() {
fileMap = new HashMap<> ();
fileMap.put(1,"thing1.xml");
fileMap.put(2,"thing2.xml");
fileMap.put(3,"Otherthingy.xml");
}
Now you can write much cleaner code in your doGet method.
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws IOException {
String uri = request.getRequestURI();
int slash = uri.indexOf("/");
if(slash+1 == uri.length())
//throw an error
String idString = request.getRequestURI().substring(slash+1);
int id = Integer.parseInt(idString);
//should put this in a try catch block and throw an error if it is not a number.
String requestedFile = fileMap.get(id);
if(requestedFile == null)
//throw some error.
// Do whatever you need to do with the file.
}
You should
Move each URI specific logic into a different servlet.
Use the servlet mapping concept from Java Servlet Specification and define the following in your web.xml
<!- Define the servlets here, ofcourse you should use meaningful names -->
<servlet>
<servlet-name>servlet-1</servlet-name>
<servlet-class>com.vikdor.webapps.ServletForURI1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet-2</servlet-name>
<servlet-class>com.vikdor.webapps.ServletForURI2</servlet-class>
</servlet>
....
<!- Map the servlet to the URL pattern -->
<servlet-mapping>
<servlet-name>servlet-1</servlet-name>
<url-pattern>/1/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet-2</servlet-name>
<url-pattern>/2/</url-pattern>
</servlet-mapping>
....
This will push the routing logic to web.xml and each servlet does a specific job.
Use a framework. That's what they're for. I'm partial to Spring MVC but for a basic use case like this anything modern should be fine. It might feel like a pain to first get it setup but once it's configured development is much faster and much more maintainable.
Edit: I just noticed the original question says not to use any frameworks/libraries. I still stick by this answer though as the majority of the time that's a bad idea. If you really have to then you can create Maps as suggested by a few other people but in the long run it's not maintainable so avoid it while you can.
You can specify in your web.xml uri patterns to route to specific servlet classes. Forgive my bad servlet names.
<servlet>
<servlet-name>processUserXml</servlet-name>
<servlet-class>com.example.server.ProcessUserXml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>processUserXml</servlet-name>
<url-pattern>/processuser</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>processItemXml</servlet-name>
<servlet-class>com.example.server.ProcessItemXml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>processItemXml</servlet-name>
<url-pattern>/processitem</url-pattern>
</servlet-mapping>
So instead of accepting uri requests from a single gateway and routing them through if statements (as you're doing now), when a user calls a specific uri, it'll get routed to the appropriate servlet class for you. So calling uri /processuser will route the call to the ProcessUserXml servlet class doGet method (or doPost, if it's defined and that's how the user made the HTTP call).

Categories