From 743b32faab13dbd426a255ec770dff02fa2550f3 Mon Sep 17 00:00:00 2001 From: lresende Date: Sat, 1 Jan 2011 23:22:49 +0000 Subject: TUSCANY-3812 - Fixing ETag and Cache headers to avoid refresh issues in IE git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1054319 13f79535-47bb-0310-9956-ffa450edef68 --- .../binding/atom/provider/AtomBindingInvoker.java | 15 +-- .../atom/provider/AtomBindingListenerServlet.java | 124 +++++++++++---------- .../binding/atom/utils/AtomBindingHttpUtils.java | 56 ++++++++++ 3 files changed, 132 insertions(+), 63 deletions(-) create mode 100644 sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java (limited to 'sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org') diff --git a/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java index c1de646f54..e569276b08 100644 --- a/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java +++ b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java @@ -6,15 +6,15 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations - * under the License. + * under the License. */ package org.apache.tuscany.sca.binding.atom.provider; @@ -50,7 +50,7 @@ import org.oasisopen.sca.ServiceRuntimeException; /** * Invoker for the Atom binding. - * + * * @version $Rev$ $Date$ */ class AtomBindingInvoker implements Invoker { @@ -205,7 +205,7 @@ class AtomBindingInvoker implements Invoker { // Write the Atom entry StringWriter writer = new StringWriter(); feedEntry.writeTo(writer); - // postMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); + // postMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); - TUSCANY-3734 postMethod.setHeader("Content-type", "application/atom+xml;type=entry"); postMethod.setEntity(new StringEntity(writer.toString())); @@ -228,7 +228,7 @@ class AtomBindingInvoker implements Invoker { } else { - // Returns the id of the created entry + // Returns the id of the created entry msg.setBody(createdEntry.getId().toString()); } @@ -304,7 +304,8 @@ class AtomBindingInvoker implements Invoker { // Write the Atom entry StringWriter writer = new StringWriter(); feedEntry.writeTo(writer); - putMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); + //putMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); - TUSCANY-3734 + putMethod.setHeader("Content-type", "application/atom+xml;type=entry"); putMethod.setEntity(new StringEntity(writer.toString())); response = httpClient.execute(putMethod); diff --git a/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java index 7779203a3a..e8f1edc44a 100644 --- a/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java +++ b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java @@ -6,15 +6,15 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations - * under the License. + * under the License. */ package org.apache.tuscany.sca.binding.atom.provider; @@ -50,7 +50,7 @@ import org.apache.abdera.model.Workspace; import org.apache.abdera.parser.ParseException; import org.apache.abdera.parser.Parser; import org.apache.abdera.writer.WriterFactory; -import org.apache.commons.codec.binary.Base64; +import org.apache.tuscany.sca.binding.atom.utils.AtomBindingHttpUtils; import org.apache.tuscany.sca.common.http.HTTPCacheContext; import org.apache.tuscany.sca.common.http.HTTPUtils; import org.apache.tuscany.sca.data.collection.Entry; @@ -77,9 +77,9 @@ class AtomBindingListenerServlet extends HttpServlet { private static final Factory abderaFactory = Abdera.getNewFactory(); private static final Parser abderaParser = Abdera.getNewParser(); private static final String ETAG = "ETag"; - private static final String LASTMODIFIED = "Last-Modified"; - private static final String LOCATION = "Location"; - private static final String CONTENTLOCATION = "Content-Location"; + private static final String LASTMODIFIED = "Last-Modified"; + private static final String LOCATION = "Location"; + private static final String CONTENTLOCATION = "Content-Location"; private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time private Invocable wire; @@ -102,7 +102,7 @@ class AtomBindingListenerServlet extends HttpServlet { /** * Constructs a new binding listener. - * + * * @param wire * @param messageFactory * @param feedType @@ -149,7 +149,7 @@ class AtomBindingListenerServlet extends HttpServlet { if (itemClass == org.apache.abdera.model.Entry.class) { supportsFeedEntries = true; } - //We assume that the item type is the same for both input and + //We assume that the item type is the same for both input and //ouput for all operations on the interface itemClassType = getOperation.getOutputType().getLogical().get(0); } @@ -161,10 +161,10 @@ class AtomBindingListenerServlet extends HttpServlet { // No authentication required for a get request // Test for any cache info in the request - HTTPCacheContext cacheContext = null; - try { + HTTPCacheContext cacheContext = null; + try { cacheContext = HTTPCacheContext.createCacheContextFromRequest( request ); - } catch ( java.text.ParseException e ) { + } catch ( java.text.ParseException e ) { } // System.out.println( "AtomBindingListener.doGet cache context=" + cacheContext ); @@ -173,7 +173,7 @@ class AtomBindingListenerServlet extends HttpServlet { //String path = URLDecoder.decode(request.getRequestURI().substring(servletPathLength), "UTF-8"); String path = URLDecoder.decode(HTTPUtils.getRequestPath(request), "UTF-8"); - + logger.fine("get " + request.getRequestURI()); // Handle an Atom request @@ -193,7 +193,8 @@ class AtomBindingListenerServlet extends HttpServlet { */ // Return the Atom service document - response.setContentType("application/atomsvc+xml; charset=utf-8"); + response.setDateHeader("Date", System.currentTimeMillis()); + response.setContentType("application/atomsvc+xml"); String href = request.getRequestURL().toString(); href = href.substring(0, href.length() - "/atomsvc".length()); @@ -230,17 +231,17 @@ class AtomBindingListenerServlet extends HttpServlet { collection.addAccepts("application/json;type=entry"); List categories = feed.getCategories(); if ( categories != null ) { - collection.addCategories(categories, false, null); + collection.addCategories(categories, false, null); } else { collection.addCategories().setFixed(false); } } else { collection.setTitle("entries"); - // collection.addAccepts("application/atom+xml;type=feed"); - collection.addAccepts("application/atom+xml; type=entry"); + + collection.addAccepts("application/atom+xml;type=entry"); collection.addAccepts("application/json;type=entry"); - collection.addCategories().setFixed(false); + collection.addCategories().setFixed(false); } workspace.addCollection(collection); service.addWorkspace(workspace); @@ -258,8 +259,7 @@ class AtomBindingListenerServlet extends HttpServlet { Feed feed = getFeed( request ); if (feed != null) { String feedETag = null; - if (feed.getId() != null) - feedETag = "\"" + feed.getId().toString() + "\""; + feedETag = HTTPUtils.calculateHashETag(feed.toString().getBytes("utf-8")); Date feedUpdated = feed.getUpdated(); // Test request for predicates. String predicate = request.getHeader( "If-Match" ); @@ -275,28 +275,28 @@ class AtomBindingListenerServlet extends HttpServlet { return; } if ( feedUpdated != null ) { - predicate = request.getHeader( "If-Unmodified-Since" ); + predicate = request.getHeader( "If-Unmodified-Since" ); if ( predicate != null ) { try { - Date predicateDate = dateFormat.parse( predicate ); + Date predicateDate = dateFormat.parse( predicate ); if ( predicateDate.compareTo( exactSeconds(feedUpdated) ) < 0 ) { // Match, should short circuit response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; - } + } } catch ( java.text.ParseException e ) { // Ignore and move on } } - predicate = request.getHeader( "If-Modified-Since" ); + predicate = request.getHeader( "If-Modified-Since" ); if ( predicate != null ) { try { - Date predicateDate = dateFormat.parse( predicate ); + Date predicateDate = dateFormat.parse( predicate ); if ( predicateDate.compareTo( exactSeconds(feedUpdated) ) >= 0 ) { // Match, should short circuit response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; - } + } } catch ( java.text.ParseException e ) { // Ignore and move on } @@ -311,9 +311,12 @@ class AtomBindingListenerServlet extends HttpServlet { response.addHeader(LASTMODIFIED, dateFormat.format( feedUpdated )); } + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + // Content negotiation String acceptType = request.getHeader( "Accept" ); - String preferredType = getContentPreference( acceptType ); + String preferredType = getContentPreference( acceptType ); if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) { // JSON response body response.setContentType("application/json;type=feed"); @@ -325,7 +328,7 @@ class AtomBindingListenerServlet extends HttpServlet { feed.writeTo(json, response.getWriter()); } catch (Exception e) { throw new ServletException(e); - } + } } else { // Write the Atom feed @@ -360,23 +363,22 @@ class AtomBindingListenerServlet extends HttpServlet { } if (supportsFeedEntries) { - // The service implementation returns a feed entry + // The service implementation returns a feed entry feedEntry = responseMessage.getBody(); } else { // The service implementation only returns a data item, create an entry // from it - Entry entry = new Entry(id, responseMessage.getBody()); + Entry entry = new Entry(id, responseMessage.getBody()); feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory); } // Write the Atom entry if (feedEntry != null) { String entryETag = null; - if (feedEntry.getId() != null) - entryETag = feedEntry.getId().toString(); + entryETag = HTTPUtils.calculateHashETag(feedEntry.toString().getBytes("utf-8")); Date entryUpdated = feedEntry.getUpdated(); if ( entryUpdated != null ) response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated )); - // TODO Check If-Modified-Since If-Unmodified-Since predicates against LASTMODIFIED. + // TODO Check If-Modified-Since If-Unmodified-Since predicates against LASTMODIFIED. // If true return 304 and null body. Link link = feedEntry.getSelfLink(); @@ -403,28 +405,28 @@ class AtomBindingListenerServlet extends HttpServlet { return; } if ( entryUpdated != null ) { - predicate = request.getHeader( "If-Unmodified-Since" ); + predicate = request.getHeader( "If-Unmodified-Since" ); if ( predicate != null ) { try { - Date predicateDate = dateFormat.parse( predicate ); + 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" ); + predicate = request.getHeader( "If-Modified-Since" ); if ( predicate != null ) { try { - Date predicateDate = dateFormat.parse( predicate ); + 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 } @@ -439,9 +441,12 @@ class AtomBindingListenerServlet extends HttpServlet { response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated )); } + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + // Content negotiation String acceptType = request.getHeader( "Accept" ); - String preferredType = getContentPreference( acceptType ); + String preferredType = getContentPreference( acceptType ); if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) { // JSON response body response.setContentType("application/json;type=entry"); @@ -452,9 +457,9 @@ class AtomBindingListenerServlet extends HttpServlet { feedEntry.writeTo(json, response.getWriter()); } catch (Exception e) { throw new ServletException(e); - } + } } else { - // XML response body + // XML response body response.setContentType("application/atom+xml;type=entry"); try { feedEntry.writeTo(getWriter(response)); @@ -486,7 +491,7 @@ class AtomBindingListenerServlet extends HttpServlet { if (responseMessage.isFault()) { throw new ServletException((Throwable)responseMessage.getBody()); } - return (Feed)responseMessage.getBody(); + return (Feed)responseMessage.getBody(); } else { // The service implementation does not support feed entries, @@ -624,10 +629,11 @@ class AtomBindingListenerServlet extends HttpServlet { // A new entry for non-media was created successfully. if (createdFeedEntry != null) { + // Set location of the created entry in the Location header IRI feedId = createdFeedEntry.getId(); if ( feedId != null ) { - response.addHeader(ETAG, "\"" + feedId.toString() + "\"" ); + response.addHeader(ETAG, "\"" + HTTPUtils.calculateHashETag(createdFeedEntry.toString().getBytes("utf-8")) + "\"" ); } Date entryUpdated = createdFeedEntry.getUpdated(); if ( entryUpdated != null ) { @@ -636,7 +642,7 @@ class AtomBindingListenerServlet extends HttpServlet { Link link = createdFeedEntry.getSelfLink(); if (link != null) { response.addHeader(LOCATION, link.getHref().toString()); - } + } Link editLink = createdFeedEntry.getEditLink(); if (editLink != null) { response.addHeader(LOCATION, editLink.getHref().toString()); @@ -646,6 +652,9 @@ class AtomBindingListenerServlet extends HttpServlet { response.addHeader(CONTENTLOCATION, editMediaLink.getHref().toString()); } + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + // Write the created Atom entry response.setStatus(HttpServletResponse.SC_CREATED); response.setContentType("application/atom+xml;type=entry"); @@ -788,20 +797,22 @@ class AtomBindingListenerServlet extends HttpServlet { } } } - + /** * Process the authorization header - * + * * @param request * @return * @throws ServletException */ private String processAuthorizationHeader(HttpServletRequest request) throws ServletException { - // FIXME temporarily disabling this as it doesn't work with all browsers - if (true) + // FIXME temporarily disabling this as it doesn't work with all browsers + if (true) { return "admin"; + } + /* try { String authorization = request.getHeader("Authorization"); if (authorization != null) { @@ -827,12 +838,13 @@ class AtomBindingListenerServlet extends HttpServlet { } catch (Exception e) { throw new ServletException(e); } + */ return null; } /** * Authenticate a user. - * + * * @param user * @param password * @return @@ -844,7 +856,7 @@ class AtomBindingListenerServlet extends HttpServlet { /** * Reject an unauthorized request. - * + * * @param response */ private void unauthorized(HttpServletResponse response) throws IOException { @@ -859,11 +871,11 @@ class AtomBindingListenerServlet extends HttpServlet { */ private static String getContentPreference( String acceptType ) { if (( acceptType == null ) || ( acceptType.length() < 1 )) { - return "application/atom+xml"; + return "application/atom+xml"; } StringTokenizer st = new StringTokenizer( acceptType, "," ); if ( st.hasMoreTokens() ) { - return st.nextToken(); + return st.nextToken(); } return "application/atom+xml"; } @@ -874,7 +886,7 @@ class AtomBindingListenerServlet extends HttpServlet { * @param properties */ private static void addPropertiesToHeader( HttpServletResponse response, String properties ) { - if ( properties == null ) return; + if ( properties == null ) return; StringTokenizer props = new StringTokenizer( properties, ","); while( props.hasMoreTokens()) { String prop = props.nextToken(); @@ -886,7 +898,7 @@ class AtomBindingListenerServlet extends HttpServlet { if ( keyVal.hasMoreTokens() ) val = keyVal.nextToken(); if (( key != null ) && ( val != null )) { - response.addHeader(key, val); + response.addHeader(key, val); } } } @@ -896,7 +908,7 @@ class AtomBindingListenerServlet extends HttpServlet { * @param date with millisecond precision * @return date rounded down to nearest second */ - private Date exactSeconds(Date date) { + private static Date exactSeconds(Date date) { return new Date(date.getTime() / 1000 * 1000); } diff --git a/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java new file mode 100644 index 0000000000..684dc044d1 --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom.utils; + +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tuscany.sca.common.http.HTTPUtils; + +public class AtomBindingHttpUtils { + /** + * Utility method to set common http headers and other stuff in a + * default http response. Applications / Extensions can then override and + * tweak as they see fit. + * + * @param response + */ + public static void prepareHTTPResponse(HttpServletRequest request, HttpServletResponse response) { + + // common http default response values + HTTPUtils.prepareHTTPResponse(response); + + //set Cache-Control to no-cache to avoid intermediary + //proxy/reverse-proxy caches and always hit the server + //that would identify if the value was current or not + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Expires", new Date(0).toGMTString()); + + } + + private static boolean isIE(HttpServletRequest request) { + String userAgent = request.getHeader("user-agent"); + + return (userAgent.indexOf("MSIE") > -1); + } + +} -- cgit v1.2.3