November 13th, 2013 by Lincoln Baxter III

Creating a simple static file server with Rewrite

Today, I’d like to take a quick moment to demonstrate how to make a simple file server using [[Rewrite]], and any Servlet Container, such as Tomcat, Wildfly, or Jetty. This can enable much easier file updates for static content, such as preventing the need to re-deploy an entire application just to update an image, or document.

[[Rewrite]] is an open-source Routing ↑↓ and /url/{rewriting} solution for Servlet, Java Web Frameworks, and Java EE.

To start, you’ll need to include the Rewrite dependencies in your project. If you’re using maven, this is as simple as making sure your POM has the following entries (You’ll also need the Servlet API):

Include Rewrite in your 'pom.xml'
<dependency>
   <groupId>org.ocpsoft.rewrite</groupId>
   <artifactId>rewrite-servlet</artifactId>
   <version>2.0.8.Final</version>
</dependency>
<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
   <scope>provided</scope>
</dependency>

Next, we’ll create a simple ConfigurationProvider to tell Rewrite what to do; in this case, we’ll be streaming files directly from the local filesystem. This class can go anywhere in your project:

The Rewrite ConfigurationProvider
@RewriteConfiguration
public class StaticContentConfiguration extends HttpConfigurationProvider {
   @Override
   public Configuration getConfiguration(ServletContext context) {
      /*
       * You should select a path that exposes only the directory(s) from which content should be served, otherwise the
       * entire hard drive could be accessible.
       */
      File resource = new File("/home/contentserv/{file}");

      return ConfigurationBuilder.begin()
               .addRule()
               .when(Path.matches("/{file}")
                        .and(Filesystem.fileExists(resource))
                        .and(Direction.isInbound())
               )
               .perform(Stream.from(resource)
                        .and(Response.setStatus(200))
                        .and(Response.complete())
               )
               .where("file").matches(".*");
   }

   @Override
   public int priority() {
      return 0;
   }
}
You could even add logging using the provided `Log` operation from rewrite-servlet, so that requests can be tracked in your server logs:
.perform(Stream.from(resource)
   .and(Response.setStatus(200))
   .and(Response.complete())
   .and(Log.message(Level.INFO, "Served file: {file}."))
)

It’s as simple as that! Rewrite will match inbound requests with corresponding files in the specified resource location. Thanks to the simple parameterization API that Rewrite provides, this requires very little glue code – we are free to sit back and have a nice beverage.

Now you’ve not got an extremely simple file-server running as a web-application. This is only one very basic example of what Rewrite can do, so let us know what you think as you explore more Rewrite features or dive into the documentation!


Lincoln Baxter, III

About the author:

Lincoln Baxter, III is the Chief Editor of Red Hat Developers, and has worked extensively on JBoss open-source projects; most notably as creator & project lead of JBoss Forge, author of Errai UI, and Project Lead of JBoss Windup. This content represents his personal opinions, not those of his employer.

He is a founder of OCPsoft, the author of PrettyFaces and Rewrite, the leading URL-rewriting extensions for Servlet, Java EE, and Java web frameworks; he is also the author of PrettyTime, social-style date and timestamp formatting for Java. When he is not swimming, running, or playing competitive Magic: The Gathering, Lincoln is focused on promoting open-source software and making technology more accessible for everyone.

Posted in Servlet

2 Comments

  1. Ivan St. Ivanov says:

    Hey Lincoln,

    Great feature! But could you restrict something like GET "../../"?

    Cheers,
    Ivan

    1. Hey Ivan,

      I don’t think it is required, since the File API does not respect relative paths. But you absolutely could do something like:

      return ConfigurationBuilder.begin()
                     .addRule()
                     .when(Path.matches("/{file}")
                              .and(Filesystem.fileExists(resource))
                              .and(Direction.isInbound())
                     )
                     .perform(Stream.from(resource)
                              .and(Response.setStatus(200))
                              .and(Response.complete())
                     )
                     .where("file").constrainedBy(new Constraint() {
          public boolean isSatisfied(Rewrite event, EvaluationContext context, String value)
         {
             return !value.contains("../")
         }
      });

Leave a Comment




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.