1

how wrote here http://developer.yahoo.com/performance/rules.html

For static components: implement "Never expire" policy by setting far future Expires header

i can gain performace avoiding http requests with a response as "304".
In official play! documentation i can see how set cache-controll directives, but how i can set far future Expires header?

best regards Nicola


edit: thanks for the replay now it work as well! here there are the classes:

conf/routes

  # Static files
  GET /assets/stylesheets/img/:name controllers.StaticFilesController.getBoostrapImg(name)
  GET /assets/images/*name          controllers.StaticFilesController.getImg(name)
  GET /assets/stylesheets/*name     controllers.StaticFilesController.getCss(name)
  GET /assets/javascripts/*name     controllers.StaticFilesController.getJs(name)


controllers/StaticFilesController.java

package controllers;
import org.apache.http.impl.cookie.DateUtils;
import java.util.*;
import play.mvc.*;
import services.FileName;
import play.*;
public class StaticFilesController extends Controller {

private static String nextYearString = StaticFilesController
        .getNextYearAsString();

public static Result getImg(String path) {

    FileName fileName = new FileName(path);
    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("image/" + fileName.extension());
    return ok(Play.application().getFile("/public/images/" + path));
}

public static Result getBoostrapImg(String path) {

    FileName fileName = new FileName(path);
    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("image/" + fileName.extension());
    return ok(Play.application().getFile(
            "/public/images/" + fileName.filename() + "."
                    + fileName.extension()));
}

public static Result getCss(String path) {

    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("text/css");
    return ok(Play.application().getFile("/public/stylesheets/" + path));
}

public static Result getJs(String path) {

    response().setHeader(EXPIRES, nextYearString);
    response().setContentType("application/x-javascript");
    return ok(Play.application().getFile("/public/javascripts/" + path));
}

private static String getNextYearAsString() {
    Calendar calendar = new GregorianCalendar();
    calendar.add(Calendar.YEAR, 1);
    return DateUtils.formatDate(calendar.getTime());
}
}


services/FileName.java

package services;

/**
* This class assumes that the string used to initialize fullPath has a
* directory path, filename, and extension. The methods won't work if it
* doesn't.
*/
public class FileName {
private String fullPath;
private char pathSeparator, extensionSeparator;

public FileName(String str, char sep, char ext) {
 fullPath = str;
 pathSeparator = sep;
 extensionSeparator = ext;
}

public FileName(String str)
{
  fullPath = str;
  pathSeparator = '/';
  extensionSeparator = '.';
}

public String extension() {
  int dot = fullPath.lastIndexOf(extensionSeparator);
  return fullPath.substring(dot + 1);
 }

 public String filename() { // gets filename without extension
 int dot = fullPath.lastIndexOf(extensionSeparator);
 int sep = fullPath.lastIndexOf(pathSeparator);
  return fullPath.substring(sep + 1, dot);
}

 public String path() {
  int sep = fullPath.lastIndexOf(pathSeparator);
   return fullPath.substring(0, sep);
  }
 }


And the views/main.scala.html

 @(skin: String)(content: Html)

 <!DOCTYPE html>

 <html lang="en">
   <head>
     <meta charset="utf-8"> 
     <title>LibreTitan</title>
      <link rel="stylesheet" media="screen"    href="@routes.StaticFilesController.getCss("bootstrap/bootstrap.min.css")">
    @if(skin != null && !skin.equals("")) {
    <link rel="stylesheet" media="screen" href="@routes.StaticFilesController.getCss(skin+".min.css")">
    }
    <link rel="shortcut icon" type="image/png" href="@routes.StaticFilesController.getImg("favicon.png")">
    <script async src="@routes.StaticFilesController.getJs("jquery-1.9.0.min.js")"></script>
    <script async src="@routes.StaticFilesController.getJs("bootstrap.min.js")"></script>
</head>
<body>
    <div class="container">
        @content
     </div>
    </body>
 </html>

enter image description here

user2054758
  • 321
  • 3
  • 18

1 Answers1

1

Never expire policy in this context means that should add Expire header to your response which date is in far future, for an example 10 years since now. You can easily do that in Play as described in the Manipulating the response doc. example:

public static Result index() {
    response().setHeader(EXPIRES, "Thu, 16 Feb 2023 20:00:00 GMT");
    return ok("<h1>Hello World!</h1>");
}

Of course instead giving expire date as a String you should use some method to calculate it dynamicaly, you can do that with org.joda.time.DateTime (available in Play) and its methods like: plusYears(int years). Most important is that should be formatted finally in RFC 1123 date format.

Edit, of course you can return different kinds of Results - also binary as described in the doc ('Results' section), to check all available options look into API of: play.mvc.Results it can be: ok(Content content) - typical when you render some view, ok(java.io.File content), ok(java.io.InputStream content), etc.

On the other hand...

I'd definitely recommend to DO NOT use Play for serving far future static contents (and any other static, public contents as well). Although you can do it easily, as described above, IMHO it's redundant waste of Play's resources as you need to handle all requests for any single image, script, style... whatever.

Consider using common... HTTP server for that job (nginx, lighthttpd, Apache) or even better some distributed CDN. In that case your app can concern on executing its logic instead of searching the stylesheets on the disk.

P.S. Remember that if you are using Play instead of HTTP server, for adding new static contents which are served from the /public folder you'll need to restart the application, so at least make sure that you are keeping them in some dedicated folder apart of application, so you can add/remove/replace them without stopping the application.

biesior
  • 55,576
  • 10
  • 125
  • 182
  • thanks, but how i can return the binary content of an image from the controller as you made? – user2054758 Feb 16 '13 at 11:39
  • thanks solved! i will continue use play for static resource because now clients only ask for statics contents only one time time (at first request) – user2054758 Feb 16 '13 at 13:31
  • Anyway I still recommend external server, check my post scriptum :) – biesior Feb 16 '13 at 13:43
  • yes for sure it's the best solution but anyway i want offer a simple way to run my open source projetc in one server. do you know if the asset/ coffe and less resources are still compiled without invoking Asset class? – user2054758 Feb 16 '13 at 14:46