package examples.performance;
import com.vtence.molecule.WebServer;
import com.vtence.molecule.middlewares.Compressor;
import com.vtence.molecule.middlewares.ConditionalGet;
import com.vtence.molecule.middlewares.ContentLengthHeader;
import com.vtence.molecule.middlewares.ETag;
import com.vtence.molecule.middlewares.FileServer;
import com.vtence.molecule.middlewares.StaticAssets;
import com.vtence.molecule.templating.JMustacheRenderer;
import com.vtence.molecule.templating.Template;
import com.vtence.molecule.templating.Templates;
import com.vtence.molecule.testing.ResourceLocator;
import java.io.File;
import java.io.IOException;
import java.time.Clock;
import static com.vtence.molecule.http.HeaderNames.CACHE_CONTROL;
import static com.vtence.molecule.http.HeaderNames.CONTENT_TYPE;
import static com.vtence.molecule.http.HeaderNames.LAST_MODIFIED;
import static com.vtence.molecule.http.MimeTypes.CSS;
import static com.vtence.molecule.http.MimeTypes.HTML;
import static com.vtence.molecule.http.MimeTypes.JAVASCRIPT;
/**
* <p>
* This example shows how to compress responses and do HTTP caching. We serve files files located in the
* <code>src/test/resources/examples/fox</code> directory to demonstrate both the
* expiration model and validation model (see <a href="http://tools.ietf.org/html/rfc2616">RFC 2616</a>).
* </p>
* <p>
* We specify how long a response should be considered “fresh” by the client by including both of the
* <code>Cache-Control: max-age=N</code> and <code>Expires</code> headers. A client that understands
* expiration will not make the same request until the cached version reaches its expiration time
* and becomes “stale”.
* </p>
* <p>
* For more dynamic resources - such as web pages - where changes in resource state can occur frequently and
* unpredictably, we use the <code>Last-Modified</code> and <code>ETag</code> headers. A client
* that understands cache validators can validate the freshness of its stored response
* without requiring our webapp to generate or transmit the response body again.
* </p>
*/
public class CachingAndCompressionExample {
private static final Void NO_CONTEXT = null;
private final Clock clock;
public CachingAndCompressionExample(Clock clock) {
this.clock = clock;
}
public void run(WebServer server) throws IOException {
// We serve files located under the examples/fox directory.
File content = ResourceLocator.locateOnClasspath("examples/fox");
// Setup the file server with a cache directive of public; max-age=60
FileServer files = new FileServer(content).header(CACHE_CONTROL, "public; max-age=60");
// For requests paths starting with /css, /js and /images, we serve static assets
StaticAssets assets = new StaticAssets(files).serve("/css", "/js", "/images");
// For other requests, we'll serve web pages.
// Our web pages are Mustache templates with an .html extension
Templates templates = new Templates(new JMustacheRenderer().fromDir(content).extension("html"));
// This is our index.html template
final Template<Void> index = templates.named("index");
// Add Content-Length header to the response when size of content is known
server.add(new ContentLengthHeader())
// Make GET and HEAD requests conditional to freshness of client stored representation.
// This is the validation model
.add(new ConditionalGet())
// Add an ETag header if response has no freshness information.
// This is the case for dynamic content, but static files have a Last-Modified header.
.add(new ETag())
// Compress text response bodies (js, css, html but not images)
.add(new Compressor().compressibleTypes(JAVASCRIPT, CSS, HTML))
// We serve static assets with freshness information. This is the expiration model.
.add(assets)
// If client is not requesting a static file, we'll serve our index.html mustache template
.start((request, response) -> {
response.header(CONTENT_TYPE, HTML);
// We add freshness information only when query parameter 'conditional' is present
if (request.hasParameter("conditional")) {
response.header(LAST_MODIFIED, clock.instant());
}
// This will render our index.html template
response.done(index.render(NO_CONTEXT));
});
}
public static void main(String[] args) throws IOException {
CachingAndCompressionExample example = new CachingAndCompressionExample(Clock.systemDefaultZone());
WebServer webServer = WebServer.create();
example.run(webServer);
// Try accessing /?conditional then / to get a 304
System.out.println("Access at " + webServer.uri());
}
}