diff --git a/cleanup.xml b/cleanup.xml
new file mode 100644
index 0000000..40f9b7a
--- /dev/null
+++ b/cleanup.xml
@@ -0,0 +1,56 @@
+
+
The default user agent. It is not marked final so - * buggy java compiler will not write this string - * into all classes that reference it.
- * - *http://tinyurl.com/64t5n points to https://rome.dev.java.net - * Some servers ban user agents with "Java" in the name.
- * + *+ * The default user agent. It is not marked final so buggy java compiler will not write this string into all classes that reference it. + *
+ * + *+ * http://tinyurl.com/64t5n points to https://rome.dev.java.net Some servers ban user agents with "Java" in the name. + *
+ * */ public static String DEFAULT_USER_AGENT = "Rome Client (http://tinyurl.com/64t5n)"; @@ -47,43 +46,56 @@ public interface FeedFetcher { public abstract String getUserAgent(); /** - *Turn on or off rfc3229 delta encoding
- * - *See http://www.ietf.org/rfc/rfc3229.txt and http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html
- * - *NOTE: This is experimental and feedback is welcome!
- * + *+ * Turn on or off rfc3229 delta encoding + *
+ * + *+ * See http://www.ietf.org/rfc/rfc3229.txt and http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html + *
+ * + *+ * NOTE: This is experimental and feedback is welcome! + *
+ * * @param useDeltaEncoding */ public abstract void setUsingDeltaEncoding(boolean useDeltaEncoding); /** - *Is this fetcher using rfc3229 delta encoding?
- * + *+ * Is this fetcher using rfc3229 delta encoding? + *
+ * * @return */ public abstract boolean isUsingDeltaEncoding(); /** - *Add a FetcherListener.
- * - *The FetcherListener will receive an FetcherEvent when - * a Fetcher event (feed polled, retrieved, etc) occurs
- * + *+ * Add a FetcherListener. + *
+ * + *+ * The FetcherListener will receive an FetcherEvent when a Fetcher event (feed polled, retrieved, etc) occurs + *
+ * * @param listener The FetcherListener to recieve the event */ public abstract void addFetcherEventListener(FetcherListener listener); /** - *Remove a FetcherListener
- * + *+ * Remove a FetcherListener + *
+ * * @param listener The FetcherListener to remove */ public abstract void removeFetcherEventListener(FetcherListener listener); /** * Retrieve a feed over HTTP - * + * * @param feedUrl A non-null URL of a RSS/Atom feed to retrieve * @return A {@link com.sun.syndication.feed.synd.SyndFeed} object * @throws IllegalArgumentException if the URL is null; @@ -91,15 +103,13 @@ public interface FeedFetcher { * @throws FeedException if the feed is not valid * @throws FetcherException if a HTTP error occurred */ - public abstract SyndFeed retrieveFeed(URL feedUrl) - throws IllegalArgumentException, IOException, FeedException, FetcherException; + public abstract SyndFeed retrieveFeed(URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException; - public SyndFeed retrieveFeed(String userAgent, URL url) - throws IllegalArgumentException, IOException, FeedException, FetcherException; + public SyndFeed retrieveFeed(String userAgent, URL url) throws IllegalArgumentException, IOException, FeedException, FetcherException; /** - * If set to true, the WireFeed will be made accessible from the SyndFeed object returned from the Fetcher - * via the originalWireFeed() method. Each Entry in the feed will have the corresponding wireEntry property set. + * If set to true, the WireFeed will be made accessible from the SyndFeed object returned from the Fetcher via the originalWireFeed() method. Each Entry in + * the feed will have the corresponding wireEntry property set. */ void setPreserveWireFeed(boolean preserveWireFeed); } diff --git a/src/main/java/org/rometools/fetcher/FetcherEvent.java b/src/main/java/org/rometools/fetcher/FetcherEvent.java index 232af89..8010113 100644 --- a/src/main/java/org/rometools/fetcher/FetcherEvent.java +++ b/src/main/java/org/rometools/fetcher/FetcherEvent.java @@ -5,81 +5,85 @@ import java.util.EventObject; import com.sun.syndication.feed.synd.SyndFeed; /** - * Implementation note: FetcherEvent is not thread safe. Make sure that - * they are only ever accessed by one thread. If necessary, make all getters - * and setters synchronized, or alternatively make all fields final. + * Implementation note: FetcherEvent is not thread safe. Make sure that they are only ever accessed by one thread. If necessary, make all getters and setters + * synchronized, or alternatively make all fields final. * * @author nl */ public class FetcherEvent extends EventObject { - private static final long serialVersionUID = 3985600601904140103L; + private static final long serialVersionUID = 3985600601904140103L; - public static final String EVENT_TYPE_FEED_POLLED = "FEED_POLLED"; - public static final String EVENT_TYPE_FEED_RETRIEVED = "FEED_RETRIEVED"; - public static final String EVENT_TYPE_FEED_UNCHANGED = "FEED_UNCHANGED"; - - private String eventType; - private String urlString; - private SyndFeed feed; - - public FetcherEvent(Object source) { - super(source); - } + public static final String EVENT_TYPE_FEED_POLLED = "FEED_POLLED"; + public static final String EVENT_TYPE_FEED_RETRIEVED = "FEED_RETRIEVED"; + public static final String EVENT_TYPE_FEED_UNCHANGED = "FEED_UNCHANGED"; + private String eventType; + private String urlString; + private SyndFeed feed; - public FetcherEvent(Object source, String urlStr, String eventType) { - this(source); - setUrlString(urlStr); - setEventType(eventType); - } + public FetcherEvent(final Object source) { + super(source); + } + + public FetcherEvent(final Object source, final String urlStr, final String eventType) { + this(source); + setUrlString(urlStr); + setEventType(eventType); + } + + public FetcherEvent(final Object source, final String urlStr, final String eventType, final SyndFeed feed) { + this(source, urlStr, eventType); + setFeed(feed); + } - public FetcherEvent(Object source, String urlStr, String eventType, SyndFeed feed) { - this(source, urlStr, eventType); - setFeed(feed); - } - - /** * @return Returns the feed. * - *The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED
+ *+ * The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED + *
*/ public SyndFeed getFeed() { return feed; } - + /** * @param feed The feed to set. * - *The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED
+ *+ * The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED + *
*/ - public void setFeed(SyndFeed feed) { + public void setFeed(final SyndFeed feed) { this.feed = feed; } - - /** - * @return Returns the eventType. - */ - public String getEventType() { - return eventType; - } - /** - * @param eventType The eventType to set. - */ - public void setEventType(String eventType) { - this.eventType = eventType; - } - /** - * @return Returns the urlString. - */ - public String getUrlString() { - return urlString; - } - /** - * @param urlString The urlString to set. - */ - public void setUrlString(String urlString) { - this.urlString = urlString; - } + + /** + * @return Returns the eventType. + */ + public String getEventType() { + return eventType; + } + + /** + * @param eventType The eventType to set. + */ + public void setEventType(final String eventType) { + this.eventType = eventType; + } + + /** + * @return Returns the urlString. + */ + public String getUrlString() { + return urlString; + } + + /** + * @param urlString The urlString to set. + */ + public void setUrlString(final String urlString) { + this.urlString = urlString; + } } diff --git a/src/main/java/org/rometools/fetcher/FetcherException.java b/src/main/java/org/rometools/fetcher/FetcherException.java index 48575ec..afaf432 100644 --- a/src/main/java/org/rometools/fetcher/FetcherException.java +++ b/src/main/java/org/rometools/fetcher/FetcherException.java @@ -18,34 +18,34 @@ package org.rometools.fetcher; /** * @author Nick Lothian - * + * */ public class FetcherException extends Exception { - private static final long serialVersionUID = -7479645796948092380L; + private static final long serialVersionUID = -7479645796948092380L; - int responseCode; - - public FetcherException(Throwable cause) { - super(); - initCause(cause); - } - - public FetcherException(String message, Throwable cause) { - super(message); - initCause(cause); - } - - public FetcherException(String message) { - super(message); - } - - public FetcherException(int responseCode, String message) { - this(message); - this.responseCode = responseCode; - } + int responseCode; - public int getResponseCode() { - return responseCode; - } + public FetcherException(final Throwable cause) { + super(); + initCause(cause); + } + + public FetcherException(final String message, final Throwable cause) { + super(message); + initCause(cause); + } + + public FetcherException(final String message) { + super(message); + } + + public FetcherException(final int responseCode, final String message) { + this(message); + this.responseCode = responseCode; + } + + public int getResponseCode() { + return responseCode; + } } diff --git a/src/main/java/org/rometools/fetcher/FetcherListener.java b/src/main/java/org/rometools/fetcher/FetcherListener.java index 3424456..d5640b5 100644 --- a/src/main/java/org/rometools/fetcher/FetcherListener.java +++ b/src/main/java/org/rometools/fetcher/FetcherListener.java @@ -2,14 +2,15 @@ package org.rometools.fetcher; import java.util.EventListener; - public interface FetcherListener extends EventListener { - /** - *Called when a fetcher event occurs
- * - * @param event the event that fired - */ - public void fetcherEvent(FetcherEvent event); - + /** + *+ * Called when a fetcher event occurs + *
+ * + * @param event the event that fired + */ + public void fetcherEvent(FetcherEvent event); + } diff --git a/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcher.java b/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcher.java index 4b1823f..c8b01b3 100644 --- a/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcher.java +++ b/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcher.java @@ -26,194 +26,203 @@ import java.util.Iterator; import java.util.Properties; import java.util.Set; -import com.sun.syndication.feed.synd.SyndFeed; import org.rometools.fetcher.FeedFetcher; import org.rometools.fetcher.FetcherEvent; import org.rometools.fetcher.FetcherException; import org.rometools.fetcher.FetcherListener; +import com.sun.syndication.feed.synd.SyndFeed; public abstract class AbstractFeedFetcher implements FeedFetcher { - private final Set fetcherEventListeners; - private String userAgent; - private boolean usingDeltaEncoding; - private boolean preserveWireFeed; - - - - public AbstractFeedFetcher() { - fetcherEventListeners = Collections.synchronizedSet(new HashSet()); - - Properties props = new Properties(System.getProperties()); - String resourceName = "fetcher.properties"; - - try { - InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resourceName); - if (inputStream == null) { - inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName); - } - if (inputStream != null) { - props.load(inputStream); - System.getProperties().putAll(props); - inputStream.close(); - } else { - System.err.println("Could not find " + resourceName + " on classpath"); - } - } catch (IOException e) { - // do nothing - we don't want to fail just because we could not find the version - System.err.println("Error reading " + resourceName + " from classpath: " + e.getMessage()); - } - - - setUserAgent(DEFAULT_USER_AGENT + " Ver: " + System.getProperty("rome.fetcher.version", "UNKNOWN")); - } + private final Set fetcherEventListeners; + private String userAgent; + private boolean usingDeltaEncoding; + private boolean preserveWireFeed; - /** - * @return the User-Agent currently being sent to servers - */ - public synchronized String getUserAgent() { - return userAgent; - } + public AbstractFeedFetcher() { + fetcherEventListeners = Collections.synchronizedSet(new HashSet()); - /** - * @param string The User-Agent to sent to servers - */ - public synchronized void setUserAgent(String string) { - userAgent = string; - } + final Properties props = new Properties(System.getProperties()); + final String resourceName = "fetcher.properties"; - /** - * @param eventType The event type to fire - * @param connection the current connection - */ - protected void fireEvent(String eventType, URLConnection connection) { - fireEvent(eventType, connection.getURL().toExternalForm(), null); - } - - - /** - * @param eventType The event type to fire - * @param connection the current connection - * @param feed The feed to pass to the event - */ - protected void fireEvent(String eventType, URLConnection connection, SyndFeed feed) { - fireEvent(eventType, connection.getURL().toExternalForm(), feed); - } + try { + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resourceName); + if (inputStream == null) { + inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName); + } + if (inputStream != null) { + props.load(inputStream); + System.getProperties().putAll(props); + inputStream.close(); + } else { + System.err.println("Could not find " + resourceName + " on classpath"); + } + } catch (final IOException e) { + // do nothing - we don't want to fail just because we could not find the version + System.err.println("Error reading " + resourceName + " from classpath: " + e.getMessage()); + } - /** - * @param eventType The event type to fire - * @param urlStr the current url as a string - */ - protected void fireEvent(String eventType, String urlStr) { - fireEvent(eventType, urlStr, null); - } - - /** - * @param eventType The event type to fire - * @param urlStr the current url as a string - * @param feed The feed to pass to the event - */ - protected void fireEvent(String eventType, String urlStr, SyndFeed feed) { - FetcherEvent fetcherEvent = new FetcherEvent(this, urlStr, eventType, feed); - synchronized(fetcherEventListeners) { - Iterator iter = fetcherEventListeners.iterator(); - while ( iter.hasNext()) { - FetcherListener fetcherEventListener = (FetcherListener) iter.next(); - fetcherEventListener.fetcherEvent(fetcherEvent); - } - } - } - - /** - * @see com.sun.syndication.fetcher.FeedFetcher#addFetcherEventListener(com.sun.syndication.fetcher.FetcherListener) - */ - public void addFetcherEventListener(FetcherListener listener) { - if (listener != null) { - fetcherEventListeners.add(listener); - } - - } + setUserAgent(DEFAULT_USER_AGENT + " Ver: " + System.getProperty("rome.fetcher.version", "UNKNOWN")); + } - /** - * @see com.sun.syndication.fetcher.FeedFetcher#removeFetcherEventListener(com.sun.syndication.fetcher.FetcherListener) - */ - public void removeFetcherEventListener(FetcherListener listener) { - if (listener != null) { - fetcherEventListeners.remove(listener); - } - } + /** + * @return the User-Agent currently being sent to servers + */ + @Override + public synchronized String getUserAgent() { + return userAgent; + } + + /** + * @param string The User-Agent to sent to servers + */ + @Override + public synchronized void setUserAgent(final String string) { + userAgent = string; + } + + /** + * @param eventType The event type to fire + * @param connection the current connection + */ + protected void fireEvent(final String eventType, final URLConnection connection) { + fireEvent(eventType, connection.getURL().toExternalForm(), null); + } + + /** + * @param eventType The event type to fire + * @param connection the current connection + * @param feed The feed to pass to the event + */ + protected void fireEvent(final String eventType, final URLConnection connection, final SyndFeed feed) { + fireEvent(eventType, connection.getURL().toExternalForm(), feed); + } + + /** + * @param eventType The event type to fire + * @param urlStr the current url as a string + */ + protected void fireEvent(final String eventType, final String urlStr) { + fireEvent(eventType, urlStr, null); + } + + /** + * @param eventType The event type to fire + * @param urlStr the current url as a string + * @param feed The feed to pass to the event + */ + protected void fireEvent(final String eventType, final String urlStr, final SyndFeed feed) { + final FetcherEvent fetcherEvent = new FetcherEvent(this, urlStr, eventType, feed); + synchronized (fetcherEventListeners) { + final Iterator iter = fetcherEventListeners.iterator(); + while (iter.hasNext()) { + final FetcherListener fetcherEventListener = (FetcherListener) iter.next(); + fetcherEventListener.fetcherEvent(fetcherEvent); + } + } + } + + /** + * @see com.sun.syndication.fetcher.FeedFetcher#addFetcherEventListener(com.sun.syndication.fetcher.FetcherListener) + */ + @Override + public void addFetcherEventListener(final FetcherListener listener) { + if (listener != null) { + fetcherEventListeners.add(listener); + } + + } + + /** + * @see com.sun.syndication.fetcher.FeedFetcher#removeFetcherEventListener(com.sun.syndication.fetcher.FetcherListener) + */ + @Override + public void removeFetcherEventListener(final FetcherListener listener) { + if (listener != null) { + fetcherEventListeners.remove(listener); + } + } /** * @return Returns the useDeltaEncoding. */ + @Override public synchronized boolean isUsingDeltaEncoding() { return usingDeltaEncoding; } + /** * @param useDeltaEncoding The useDeltaEncoding to set. */ - public synchronized void setUsingDeltaEncoding(boolean useDeltaEncoding) { - this.usingDeltaEncoding = useDeltaEncoding; - } - - /** - *Handles HTTP error codes.
- * - * @param responseCode the HTTP response code - * @throws FetcherException if response code is in the range 400 to 599 inclusive - */ - protected void handleErrorCodes(int responseCode) throws FetcherException { - // Handle 2xx codes as OK, so ignore them here - // 3xx codes are handled by the HttpURLConnection class - if (responseCode == 403) { - // Authentication is required - throwAuthenticationError(responseCode); - } else if (responseCode >= 400 && responseCode < 500) { - throw4XXError(responseCode); - } else if (responseCode >= 500 && responseCode < 600) { - throw new FetcherException(responseCode, "The server encounted an error. HTTP Response code was:" + responseCode); - } - } - - protected void throw4XXError(int responseCode) throws FetcherException { - throw new FetcherException(responseCode, "The requested resource could not be found. HTTP Response code was:" + responseCode); - } + @Override + public synchronized void setUsingDeltaEncoding(final boolean useDeltaEncoding) { + usingDeltaEncoding = useDeltaEncoding; + } - protected void throwAuthenticationError(int responseCode) throws FetcherException { - throw new FetcherException(responseCode, "Authentication required for that resource. HTTP Response code was:" + responseCode); - } - - /** - *Combine the entries in two feeds into a single feed.
- * - *The returned feed will have the same data as the newFeed parameter, with - * the entries from originalFeed appended to the end of its entries.
- * - * @param originalFeed - * @param newFeed - * @return - */ - public static SyndFeed combineFeeds(SyndFeed originalFeed, SyndFeed newFeed) { - SyndFeed result; + /** + *+ * Handles HTTP error codes. + *
+ * + * @param responseCode the HTTP response code + * @throws FetcherException if response code is in the range 400 to 599 inclusive + */ + protected void handleErrorCodes(final int responseCode) throws FetcherException { + // Handle 2xx codes as OK, so ignore them here + // 3xx codes are handled by the HttpURLConnection class + if (responseCode == 403) { + // Authentication is required + throwAuthenticationError(responseCode); + } else if (responseCode >= 400 && responseCode < 500) { + throw4XXError(responseCode); + } else if (responseCode >= 500 && responseCode < 600) { + throw new FetcherException(responseCode, "The server encounted an error. HTTP Response code was:" + responseCode); + } + } + + protected void throw4XXError(final int responseCode) throws FetcherException { + throw new FetcherException(responseCode, "The requested resource could not be found. HTTP Response code was:" + responseCode); + } + + protected void throwAuthenticationError(final int responseCode) throws FetcherException { + throw new FetcherException(responseCode, "Authentication required for that resource. HTTP Response code was:" + responseCode); + } + + /** + *+ * Combine the entries in two feeds into a single feed. + *
+ * + *+ * The returned feed will have the same data as the newFeed parameter, with the entries from originalFeed appended to the end of its entries. + *
+ * + * @param originalFeed + * @param newFeed + * @return + */ + public static SyndFeed combineFeeds(final SyndFeed originalFeed, final SyndFeed newFeed) { + SyndFeed result; try { result = (SyndFeed) newFeed.clone(); - + result.getEntries().addAll(result.getEntries().size(), originalFeed.getEntries()); - + return result; - } catch (CloneNotSupportedException e) { - IllegalArgumentException iae = new IllegalArgumentException("Cannot clone feed"); + } catch (final CloneNotSupportedException e) { + final IllegalArgumentException iae = new IllegalArgumentException("Cannot clone feed"); iae.initCause(e); throw iae; - } - } + } + } - public boolean isPreserveWireFeed() { - return preserveWireFeed; - } + public boolean isPreserveWireFeed() { + return preserveWireFeed; + } + + @Override + public void setPreserveWireFeed(final boolean preserveWireFeed) { + this.preserveWireFeed = preserveWireFeed; + } - public void setPreserveWireFeed(boolean preserveWireFeed) { - this.preserveWireFeed = preserveWireFeed; - } - } diff --git a/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcherBeanInfo.java b/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcherBeanInfo.java index 7342901..34f43bd 100644 --- a/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcherBeanInfo.java +++ b/src/main/java/org/rometools/fetcher/impl/AbstractFeedFetcherBeanInfo.java @@ -9,21 +9,22 @@ import org.rometools.fetcher.FetcherListener; public class AbstractFeedFetcherBeanInfo extends SimpleBeanInfo { - public EventSetDescriptor[] getEventSetDescriptors() { - try { - Class clz = AbstractFeedFetcher.class; // get the class object which we'll describe - Method addMethod = clz.getMethod("addFetcherEventListener", new Class[] { FetcherListener.class }); - Method removeMethod = clz.getMethod("removeFetcherEventListener", new Class[] { FetcherListener.class }); - Method listenerMethod = FetcherListener.class.getMethod("fetcherEvent", new Class[] { FetcherEvent.class }); + @Override + public EventSetDescriptor[] getEventSetDescriptors() { + try { + final Class clz = AbstractFeedFetcher.class; // get the class object which we'll describe + final Method addMethod = clz.getMethod("addFetcherEventListener", new Class[] { FetcherListener.class }); + final Method removeMethod = clz.getMethod("removeFetcherEventListener", new Class[] { FetcherListener.class }); + final Method listenerMethod = FetcherListener.class.getMethod("fetcherEvent", new Class[] { FetcherEvent.class }); - EventSetDescriptor est = new EventSetDescriptor("fetcherEvent", clz, new Method[] { listenerMethod }, addMethod, removeMethod); - EventSetDescriptor[] results = new EventSetDescriptor[] { est }; + final EventSetDescriptor est = new EventSetDescriptor("fetcherEvent", clz, new Method[] { listenerMethod }, addMethod, removeMethod); + final EventSetDescriptor[] results = new EventSetDescriptor[] { est }; - return results; - } catch (Exception e) { - // IntrospectionException, SecurityException and/or NoSuchMethodException can be thrown here - // the best we can do is to convert them to runtime exceptions - throw new RuntimeException(e); - } - } + return results; + } catch (final Exception e) { + // IntrospectionException, SecurityException and/or NoSuchMethodException can be thrown here + // the best we can do is to convert them to runtime exceptions + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/org/rometools/fetcher/impl/DiskFeedInfoCache.java b/src/main/java/org/rometools/fetcher/impl/DiskFeedInfoCache.java index df788bc..87ab0d2 100644 --- a/src/main/java/org/rometools/fetcher/impl/DiskFeedInfoCache.java +++ b/src/main/java/org/rometools/fetcher/impl/DiskFeedInfoCache.java @@ -23,112 +23,113 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URL; -import javax.swing.text.Utilities; - /** * Disk based cache. */ public class DiskFeedInfoCache implements FeedFetcherCache { - + protected String cachePath = null; - public DiskFeedInfoCache(String cachePath) { + + public DiskFeedInfoCache(final String cachePath) { this.cachePath = cachePath; } - public SyndFeedInfo getFeedInfo(URL url) { + + @Override + public SyndFeedInfo getFeedInfo(final URL url) { SyndFeedInfo info = null; - String fileName = cachePath + File.separator + "feed_" - + replaceNonAlphanumeric(url.toString(),'_').trim(); + final String fileName = cachePath + File.separator + "feed_" + replaceNonAlphanumeric(url.toString(), '_').trim(); FileInputStream fis; try { fis = new FileInputStream(fileName); - ObjectInputStream ois = new ObjectInputStream(fis); - info = (SyndFeedInfo)ois.readObject(); + final ObjectInputStream ois = new ObjectInputStream(fis); + info = (SyndFeedInfo) ois.readObject(); fis.close(); - } catch (FileNotFoundException fnfe) { + } catch (final FileNotFoundException fnfe) { // That's OK, we'l return null - } catch (ClassNotFoundException cnfe) { + } catch (final ClassNotFoundException cnfe) { // Error writing to cache is fatal throw new RuntimeException("Attempting to read from cache", cnfe); - } catch (IOException fnfe) { + } catch (final IOException fnfe) { // Error writing to cache is fatal throw new RuntimeException("Attempting to read from cache", fnfe); } return info; } - - public void setFeedInfo(URL url, SyndFeedInfo feedInfo) { - String fileName = cachePath + File.separator + "feed_" - + replaceNonAlphanumeric(url.toString(),'_').trim(); + + @Override + public void setFeedInfo(final URL url, final SyndFeedInfo feedInfo) { + final String fileName = cachePath + File.separator + "feed_" + replaceNonAlphanumeric(url.toString(), '_').trim(); FileOutputStream fos; try { fos = new FileOutputStream(fileName); - ObjectOutputStream oos = new ObjectOutputStream(fos); + final ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(feedInfo); fos.flush(); fos.close(); - } catch (Exception e) { + } catch (final Exception e) { // Error writing to cache is fatal throw new RuntimeException("Attempting to write to cache", e); } } - - public static String replaceNonAlphanumeric(String str, char subst) { - StringBuffer ret = new StringBuffer(str.length()); - char[] testChars = str.toCharArray(); - for (int i = 0; i < testChars.length; i++) { - if (Character.isLetterOrDigit(testChars[i])) { - ret.append(testChars[i]); + + public static String replaceNonAlphanumeric(final String str, final char subst) { + final StringBuffer ret = new StringBuffer(str.length()); + final char[] testChars = str.toCharArray(); + for (final char testChar : testChars) { + if (Character.isLetterOrDigit(testChar)) { + ret.append(testChar); } else { - ret.append( subst ); + ret.append(subst); } } return ret.toString(); } - + /** - * Clear the cache. + * Clear the cache. */ + @Override public synchronized void clear() { - final File file = new File(this.cachePath); - //only do the delete if the directory exists - if( file.exists() && file.canWrite() ) { - //make the directory empty - final String[] files = file.list(); - final int len = files.length; - for( int i=0; i+ * An interface to allow caching of feed details. Implementing this allows the {@link org.rometools.fetcher.io.HttpURLFeedFetcher} class to enable conditional + * gets + *
* * @author Nick Lothian - * + * */ public interface FeedFetcherCache { - /** - * Get a SyndFeedInfo object from the cache. - * - * @param feedUrl The url of the feed - * @return A SyndFeedInfo or null if it is not in the cache - */ - public SyndFeedInfo getFeedInfo(URL feedUrl); - - /** - * Add a SyndFeedInfo object to the cache - * - * @param feedUrl The url of the feed - * @param syndFeedInfo A SyndFeedInfo for the feed - */ - public void setFeedInfo(URL feedUrl, SyndFeedInfo syndFeedInfo); - - /** - * Removes all items from the cache. - */ - public void clear(); - - /** - * Removes the SyndFeedInfo identified by the url from the cache. - * @return The removed SyndFeedInfo - */ - public SyndFeedInfo remove( URL feedUrl ); + /** + * Get a SyndFeedInfo object from the cache. + * + * @param feedUrl The url of the feed + * @return A SyndFeedInfo or null if it is not in the cache + */ + public SyndFeedInfo getFeedInfo(URL feedUrl); + + /** + * Add a SyndFeedInfo object to the cache + * + * @param feedUrl The url of the feed + * @param syndFeedInfo A SyndFeedInfo for the feed + */ + public void setFeedInfo(URL feedUrl, SyndFeedInfo syndFeedInfo); + + /** + * Removes all items from the cache. + */ + public void clear(); + + /** + * Removes the SyndFeedInfo identified by the url from the cache. + * + * @return The removed SyndFeedInfo + */ + public SyndFeedInfo remove(URL feedUrl); } diff --git a/src/main/java/org/rometools/fetcher/impl/HashMapFeedInfoCache.java b/src/main/java/org/rometools/fetcher/impl/HashMapFeedInfoCache.java index 5f60825..b7279f5 100644 --- a/src/main/java/org/rometools/fetcher/impl/HashMapFeedInfoCache.java +++ b/src/main/java/org/rometools/fetcher/impl/HashMapFeedInfoCache.java @@ -22,104 +22,113 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - /** - *A very simple implementation of the {@link org.rometools.fetcher.impl.FeedFetcherCache} interface.
+ *+ * A very simple implementation of the {@link org.rometools.fetcher.impl.FeedFetcherCache} interface. + *
* - *This implementation uses a HashMap to cache retrieved feeds. This implementation is - * most suitible for sort term (client aggregator?) use, as the memory usage will increase - * over time as the number of feeds in the cache increases.
+ *+ * This implementation uses a HashMap to cache retrieved feeds. This implementation is most suitible for sort term (client aggregator?) use, as the memory usage + * will increase over time as the number of feeds in the cache increases. + *
* * @author Nick Lothian - * + * */ public class HashMapFeedInfoCache implements FeedFetcherCache, Serializable { - private static final long serialVersionUID = -1594665619950916222L; + private static final long serialVersionUID = -1594665619950916222L; - static HashMapFeedInfoCache _instance; - - private Map infoCache; - - /** - *Constructor for HashMapFeedInfoCache
- * - *Only use this if you want multiple instances of the cache. - * Usually getInstance() is more appropriate.
- * - */ - public HashMapFeedInfoCache() { - setInfoCache(createInfoCache()); - } + static HashMapFeedInfoCache _instance; - /** - * Get the global instance of the cache - * @return an implementation of FeedFetcherCache - */ - public static synchronized FeedFetcherCache getInstance() { - if (_instance == null) { - _instance = new HashMapFeedInfoCache(); - } - return _instance; - } + private Map infoCache; - protected Map createInfoCache() { - return (Collections.synchronizedMap(new HashMap())); - } + /** + *+ * Constructor for HashMapFeedInfoCache + *
+ * + *+ * Only use this if you want multiple instances of the cache. Usually getInstance() is more appropriate. + *
+ * + */ + public HashMapFeedInfoCache() { + setInfoCache(createInfoCache()); + } - - protected Object get(Object key) { - return getInfoCache().get(key); - } + /** + * Get the global instance of the cache + * + * @return an implementation of FeedFetcherCache + */ + public static synchronized FeedFetcherCache getInstance() { + if (_instance == null) { + _instance = new HashMapFeedInfoCache(); + } + return _instance; + } - /** - * @see extensions.io.FeedFetcherCache#getFeedInfo(java.net.URL) - */ - public SyndFeedInfo getFeedInfo(URL feedUrl) { - return (SyndFeedInfo) get(feedUrl.toString()); - } + protected Map createInfoCache() { + return Collections.synchronizedMap(new HashMap()); + } - protected void put(Object key, Object value) { - getInfoCache().put(key, value); - } + protected Object get(final Object key) { + return getInfoCache().get(key); + } - /** - * @see extensions.io.FeedFetcherCache#setFeedInfo(java.net.URL, extensions.io.SyndFeedInfo) - */ - public void setFeedInfo(URL feedUrl, SyndFeedInfo syndFeedInfo) { - put(feedUrl.toString(), syndFeedInfo); - } + /** + * @see extensions.io.FeedFetcherCache#getFeedInfo(java.net.URL) + */ + @Override + public SyndFeedInfo getFeedInfo(final URL feedUrl) { + return (SyndFeedInfo) get(feedUrl.toString()); + } - protected synchronized final Map getInfoCache() { - return infoCache; - } + protected void put(final Object key, final Object value) { + getInfoCache().put(key, value); + } - /** - * The API of this class indicates that map must thread safe. In other - * words, be sure to wrap it in a synchronized map unless you know - * what you are doing. - * - * @param map the map to use as the info cache. - */ - protected synchronized final void setInfoCache(Map map) { - infoCache = map; - } - - /** - * @see com.sun.syndication.fetcher.impl.FeedFetcherCache#clear() - */ - public void clear() { - synchronized( infoCache ) { - infoCache.clear(); - } - } - - /** - * @see com.sun.syndication.fetcher.impl.FeedFetcherCache#remove(java.net.URL) - */ - public SyndFeedInfo remove( final URL url ) { - if( url == null ) return null; - - return (SyndFeedInfo) infoCache.remove( url.toString() ); - } + /** + * @see extensions.io.FeedFetcherCache#setFeedInfo(java.net.URL, extensions.io.SyndFeedInfo) + */ + @Override + public void setFeedInfo(final URL feedUrl, final SyndFeedInfo syndFeedInfo) { + put(feedUrl.toString(), syndFeedInfo); + } + + protected synchronized final Map getInfoCache() { + return infoCache; + } + + /** + * The API of this class indicates that map must thread safe. In other words, be sure to wrap it in a synchronized map unless you know what you are doing. + * + * @param map the map to use as the info cache. + */ + protected synchronized final void setInfoCache(final Map map) { + infoCache = map; + } + + /** + * @see com.sun.syndication.fetcher.impl.FeedFetcherCache#clear() + */ + @Override + public void clear() { + synchronized (infoCache) { + infoCache.clear(); + } + } + + /** + * @see com.sun.syndication.fetcher.impl.FeedFetcherCache#remove(java.net.URL) + */ + @Override + public SyndFeedInfo remove(final URL url) { + if (url == null) { + return null; + } + + return (SyndFeedInfo) infoCache.remove(url.toString()); + } } diff --git a/src/main/java/org/rometools/fetcher/impl/HttpClientFeedFetcher.java b/src/main/java/org/rometools/fetcher/impl/HttpClientFeedFetcher.java index 4d3b8bd..4917f93 100644 --- a/src/main/java/org/rometools/fetcher/impl/HttpClientFeedFetcher.java +++ b/src/main/java/org/rometools/fetcher/impl/HttpClientFeedFetcher.java @@ -16,10 +16,12 @@ */ package org.rometools.fetcher.impl; -import com.sun.syndication.feed.synd.SyndFeed; -import com.sun.syndication.io.FeedException; -import com.sun.syndication.io.SyndFeedInput; -import com.sun.syndication.io.XmlReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.zip.GZIPInputStream; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.Header; @@ -28,19 +30,13 @@ import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpClientParams; - import org.rometools.fetcher.FetcherEvent; import org.rometools.fetcher.FetcherException; -import java.io.IOException; -import java.io.InputStream; - -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; - -import java.util.zip.GZIPInputStream; - +import com.sun.syndication.feed.synd.SyndFeed; +import com.sun.syndication.io.FeedException; +import com.sun.syndication.io.SyndFeedInput; +import com.sun.syndication.io.XmlReader; /** * @author Nick Lothian @@ -59,53 +55,52 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { /** * @param cache */ - public HttpClientFeedFetcher(FeedFetcherCache cache) { + public HttpClientFeedFetcher(final FeedFetcherCache cache) { this(); setFeedInfoCache(cache); } - public HttpClientFeedFetcher(FeedFetcherCache cache, CredentialSupplier credentialSupplier) { + public HttpClientFeedFetcher(final FeedFetcherCache cache, final CredentialSupplier credentialSupplier) { this(cache); setCredentialSupplier(credentialSupplier); } /** - * @param timeout Sets the connect timeout for the HttpClient but using the URLConnection method name. - * Uses the HttpClientParams method setConnectionManagerTimeout instead of setConnectTimeout - * - */ - public synchronized void setConnectTimeout(int timeout) { + * @param timeout Sets the connect timeout for the HttpClient but using the URLConnection method name. Uses the HttpClientParams method + * setConnectionManagerTimeout instead of setConnectTimeout + * + */ + public synchronized void setConnectTimeout(final int timeout) { httpClientParams.setConnectionManagerTimeout(timeout); } /** - * @return The currently used connect timeout for the HttpClient but using the URLConnection method name. - * Uses the HttpClientParams method getConnectionManagerTimeout instead of getConnectTimeout - * + * @return The currently used connect timeout for the HttpClient but using the URLConnection method name. Uses the HttpClientParams method + * getConnectionManagerTimeout instead of getConnectTimeout + * */ public int getConnectTimeout() { - return (int) this.getHttpClientParams() - .getConnectionManagerTimeout(); + return (int) getHttpClientParams().getConnectionManagerTimeout(); } /** * @param credentialSupplier The credentialSupplier to set. */ - public synchronized void setCredentialSupplier(CredentialSupplier credentialSupplier) { + public synchronized void setCredentialSupplier(final CredentialSupplier credentialSupplier) { this.credentialSupplier = credentialSupplier; } /** - * @return Returns the credentialSupplier. - */ + * @return Returns the credentialSupplier. + */ public synchronized CredentialSupplier getCredentialSupplier() { return credentialSupplier; } /** - * @param feedInfoCache the feedInfoCache to set - */ - public synchronized void setFeedInfoCache(FeedFetcherCache feedInfoCache) { + * @param feedInfoCache the feedInfoCache to set + */ + public synchronized void setFeedInfoCache(final FeedFetcherCache feedInfoCache) { this.feedInfoCache = feedInfoCache; } @@ -116,7 +111,7 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { return feedInfoCache; } - public synchronized void setHttpClientMethodCallback(HttpClientMethodCallbackIntf httpClientMethodCallback) { + public synchronized void setHttpClientMethodCallback(final HttpClientMethodCallbackIntf httpClientMethodCallback) { this.httpClientMethodCallback = httpClientMethodCallback; } @@ -127,21 +122,21 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { /** * @param httpClientParams The httpClientParams to set. */ - public synchronized void setHttpClientParams(HttpClientParams httpClientParams) { + public synchronized void setHttpClientParams(final HttpClientParams httpClientParams) { this.httpClientParams = httpClientParams; } /** - * @return Returns the httpClientParams. - */ + * @return Returns the httpClientParams. + */ public synchronized HttpClientParams getHttpClientParams() { - return this.httpClientParams; + return httpClientParams; } /** * @return The currently used read timeout for the URLConnection, 0 is unlimited, i.e. no timeout */ - public synchronized void setReadTimeout(int timeout) { + public synchronized void setReadTimeout(final int timeout) { httpClientParams.setSoTimeout(timeout); } @@ -149,46 +144,43 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { * @return timeout the read timeout for the URLConnection to a specified timeout, in milliseconds. */ public int getReadTimeout() { - return (int) this.getHttpClientParams() - .getSoTimeout(); + return getHttpClientParams().getSoTimeout(); } - public SyndFeed retrieveFeed(URL url) throws IllegalArgumentException, IOException, FeedException, FetcherException { - return this.retrieveFeed(this.getUserAgent(), url); + @Override + public SyndFeed retrieveFeed(final URL url) throws IllegalArgumentException, IOException, FeedException, FetcherException { + return this.retrieveFeed(getUserAgent(), url); } /** * @see org.rometools.fetcher.FeedFetcher#retrieveFeed(java.net.URL) */ - public SyndFeed retrieveFeed(String userAgent, URL feedUrl) - throws IllegalArgumentException, IOException, FeedException, FetcherException { + @Override + public SyndFeed retrieveFeed(final String userAgent, final URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException { if (feedUrl == null) { throw new IllegalArgumentException("null is not a valid URL"); } // TODO Fix this - //System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); - HttpClient client = new HttpClient(httpClientParams); + // System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); + final HttpClient client = new HttpClient(httpClientParams); if (getCredentialSupplier() != null) { - client.getState() - .setAuthenticationPreemptive(true); + client.getState().setAuthenticationPreemptive(true); // TODO what should realm be here? - Credentials credentials = getCredentialSupplier() - .getCredentials(null, feedUrl.getHost()); + final Credentials credentials = getCredentialSupplier().getCredentials(null, feedUrl.getHost()); if (credentials != null) { - client.getState() - .setCredentials(null, feedUrl.getHost(), credentials); + client.getState().setCredentials(null, feedUrl.getHost(), credentials); } } System.setProperty("httpclient.useragent", userAgent); - String urlStr = feedUrl.toString(); + final String urlStr = feedUrl.toString(); - HttpMethod method = new GetMethod(urlStr); + final HttpMethod method = new GetMethod(urlStr); method.addRequestHeader("Accept-Encoding", "gzip"); method.addRequestHeader("User-Agent", userAgent); method.setFollowRedirects(true); @@ -199,7 +191,7 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { } } - FeedFetcherCache cache = getFeedInfoCache(); + final FeedFetcherCache cache = getFeedInfoCache(); if (cache != null) { // retrieve feed @@ -220,7 +212,7 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { } } - int statusCode = client.executeMethod(method); + final int statusCode = client.executeMethod(method); fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, urlStr); handleErrorCodes(statusCode); @@ -240,9 +232,9 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { method.recycle(); } } else { - // cache is not in use + // cache is not in use try { - int statusCode = client.executeMethod(method); + final int statusCode = client.executeMethod(method); fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, urlStr); handleErrorCodes(statusCode); @@ -254,30 +246,30 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { } } - private SyndFeed getFeed(SyndFeedInfo syndFeedInfo, String urlStr, HttpMethod method, int statusCode) - throws IOException, HttpException, FetcherException, FeedException { - if ((statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) && (syndFeedInfo != null)) { + private SyndFeed getFeed(final SyndFeedInfo syndFeedInfo, final String urlStr, final HttpMethod method, final int statusCode) throws IOException, + HttpException, FetcherException, FeedException { + if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED && syndFeedInfo != null) { fireEvent(FetcherEvent.EVENT_TYPE_FEED_UNCHANGED, urlStr); return syndFeedInfo.getSyndFeed(); } - SyndFeed feed = retrieveFeed(urlStr, method); + final SyndFeed feed = retrieveFeed(urlStr, method); fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, urlStr, feed); return feed; } /** - * @param feedUrl - * @param urlStr - * @param method - * @param feed - * @return - * @throws MalformedURLException - */ - private SyndFeedInfo buildSyndFeedInfo(URL feedUrl, String urlStr, HttpMethod method, SyndFeed feed, int statusCode) - throws MalformedURLException { + * @param feedUrl + * @param urlStr + * @param method + * @param feed + * @return + * @throws MalformedURLException + */ + private SyndFeedInfo buildSyndFeedInfo(final URL feedUrl, final String urlStr, final HttpMethod method, SyndFeed feed, final int statusCode) + throws MalformedURLException { SyndFeedInfo syndFeedInfo; syndFeedInfo = new SyndFeedInfo(); @@ -285,19 +277,18 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { syndFeedInfo.setUrl(new URL(urlStr)); syndFeedInfo.setId(feedUrl.toString()); - Header imHeader = method.getResponseHeader("IM"); + final Header imHeader = method.getResponseHeader("IM"); - if ((imHeader != null) && (imHeader.getValue() - .indexOf("feed") >= 0) && isUsingDeltaEncoding()) { - FeedFetcherCache cache = getFeedInfoCache(); + if (imHeader != null && imHeader.getValue().indexOf("feed") >= 0 && isUsingDeltaEncoding()) { + final FeedFetcherCache cache = getFeedInfoCache(); - if ((cache != null) && (statusCode == 226)) { + if (cache != null && statusCode == 226) { // client is setup to use http delta encoding and the server supports it and has returned a delta encoded response // This response only includes new items - SyndFeedInfo cachedInfo = cache.getFeedInfo(feedUrl); + final SyndFeedInfo cachedInfo = cache.getFeedInfo(feedUrl); if (cachedInfo != null) { - SyndFeed cachedFeed = cachedInfo.getSyndFeed(); + final SyndFeed cachedFeed = cachedInfo.getSyndFeed(); // set the new feed to be the orginal feed plus the new items feed = combineFeeds(cachedFeed, feed); @@ -305,13 +296,13 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { } } - Header lastModifiedHeader = method.getResponseHeader("Last-Modified"); + final Header lastModifiedHeader = method.getResponseHeader("Last-Modified"); if (lastModifiedHeader != null) { syndFeedInfo.setLastModified(lastModifiedHeader.getValue()); } - Header eTagHeader = method.getResponseHeader("ETag"); + final Header eTagHeader = method.getResponseHeader("ETag"); if (eTagHeader != null) { syndFeedInfo.setETag(eTagHeader.getValue()); @@ -323,21 +314,19 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { } /** - * @param client - * @param urlStr - * @param method - * @return - * @throws IOException - * @throws HttpException - * @throws FetcherException - * @throws FeedException - */ - private SyndFeed retrieveFeed(String urlStr, HttpMethod method) - throws IOException, HttpException, FetcherException, FeedException { + * @param client + * @param urlStr + * @param method + * @return + * @throws IOException + * @throws HttpException + * @throws FetcherException + * @throws FeedException + */ + private SyndFeed retrieveFeed(final String urlStr, final HttpMethod method) throws IOException, HttpException, FetcherException, FeedException { InputStream stream = null; - if ((method.getResponseHeader("Content-Encoding") != null) && - ("gzip".equalsIgnoreCase(method.getResponseHeader("Content-Encoding").getValue()))) { + if (method.getResponseHeader("Content-Encoding") != null && "gzip".equalsIgnoreCase(method.getResponseHeader("Content-Encoding").getValue())) { stream = new GZIPInputStream(method.getResponseBodyAsStream()); } else { stream = method.getResponseBodyAsStream(); @@ -352,7 +341,7 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { reader = new XmlReader(stream, true); } - SyndFeedInput syndFeedInput = new SyndFeedInput(); + final SyndFeedInput syndFeedInput = new SyndFeedInput(); syndFeedInput.setPreserveWireFeed(isPreserveWireFeed()); return syndFeedInput.build(reader); @@ -369,12 +358,11 @@ public class HttpClientFeedFetcher extends AbstractFeedFetcher { public interface HttpClientMethodCallbackIntf { /** - * Allows access to the underlying HttpClient HttpMethod object. - * Note that in most cases, method.setRequestHeader(String, String) - * is what you want to do (rather than method.addRequestHeader(String, String)) - * - * @param method - */ + * Allows access to the underlying HttpClient HttpMethod object. Note that in most cases, method.setRequestHeader(String, String) is what you want to do + * (rather than method.addRequestHeader(String, String)) + * + * @param method + */ public void afterHttpClientMethodCreate(HttpMethod method); } } diff --git a/src/main/java/org/rometools/fetcher/impl/HttpURLFeedFetcher.java b/src/main/java/org/rometools/fetcher/impl/HttpURLFeedFetcher.java index 6a9026d..16e3e57 100644 --- a/src/main/java/org/rometools/fetcher/impl/HttpURLFeedFetcher.java +++ b/src/main/java/org/rometools/fetcher/impl/HttpURLFeedFetcher.java @@ -24,276 +24,292 @@ import java.net.URL; import java.net.URLConnection; import java.util.zip.GZIPInputStream; -import com.sun.syndication.feed.synd.SyndFeed; import org.rometools.fetcher.FetcherEvent; import org.rometools.fetcher.FetcherException; + +import com.sun.syndication.feed.synd.SyndFeed; import com.sun.syndication.io.FeedException; import com.sun.syndication.io.SyndFeedInput; import com.sun.syndication.io.XmlReader; /** - *Class to retrieve syndication files via HTTP.
- * - *If passed a {@link org.rometools.fetcher.impl.FeedFetcherCache} in the - * constructor it will use conditional gets to only retrieve modified content.
- * - *The class uses the Accept-Encoding: gzip header to retrieve gzipped feeds where - * supported by the server.
- * - *Simple usage: - *
- * // create the cache - * FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getFeedInfoCache(); - * // retrieve the feed the first time - * // any subsequent request will use conditional gets and only - * // retrieve the resource if it has changed - * SyndFeed feed = new HttpURLFeedFetcher(feedInfoCache).retrieveFeed(feedUrl); - *- * + *
+ * Class to retrieve syndication files via HTTP. *
- * - * @see http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers - * @see http://diveintomark.org/archives/2003/07/21/atom_aggregator_behavior_http_level + * + *+ * If passed a {@link org.rometools.fetcher.impl.FeedFetcherCache} in the constructor it will use conditional gets to only retrieve modified content. + *
+ * + *+ * The class uses the Accept-Encoding: gzip header to retrieve gzipped feeds where supported by the server. + *
+ * + *+ * Simple usage: + * + *
+ * // create the cache + * FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getFeedInfoCache(); + * // retrieve the feed the first time + * // any subsequent request will use conditional gets and only + * // retrieve the resource if it has changed + * SyndFeed feed = new HttpURLFeedFetcher(feedInfoCache).retrieveFeed(feedUrl); + *+ * + * + * + * @see http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers + * @see http://diveintomark.org/archives/2003/07/21/atom_aggregator_behavior_http_level * @see http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html * @author Nick Lothian */ public class HttpURLFeedFetcher extends AbstractFeedFetcher { - static final int POLL_EVENT = 1; - static final int RETRIEVE_EVENT = 2; - static final int UNCHANGED_EVENT = 3; + static final int POLL_EVENT = 1; + static final int RETRIEVE_EVENT = 2; + static final int UNCHANGED_EVENT = 3; - private FeedFetcherCache feedInfoCache; + private FeedFetcherCache feedInfoCache; + /** + * Constructor to use HttpURLFeedFetcher without caching of feeds + * + */ + public HttpURLFeedFetcher() { + super(); + } - /** - * Constructor to use HttpURLFeedFetcher without caching of feeds - * - */ - public HttpURLFeedFetcher() { - super(); - } + /** + * Constructor to enable HttpURLFeedFetcher to cache feeds + * + * @param feedCache - an instance of the FeedFetcherCache interface + */ + public HttpURLFeedFetcher(final FeedFetcherCache feedInfoCache) { + this(); + setFeedInfoCache(feedInfoCache); + } - /** - * Constructor to enable HttpURLFeedFetcher to cache feeds - * - * @param feedCache - an instance of the FeedFetcherCache interface - */ - public HttpURLFeedFetcher(FeedFetcherCache feedInfoCache) { - this(); - setFeedInfoCache(feedInfoCache); - } + @Override + public SyndFeed retrieveFeed(final URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException { + return this.retrieveFeed(getUserAgent(), feedUrl); + } - public SyndFeed retrieveFeed(URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException { - return this.retrieveFeed(this.getUserAgent(), feedUrl); + /** + * Retrieve a feed over HTTP + * + * @param feedUrl A non-null URL of a RSS/Atom feed to retrieve + * @return A {@link com.sun.syndication.feed.synd.SyndFeed} object + * @throws IllegalArgumentException if the URL is null; + * @throws IOException if a TCP error occurs + * @throws FeedException if the feed is not valid + * @throws FetcherException if a HTTP error occurred + */ + @Override + public SyndFeed retrieveFeed(final String userAgent, final URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException { + if (feedUrl == null) { + throw new IllegalArgumentException("null is not a valid URL"); } - /** - * Retrieve a feed over HTTP - * - * @param feedUrl A non-null URL of a RSS/Atom feed to retrieve - * @return A {@link com.sun.syndication.feed.synd.SyndFeed} object - * @throws IllegalArgumentException if the URL is null; - * @throws IOException if a TCP error occurs - * @throws FeedException if the feed is not valid - * @throws FetcherException if a HTTP error occurred - */ - public SyndFeed retrieveFeed(String userAgent, URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException { - if (feedUrl == null) { - throw new IllegalArgumentException("null is not a valid URL"); - } - - URLConnection connection = feedUrl.openConnection(); - if (!(connection instanceof HttpURLConnection)) { - throw new IllegalArgumentException(feedUrl.toExternalForm() + " is not a valid HTTP Url"); - } - HttpURLConnection httpConnection = (HttpURLConnection)connection; - // httpConnection.setInstanceFollowRedirects(true); // this is true by default, but can be changed on a claswide basis - - FeedFetcherCache cache = getFeedInfoCache(); - if (cache != null) { - SyndFeedInfo syndFeedInfo = cache.getFeedInfo(feedUrl); - setRequestHeaders(connection, syndFeedInfo); - httpConnection.connect(); - try { - fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, connection); - - if (syndFeedInfo == null) { - // this is a feed that hasn't been retrieved - syndFeedInfo = new SyndFeedInfo(); - retrieveAndCacheFeed(feedUrl, syndFeedInfo, httpConnection); - } else { - // check the response code - int responseCode = httpConnection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) { - // the response code is not 304 NOT MODIFIED - // This is either because the feed server - // does not support condition gets - // or because the feed hasn't changed - retrieveAndCacheFeed(feedUrl, syndFeedInfo, httpConnection); - } else { - // the feed does not need retrieving - fireEvent(FetcherEvent.EVENT_TYPE_FEED_UNCHANGED, connection); - } - } - - return syndFeedInfo.getSyndFeed(); - } finally { - httpConnection.disconnect(); - } - } else { - fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, connection); - InputStream inputStream = null; - setRequestHeaders(connection, null); + final URLConnection connection = feedUrl.openConnection(); + if (!(connection instanceof HttpURLConnection)) { + throw new IllegalArgumentException(feedUrl.toExternalForm() + " is not a valid HTTP Url"); + } + final HttpURLConnection httpConnection = (HttpURLConnection) connection; + // httpConnection.setInstanceFollowRedirects(true); // this is true by default, but can be changed on a claswide basis - connection.addRequestProperty("User-Agent", userAgent); + final FeedFetcherCache cache = getFeedInfoCache(); + if (cache != null) { + SyndFeedInfo syndFeedInfo = cache.getFeedInfo(feedUrl); + setRequestHeaders(connection, syndFeedInfo); + httpConnection.connect(); + try { + fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, connection); - httpConnection.connect(); - try { - inputStream = httpConnection.getInputStream(); - return getSyndFeedFromStream(inputStream, connection); - } catch (java.io.IOException e) { - handleErrorCodes(((HttpURLConnection)connection).getResponseCode()); - } finally { - if (inputStream != null) { - inputStream.close(); - } - httpConnection.disconnect(); - } - // we will never actually get to this line - return null; - } - } + if (syndFeedInfo == null) { + // this is a feed that hasn't been retrieved + syndFeedInfo = new SyndFeedInfo(); + retrieveAndCacheFeed(feedUrl, syndFeedInfo, httpConnection); + } else { + // check the response code + final int responseCode = httpConnection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) { + // the response code is not 304 NOT MODIFIED + // This is either because the feed server + // does not support condition gets + // or because the feed hasn't changed + retrieveAndCacheFeed(feedUrl, syndFeedInfo, httpConnection); + } else { + // the feed does not need retrieving + fireEvent(FetcherEvent.EVENT_TYPE_FEED_UNCHANGED, connection); + } + } - protected void retrieveAndCacheFeed(URL feedUrl, SyndFeedInfo syndFeedInfo, HttpURLConnection connection) throws IllegalArgumentException, FeedException, FetcherException, IOException { - handleErrorCodes(connection.getResponseCode()); + return syndFeedInfo.getSyndFeed(); + } finally { + httpConnection.disconnect(); + } + } else { + fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, connection); + InputStream inputStream = null; + setRequestHeaders(connection, null); - resetFeedInfo(feedUrl, syndFeedInfo, connection); - FeedFetcherCache cache = getFeedInfoCache(); - // resetting feed info in the cache - // could be needed for some implementations - // of FeedFetcherCache (eg, distributed HashTables) - if (cache != null) { - cache.setFeedInfo(feedUrl, syndFeedInfo); - } - } + connection.addRequestProperty("User-Agent", userAgent); - protected void resetFeedInfo(URL orignalUrl, SyndFeedInfo syndFeedInfo, HttpURLConnection connection) throws IllegalArgumentException, IOException, FeedException { - // need to always set the URL because this may have changed due to 3xx redirects - syndFeedInfo.setUrl(connection.getURL()); + httpConnection.connect(); + try { + inputStream = httpConnection.getInputStream(); + return getSyndFeedFromStream(inputStream, connection); + } catch (final java.io.IOException e) { + handleErrorCodes(((HttpURLConnection) connection).getResponseCode()); + } finally { + if (inputStream != null) { + inputStream.close(); + } + httpConnection.disconnect(); + } + // we will never actually get to this line + return null; + } + } - // the ID is a persistant value that should stay the same even if the URL for the - // feed changes (eg, by 3xx redirects) - syndFeedInfo.setId(orignalUrl.toString()); + protected void retrieveAndCacheFeed(final URL feedUrl, final SyndFeedInfo syndFeedInfo, final HttpURLConnection connection) + throws IllegalArgumentException, FeedException, FetcherException, IOException { + handleErrorCodes(connection.getResponseCode()); - // This will be 0 if the server doesn't support or isn't setting the last modified header - syndFeedInfo.setLastModified(new Long(connection.getLastModified())); + resetFeedInfo(feedUrl, syndFeedInfo, connection); + final FeedFetcherCache cache = getFeedInfoCache(); + // resetting feed info in the cache + // could be needed for some implementations + // of FeedFetcherCache (eg, distributed HashTables) + if (cache != null) { + cache.setFeedInfo(feedUrl, syndFeedInfo); + } + } - // This will be null if the server doesn't support or isn't setting the ETag header - syndFeedInfo.setETag(connection.getHeaderField("ETag")); + protected void resetFeedInfo(final URL orignalUrl, final SyndFeedInfo syndFeedInfo, final HttpURLConnection connection) throws IllegalArgumentException, + IOException, FeedException { + // need to always set the URL because this may have changed due to 3xx redirects + syndFeedInfo.setUrl(connection.getURL()); - // get the contents - InputStream inputStream = null; - try { - inputStream = connection.getInputStream(); - SyndFeed syndFeed = getSyndFeedFromStream(inputStream, connection); - - String imHeader = connection.getHeaderField("IM"); - if (isUsingDeltaEncoding() && (imHeader!= null && imHeader.indexOf("feed") >= 0)) { - FeedFetcherCache cache = getFeedInfoCache(); - if (cache != null && connection.getResponseCode() == 226) { - // client is setup to use http delta encoding and the server supports it and has returned a delta encoded response - // This response only includes new items - SyndFeedInfo cachedInfo = cache.getFeedInfo(orignalUrl); - if (cachedInfo != null) { - SyndFeed cachedFeed = cachedInfo.getSyndFeed(); - - // set the new feed to be the orginal feed plus the new items - syndFeed = combineFeeds(cachedFeed, syndFeed); - } - } - } - - syndFeedInfo.setSyndFeed(syndFeed); - } finally { - if (inputStream != null) { - inputStream.close(); - } - } - } + // the ID is a persistant value that should stay the same even if the URL for the + // feed changes (eg, by 3xx redirects) + syndFeedInfo.setId(orignalUrl.toString()); - /** - *
Set appropriate HTTP headers, including conditional get and gzip encoding headers
- * - * @param connection A URLConnection - * @param syndFeedInfo The SyndFeedInfo for the feed to be retrieved. May be null - */ - protected void setRequestHeaders(URLConnection connection, SyndFeedInfo syndFeedInfo) { - if (syndFeedInfo != null) { - // set the headers to get feed only if modified - // we support the use of both last modified and eTag headers - if (syndFeedInfo.getLastModified() != null) { - Object lastModified = syndFeedInfo.getLastModified(); - if (lastModified instanceof Long) { - connection.setIfModifiedSince(((Long)syndFeedInfo.getLastModified()).longValue()); - } - } - if (syndFeedInfo.getETag() != null) { - connection.setRequestProperty("If-None-Match", syndFeedInfo.getETag()); - } + // This will be 0 if the server doesn't support or isn't setting the last modified header + syndFeedInfo.setLastModified(new Long(connection.getLastModified())); - } - // header to retrieve feed gzipped - connection.setRequestProperty("Accept-Encoding", "gzip"); + // This will be null if the server doesn't support or isn't setting the ETag header + syndFeedInfo.setETag(connection.getHeaderField("ETag")); - if (isUsingDeltaEncoding()) { - connection.addRequestProperty("A-IM", "feed"); - } - } + // get the contents + InputStream inputStream = null; + try { + inputStream = connection.getInputStream(); + SyndFeed syndFeed = getSyndFeedFromStream(inputStream, connection); - private SyndFeed readSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException { - BufferedInputStream is; - if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { - // handle gzip encoded content - is = new BufferedInputStream(new GZIPInputStream(inputStream)); - } else { - is = new BufferedInputStream(inputStream); - } + final String imHeader = connection.getHeaderField("IM"); + if (isUsingDeltaEncoding() && imHeader != null && imHeader.indexOf("feed") >= 0) { + final FeedFetcherCache cache = getFeedInfoCache(); + if (cache != null && connection.getResponseCode() == 226) { + // client is setup to use http delta encoding and the server supports it and has returned a delta encoded response + // This response only includes new items + final SyndFeedInfo cachedInfo = cache.getFeedInfo(orignalUrl); + if (cachedInfo != null) { + final SyndFeed cachedFeed = cachedInfo.getSyndFeed(); - //InputStreamReader reader = new InputStreamReader(is, ResponseHandler.getCharacterEncoding(connection)); + // set the new feed to be the orginal feed plus the new items + syndFeed = combineFeeds(cachedFeed, syndFeed); + } + } + } - //SyndFeedInput input = new SyndFeedInput(); + syndFeedInfo.setSyndFeed(syndFeed); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } - XmlReader reader = null; - if (connection.getHeaderField("Content-Type") != null) { - reader = new XmlReader(is, connection.getHeaderField("Content-Type"), true); - } else { - reader = new XmlReader(is, true); - } - - SyndFeedInput syndFeedInput = new SyndFeedInput(); - syndFeedInput.setPreserveWireFeed(isPreserveWireFeed()); - - return syndFeedInput.build(reader); - - } + /** + *+ * Set appropriate HTTP headers, including conditional get and gzip encoding headers + *
+ * + * @param connection A URLConnection + * @param syndFeedInfo The SyndFeedInfo for the feed to be retrieved. May be null + */ + protected void setRequestHeaders(final URLConnection connection, final SyndFeedInfo syndFeedInfo) { + if (syndFeedInfo != null) { + // set the headers to get feed only if modified + // we support the use of both last modified and eTag headers + if (syndFeedInfo.getLastModified() != null) { + final Object lastModified = syndFeedInfo.getLastModified(); + if (lastModified instanceof Long) { + connection.setIfModifiedSince(((Long) syndFeedInfo.getLastModified()).longValue()); + } + } + if (syndFeedInfo.getETag() != null) { + connection.setRequestProperty("If-None-Match", syndFeedInfo.getETag()); + } - private SyndFeed getSyndFeedFromStream(InputStream inputStream, URLConnection connection) throws IOException, IllegalArgumentException, FeedException { - SyndFeed feed = readSyndFeedFromStream(inputStream, connection); - fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, connection, feed); - return feed; - } + } + // header to retrieve feed gzipped + connection.setRequestProperty("Accept-Encoding", "gzip"); - /** - * @return The FeedFetcherCache used by this fetcher (Could be null) - */ - public synchronized FeedFetcherCache getFeedInfoCache() { - return feedInfoCache; - } + if (isUsingDeltaEncoding()) { + connection.addRequestProperty("A-IM", "feed"); + } + } - /** - * @param cache The cache to be used by this fetcher (pass null to stop using a cache) - */ - public synchronized void setFeedInfoCache(FeedFetcherCache cache) { - feedInfoCache = cache; - } + private SyndFeed readSyndFeedFromStream(final InputStream inputStream, final URLConnection connection) throws IOException, IllegalArgumentException, + FeedException { + BufferedInputStream is; + if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + // handle gzip encoded content + is = new BufferedInputStream(new GZIPInputStream(inputStream)); + } else { + is = new BufferedInputStream(inputStream); + } + + // InputStreamReader reader = new InputStreamReader(is, ResponseHandler.getCharacterEncoding(connection)); + + // SyndFeedInput input = new SyndFeedInput(); + + XmlReader reader = null; + if (connection.getHeaderField("Content-Type") != null) { + reader = new XmlReader(is, connection.getHeaderField("Content-Type"), true); + } else { + reader = new XmlReader(is, true); + } + + final SyndFeedInput syndFeedInput = new SyndFeedInput(); + syndFeedInput.setPreserveWireFeed(isPreserveWireFeed()); + + return syndFeedInput.build(reader); + + } + + private SyndFeed getSyndFeedFromStream(final InputStream inputStream, final URLConnection connection) throws IOException, IllegalArgumentException, + FeedException { + final SyndFeed feed = readSyndFeedFromStream(inputStream, connection); + fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, connection, feed); + return feed; + } + + /** + * @return The FeedFetcherCache used by this fetcher (Could be null) + */ + public synchronized FeedFetcherCache getFeedInfoCache() { + return feedInfoCache; + } + + /** + * @param cache The cache to be used by this fetcher (pass null to stop using a cache) + */ + public synchronized void setFeedInfoCache(final FeedFetcherCache cache) { + feedInfoCache = cache; + } } diff --git a/src/main/java/org/rometools/fetcher/impl/LinkedHashMapFeedInfoCache.java b/src/main/java/org/rometools/fetcher/impl/LinkedHashMapFeedInfoCache.java index 82abf6b..0ddbe22 100644 --- a/src/main/java/org/rometools/fetcher/impl/LinkedHashMapFeedInfoCache.java +++ b/src/main/java/org/rometools/fetcher/impl/LinkedHashMapFeedInfoCache.java @@ -5,66 +5,75 @@ import java.util.LinkedHashMap; import java.util.Map; /** - *An implementation of the {@link org.rometools.fetcher.impl.FeedFetcherCache} interface.
+ *+ * An implementation of the {@link org.rometools.fetcher.impl.FeedFetcherCache} interface. + *
* - *Unlike the HashMapFeedInfoCache this implementation will not grow unbound
+ *+ * Unlike the HashMapFeedInfoCache this implementation will not grow unbound + *
* * @author Javier Kohen * @author Nick Lothian - * + * */ public class LinkedHashMapFeedInfoCache extends HashMapFeedInfoCache { - private final class CacheImpl extends LinkedHashMap { - private static final long serialVersionUID = -6977191330127794920L; + private final class CacheImpl extends LinkedHashMap { + private static final long serialVersionUID = -6977191330127794920L; - public CacheImpl() { - super(16, 0.75F, true); - } - - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > getMaxEntries(); - } - } - - private static final int DEFAULT_MAX_ENTRIES = 20; + public CacheImpl() { + super(16, 0.75F, true); + } - private static final long serialVersionUID = 1694228973357997417L; + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > getMaxEntries(); + } + } - private int maxEntries = DEFAULT_MAX_ENTRIES; + private static final int DEFAULT_MAX_ENTRIES = 20; - private final static LinkedHashMapFeedInfoCache _instance = new LinkedHashMapFeedInfoCache(); - - - /** - * Get the global instance of the cache - * @return an implementation of FeedFetcherCache - */ - public static final FeedFetcherCache getInstance() { - return _instance; - } + private static final long serialVersionUID = 1694228973357997417L; - /** - *Constructor for HashMapFeedInfoCache
- * - *Only use this if you want multiple instances of the cache. - * Usually {@link #getInstance()} is more appropriate.
- * - * @see #getInstance() - */ - public LinkedHashMapFeedInfoCache() { - super(); - } + private int maxEntries = DEFAULT_MAX_ENTRIES; - protected Map createInfoCache() { - return Collections.synchronizedMap(new CacheImpl()); - } + private final static LinkedHashMapFeedInfoCache _instance = new LinkedHashMapFeedInfoCache(); - public synchronized final int getMaxEntries() { - return maxEntries; - } + /** + * Get the global instance of the cache + * + * @return an implementation of FeedFetcherCache + */ + public static final FeedFetcherCache getInstance() { + return _instance; + } + + /** + *+ * Constructor for HashMapFeedInfoCache + *
+ * + *+ * Only use this if you want multiple instances of the cache. Usually {@link #getInstance()} is more appropriate. + *
+ * + * @see #getInstance() + */ + public LinkedHashMapFeedInfoCache() { + super(); + } + + @Override + protected Map createInfoCache() { + return Collections.synchronizedMap(new CacheImpl()); + } + + public synchronized final int getMaxEntries() { + return maxEntries; + } + + public synchronized final void setMaxEntries(final int maxEntries) { + this.maxEntries = maxEntries; + } - public synchronized final void setMaxEntries(int maxEntries) { - this.maxEntries = maxEntries; - } - } diff --git a/src/main/java/org/rometools/fetcher/impl/ResponseHandler.java b/src/main/java/org/rometools/fetcher/impl/ResponseHandler.java index 952edf3..432e3bd 100644 --- a/src/main/java/org/rometools/fetcher/impl/ResponseHandler.java +++ b/src/main/java/org/rometools/fetcher/impl/ResponseHandler.java @@ -22,36 +22,37 @@ import java.util.regex.Pattern; /** * Utility class to help deal with HTTP responses - * + * */ public class ResponseHandler { - public static final String defaultCharacterEncoding = "ISO-8859-1"; - - private final static Pattern characterEncodingPattern = Pattern.compile("charset=([.[^; ]]*)"); - - public static String getCharacterEncoding(URLConnection connection) { - return getCharacterEncoding(connection.getContentType()); - } - - /** - * - *Gets the character encoding of a response. (Note that this is different to - * the content-encoding)
- * - * @param contentTypeHeader the value of the content-type HTTP header eg: text/html; charset=ISO-8859-4 - * @return the character encoding, eg: ISO-8859-4 - */ - public static String getCharacterEncoding(String contentTypeHeader) { - if (contentTypeHeader == null) { - return defaultCharacterEncoding; - } - - Matcher m = characterEncodingPattern.matcher(contentTypeHeader); - //if (!m.matches()) { - if (!m.find()) { - return defaultCharacterEncoding; - } else { - return m.group(1); - } - } + public static final String defaultCharacterEncoding = "ISO-8859-1"; + + private final static Pattern characterEncodingPattern = Pattern.compile("charset=([.[^; ]]*)"); + + public static String getCharacterEncoding(final URLConnection connection) { + return getCharacterEncoding(connection.getContentType()); + } + + /** + * + *+ * Gets the character encoding of a response. (Note that this is different to the content-encoding) + *
+ * + * @param contentTypeHeader the value of the content-type HTTP header eg: text/html; charset=ISO-8859-4 + * @return the character encoding, eg: ISO-8859-4 + */ + public static String getCharacterEncoding(final String contentTypeHeader) { + if (contentTypeHeader == null) { + return defaultCharacterEncoding; + } + + final Matcher m = characterEncodingPattern.matcher(contentTypeHeader); + // if (!m.matches()) { + if (!m.find()) { + return defaultCharacterEncoding; + } else { + return m.group(1); + } + } } diff --git a/src/main/java/org/rometools/fetcher/impl/SyndFeedInfo.java b/src/main/java/org/rometools/fetcher/impl/SyndFeedInfo.java index 587f309..1f9eb73 100644 --- a/src/main/java/org/rometools/fetcher/impl/SyndFeedInfo.java +++ b/src/main/java/org/rometools/fetcher/impl/SyndFeedInfo.java @@ -23,35 +23,39 @@ import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.synd.SyndFeed; /** - *A class to represent a {@link com.sun.syndication.feed.synd.SyndFeed} - * and some useful information about it.
+ *+ * A class to represent a {@link com.sun.syndication.feed.synd.SyndFeed} and some useful information about it. + *
+ * + *+ * This class is thread safe, as expected by the different feed fetcher implementations. + *
* - *This class is thread safe, as expected by the different feed fetcher - * implementations.
- * * @author Nick Lothian */ public class SyndFeedInfo implements Cloneable, Serializable { - private static final long serialVersionUID = -1874786860901426015L; - - private final ObjectBean _objBean; - private String id; - private URL url; - private Object lastModified; - private String eTag; - private SyndFeed syndFeed; + private static final long serialVersionUID = -1874786860901426015L; + + private final ObjectBean _objBean; + private String id; + private URL url; + private Object lastModified; + private String eTag; + private SyndFeed syndFeed; public SyndFeedInfo() { - _objBean = new ObjectBean(this.getClass(),this); + _objBean = new ObjectBean(this.getClass(), this); } /** * Creates a deep 'bean' clone of the object. *+ * * @return a clone of the object. * @throws CloneNotSupportedException thrown if an element of the object cannot be cloned. - * + * */ + @Override public Object clone() throws CloneNotSupportedException { return _objBean.clone(); } @@ -59,11 +63,13 @@ public class SyndFeedInfo implements Cloneable, Serializable { /** * Indicates whether some other object is "equal to" this one as defined by the Object equals() method. *
+ * * @param other he reference object with which to compare. * @return true if 'this' object is equal to the 'other' object. - * + * */ - public boolean equals(Object other) { + @Override + public boolean equals(final Object other) { return _objBean.equals(other); } @@ -72,9 +78,11 @@ public class SyndFeedInfo implements Cloneable, Serializable { *
* It follows the contract defined by the Object hashCode() method. *
+ * * @return the hashcode of the bean object. - * + * */ + @Override public int hashCode() { return _objBean.hashCode(); } @@ -82,68 +90,68 @@ public class SyndFeedInfo implements Cloneable, Serializable { /** * Returns the String representation for the object. *
+ * * @return String representation for the object. - * + * */ + @Override public String toString() { return _objBean.toString(); } + /** + * @return the ETag the feed was last retrieved with + */ + public synchronized String getETag() { + return eTag; + } - /** - * @return the ETag the feed was last retrieved with - */ - public synchronized String getETag() { - return eTag; - } + /** + * @return the last modified date for the feed + */ + public synchronized Object getLastModified() { + return lastModified; + } - /** - * @return the last modified date for the feed - */ - public synchronized Object getLastModified() { - return lastModified; - } + /** + * @return the URL the feed was served from + */ + public synchronized URL getUrl() { + return url; + } - /** - * @return the URL the feed was served from - */ - public synchronized URL getUrl() { - return url; - } + public synchronized void setETag(final String string) { + eTag = string; + } - public synchronized void setETag(String string) { - eTag = string; - } + public synchronized void setLastModified(final Object o) { + lastModified = o; + } - public synchronized void setLastModified(Object o) { - lastModified = o; - } + public synchronized void setUrl(final URL url) { + this.url = url; + } - public synchronized void setUrl(URL url) { - this.url = url; - } + public synchronized SyndFeed getSyndFeed() { + return syndFeed; + } - public synchronized SyndFeed getSyndFeed() { - return syndFeed; - } + public synchronized void setSyndFeed(final SyndFeed feed) { + syndFeed = feed; + } - public synchronized void setSyndFeed(SyndFeed feed) { - syndFeed = feed; - } + /** + * @return A unique ID to identify the feed + */ + public synchronized String getId() { + return id; + } - /** - * @return A unique ID to identify the feed - */ - public synchronized String getId() { - return id; - } - - /** - * @param string A unique ID to identify the feed. Note that if the URL of the feed - * changes this will remain the same - */ - public synchronized void setId(String string) { - id = string; - } + /** + * @param string A unique ID to identify the feed. Note that if the URL of the feed changes this will remain the same + */ + public synchronized void setId(final String string) { + id = string; + } } diff --git a/src/main/java/org/rometools/fetcher/samples/FeedAggregator.java b/src/main/java/org/rometools/fetcher/samples/FeedAggregator.java index 090a0c0..3a82797 100644 --- a/src/main/java/org/rometools/fetcher/samples/FeedAggregator.java +++ b/src/main/java/org/rometools/fetcher/samples/FeedAggregator.java @@ -21,33 +21,37 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; -import com.sun.syndication.feed.synd.SyndFeedImpl; -import com.sun.syndication.feed.synd.SyndFeed; import org.rometools.fetcher.FeedFetcher; import org.rometools.fetcher.impl.FeedFetcherCache; import org.rometools.fetcher.impl.HashMapFeedInfoCache; import org.rometools.fetcher.impl.HttpURLFeedFetcher; + +import com.sun.syndication.feed.synd.SyndFeed; +import com.sun.syndication.feed.synd.SyndFeedImpl; import com.sun.syndication.io.SyndFeedOutput; /** - *
It aggregates a list of RSS/Atom feeds (they can be of different types) - * into a single feed of the specified type.
- * - *Converted from the original FeedAggregator sample
- * + *+ * It aggregates a list of RSS/Atom feeds (they can be of different types) into a single feed of the specified type. + *
+ * + *+ * Converted from the original FeedAggregator sample + *
+ * * @author Alejandro Abdelnur * @author Nick Lothian - * + * */ public class FeedAggregator { - public static void main(String[] args) { + public static void main(final String[] args) { boolean ok = false; - if (args.length>=2) { + if (args.length >= 2) { try { - String outputType = args[0]; + final String outputType = args[0]; - SyndFeed feed = new SyndFeedImpl(); + final SyndFeed feed = new SyndFeedImpl(); feed.setFeedType(outputType); feed.setTitle("Aggregated Feed"); @@ -55,25 +59,24 @@ public class FeedAggregator { feed.setAuthor("anonymous"); feed.setLink("http://www.anonymous.com"); - List entries = new ArrayList(); + final List entries = new ArrayList(); feed.setEntries(entries); - FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance(); - FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache); + final FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance(); + final FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache); - for (int i=1;i