15

I'm inexperienced in Spring and everything I need to do now is to access and obtain a reference to files and folders in the webapp folder. Here is my relevant project hierarchy:

-src
--main
---java (marked as source root)
----my
-----package
------controller
-------Home.java
---webapp
----images
-----avatars

My code in Home.java:

@Controller
@RequestMapping("/")
public class Home
{
    @RequestMapping(method = RequestMethod.GET)
    public String index(Model model,
                        HttpServletRequest request) throws Exception
    {
        String test1 = request.getSession().getServletContext().getRealPath("");
        String test2 = request.getSession().getServletContext().getRealPath("/");
        String test3 = request.getRealPath("");
        String test4 = request.getRealPath("/");
        String test5 = request.getSession().getServletContext().getRealPath(request.getServletPath());

        return "index";
    }
}

All the 5 requests return null. Am I doing something wrong?

web.xml:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

    <display-name>Test</display-name>

    <servlet>
        <servlet-name>test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
</web-app>

What I'm trying to achieve (not shown in this example), is to code a controller responsible for the scaling of an image. For this reason I would need access to the src/main/webapp/_images folder.

Thank you!

Update: simplified the example for better understanding.

Update2: thanks to the suggestion of @gigadot, I deployed the application as an exploded WAR and the problem is partly solved. Can someone tell me what's the difference in deploying the WAR as exploded? Is it something not recommended to do on a production server? Advantages/disadvantages?

I think it's worth to explain the situation with an example. Let's say I'm coding a social network and I have to possibility to upload my personal profile picture. This picture will be uploaded to the src/main/webapp/_images/avatars/[myid].jpg folder. Is it recommended to upload pictures to the webapp folder? Or is there a better solution? I would like to be able to return a scaled instance of the image when accessing the URL /images/[width]x[height]/[userid].jpg.

Deploying the WAR as exploded and implementing the ResourceLoaderAware (thanks @KevinSchmidt), I can make it work using this:

resourceLoader.getResource("file:" + request.getSession().getServletContext().getRealPath("/") + "_images/avatars/");

To me it looks quite dirty, is it a good idea for a production server? Is there a better solution?

satoshi
  • 3,963
  • 6
  • 46
  • 57
  • I care if the directory exists – satoshi Feb 07 '12 at 10:08
  • What do you want to do with this files, just return them to the client, or processing its content? – Ralph Feb 20 '12 at 07:52
  • I've tried your code and it works. Can you tell us how you test it? Do you generate a war with Maven and put it into Tomcat or do execute it directly from Eclipse? – sinuhepop Feb 21 '12 at 11:47

6 Answers6

15

How exactly did you deploy your application?

ServletContext().getRealPath("/") may return null if it is not deployed as exploded. Read the link below for further information. However, the method to configure this may not be the same for your servlet container.

http://ananthkannan.blogspot.com/2009/12/servletcontextgetrealpath-returns-null.html

Updates

Can someone tell me what's the difference in deploying the WAR as exploded?

When you deploy the war file as exploded, the servlet container, e.g. Tomcat, will extract the content of war file into a temporary folder and runs everything from that folder so the {WEB_ROOT]/_images/avatars/[myid].jpg is actually exist on file system (hard disk). Therefore, you can actually get the real path (as it already says in the name of the method). However, if your servlet container does not extract the war file, the folder you are looking for is inside the war file and there is no real path to it so it will return null.

Is it something not recommended to do on a production server? Advantages/disadvantages?

You should not store dynamic contents under your source folder or the webroot folder (webapp) since servlet container will use it temporarily and delete it or change to a new folder when you redeploy your web application. You will likely lost the dynamic data you put into these folders. The web root folder is usually designed for storing static content, which means content you don't want to change - for example, graphic images for your web component like background images, css, etc.

The usual method for storing user data is to somehow create a folder in userspace and put your dynamic data in there. However, you will not be able to serve the content of the folder outside webroot. You will need to write your own static servlet to pipe the data when they are requested. This is quite complicated for a beginner.

The easiest way for implementing your own static servlet to serve dynamic content is to extend the static servlet of your servlet container. However, your code will highly depend on the servlet container you are deploying to.

Since you are going to provide a REST interface for resizing images, you can create a controller which reads in the original images from the dynamic content folder, do the resizing, save it as a temporary file or flush the content of the HttpResponse.

gigadot
  • 8,879
  • 7
  • 35
  • 51
  • Thank you, this fixed the problem. Can you tell me what's the difference in deploying the WAR as exploded? Is it something not recommended to do on a production server? Advantages/disadvantages? – satoshi Feb 23 '12 at 14:14
  • Thank you, @gigadot. I googled for the solutions you suggested but I can't find any example. As I said I'm a beginner so if you can provide a link or a small example it would be great. While googleing, I found that people normally store this kind of images in the DB (blob). I can imagine that this has a big advantages when clustering (something I will need in the future) and I can't see any disadvantage. Is it something you suggest? Thanks! – satoshi Feb 24 '12 at 10:11
  • @satoshi For this particular problem, I don' think you need static servlet, unless you want to serve the dynamic contents wihtout doing any processing and, here, you want to resize the image first so you can just write your resized image stream to HttpResponse. Using BLOB is one way to solve your problem. The limit is the size of BLOB is no larger than 2GB. I cannot think of any other problem for now. – gigadot Feb 24 '12 at 13:34
  • If you want to find out about static servlet (which I don't think you need), read here http://stackoverflow.com/questions/132052/servlet-for-serving-static-content – gigadot Feb 24 '12 at 13:35
  • Thank you very much, @gigadot. I think I'm going to opt for the Blob solution. The scaling functionality I coded is chaching the scaled images, where do you think it's better to store them (considering that if they get deleted it's not an issue)? I guess the `webapp` folder suits fine in this case? Thank you. – satoshi Feb 24 '12 at 14:08
  • You can store the resized images in memory and discard them but it might put a high load on your server if there are many requests. Alternatively, you can have a cache system. This can be in webapp or BLOB. If you use webapp, you won't need to worry about defining static servlet. However, you might find a file locking problem on some servlet containers and configurations. The default static servlets in most servlet containers are optimised to use cache and therefore lock your files in the webapp folder so you may not be able to write to the existing files in the webapp folder. – gigadot Feb 24 '12 at 15:01
  • Perfect, thank you. I won't encounter any problem related to file locking since I'm already managing this concurrency with semaphores. – satoshi Feb 24 '12 at 15:03
4

This is my code and it works I dont get null.

I'm running JDK 1.7_02 Tomcat 7.0.25 Spring 3.1.1

And i'm not getting nulls

    @Controller
    public class IndexController {

        @RequestMapping("/index")
        public String go(HttpServletRequest request, HttpServletResponse response) {
            HttpSession session = request.getSession();
            ServletContext sc = session.getServletContext();
            String x = sc.getRealPath("/");
            System.out.println(x);
            return "index";
        }
    }
4

Considering the location of the file, you should look into the Spring ResourceLoader, like this:

public class ArbitraryResourceLoader implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    public void test() {
        Resource resource = resourceLoader.getResource("file:webapp/images/avatars/avatar.jpg");
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

Please be aware that your container needs to have permission to access the file.

Kevin Schmidt
  • 2,111
  • 2
  • 14
  • 12
0

ClassPathResource might be of help instead. Your way of accessing a resource in the classpath if flatly wrong.

For what concerns the location of web resources, those stored in the src/main/resources would be deployed in the WEB-INF/classes folder, so it is clearly a no go. User webapp instead.

Update: now I see where you are coming from. I do not see anything wrong with the mapping, although extremely exotic. Chances are that the DispatcherServlet mapping does not cover the images folder (i.e. it is not /*).

For what concerns the File opening, the webapp directory is not visible from the class loader with the only exception of the WEB-INF folder. As such, you will need to invoke the HttpServletRequest.getRealPath() method in order to get the real path of the image.

Please note that, although unlikely to be your case, hardened Java JVMs (i.e. JVMs with security policies in place) may require extra grants to let the servlet open a file from an arbitrary location.

Alessandro Santini
  • 1,943
  • 13
  • 17
  • Thank you for your answer. I now moved all the CSS/JS/images files and folders to the webapp folder. Now the images folder is located at src/main/webapp/images, how can I reference it from my code? It's not in the classpath, right? – satoshi Feb 06 '12 at 18:24
  • Can you disclose a bit more about what you exactly want to accomplish? – Alessandro Santini Feb 06 '12 at 18:43
  • @AlessandroSantini Minor note: `HttpServletRequest.getRealPath()` is deprecated. `ServletContext.getRealPath()` should be used. – Michael Feb 06 '12 at 19:33
  • Thank you both. Unfortunately request.getSession().getServletContext().getRealPath("images") returns a null object. I tried adding slashes before and after "images" as well... – satoshi Feb 06 '12 at 20:04
  • Would be nice if you could post the solution for the benefits of the other users. – Alessandro Santini Feb 06 '12 at 20:38
  • Well, but I don't have a solution - when I said "done" I meant posted the web.xml in the original question! – satoshi Feb 06 '12 at 21:44
  • Oops! Sorry for the misunderstanding - I'll give it a look and get back to you. – Alessandro Santini Feb 06 '12 at 22:07
0

Take a look at using <mvc:resources> in the Spring Reference, section 16.14.5 Configuring Serving of Resources. I use this method for my JavaScript/CSS/images and it works great - I just had to add a single line to my application-context.xml file. The documentation states:

This provides a convenient way to serve static resources from locations other than the web application root, including locations on the classpath.

Paul
  • 19,704
  • 14
  • 78
  • 96
0

you can try this:

@Controller
@RequestMapping("/")
public class Home implements ServletContextAware
{
    private ServletContext servletContext;
    public void setServletContext(ServletContext servletCtx){
       this.servletContext=servletCtx;
    }

    @RequestMapping(method=RequestMethod.GET)
    public String index(Model model,HttpServletRequest) throws Exception{
       String rootPath = servletContext.getRealPath("/");
       //...
    }
}
horaceman
  • 375
  • 1
  • 5