For those websites that read data as often as it is written, the static HTTP validation approach would be wrong because updating the file takes too many resources. In the case of dynamic HTTP validation, the server application framework has to manage everything, which includes the generation and verification of the entity tag.
The major problem to solve is the generation of the entity tag (ETag) identifier. Traditionally, web developers do not write an ETag identifier. But what is an entity tag for dynamic content? Calculating the entity tag for the generated content does not make sense because there could be small changes in the generated content that do not quantify as changed content. There is a solution, and one that functions like an entity tag but is not called that.
The proposed solution uses a hash code that is assigned as an entity tag value. A hash code is a reasonably unique value for a given state of an object. In .NET and Java, each object has a hash code method that can be overridden. Imagine the following Book class that represents a book. Ignore the details of the book definition, but understand that the book represents some type. To calculate the hash code for the book, you could write the following source code:
public class Book { private String _ISBN; private String _author; private String _title; private int _staticHashCode;
private boolean _isHashCodeAssigned = false; public int hashCode() {
if( _isHashCodeAssigned) { return _staticHashCode; }
else {
return new HashCodeBuilder() .append( _ISBN) .append( _author) .append( _title) .append( _comments).toHashCode(); } }
public void assignHashCode(int hashcode) { _staticHashCode = hashCode();
_isHashCodeAssigned = true; }
public void resetAssignedHashCode() { _isHashCodeAssigned = false; }
}
In the implementation of Book, there are several methods and data members. The data member _staticHashCode represents an old hash code value. The old hash code value is needed to verify whether content has changed. Consider the following context. The client makes a request for a resource. The server instantiates the needed objects and from those objects generates a hash code that is stored in the object state itself (staticHashCode). The client comes back and asks for the same resource, sending the hash code as an entity tag. As a quick check technique, the server does not load all of the objects, but loads the saved hash code (staticHashCode) and compares the sent entity tag with the hash code. If they match, the server generates an HTTP 304 error or sends the new state. What makes this architecture work is that whenever the objects are updated, the saved hash code must be updated. Otherwise, the saved hash code will reflect an old state.
The method assignHashCode assigns a predefined hash code that will be used by the caller to test for change. The method resetAssignedHashCode resets the flag so that the hash code is computed from the state. The example used in the Book class is to declare a local data member, but that is not the only way to implement the optimization. You can implement it however you want. What is important is to associate the hash code with the data members that uniquely represent the object. The Builder pattern could be applied that accepts as a parameter the entity tag and then either constructs the object hierarchy or generates a result code to indicate no change. How you would implement this logic is left to you. In the case of the Book class,
the unique identifier is the ISBN number. As an alternative, a SQL table with two columns (ISBN and Hash Code) could be created.
Wrapping up the architecture, it is obvious that the best way of caching data is to comple- ment the Internet infrastructure and use HTTP validation. HTTP validation is not a task left for the HTTP server to implement, but is part of implementing any resource. The following section illustrates how to implement static and dynamic HTTP validation.
Implementation
As the Cache Controller pattern uses HTTP validation that is already implemented by the browser or HTTP server, implementing the Cache Controller pattern requires writing code that is needed. It might be necessary for a given content to write only client code, or maybe only server code, or maybe both client and server code. The point is that what you need to write depends on the context. When writing either the client or server caching code, it is important to implement the HTTP validation contract. Implementing the HTTP validation correctly ensures that either your client or server fits into an already existing Internet infrastructure. The important piece of the implementation is that both the client and server when implemented follow the contract of HTTP validation.
The example application manages books, as illustrated in the “Architecture” section. The focus of the application is to illustrate the state of a type and how it affects the entity tag. On the client side, the Cache Controller code is a minimal implementation and is receiving a reference number that is used by the server to indicate whether new content needs to be sent. More complicated is the server side, which needs to generate and validate the reference number, resulting in a larger explanation of the server-side code.