You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuscany.apache.org by an...@apache.org on 2008/08/20 10:43:58 UTC

svn commit: r687289 - /tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java

Author: antelder
Date: Wed Aug 20 01:43:57 2008
New Revision: 687289

URL: http://svn.apache.org/viewvc?rev=687289&view=rev
Log:
TUSCANY-2537: Apply patch from Dan Becker for Demonstrate Atom Binding end to end caching (ETag, Last-modified use) in

Modified:
    tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java

Modified: tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java
URL: http://svn.apache.org/viewvc/tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java?rev=687289&r1=687288&r2=687289&view=diff
==============================================================================
--- tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java (original)
+++ tuscany/java/sca/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java Wed Aug 20 01:43:57 2008
@@ -27,7 +27,9 @@
 import java.io.Writer;
 import java.net.URLDecoder;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.logging.Logger;
 
@@ -51,6 +53,7 @@
 import org.apache.abdera.writer.WriterFactory;
 
 import org.apache.commons.codec.binary.Base64;
+import org.apache.tuscany.sca.binding.atom.CacheContext;
 import org.apache.tuscany.sca.data.collection.Entry;
 import org.apache.tuscany.sca.databinding.Mediator;
 import org.apache.tuscany.sca.interfacedef.DataType;
@@ -157,6 +160,14 @@
 
         // No authentication required for a get request
     	
+    	// Test for any cache info in the request
+ 	    CacheContext cacheContext = null;    	
+    	try { 
+    	   cacheContext = getCacheContextFromRequest( request );
+    	} catch ( java.text.ParseException e ) {    
+    	}
+    	// System.out.println( "AtomBindingListener.doGet cache context=" + cacheContext );
+    	
         // Get the request path
     	int servletPathLength = request.getContextPath().length() + request.getServletPath().length();
         String path = URLDecoder.decode(request.getRequestURI().substring(servletPathLength), "UTF-8");
@@ -260,20 +271,20 @@
                     // All feeds must provide Id and updated elements.
                     // However, some do not, so provide some program protection.
                     feed.setId( "Feed" + feed.hashCode());
-                    Date lastModified = new Date( 0 );
+                    Date responseLastModified = new Date( 0 );
                     
                     // Add entries to the feed
                     for (Entry<Object, Object> entry: collection) {
                         org.apache.abdera.model.Entry feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory);
                         // Use the most recent entry update as the feed update
                         Date entryUpdated = feedEntry.getUpdated();
-                        if (( entryUpdated != null ) && (entryUpdated.compareTo( lastModified  ) > 0 ))
-                        	lastModified = entryUpdated;
+                        if (( entryUpdated != null ) && (entryUpdated.compareTo( responseLastModified  ) > 0 ))
+                        	responseLastModified = entryUpdated;
                         feed.addEntry(feedEntry);
                     }
                     // If no entries were newly updated,
-                    if ( lastModified.compareTo( new Date( 0 ) ) == 0 ) 
-                    	lastModified = new Date();
+                    if ( responseLastModified.compareTo( new Date( 0 ) ) == 0 ) 
+                    	responseLastModified = new Date();
                 }
             }
             if (feed != null) {
@@ -320,6 +331,11 @@
                 		}
                 	}
                 }
+        		// Provide Etag based on Id and time.               
+        		response.addHeader(ETAG, feedETag );
+        		if ( feedUpdated != null )
+        			response.addHeader(LASTMODIFIED, dateFormat.format( feedUpdated ));
+        		
                 // Content negotiation
             	String acceptType = request.getHeader( "Accept" );
             	String preferredType = getContentPreference( acceptType ); 
@@ -339,10 +355,6 @@
             	} else {
             		// Write the Atom feed
             		response.setContentType("application/atom+xml;type=feed");
-            		// Provide Etag based on Id and time.               
-            		response.addHeader(ETAG, feedETag );
-            		if ( feedUpdated != null )
-            			response.addHeader(LASTMODIFIED, dateFormat.format( feedUpdated ));
             		try {
             			feed.getDocument().writeTo(response.getOutputStream());
             		} catch (IOException ioe) {
@@ -376,14 +388,13 @@
             }
             // Write the Atom entry
             if (feedEntry != null) {
-                IRI feedId = feedEntry.getId();
-                if ( feedId != null )
-                   response.addHeader(ETAG, "\"" + feedId.toString() + "\"" );
+                String entryETag = "\"" + generateEntryETag( feedEntry ) + "\"";
                 Date entryUpdated = feedEntry.getUpdated();
                 if ( entryUpdated != null )
                    response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated ));
                 // TODO Check If-Modified-Since If-Unmodified-Since predicates against LASTMODIFIED. 
-                // If true return 304 and null body.            
+                // If true return 304 and null body.
+                
                 Link link = feedEntry.getSelfLink();
                 if (link != null) {
                     response.addHeader(LOCATION, link.getHref().toString());
@@ -394,6 +405,52 @@
                    }
                 }
 
+                // Test request for predicates.
+                String predicate = request.getHeader( "If-Match" );
+                if (( predicate != null ) && ( !predicate.equals(entryETag) )) {
+                	// No match, should short circuit
+                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return;
+                }
+                predicate = request.getHeader( "If-None-Match" );
+                if (( predicate != null ) && ( predicate.equals(entryETag) )) {
+                	// Match, should short circuit
+                    response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+                    return;
+                }
+                if ( entryUpdated != null ) {
+                	predicate = request.getHeader( "If-Unmodified-Since" );                
+                	if ( predicate != null ) {
+                		try {
+                			Date predicateDate = dateFormat.parse( predicate ); 
+                			if ( predicateDate.compareTo( entryUpdated ) < 0 ) {
+                				// Match, should short circuit
+                				response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+                				return;
+                			}             		
+                		} catch ( java.text.ParseException e ) {
+                			// Ignore and move on
+                		}
+                	}
+                	predicate = request.getHeader( "If-Modified-Since" );                
+                	if ( predicate != null ) {
+                		try {
+                			Date predicateDate = dateFormat.parse( predicate ); 
+                			if ( predicateDate.compareTo( entryUpdated ) > 0 ) {
+                				// Match, should short circuit
+                				response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
+                				return;
+                			}             		
+                		} catch ( java.text.ParseException e ) {
+                			// Ignore and move on
+                		}
+                	}
+                }
+        		// Provide Etag based on Id and time.               
+        		response.addHeader(ETAG, entryETag );
+        		if ( entryUpdated != null )
+        			response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated ));
+        		
                 // Content negotiation
                 String acceptType = request.getHeader( "Accept" );
             	String preferredType = getContentPreference( acceptType ); 
@@ -726,6 +783,8 @@
     
     /**
      * Generate ETag based on feed Id and updated fields.
+     * Note that the feed id should be unique per feed, immutable, and unchanging,
+     * but the ETag should change whenever the feed contents change. 
      * @param feed
      * @return ETag
      */
@@ -748,6 +807,32 @@
         return feedId + "-" + feedUpdated.hashCode();
     }
 
+    /**
+     * Generate ETag based on entry Id and updated fields.
+     * Note that the entry id should be unique per entry, immutable, and unchanging,
+     * but the ETag should change whenever the entry contents change. 
+     * @param feed
+     * @return ETag
+     */
+    public static String generateEntryETag( org.apache.abdera.model.Entry entry ) {
+    	if ( entry == null ) {
+    		return null; 
+    	}
+        
+    	IRI entryIdIRI = entry.getId();
+        String entryId = "ID";
+        if ( entryIdIRI != null ) {
+        	entryId = entryIdIRI.toString();
+        }
+        
+        Date entryUpdated = entry.getUpdated();
+        if ( entryUpdated == null ) {
+        	return entryId;
+        }
+        
+        return entryId + "-" + entryUpdated.hashCode();
+    }
+
     public static String getContentPreference( String acceptType ) {
     	if (( acceptType == null ) || ( acceptType.length() < 1 )) {
             return "application/atom+xml";    		
@@ -757,4 +842,40 @@
     		return st.nextToken();    		
         return "application/atom+xml";
     }
+
+    /**
+     * Gets the cache context information (ETag, LastModified, predicates) from the Http request.
+     * @param request
+     * @return
+     */
+    public CacheContext getCacheContextFromRequest( HttpServletRequest request ) throws java.text.ParseException {
+    	CacheContext context = new CacheContext();
+    	List<String> predicates = new ArrayList<String>();
+    	
+    	String eTag = request.getHeader( "If-Match" );    	
+    	if ( eTag != null ) {
+    	   context.setETag( eTag );
+    	   predicates.add( "If-Match" );
+    	}
+    	eTag = request.getHeader( "If-None-Match" );    	
+    	if ( eTag != null ) {
+    	   context.setETag( eTag );
+    	   predicates.add( "If-None-Match" );
+    	}
+        String lastModifiedString = request.getHeader( "If-Modified-Since" );        
+    	if ( lastModifiedString != null ) {
+     	   context.setLastModified( lastModifiedString );
+     	   predicates.add( "If-Modified-Since" );
+     	}
+        lastModifiedString = request.getHeader( "If-Unmodified-Since" );        
+    	if ( lastModifiedString != null ) {
+     	   context.setLastModified( lastModifiedString );
+     	   predicates.add( "If-Unmodified-Since" );
+     	}
+    	if ( predicates.size() > 0 ) {
+         	context.setPredicates( predicates.toArray( new String[ 0 ] ) );
+    	}
+    	return context;
+    }
+
 }