Showing posts with label Resource. Show all posts
Showing posts with label Resource. Show all posts

Sunday, 4 November 2012

Serving Static Resources With Spring MVC

This post explains how to server static content from a Spring MVC web application. It is based on the Spring MVC With Annotations example. The code is available on GitHub in the Spring-Serving-Static-Resources directory.

JSP Page

We keep only the main index.jsp page:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;" charset=UTF-8">
  <script type="text/javascript"
    src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script type="text/javascript"
    src="<c:url value='/resources/js/SomeJavascript.js'/>">
  </script>
  <title>Welcome To Spring Serving Static Resources !!!</title>
</head>
<body>
    <h1>Spring - Serving Static Resources !!!</h1>
    <img border="0"
      src="<c:url value='/resources/img/MyArt.png'/>" alt="My Art" />
</body>
</html>
Regarding static resources:
  • We link JQuery from Google's CDN.
  • We link our own SomeJavascript.js and make sure the URL is properly constructed.
  • In the body, we add an image.

Resources

Spring Project Structure For Resources
In the Spring project itself, we put all the resources under a separate resources directory which does not stand under WEB-INF.
  • Images are stored under /resources/img.
  • Javascript is storted under /resources/js.
Our Javascript is a simple pop-up messages displayed when the HTML document is loaded:
$(document).ready(function() {
    alert("JS loaded successfully!");
});

Web Configuration & Controller

We need to modify our web configuration to add a static resource handler and configure it by specifying where to fetch these:
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.jverstry")
public class WebConfig extends WebMvcConfigurerAdapter {

    ...

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

      registry
        .addResourceHandler("/resources/**")
        .addResourceLocations("/resources/");
    }

}
We also simplify our controller:
@Controller
public class MyController {

    @RequestMapping(value = "/")
    public String home(Model model) {
        return "index";
    }

}

Running The Example

Once compiled, the example can be run with mvn tomcat:run. Then, browse:

  http://localhost:8585/spring-serving-static-resources/

The image and the pop-up will be displayed:



More Spring related posts here.

Saturday, 15 September 2012

How To Mix Spring XML And Java Configuration?

The @Configuration Spring annotation allows one to perform Spring configuration from Java (such as bean declaration for example). It is possible to implement a Spring application without any XML configuration (see here). It is even possible to get rid of the web application web.xml file too.

However, some Spring modules, such as the security module, still require plain XML configuration. Some applications may refer to old legacy code which has not been converted to Java configuration too.

In this case, one needs to mix Spring XML and Java configuration. This can be achieved with the @ImportResource Spring annotation:
@Configuration
@ImportResource({"classpath:/WEB-INF/spring-security.xml",
"classpath:/WEB-INF/legacy-config.xml"})
public class Config {

    @Bean
    MyBean MyBean() {
        return new MyBean();
    }

}
The above imports two XML files and declares a bean using Java.

More Spring related posts here.

Sunday, 12 August 2012

How to Silently Close Resources in Java?

The following piece of code describes how to silently and properly close a stream resource in Java:
File f = new File("SomeFile.txt");
FileOutputStream fos = null;

try {

    fos = new FileOutputStream(f);<

    // Do something...

} catch (FileNotFoundException ex) {

    // Display error message

} finally {

    // Closing resource
    if ( fos != null )  {

        try {

            fos.close();

        } catch (IOException ex) {

            // Silently ignore it

        }

    }

}
Yet, the amount of code required to close each stream in finally is nothing but clutter which does not help with readability too. Fortunately, the Apache Commons library offers a tool solving this issue:
} finally {

    IOUtils.closeQuietly(out);

}

Thursday, 2 August 2012

Understanding Absolute and Relative Path with getResourceAsStream()

Not making the difference between absolute and relative paths when loading resources in Java is a common source of errors leading to NullPointerException (NPE).

Assuming the following structure and content in a maven project:

My Project
  |-src
    |-main
      |-java
      | |-SomePackage
      |   |-SomeClass.java
      |-resources
        |-Root.txt
        |-SomePackage
          |-MyData.txt
          |-SomePackage2
            |-MySubData.txt

Content will be re-organized as following in the .jar:
  |-Root.txt
  |-SomePackage
    |-SomeClass.java
    |-MyData.txt
    |-SomePackage2
      |-MySubData.txt

The following indicates what works and what does not work to retrieve resource data:
InputStream IS;

IS = SomeClass.class.getResourceAsStream(
    "Root.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "/Root.txt"); // OK

IS = SomeClass.class.getResourceAsStream(
    "/MyData.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "MyData.txt"); // OK

IS = SomeClass.class.getResourceAsStream(
    "/SomePackage/MyData.txt"); // OK

IS = SomeClass.class.getResourceAsStream(
    "SomePackage/MyData.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "MySubData.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "SomePackage/SomePackage2/MySubData.txt"); // OK

IS = SomeClass.class.getResourceAsStream(
    "/SomePackage/SomePackage2/MySubData.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "/SomePackage2/MySubData.txt"); // Not OK

IS = SomeClass.class.getResourceAsStream(
    "SomePackage2/MySubData.txt"); // OK

getResourceAsStream() operates relative to the package corresponding to the called Class instance.

Friday, 27 July 2012

How to Access Files/Data in the Resource Directory at Runtime?

A common concern of developers using Maven for the first time is how to access a file (binary or text) declared as a resource in Maven. Contrary to typical NetBeans projects (for example), maven insists on using a standard directory structure (more on Maven concepts here):

My Project
  |-src
    |-main
      |-java
      | |-MyPackage
      |   |-MyClass.java
      |-resources
        |-MyPackage
          |-MyData.txt

In a standard NetBeans project, since MyClass.java uses MyData.txt, both are (often) located in the same directory corresponding to the MyPackage Java package. However, in a Maven project, code is separated from data in different directories.

Some developers believe that the Maven directory structure will be replicated in the .jar after compilation. Therefore, they are tempted to try to open the data resource with a strategy similar to this one:
String loc = new File(".").getAbsolutePath()
  + "\src\main\resources\MyPackage\MyData.txt";
FileInputStream FIS = new FileInputStream(new File(loc));
...
In other words, they believe they have to handle the Maven directory structure themselves, including the absolute path to the current .jar location. This does not work.

First, one must keep in mind that Maven does not "push" its directory structure in .jars. Both MyClass.java and MyData.txt will be located in the same \MyPackage directory within the .jar. You can check this by opening the .jar with winzip for example.

The solution is the following (resource location is relative to Myclass location or package (*)):
InputStream IS = MyClass.class
    .getResourceAsStream("MyData.txt");

Caveat - Encoding Issue

If your data is not binary, you need to pay attention to the encoding format in the file itself (for example, set it to UTF-8) and make sure it is the same encoding as in your Maven project. You should have the following setting in your pom.xml:
<properties>
  <project.build.sourceEncoding>
    UTF-8
  </project.build.sourceEncoding>
  ...
<properties>
This is an issue that took me hours to solve, because encoding/display errors would only happen at runtime.

(*) More on absolute and relative path here.

REM: This post is a follow-up to an answer I provided on Stackoverflow.com.