abdera-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jmsn...@apache.org
Subject svn commit: r428793 [1/2] - in /incubator/abdera/java/trunk/client: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/abdera/ src/main/java/org/apache/abdera/protocol/ src/main/java/org/apache/abdera...
Date Fri, 04 Aug 2006 17:36:35 GMT
Author: jmsnell
Date: Fri Aug  4 10:36:34 2006
New Revision: 428793

URL: http://svn.apache.org/viewvc?rev=428793&view=rev
Log:
This still needs LOTS of work to complete.  The client itself is rather straightforward and complete, conditional gets, 
cache-control, gzip/compress/zip compression is supported and enabled by default, extension HTTP methods (e.g. COPY) 
can be used with nominal effort..

The part that really needs the most work is the client side HTTP cache.  It's generally functional but still needs lots of 
refinements, testing, tweaking... and a generally-better-all-the-way-around implementation.  The goal is to provide a functionally
complete client-side private cache that supports most of the nuances of HTTP caching (e.g. Vary headers, max-stale, min-fresh, 
no-cache and private headers, and so on).  Use of the cache can be controlled via the RequestOptions interface 
(e.g. options.setNoCache(true) to disable the use of the cache via the Cache-Control header).

I had imagined ultimately shipping three cache implementations with this.  An in-memory LRU cache, A write-to-disk cache, and a
JCS-backed cache implementation.  Other impls are possible (e.g. derby-based, etc).

The client code currently depends 100% on the Apache Commons HTTP Client. Changing the http stack would require fairly significant
changes to this code.  

Added:
    incubator/abdera/java/trunk/client/
    incubator/abdera/java/trunk/client/src/
    incubator/abdera/java/trunk/client/src/main/
    incubator/abdera/java/trunk/client/src/main/java/
    incubator/abdera/java/trunk/client/src/main/java/org/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/Cache.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheBase.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheDisposition.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheException.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheFactory.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheKey.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponse.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponseBase.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCache.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCachedResponse.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/SimpleCacheKey.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCache.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCacheFactory.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/BaseRequestEntity.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Client.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ClientException.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsClient.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsResponse.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/DataSourceRequestEntity.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ExtensionMethod.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/RequestOptions.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Response.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ResponseBase.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlParser.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlUtil.java
    incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CachingInputStream.java

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/Cache.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/Cache.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/Cache.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/Cache.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,58 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import org.apache.abdera.protocol.client.RequestOptions;
+import org.apache.abdera.protocol.client.Response;
+
+public interface Cache {
+
+  CacheKey getCacheKey(
+    String uri, 
+    RequestOptions options);
+  
+  CacheDisposition getDisposition(
+    String uri, 
+    RequestOptions options);
+  
+  CachedResponse getIfFreshEnough(
+    String uri, 
+    RequestOptions options);
+  
+  CachedResponse get(
+    String uri, 
+    RequestOptions options);
+  
+  CachedResponse get(
+    CacheKey key);
+  
+  void clear();
+  
+  void remove(
+    String uri, 
+    RequestOptions options);
+  
+  void remove(
+    CacheKey key);
+  
+  Response update(
+    String uri, 
+    RequestOptions options, 
+    Response response);
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheBase.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheBase.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheBase.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheBase.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,139 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import java.io.IOException;
+
+import org.apache.abdera.protocol.client.RequestOptions;
+import org.apache.abdera.protocol.client.Response;
+
+public abstract class CacheBase 
+  implements Cache {
+
+  public CachedResponse get(String uri, RequestOptions options) {
+    return get(getCacheKey(uri,options));
+  }
+
+  public CacheDisposition getDisposition(
+    String uri, 
+    RequestOptions options) {
+      CacheKey key = getCacheKey(uri, options);
+      CachedResponse response = get(key);
+      if (response != null) {
+        String[] pragma = options.getHeaders("Pragma");
+        for (String s: pragma) {
+          if (s.equalsIgnoreCase("no-cache")) {
+            return CacheDisposition.TRANSPARENT;
+          }
+        }
+        if (options != null && options.getNoCache()) 
+          return CacheDisposition.TRANSPARENT;
+        else if (response.isNoCache())
+          return CacheDisposition.STALE;
+        else if (options != null && options.getOnlyIfCached())
+          return CacheDisposition.FRESH;
+        else if (response.isMustRevalidate())
+          return CacheDisposition.STALE;
+        else if (response.getCachedTime() != -1) {
+          if (response.isFresh()) {
+            long maxAge = (options != null) ? options.getMaxAge() : -1;
+            long currentAge = response.getCurrentAge();
+            if (maxAge != -1) {
+              return (maxAge > currentAge) ? 
+                CacheDisposition.FRESH:
+                CacheDisposition.STALE;
+            }
+            long minFresh = (options != null) ? options.getMinFresh() : -1;
+            if (minFresh != -1) {
+              long lifetime = response.getFreshnessLifetime();
+              long age = currentAge;
+              return (lifetime < age + minFresh) ? 
+                CacheDisposition.TRANSPARENT : 
+                CacheDisposition.FRESH;
+            }
+            return CacheDisposition.FRESH;
+          } else {
+            long maxStale = (options != null) ? options.getMaxStale() : -1;
+            if (maxStale != -1) {
+              long howStale = response.getHowStale();
+              return (maxStale < howStale) ? 
+                CacheDisposition.STALE : 
+                CacheDisposition.FRESH;
+            } 
+            return CacheDisposition.STALE;
+          }
+        }
+      }
+      return CacheDisposition.TRANSPARENT;
+
+  }
+
+  public void remove(String uri, RequestOptions options) {
+    remove(getCacheKey(uri,options));
+  }
+
+  protected abstract void add(
+    CacheKey key, 
+    CachedResponse response);
+
+  protected abstract CachedResponse createCachedResponse(
+    Response response, CacheKey key) throws IOException;
+  
+  protected abstract CacheKey getCacheKey(
+    String uri, 
+    RequestOptions options, 
+    Response response);
+  
+  public CachedResponse getIfFreshEnough(
+    String uri, 
+    RequestOptions options) {
+      CacheKey key = getCacheKey(uri,options);
+      CachedResponse response = get(key);
+      if (!response.isFresh()) {
+        // if the milk is only slightly sour, we'll still go ahead and take a drink
+        long max_stale = (options != null) ? options.getMaxStale() : -1;
+        if (max_stale != -1 && response.getHowStale() > max_stale)
+          return null;
+      } else {
+        long min_fresh = (options != null) ? options.getMinFresh() : -1;
+        if (min_fresh != -1 && response.getCurrentAge() < min_fresh)
+          return null;
+      }
+      return response;
+  }
+  
+  public Response update(
+    String uri, 
+    RequestOptions options,
+    Response response) {
+    CacheKey key = getCacheKey(uri, options,response);
+    if ((response != null && response.isNoStore()) ||
+        options != null && options.getNoStore()) {
+     remove(key);
+   } else {
+     try {
+       CachedResponse cachedResponse = createCachedResponse(response, key);
+       add(key, cachedResponse);
+     } catch (IOException e) {
+       throw new CacheException(e);
+     }
+   }
+   return response;
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheDisposition.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheDisposition.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheDisposition.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheDisposition.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,24 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+public enum CacheDisposition {
+
+  STALE, FRESH, TRANSPARENT
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheException.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheException.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheException.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheException.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,42 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+public class CacheException extends RuntimeException {
+
+  private static final long serialVersionUID = -6092683191836155722L;
+
+  public CacheException() {
+    super();
+  }
+
+  public CacheException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public CacheException(String message) {
+    super(message);
+  }
+
+  public CacheException(Throwable cause) {
+    super(cause);
+  }
+
+  
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheFactory.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheFactory.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheFactory.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheFactory.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,31 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import org.apache.abdera.util.ServiceUtil;
+
+public interface CacheFactory {
+
+  public static final CacheFactory INSTANCE = 
+    (CacheFactory)ServiceUtil.newInstance(
+      "org.apache.abdera.protocol.cache.CacheFactory",
+      "org.apache.abdera.protocol.cache.lru.LRUCacheFactory");
+  
+  Cache getCache();
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheKey.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheKey.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheKey.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CacheKey.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,24 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+public interface CacheKey {
+
+  boolean isMatch(CacheKey cacheKey);
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponse.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponse.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponse.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponse.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,42 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import org.apache.abdera.protocol.client.Response;
+
+public interface CachedResponse extends Response {
+
+  CacheKey geKey();
+  
+  Cache getCache();
+  
+  long getCachedTime();
+  
+  long getInitialAge();
+  
+  long getResidentAge();
+
+  long getCurrentAge();
+  
+  long getFreshnessLifetime();
+
+  long getHowStale();
+
+  boolean isFresh();
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponseBase.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponseBase.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponseBase.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/CachedResponseBase.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,107 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import java.util.Date;
+
+import org.apache.abdera.protocol.client.ResponseBase;
+
+public abstract class CachedResponseBase 
+  extends ResponseBase 
+  implements CachedResponse {
+
+  protected CacheKey key = null;;
+  protected Cache cache = null;
+  protected long initial_age = -1;
+  
+  protected CachedResponseBase(CacheKey key, Cache cache) {
+    super();
+    this.key = key;
+    this.cache = cache;
+  }
+  
+  public CacheKey geKey() {
+    return key;
+  }
+
+  public Cache getCache() {
+    return cache;
+  }
+
+  public void release() {
+    if (cache != null) {
+      cache.remove(key); 
+      this.cache= null;
+    }
+  }
+  
+  public CacheDisposition getDisposition() {
+    return null;
+  }
+  
+  public long getInitialAge() {
+    if (initial_age == -1) {
+      long age_value = getAge();
+      long now = (new Date()).getTime();
+      long cachedTime = getCachedTime();
+      long date_value = (cachedTime != -1) ? cachedTime : 0;
+      long apparent_age = Math.max(0,now - date_value);
+      long corrected_received_age = Math.max(apparent_age, age_value);
+      initial_age = corrected_received_age / 1000;
+    }
+    return initial_age;
+  }
+
+  public long getCachedTime() {
+    return getServerDate().getTime();
+  }
+  
+  public long getResidentAge() {
+    long now = (new Date()).getTime();
+    long init = getCachedTime();
+    return Math.max(0,(now - init))/1000;
+  }
+  
+  public long getCurrentAge() {
+    return getInitialAge() + getResidentAge();
+  }
+  
+  public long getFreshnessLifetime() {
+    long lifetime = getMaxAge();
+    if (lifetime == -1) {
+      Date expires_date = getExpires();
+      long expires = (expires_date != null) ? expires_date.getTime() : -1; 
+      long cachedTime = getCachedTime();
+      if (expires != -1) {
+        lifetime = (expires > cachedTime) ? (expires - cachedTime)/1000 : 0; 
+      } // else, expires is not set, return -1 for now. TODO: apply heuristics
+    }
+    return lifetime;
+  }
+  
+  public long getHowStale() {
+    return (!isFresh()) ? getCurrentAge() - getFreshnessLifetime() : 0;
+  }
+  
+  public boolean isFresh() {
+    long lifetime = getFreshnessLifetime();
+    long currentage = getCurrentAge();
+    return (lifetime != -1) ? lifetime > currentage : true;
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCache.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCache.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCache.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCache.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,79 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.abdera.protocol.client.RequestOptions;
+import org.apache.abdera.protocol.client.Response;
+
+public abstract class InMemoryCache 
+  extends CacheBase {
+
+  protected transient Map<CacheKey,CachedResponse> cache = null;
+
+  protected void setMap(Map<CacheKey,CachedResponse> map) {
+    cache = Collections.synchronizedMap(map);
+  }
+  
+  @Override
+  protected CachedResponse createCachedResponse(
+    Response response, 
+    CacheKey key) 
+      throws IOException {
+    return new InMemoryCachedResponse(this, key, response);
+  }
+
+  public void clear() {
+    cache.clear();
+  }
+
+  public CachedResponse get(
+    CacheKey key) {
+      return cache.get(key);
+  }
+
+  public CacheKey getCacheKey(
+    String uri, 
+    RequestOptions options) {
+      //TODO: We need a complete solution that takes the Vary header into account
+      return new SimpleCacheKey(uri);
+  }
+  
+  public CacheKey getCacheKey(
+    String uri,
+    RequestOptions options,
+    Response response) {
+      //TODO: We need a complete solution that takes the Vary header into account
+      return new SimpleCacheKey(uri);
+  }
+
+  public void remove(
+    CacheKey key) {
+      cache.remove(key);
+  }
+
+  protected void add(
+    CacheKey key, 
+    CachedResponse response) {
+      cache.put(key, response);
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCachedResponse.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCachedResponse.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCachedResponse.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/InMemoryCachedResponse.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,129 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.abdera.protocol.client.Response;
+import org.apache.abdera.protocol.util.CachingInputStream;
+
+public class InMemoryCachedResponse 
+  extends CachedResponseBase
+  implements CachedResponse {
+
+  private int status = 0;
+  private String status_text = null;
+  private String uri = null;
+  private Map<String,List<String>> headers = null;
+  private ByteArrayOutputStream out;
+  private InputStream in = null;
+  
+  public InMemoryCachedResponse(
+    Cache cache,
+    CacheKey key,
+    Response response) 
+      throws IOException {
+    super(key,cache);
+    this.status = response.getStatus();
+    this.status_text = response.getStatusText();
+    this.uri = response.getUri();
+    String[] headers = response.getHeaderNames();
+    for (String header : headers) {
+      if (!isNoCacheOrPrivate(header, response)) {
+        String[] values = response.getHeaders(header);
+        List<String> list = Arrays.asList(values);
+        getHeaders().put(header, list);
+      }
+    }
+    getServerDate();
+    getInitialAge();
+    out = new ByteArrayOutputStream();
+    in = new CachingInputStream(response.getInputStream(),out,true);
+    response.setInputStream(in);
+  }
+
+  private Map<String,List<String>> getHeaders() {
+    if (headers == null)
+      headers = new HashMap<String,List<String>>();
+    return headers;
+  }
+  
+  private boolean isNoCacheOrPrivate(
+    String header, 
+    Response response) {
+      String[] no_cache_headers = response.getNoCacheHeaders();
+      String[] private_headers = response.getPrivateHeaders();
+      return contains(no_cache_headers,header) ||
+             contains(private_headers,header);
+  }
+
+  private boolean contains(String[] headers, String header) {
+    if (headers != null) {
+      for (String h : headers) {
+        if (h.equals(header)) return true;
+      }
+    } 
+    return false;
+  }
+  
+  public String getHeader(String header) {
+    List<String> values = getHeaders().get(header);
+    return (values != null) ? values.get(0) : null;
+  }
+
+  public String[] getHeaderNames() {
+    return getHeaders().keySet().toArray(new String[getHeaders().size()]);
+  }
+
+  public String[] getHeaders(String header) {
+    List<String> values = getHeaders().get(header);
+    return (values != null) ?
+      values.toArray(new String[values.size()]) :
+      new String[0];
+  }
+
+  public int getStatus() {
+    return status;
+  }
+
+  public String getStatusText() {
+    return status_text;
+  }
+
+  public String getUri() {
+    return uri;
+  }
+
+  @Override
+  public InputStream getInputStream() throws IOException {
+    return new ByteArrayInputStream(out.toByteArray());
+  }
+
+  @Override
+  public void setInputStream(InputStream in) {
+    throw new UnsupportedOperationException();
+  }
+    
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/SimpleCacheKey.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/SimpleCacheKey.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/SimpleCacheKey.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/SimpleCacheKey.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,61 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+/**
+ * Simple cache key based on an md5 hash of the URI.
+ * A more complete implementation should take the Vary header into account
+ */
+public class SimpleCacheKey implements CacheKey {
+
+  private static final long serialVersionUID = 8757289485580165536L;
+  private byte[] key = null;
+  
+  private MessageDigest md = null;
+  
+  public SimpleCacheKey(String uri) {
+    try {
+      md = MessageDigest.getInstance("md5");
+      key = md.digest(uri.getBytes());
+    } catch (Exception e) {}
+  }
+  
+  public byte[] getKey() {
+    return key;
+  }
+
+  public boolean isMatch(CacheKey cacheKey) {
+    return (cacheKey instanceof SimpleCacheKey) ?
+      MessageDigest.isEqual(
+        key, ((SimpleCacheKey)cacheKey).key) : false;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    return (obj instanceof CacheKey) ? isMatch((CacheKey)obj) : false;
+  }
+  
+  @Override
+  public int hashCode() {
+    return 31 + Arrays.hashCode(key);
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCache.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCache.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCache.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCache.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,51 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache.lru;
+
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import org.apache.abdera.protocol.cache.Cache;
+import org.apache.abdera.protocol.cache.CacheKey;
+import org.apache.abdera.protocol.cache.CachedResponse;
+import org.apache.abdera.protocol.cache.InMemoryCache;
+
+@SuppressWarnings("serial")
+public class LRUCache
+  extends InMemoryCache
+  implements Cache {
+
+  private final static int DEFAULT_SIZE = 10;
+  
+  public LRUCache() {
+    this(DEFAULT_SIZE);
+  }
+  
+  public LRUCache(final int size) {
+    setMap(
+      new LinkedHashMap<CacheKey,CachedResponse>(size,0.75f,true) {
+        @Override
+        protected boolean removeEldestEntry(
+          Entry<CacheKey, CachedResponse> eldest) {
+            return size() > size;
+        }
+      }
+    );
+  }
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCacheFactory.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCacheFactory.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCacheFactory.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/cache/lru/LRUCacheFactory.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,29 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.cache.lru;
+
+import org.apache.abdera.protocol.cache.Cache;
+import org.apache.abdera.protocol.cache.CacheFactory;
+
+public class LRUCacheFactory implements CacheFactory {
+
+  public Cache getCache() {
+    return new LRUCache();
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/BaseRequestEntity.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/BaseRequestEntity.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/BaseRequestEntity.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/BaseRequestEntity.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,75 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.model.Service;
+import org.apache.commons.httpclient.methods.RequestEntity;
+
+/**
+ * Required for the Apache Commons HTTP Client.
+ */
+public class BaseRequestEntity 
+  implements RequestEntity {
+
+  Base base = null;
+  
+  public BaseRequestEntity(Base base) {
+    this.base = base;
+  }
+  
+  public boolean isRepeatable() {
+    return true;
+  }
+
+  public void writeRequest(OutputStream out) throws IOException {
+    base.writeTo(out);
+  }
+
+  public long getContentLength() {
+    return -1;  // chunk the response
+  }
+
+  public String getContentType() {
+    String type = null;
+    if (base instanceof Document) {
+      type = ((Document)base).getContentType().toString();
+    } else if (base instanceof Feed || base instanceof Entry) {
+      Document doc = ((Element)base).getDocument();
+      if (doc != null)
+        type = doc.getContentType().toString();
+      if (type == null)
+        type = "application/atom+xml";
+    } else if (base instanceof Service) {
+      Document doc = ((Element)base).getDocument();
+      if (doc != null)
+        type = doc.getContentType().toString();
+      if (type == null)
+        type = "application/atomserv+xml";      
+    }
+    return (type != null) ? type : "application/xml";
+  }
+  
+}
\ No newline at end of file

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Client.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Client.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Client.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Client.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,161 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.InputStream;
+
+import org.apache.abdera.model.Base;
+import org.apache.abdera.protocol.cache.Cache;
+import org.apache.abdera.protocol.cache.CacheFactory;
+import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
+import org.apache.commons.httpclient.methods.RequestEntity;
+
+public abstract class Client {
+
+  protected Cache cache = null;
+
+  public abstract RequestOptions getDefaultRequestOptions();
+  
+  public abstract void usePreemptiveAuthentication(boolean val);
+  
+  public abstract void init(String userAgent);
+  
+  public Cache getCache() {
+    if (cache == null) {
+      CacheFactory factory = CacheFactory.INSTANCE;
+      if (factory != null)
+        cache = factory.getCache();
+    }
+    return cache;
+  }
+  
+  public Response head(
+    String uri, 
+    RequestOptions options) {
+      return execute("HEAD", uri, null, options);
+  }
+  
+  public Response get(
+    String uri, 
+    RequestOptions options) {
+      return execute("GET", uri, null, options);
+  }
+  
+  public Response post(
+    String uri, 
+    RequestEntity entity, 
+    RequestOptions options) {
+      return execute("POST", uri, entity, options);
+  }
+
+  public Response post(
+    String uri, 
+    InputStream in, 
+    RequestOptions options) {
+      return execute("POST", uri, new InputStreamRequestEntity(in), options);
+  }
+
+  public Response post(
+    String uri, 
+    Base base, 
+    RequestOptions options) {
+      return execute("POST", uri, new BaseRequestEntity(base), options);
+  }
+    
+  public Response put(
+    String uri, 
+    RequestEntity entity, 
+    RequestOptions options) {
+      return execute("PUT", uri, entity, options);
+  }
+
+  public Response put(
+    String uri, 
+    InputStream in, 
+    RequestOptions options) {
+      return execute("PUT", uri, new InputStreamRequestEntity(in), options);
+  }
+
+  public Response put(
+      String uri, 
+      Base base, 
+      RequestOptions options) {
+    return execute("PUT", uri, new BaseRequestEntity(base), options);
+  }
+      
+  public Response delete(
+    String uri, 
+    RequestOptions options) {
+      return execute("DELETE", uri, null, options);
+  }
+  
+  public Response head(String uri) {
+    return head(uri, getDefaultRequestOptions());
+  }
+  
+  public Response get(String uri) {
+    return get(uri, getDefaultRequestOptions());
+  }
+    
+  public Response post(
+    String uri, 
+    RequestEntity entity) {
+    return post(uri, entity, getDefaultRequestOptions());
+  }
+  
+  public Response post(
+    String uri, 
+    InputStream in) {
+      return post(uri, in, getDefaultRequestOptions());
+  }
+  
+  public Response post(
+    String uri, 
+    Base base) {
+      return post(uri, base, getDefaultRequestOptions());
+  }
+
+  public Response put(
+    String uri, 
+    RequestEntity entity) {
+      return put(uri, entity, getDefaultRequestOptions());
+  }
+  
+  public Response put(
+    String uri, 
+    InputStream in) {
+      return put(uri, in, getDefaultRequestOptions());
+  }
+  
+  public Response put(
+    String uri, 
+    Base base) {
+      return put(uri, base, getDefaultRequestOptions());
+  }
+
+  public Response delete(
+    String uri) {
+      return delete(uri, getDefaultRequestOptions());
+  }
+  
+  public abstract Response execute(
+    String method, 
+    String uri, 
+    RequestEntity entity, 
+    RequestOptions options);
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ClientException.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ClientException.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ClientException.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ClientException.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,40 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+public class ClientException extends RuntimeException {
+
+  private static final long serialVersionUID = -5267534800044933182L;
+
+  public ClientException() {
+    super();
+  }
+
+  public ClientException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public ClientException(String message) {
+    super(message);
+  }
+
+  public ClientException(Throwable cause) {
+    super(cause);
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsClient.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsClient.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsClient.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsClient.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,178 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import org.apache.abdera.protocol.cache.Cache;
+import org.apache.abdera.protocol.cache.CacheDisposition;
+import org.apache.abdera.protocol.cache.CachedResponse;
+import org.apache.abdera.util.Version;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.methods.OptionsMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.TraceMethod;
+import org.apache.commons.httpclient.params.HttpClientParams;
+
+public class CommonsClient extends Client {
+
+  private HttpClient client = null;
+  
+  public CommonsClient() {
+    this(Version.APP_NAME + "/" + Version.VERSION);
+  }
+  
+  public CommonsClient(String userAgent) {
+    init(userAgent);
+  }
+  
+  public void usePreemptiveAuthentication(boolean val) {
+    client.getParams().setAuthenticationPreemptive(val);
+  }
+  
+  @Override
+  public void init(String userAgent) {
+    client = new HttpClient();
+    client.getParams().setParameter(
+      HttpClientParams.USER_AGENT, 
+      userAgent);
+    client.getParams().setBooleanParameter(
+      HttpClientParams.USE_EXPECT_CONTINUE, true);    
+  }
+  
+  @Override
+  public Response execute(
+    String method, 
+    String uri, 
+    RequestEntity entity,
+    RequestOptions options) {
+      try {
+        Response response = null;
+        CachedResponse cached_response = null;
+        Cache cache = getCache();
+        CacheDisposition disp = CacheDisposition.TRANSPARENT;
+        if (cache != null) {
+          disp = cache.getDisposition(uri,options);
+          cached_response = cache.get(uri, options);
+          switch(disp) {
+            case FRESH:
+              //System.out.println("____ CACHE HIT: FRESH");
+              response = cached_response;
+              break;
+            case STALE:
+              //System.out.println("____ CACHE HIT: STALE, Need to revalidate");
+              if (options == null) 
+                options = getDefaultRequestOptions();
+              if (cached_response.getLastModified() != null)
+                options.setIfModifiedSince(cached_response.getLastModified());
+              if (cached_response.getEntityTag() != null)
+                options.setIfNoneMatch(cached_response.getEntityTag());
+              break;
+            default:
+              //System.out.println("____ CACHE MISS: TRANSPARENT");
+          }          
+        }
+        if (response == null) {
+          HttpMethod httpMethod = createMethod(method, uri, entity);
+          if (options != null) {
+            String[] headers = options.getHeaderNames();
+            for (String header : headers) {
+              String[] values = options.getHeaders(header);
+              for (String value : values) {
+                httpMethod.addRequestHeader(header, value);
+              }
+            }
+            String cc = options.getCacheControl();
+            if (cc != null && cc.length() != 0)
+              httpMethod.setRequestHeader("Cache-Control", cc);
+          }
+          int n = client.executeMethod(httpMethod);
+          if (n == 304 || n == 412 && 
+              cached_response != null &&
+              disp.equals(CacheDisposition.STALE)) {
+            response = cached_response;
+          } else {
+            response = new CommonsResponse(httpMethod);
+            if (cache != null) 
+              response = cache.update(
+                uri, options, response);
+          }
+        }
+        return response;
+      } catch (Throwable t) {
+        throw new ClientException(t);
+      }
+  }
+
+  private HttpMethod createMethod(
+    String method, 
+    String uri,
+    RequestEntity entity) {
+      if (method == null) return null;
+      if (method.equalsIgnoreCase("GET")) {
+        return new GetMethod(uri);
+      } else if (method.equalsIgnoreCase("POST")) {
+        EntityEnclosingMethod m = new PostMethod(uri);
+        if (entity != null)
+          m.setRequestEntity(entity);
+        return m;
+      } else if (method.equalsIgnoreCase("PUT")) {
+        EntityEnclosingMethod m = new PutMethod(uri);
+        if (entity != null)
+          m.setRequestEntity(entity);
+        return m;
+      } else if (method.equalsIgnoreCase("DELETE")) {
+        return new DeleteMethod(uri);
+      } else if (method.equalsIgnoreCase("HEAD")) {
+        return new HeadMethod(uri);
+      } else if (method.equalsIgnoreCase("OPTIONS")) {
+        return new OptionsMethod(uri);
+      } else if (method.equalsIgnoreCase("TRACE")) {
+        return new TraceMethod(uri);
+      } else {
+        EntityEnclosingMethod m = new ExtensionMethod(method.toUpperCase());
+        if (entity != null)
+          m.setRequestEntity(entity);
+        return m;
+      }
+  }
+  
+  @Override
+  public RequestOptions getDefaultRequestOptions() {
+    RequestOptions options = new RequestOptions();
+    options.setAcceptEncoding(
+      "gzip;q=1.0", 
+      "deflate;q=1.0", 
+      "zip;q=0.5");
+    options.setAccept(
+      "application/atom+xml",
+      "application/atomserv+xml",
+      "application/xml;q=0.8",
+      "text/xml;q=0.5",
+      "*/*;q=0.1");
+    options.setAcceptCharset(
+      "utf-8", "*;q=0.5");
+    return options;
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsResponse.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsResponse.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsResponse.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/CommonsResponse.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,122 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipInputStream;
+
+import org.apache.abdera.protocol.util.CacheControlUtil;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.URIException;
+
+public class CommonsResponse 
+  extends ResponseBase
+  implements Response {
+
+  private HttpMethod method = null;
+    
+  protected CommonsResponse(HttpMethod method) {
+    if (method.isRequestSent()) 
+      this.method = method;
+    else throw new IllegalStateException();
+    parse_cc();
+    getServerDate();
+  }
+
+  public HttpMethod getMethod() {
+    return method;
+  }
+  
+  public int getStatus() {
+    return method.getStatusCode();
+  }
+  
+  public String getStatusText() {
+    return method.getStatusText();
+  }
+  
+  public void release() {
+    method.releaseConnection();
+  }
+  
+  public String getHeader(String header) {
+    Header h = method.getResponseHeader(header);
+    if (h != null) 
+      return h.getValue();
+    else return null;
+  }
+
+  public String[] getHeaders(String header) {
+    Header[] headers = method.getResponseHeaders(header);
+    String[] values = new String[headers.length];
+    for (int n = 0; n < headers.length; n++) {
+      values[n] = headers[n].getValue();
+    }
+    return values;
+  }
+  
+  public String[] getHeaderNames() {
+    Header[] headers = method.getResponseHeaders();
+    List<String> list = new ArrayList<String>();
+    for (Header h : headers) {
+      String name = h.getName();
+      if (!list.contains(name))
+        list.add(name);
+    }
+    return list.toArray(new String[list.size()]);
+  }
+
+  public String getUri() {
+    try {
+      return method.getURI().toString();
+    } catch (URIException e) {}
+    return null; // shouldn't happen
+  }
+
+  public InputStream getInputStream() throws IOException {
+    if (in == null) {
+      in = method.getResponseBodyAsStream();
+      String ce = getHeader("Content-Encoding");
+      if (ce != null) {
+        // multiple encodings may be applied, they're listed in the order
+        // they were applied, so we need to walk the list backwards
+        String[] encodings = CacheControlUtil.splitAndTrim(ce, ",", false);
+        for (int n = encodings.length -1; n >= 0; n--) {
+          if ("gzip".equalsIgnoreCase(encodings[n]) ||
+              "x-gzip".equalsIgnoreCase(encodings[n])) {
+            in = new GZIPInputStream(in);
+          } else if ("deflate".equalsIgnoreCase(encodings[n])) {
+            in = new InflaterInputStream(in);
+          } else if ("zip".equalsIgnoreCase(encodings[n])) {
+            in = new ZipInputStream(in);
+          } else {
+            throw new IOException("Unsupported Content-Encoding");
+          }
+        }
+      } 
+    }
+    return super.getInputStream();
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/DataSourceRequestEntity.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/DataSourceRequestEntity.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/DataSourceRequestEntity.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/DataSourceRequestEntity.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,59 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataSource;
+
+import org.apache.commons.httpclient.methods.RequestEntity;
+
+public class DataSourceRequestEntity 
+  implements RequestEntity {
+
+  private DataSource dataSource = null;
+  
+  public DataSourceRequestEntity(DataSource dataSource) {
+    this.dataSource = dataSource;
+  }
+  
+  public long getContentLength() {
+    return -1;
+  }
+
+  public String getContentType() {
+    return dataSource.getContentType();
+  }
+
+  public boolean isRepeatable() {
+    return true;
+  }
+
+  public void writeRequest(OutputStream out) throws IOException {
+    InputStream in = dataSource.getInputStream();
+    byte[] buf = new byte[1024];
+    int n = -1;
+    while ((n = in.read(buf,0,1024)) != -1) {
+      out.write(buf,0,n);
+      out.flush();
+    }
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ExtensionMethod.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ExtensionMethod.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ExtensionMethod.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ExtensionMethod.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,37 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
+
+public final class ExtensionMethod 
+  extends EntityEnclosingMethod {
+
+  private String method = null;
+  
+  protected ExtensionMethod(String method) {
+    super(method);
+    this.method = method;
+  }
+
+  @Override
+  public String getName() {
+    return method;
+  }
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/RequestOptions.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/RequestOptions.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/RequestOptions.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/RequestOptions.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,308 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.activation.MimeType;
+
+import org.apache.abdera.protocol.util.CacheControlUtil;
+import org.apache.commons.httpclient.util.DateParseException;
+import org.apache.commons.httpclient.util.DateUtil;
+
+public class RequestOptions {
+
+  private byte flags = 0;
+  private final static byte NOCACHE = 1;
+  private final static byte NOSTORE = 2;
+  private final static byte NOTRANSFORM = 4;
+  private final static byte ONLYIFCACHED = 8;
+  private long max_age = -1;
+  private long max_stale = -1;
+  private long min_fresh = -1;
+  
+  private Map<String,List<String>> headers = null;  
+  
+  private Map<String,List<String>> getHeaders() {
+    if (headers == null)
+      headers = new HashMap<String,List<String>>();
+    return headers;
+  }
+
+  private String combine(String ... values) {
+    String v = "";
+    for (String val : values) {
+      if (v.length() > 0) v += ", ";
+      v += val;
+    }
+    return v;
+  }
+    
+  public void setContentType(String value) {
+    setHeader("Content-Type", value);
+  }
+  
+  public void setContentType(MimeType value) {
+    setHeader("Content-Type", value.toString());
+  }
+  
+  public String getContentType() {
+    return getHeader("Content-Type");
+  }
+  
+  public void setAuthorization(String auth) {
+    setHeader("Authorization", auth);
+  }
+  
+  public String getAuthorization() {
+    return getHeader("Authorization");
+  }
+  
+  public void setHeader(String header, String value) {
+    setHeader(header, new String[] {value});
+  }
+
+  public void setHeader(String header, String... values) {
+    List<String> list = Arrays.asList(new String[] {combine(values)});
+    getHeaders().put(header, list);
+  }
+  
+  public void setDateHeader(String header, Date value) {
+    setHeader(header, DateUtil.formatDate(value));
+  }
+  
+  public void addHeader(String header, String value) {
+    addHeader(header, new String[] {value});
+  }
+  
+  public void addHeader(String header, String... values) {
+    List<String> list = getHeaders().get(header);
+    String value = combine(values);
+    if (list != null) {
+      if (!list.contains(value)) 
+        list.add(value);
+    } else {
+      setHeader(header, new String[] {value});
+    }
+  }
+
+  public void addDateHeader(String header, Date value) {
+    addHeader(header, DateUtil.formatDate(value));
+  }
+  
+  public String getHeader(String header) {
+    List<String> list = getHeaders().get(header);
+    return (list != null) ? list.get(0) : null;
+  }
+  
+  public String[] getHeaders(String header) {
+    List<String> list = getHeaders().get(header);
+    return (list != null) ? 
+      list.toArray(new String[list.size()]) : 
+      new String[0];
+  }
+  
+  public Date getDateHeader(String header) {
+    String val = getHeader(header);
+    try {
+      return (val != null) ? DateUtil.parseDate(val) : null;
+    } catch (DateParseException e) {
+      throw new ClientException(e);
+    }
+  }
+
+  public String[] getHeaderNames() {
+    Set<String> set = getHeaders().keySet();
+    return set.toArray(new String[set.size()]);
+  }
+  
+  public String getIfMatch() {
+    return getHeader("If-Match");
+  }
+  
+  public void setIfMatch(String entity_tag) {
+    setHeader("If-Match", entity_tag);
+  }
+  
+  public void setIfMatch(String... entity_tags) {
+    setHeader("If-Match", entity_tags);
+  }
+  
+  public String getIfNoneMatch() {
+    return getHeader("If-None-Match");
+  }
+  
+  public void setIfNoneMatch(String entity_tag) {
+    setHeader("If-None-Match", entity_tag);
+  }
+  
+  public void setIfNoneMatch(String... entity_tags) {
+    setHeader("If-None-Match", entity_tags);
+  }
+  
+  public Date getIfModifiedSince() {
+    return getDateHeader("If-Modified-Since");
+  }
+  
+  public void setIfModifiedSince(Date date) {
+    setDateHeader("If-Modified-Since", date);
+  }
+  
+  public Date getIfUnmodifiedSince() {
+    return getDateHeader("If-Unmodified-Since");
+  }
+  
+  public void setIfUnmodifiedSince(Date date) {
+    setDateHeader("If-Unmodified-Since", date);
+  }
+  
+  public String getAccept() {
+    return getHeader("Accept");
+  }
+  
+  public void setAccept(String accept) {
+    setAccept(new String[] {accept});
+  }
+  
+  public void setAccept(String... accept) {
+    setHeader("Accept", accept);
+  }
+  
+  public String getAcceptLanguage() {
+    return getHeader("Accept-Language");
+  }
+  
+  public void setAcceptLanguage(String accept) {
+    setAcceptLanguage(new String[] {accept});
+  }
+  
+  public void setAcceptLanguage(String... accept) {
+    setHeader("Accept-Language", accept);
+  }
+  
+  public String getAcceptCharset() {
+    return getHeader("Accept-Charset");
+  }
+  
+  public void setAcceptCharset(String accept) {
+    setAcceptCharset(new String[] {accept});
+  }
+  
+  public void setAcceptCharset(String... accept) {
+    setHeader("Accept-Charset", accept);
+  }
+  
+  public String getAcceptEncoding() {
+    return getHeader("Accept-Encoding");
+  }
+  
+  public void setAcceptEncoding(String accept) {
+    setAcceptEncoding(new String[] {accept});
+  }
+  
+  public void setAcceptEncoding(String... accept) {
+    setHeader("Accept-Encoding", accept);
+  }
+  
+  public boolean getOnlyIfCached() {
+    return (flags & ONLYIFCACHED) == ONLYIFCACHED;
+  }
+  
+  public void setOnlyIfCached(boolean condition) {
+    if (condition) flags |= ONLYIFCACHED;
+    else flags ^= ONLYIFCACHED;
+  }
+  
+  public boolean getNoTransform() {
+    return (flags & NOTRANSFORM) == NOTRANSFORM;
+  }
+  
+  public void setNoTransform(boolean condition) {
+    if (condition) flags |= NOTRANSFORM;
+    else flags ^= NOTRANSFORM;
+  }
+  
+  public boolean getNoCache() {
+    return (flags & NOCACHE) == NOCACHE;
+  }
+  
+  public void setNoCache(boolean condition) {
+    if (condition) flags |= NOCACHE;
+    else flags ^= NOCACHE;
+  }
+  
+  public boolean getNoStore() {
+    return (flags & NOSTORE) == NOSTORE;
+  }
+  
+  public void setNoStore(boolean condition) {
+    if (condition) flags |= NOSTORE;
+    else flags ^= NOSTORE;
+  }
+  
+  public long getMaxAge() {
+    return max_age;
+  }
+  
+  public void setMaxAge(long condition) {
+    this.max_age = condition;
+  }
+  
+  public long getMaxStale() {
+    return max_stale;
+  }
+  
+  public void setMaxStale(long condition) {
+    this.max_stale = condition;
+  }
+  
+  public long getMinFresh() {
+    return this.min_fresh;
+  }
+  
+  public void setMinFresh(long condition) {
+    this.min_fresh = condition;
+  }
+  
+  public void setCacheControl(String cc) {
+    CacheControlUtil.parseCacheControl(cc, this);
+  }
+  
+  public String getCacheControl() {
+    StringBuffer buf = new StringBuffer();
+    if (getNoCache()) append(buf,"no-cache");
+    if (getNoStore()) append(buf,"no-store");
+    if (getNoTransform()) append(buf, "no-transform");
+    if (getOnlyIfCached()) append(buf, "only-if-cached");
+    if (getMaxAge() != -1) append(buf, "max-age=" + getMaxAge());
+    if (getMaxStale() != -1) append(buf, "max-stale=" + getMaxStale());
+    if (getMinFresh() != -1) append(buf, "min-fresh=" + getMinFresh());
+    return buf.toString();
+  }
+  
+  private void append(StringBuffer buf, String value) {
+    if (buf.length() > 0) buf.append(", ");
+    buf.append(value);
+  }
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Response.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Response.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Response.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/Response.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,88 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+
+import javax.activation.MimeType;
+
+public interface Response {
+
+  public static enum ResponseType {
+    SUCCESS, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, UNKNOWN
+  }
+
+  ResponseType getResponseClass();
+  
+  int getStatus();
+  
+  String getUri();
+  
+  String getStatusText();
+  
+  Date getLastModified();
+  
+  String getEntityTag();
+  
+  MimeType getContentType();
+  
+  Date getServerDate();
+  
+  String getLocation();
+  
+  String getContentLocation();
+  
+  String getHeader(String header);
+  
+  Date getDateHeader(String header);
+  
+  String[] getHeaders(String header);
+  
+  String[] getHeaderNames();
+  
+  void release();
+  
+  InputStream getInputStream() throws IOException;
+  
+  void setInputStream(InputStream in);
+  
+  boolean isPrivate();
+  
+  boolean isPublic();
+  
+  boolean isNoCache();
+  
+  boolean isNoStore();
+  
+  boolean isNoTransform();
+  
+  boolean isMustRevalidate();
+  
+  long getMaxAge();
+  
+  long getAge();
+  
+  Date getExpires();
+  
+  String[] getNoCacheHeaders();
+  
+  String[] getPrivateHeaders();
+  
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ResponseBase.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ResponseBase.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ResponseBase.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/client/ResponseBase.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,200 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+
+import javax.activation.MimeType;
+
+import org.apache.abdera.protocol.util.CacheControlUtil;
+import org.apache.commons.httpclient.util.DateParseException;
+import org.apache.commons.httpclient.util.DateUtil;
+
+public abstract class ResponseBase implements Response {
+
+  protected byte flags = 0;
+  protected final static byte NOCACHE = 1;
+  protected final static byte NOSTORE = 2;
+  protected final static byte NOTRANSFORM = 4;
+  protected final static byte PUBLIC = 8;
+  protected final static byte PRIVATE = 16;
+  protected final static byte REVALIDATE = 32;
+  protected String[] nocache_headers = null;
+  protected String[] private_headers = null;
+  protected long max_age = -1;
+  protected InputStream in = null;
+  protected Date response_date = null;
+  
+  public InputStream getInputStream() throws IOException {
+    return in;
+  }
+
+  public void setInputStream(InputStream in) {
+    this.in = in;
+  }
+
+  public ResponseType getResponseClass() {
+    int status = getStatus();
+    if (status >= 200 && status < 300) return ResponseType.SUCCESS;
+    if (status >= 300 && status < 400) return ResponseType.REDIRECTION;
+    if (status >= 400 && status < 500) return ResponseType.CLIENT_ERROR;
+    if (status >= 500 && status < 600) return ResponseType.SERVER_ERROR;
+    return ResponseType.UNKNOWN;
+  }
+  
+  public Date getDateHeader(String header) {
+    try {
+      String value = getHeader(header);
+      if (value != null)
+        return DateUtil.parseDate(value);
+      else return null;
+    } catch (DateParseException e) {
+      throw new ClientException(e); // server likely returned a bad date format
+    }
+  }
+  
+  public MimeType getContentType() {
+    try {
+      String type = getHeader("Content-Type");
+      if (type != null) 
+        return new MimeType(type);
+    } catch (Exception e) {}
+    return null;  // shouldn't happen
+  } 
+
+  public String getEntityTag() {
+    return getHeader("ETag");
+  }
+
+  public Date getLastModified() {
+    return getDateHeader("Last-Modified");
+  }
+
+  public Date getServerDate() {
+    if (response_date == null) {
+      Date date = getDateHeader("Date");
+      if (date != null)
+        response_date = date;
+    }
+    return response_date;
+  }
+  
+  public String getContentLocation() {
+    return getHeader("Content-Location");
+  }
+
+  public String getLocation() {
+    return getHeader("Location");
+  }
+
+  public long getAge() {
+    String age = getHeader("Age");
+    return (age != null) ? Long.parseLong(age) : -1;
+  }
+
+  public Date getExpires() {
+    return getDateHeader("Expires");
+  }
+
+  public long getMaxAge() {
+    return max_age;
+  }
+
+  public boolean isMustRevalidate() {
+    return (flags & REVALIDATE) == REVALIDATE;
+  }
+
+  public boolean isNoCache() {
+    return (flags & NOCACHE) == NOCACHE;
+  }
+
+  public boolean isNoStore() {
+    return (flags & NOSTORE) == NOSTORE;
+  }
+
+  public boolean isNoTransform() {
+    return (flags & NOTRANSFORM) == NOTRANSFORM;
+  }
+
+  public boolean isPrivate() {
+    return (flags & PRIVATE) == PRIVATE;
+  }
+
+  public boolean isPublic() {
+    return (flags & PUBLIC) == PUBLIC;
+  }
+  
+  public String[] getNoCacheHeaders() {
+    return nocache_headers;
+  }
+  
+  public String[] getPrivateHeaders() {
+    return private_headers;
+  }
+  
+  public void setMaxAge(long max_age) {
+    this.max_age = max_age;
+  }
+  
+  public void setMustRevalidate(boolean val) {
+    if (val) flags |= REVALIDATE;
+    else flags ^= REVALIDATE;
+  }
+  
+  public void setNoCache(boolean val) {
+    if (val) flags |= NOCACHE;
+    else flags ^= NOCACHE;
+  }
+  
+  public void setNoStore(boolean val) {
+    if (val) flags |= NOSTORE;
+    else flags ^= NOSTORE;
+  }
+  
+  public void setNoTransform(boolean val) {
+    if (val) flags |= NOTRANSFORM;
+    else flags ^= NOTRANSFORM;
+  }
+  
+  public void setPublic(boolean val) {
+    if (val) flags |= PUBLIC;
+    else flags ^= PUBLIC;
+  }
+  
+  public void setPrivate(boolean val) {
+    if (val) flags |= PRIVATE;
+    else flags ^= PRIVATE;
+  }
+  
+  public void setPrivateHeaders(String... headers) {
+    this.private_headers = headers;
+  }
+  
+  public void setNoCacheHeaders(String... headers) {
+    this.nocache_headers = headers;
+  }
+  
+  protected void parse_cc() {
+    String cc = getHeader("Cache-Control");
+    if (cc != null)
+      CacheControlUtil.parseCacheControl(cc, this);
+  }
+
+}

Added: incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlParser.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlParser.java?rev=428793&view=auto
==============================================================================
--- incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlParser.java (added)
+++ incubator/abdera/java/trunk/client/src/main/java/org/apache/abdera/protocol/util/CacheControlParser.java Fri Aug  4 10:36:34 2006
@@ -0,0 +1,90 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.protocol.util;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CacheControlParser implements Iterable<String> {
+
+  public static final HashSet<String> cacheRequestDirectives = 
+    new HashSet<String>(
+      Arrays.asList(
+        new String[] { 
+          "no-cache", 
+          "no-store", 
+          "max-age", 
+          "max-stale", 
+          "min-fresh", 
+          "no-transform", 
+          "only-if-cached" }));
+
+  public static final HashSet<String> cacheResponseDirectives = 
+    new HashSet<String>(
+      Arrays.asList(
+        new String[] { 
+          "public", 
+          "private", 
+          "no-cache", 
+          "no-store", 
+          "no-transform", 
+          "must-revalidate", 
+          "proxy-revalidate",
+          "max-age", 
+          "s-maxage" }));
+
+  private static final String REGEX = 
+    "\\s*([\\w\\-]+)\\s*(=)?\\s*(\\d+|\\\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)+\\\")?\\s*";
+
+  private static final Pattern pattern = Pattern.compile(REGEX);
+
+  private HashMap<String, String> values = new HashMap<String, String>();
+
+  public CacheControlParser(String value) {
+    Matcher matcher = pattern.matcher(value);
+    while (matcher.find()) {
+      String directive = matcher.group(1);
+      if (isDirective(directive)) {
+        values.put(directive, matcher.group(3));
+      }
+    }
+  }
+
+  private boolean isDirective(String directive) {
+    return cacheRequestDirectives.contains(directive) || 
+           cacheResponseDirectives.contains(directive);
+  }
+
+  public Map<String,String> getValues() {
+    return values;
+  }
+
+  public String getValue(String directive) {
+    return values.get(directive);
+  }
+  
+  public Iterator<String> iterator() {
+    return values.keySet().iterator();
+  }
+  
+}
\ No newline at end of file



Mime
View raw message