Initial checkin to JIRA
This commit is contained in:
parent
c8ea1a79de
commit
ecfb293405
29 changed files with 3212 additions and 0 deletions
14
License.txt
Normal file
14
License.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
|
||||||
|
Licensed 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.
|
||||||
|
|
13
build.xml
Normal file
13
build.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<project default="jar" name="rome-fetcher" basedir=".">
|
||||||
|
<import file="../../build.xml"/>
|
||||||
|
<target name="init" depends="rome.init,subproject.init" />
|
||||||
|
|
||||||
|
<target name="get-deps" depends="rome.get-deps">
|
||||||
|
<get dest="${libdir}/servletapi-2.3.jar" usetimestamp="true" ignoreerrors="true" src="http://www.ibiblio.org/maven/servletapi/jars/servletapi-2.3.jar" />
|
||||||
|
<get dest="${libdir}/jetty-4.2.12.jar" usetimestamp="true" ignoreerrors="true" src="http://www.ibiblio.org/maven/jetty/jars/jetty-4.2.12.jar" />
|
||||||
|
<get dest="${libdir}/commons-httpclient-3.0.1.jar" usetimestamp="true" ignoreerrors="true" src="http://www.ibiblio.org/maven/commons-httpclient/jars/commons-httpclient-3.0.1.jar" />
|
||||||
|
<get dest="${libdir}/commons-logging-1.0.4.jar" usetimestamp="true" ignoreerrors="true" src="http://www.ibiblio.org/maven/commons-logging/jars/commons-logging-1.0.4.jar" />
|
||||||
|
</target>
|
||||||
|
</project>
|
250
pom.xml
Normal file
250
pom.xml
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.rometools</groupId>
|
||||||
|
<artifactId>rome-fetcher</artifactId>
|
||||||
|
|
||||||
|
<name>Rome HTTP Fetcher</name>
|
||||||
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
|
<inceptionYear>2004</inceptionYear>
|
||||||
|
<organization>
|
||||||
|
<name>ROME Project</name>
|
||||||
|
<url>http://rometools.jira.com</url>
|
||||||
|
</organization>
|
||||||
|
<!--scm>
|
||||||
|
<connection>scm:cvs:pserver:snoopdave@cvs.dev.java.net:/cvs:rome/subprojects/fetcher</connection>
|
||||||
|
<url>https://rome.dev.java.net/source/browse/rome/</url>
|
||||||
|
</scm-->
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache 2</name>
|
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<name>Nick Lothian</name>
|
||||||
|
<url>http://nicklothian.com</url>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
<build>
|
||||||
|
<defaultGoal>install</defaultGoal>
|
||||||
|
<sourceDirectory>src/java</sourceDirectory>
|
||||||
|
<testSourceDirectory>src/test</testSourceDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.properties</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<testResources>
|
||||||
|
<testResource>
|
||||||
|
<directory>src/test</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
<include>**/*.properties</include>
|
||||||
|
</includes>
|
||||||
|
</testResource>
|
||||||
|
</testResources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-release-plugin</artifactId>
|
||||||
|
<version>2.0-beta-9</version>
|
||||||
|
<configuration>
|
||||||
|
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.4</source>
|
||||||
|
<target>1.4</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/Abstract*.java</exclude>
|
||||||
|
<exclude>**/*Servlet.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Test.java</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<!-- <extensions>
|
||||||
|
<extension>
|
||||||
|
<groupId>org.jvnet.wagon-svn</groupId>
|
||||||
|
<artifactId>wagon-svn</artifactId>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
</extension>
|
||||||
|
</extensions> -->
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jdom</groupId>
|
||||||
|
<artifactId>jdom</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>xerces</groupId>
|
||||||
|
<artifactId>xercesImpl</artifactId>
|
||||||
|
<version>2.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.rome</groupId>
|
||||||
|
<artifactId>rome</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Required at build time and to use HTTPClientFetcher -->
|
||||||
|
<groupId>commons-httpclient</groupId>
|
||||||
|
<artifactId>commons-httpclient</artifactId>
|
||||||
|
<version>3.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Required at build time and to use HTTPClientFetcher -->
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Required at build time and to use HTTPClientFetcher -->
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging-api</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Only required at compile time for testing -->
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<version>2.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Only required at compile time for testing -->
|
||||||
|
<groupId>jetty</groupId>
|
||||||
|
<artifactId>jetty</artifactId>
|
||||||
|
<version>4.2.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Only required at compile time for testing -->
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>3.8.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-changes-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<xmlPath>${basedir}/xdocs/changes.xml</xmlPath>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-pmd-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<rulesets>
|
||||||
|
<ruleset>/rulesets/basic.xml</ruleset>
|
||||||
|
<ruleset>/rulesets/unusedcode.xml</ruleset>
|
||||||
|
<ruleset>/rulesets/imports.xml</ruleset>
|
||||||
|
<ruleset>/rulesets/design.xml</ruleset>
|
||||||
|
</rulesets>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>jdepend-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>simian-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>taglist-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>sonatype-nexus-staging</id>
|
||||||
|
<name>Nexus Release Repository</name>
|
||||||
|
<url>http://oss.sonatype.org/service/local/staging/deploy/maven2</url>
|
||||||
|
</repository>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>sonatype-nexus-snapshots</id>
|
||||||
|
<name>Sonatype Nexus Snapshots</name>
|
||||||
|
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>release-sign-artifacts</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>performRelease</name>
|
||||||
|
<value>true</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
<version>1.0-alpha-4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sign-artifacts</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
</project>
|
98
src/java/com/sun/syndication/fetcher/FeedFetcher.java
Normal file
98
src/java/com/sun/syndication/fetcher/FeedFetcher.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.io.FeedException;
|
||||||
|
|
||||||
|
public interface FeedFetcher {
|
||||||
|
/**
|
||||||
|
* <p>The default user agent. It is not marked final so
|
||||||
|
* buggy java compiler will not write this string
|
||||||
|
* into all classes that reference it.</p>
|
||||||
|
*
|
||||||
|
* <p>http://tinyurl.com/64t5n points to https://rome.dev.java.net/
|
||||||
|
* Some servers ban user agents with "Java" in the name.</p>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static String DEFAULT_USER_AGENT = "Rome Client (http://tinyurl.com/64t5n)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the User-Agent currently being sent to servers
|
||||||
|
*/
|
||||||
|
public abstract String getUserAgent();
|
||||||
|
/**
|
||||||
|
* @param string The User-Agent to sent to servers
|
||||||
|
*/
|
||||||
|
public abstract void setUserAgent(String string);
|
||||||
|
/**
|
||||||
|
* 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 abstract SyndFeed retrieveFeed(URL feedUrl) throws IllegalArgumentException, IOException, FeedException, FetcherException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Add a FetcherListener.</p>
|
||||||
|
*
|
||||||
|
* <p>The FetcherListener will receive an FetcherEvent when
|
||||||
|
* a Fetcher event (feed polled, retrieved, etc) occurs</p>
|
||||||
|
*
|
||||||
|
* @param listener The FetcherListener to recieve the event
|
||||||
|
*/
|
||||||
|
public abstract void addFetcherEventListener(FetcherListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Remove a FetcherListener</p>
|
||||||
|
*
|
||||||
|
* @param listener The FetcherListener to remove
|
||||||
|
*/
|
||||||
|
public abstract void removeFetcherEventListener(FetcherListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Is this fetcher using rfc3229 delta encoding?</p>
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract boolean isUsingDeltaEncoding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Turn on or off rfc3229 delta encoding</p>
|
||||||
|
*
|
||||||
|
* <p>See http://www.ietf.org/rfc/rfc3229.txt and http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html</p>
|
||||||
|
*
|
||||||
|
* <p>NOTE: This is experimental and feedback is welcome!</p>
|
||||||
|
*
|
||||||
|
* @param useDeltaEncoding
|
||||||
|
*/
|
||||||
|
public abstract void setUsingDeltaEncoding(boolean useDeltaEncoding);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
85
src/java/com/sun/syndication/fetcher/FetcherEvent.java
Normal file
85
src/java/com/sun/syndication/fetcher/FetcherEvent.java
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package com.sun.syndication.fetcher;
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @author nl
|
||||||
|
*/
|
||||||
|
public class FetcherEvent extends EventObject {
|
||||||
|
|
||||||
|
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 FetcherEvent(Object source, String urlStr, String eventType) {
|
||||||
|
this(source);
|
||||||
|
setUrlString(urlStr);
|
||||||
|
setEventType(eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FetcherEvent(Object source, String urlStr, String eventType, SyndFeed feed) {
|
||||||
|
this(source, urlStr, eventType);
|
||||||
|
setFeed(feed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the feed.
|
||||||
|
*
|
||||||
|
* <p>The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED</p>
|
||||||
|
*/
|
||||||
|
public SyndFeed getFeed() {
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param feed The feed to set.
|
||||||
|
*
|
||||||
|
* <p>The feed will only be set if the eventType is EVENT_TYPE_FEED_RETRIEVED</p>
|
||||||
|
*/
|
||||||
|
public void setFeed(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;
|
||||||
|
}
|
||||||
|
}
|
51
src/java/com/sun/syndication/fetcher/FetcherException.java
Normal file
51
src/java/com/sun/syndication/fetcher/FetcherException.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nick Lothian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FetcherException extends Exception {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResponseCode() {
|
||||||
|
return responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/java/com/sun/syndication/fetcher/FetcherListener.java
Normal file
15
src/java/com/sun/syndication/fetcher/FetcherListener.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.sun.syndication.fetcher;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
|
||||||
|
public interface FetcherListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Called when a fetcher event occurs</p>
|
||||||
|
*
|
||||||
|
* @param event the event that fired
|
||||||
|
*/
|
||||||
|
public void fetcherEvent(FetcherEvent event);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.fetcher.FetcherException;
|
||||||
|
import com.sun.syndication.fetcher.FetcherListener;
|
||||||
|
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the User-Agent currently being sent to servers
|
||||||
|
*/
|
||||||
|
public synchronized String getUserAgent() {
|
||||||
|
return userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string The User-Agent to sent to servers
|
||||||
|
*/
|
||||||
|
public synchronized void setUserAgent(String string) {
|
||||||
|
userAgent = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.FeedFetcher#removeFetcherEventListener(com.sun.syndication.fetcher.FetcherListener)
|
||||||
|
*/
|
||||||
|
public void removeFetcherEventListener(FetcherListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
fetcherEventListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the useDeltaEncoding.
|
||||||
|
*/
|
||||||
|
public synchronized boolean isUsingDeltaEncoding() {
|
||||||
|
return usingDeltaEncoding;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param useDeltaEncoding The useDeltaEncoding to set.
|
||||||
|
*/
|
||||||
|
public synchronized void setUsingDeltaEncoding(boolean useDeltaEncoding) {
|
||||||
|
this.usingDeltaEncoding = useDeltaEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Handles HTTP error codes.</p>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void throwAuthenticationError(int responseCode) throws FetcherException {
|
||||||
|
throw new FetcherException(responseCode, "Authentication required for that resource. HTTP Response code was:" + responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Combine the entries in two feeds into a single feed.</p>
|
||||||
|
*
|
||||||
|
* <p>The returned feed will have the same data as the newFeed parameter, with
|
||||||
|
* the entries from originalFeed appended to the end of its entries.</p>
|
||||||
|
*
|
||||||
|
* @param originalFeed
|
||||||
|
* @param newFeed
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static SyndFeed combineFeeds(SyndFeed originalFeed, 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");
|
||||||
|
iae.initCause(e);
|
||||||
|
throw iae;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreserveWireFeed() {
|
||||||
|
return preserveWireFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreserveWireFeed(boolean preserveWireFeed) {
|
||||||
|
this.preserveWireFeed = preserveWireFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.beans.EventSetDescriptor;
|
||||||
|
import java.beans.SimpleBeanInfo;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.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 });
|
||||||
|
|
||||||
|
EventSetDescriptor est = new EventSetDescriptor("fetcherEvent", clz, new Method[] { listenerMethod }, addMethod, removeMethod);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
src/java/com/sun/syndication/fetcher/impl/DiskFeedInfoCache.java
Normal file
134
src/java/com/sun/syndication/fetcher/impl/DiskFeedInfoCache.java
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2005 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
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) {
|
||||||
|
this.cachePath = cachePath;
|
||||||
|
}
|
||||||
|
public SyndFeedInfo getFeedInfo(URL url) {
|
||||||
|
SyndFeedInfo info = null;
|
||||||
|
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();
|
||||||
|
fis.close();
|
||||||
|
} catch (FileNotFoundException fnfe) {
|
||||||
|
// That's OK, we'l return null
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
// Error writing to cache is fatal
|
||||||
|
throw new RuntimeException("Attempting to read from cache", cnfe);
|
||||||
|
} catch (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();
|
||||||
|
FileOutputStream fos;
|
||||||
|
try {
|
||||||
|
fos = new FileOutputStream(fileName);
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
||||||
|
oos.writeObject(feedInfo);
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
} catch (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]);
|
||||||
|
} else {
|
||||||
|
ret.append( subst );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache.
|
||||||
|
*/
|
||||||
|
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<len; i++ ) {
|
||||||
|
File deleteMe = new File(this.cachePath + File.separator + files[i]);
|
||||||
|
deleteMe.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't delete the cache directory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyndFeedInfo remove( URL url ) {
|
||||||
|
SyndFeedInfo info = null;
|
||||||
|
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();
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
File file = new File( fileName );
|
||||||
|
if( file.exists() ) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException fnfe) {
|
||||||
|
// That's OK, we'l return null
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
// Error writing to cahce is fatal
|
||||||
|
throw new RuntimeException("Attempting to read from cache", cnfe);
|
||||||
|
} catch (IOException fnfe) {
|
||||||
|
// Error writing to cahce is fatal
|
||||||
|
throw new RuntimeException("Attempting to read from cache", fnfe);
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An interface to allow caching of feed details. Implementing this allows the
|
||||||
|
* {@link com.sun.syndication.fetcher.io.HttpURLFeedFetcher} class to
|
||||||
|
* enable conditional gets</p>
|
||||||
|
*
|
||||||
|
* @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 );
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A very simple implementation of the {@link com.sun.syndication.fetcher.impl.FeedFetcherCache} interface.</p>
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* @author Nick Lothian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HashMapFeedInfoCache implements FeedFetcherCache, Serializable {
|
||||||
|
private static final long serialVersionUID = -1594665619950916222L;
|
||||||
|
|
||||||
|
static HashMapFeedInfoCache _instance;
|
||||||
|
|
||||||
|
private Map infoCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructor for HashMapFeedInfoCache</p>
|
||||||
|
*
|
||||||
|
* <p>Only use this if you want multiple instances of the cache.
|
||||||
|
* Usually getInstance() is more appropriate.</p>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public HashMapFeedInfoCache() {
|
||||||
|
setInfoCache(createInfoCache());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map createInfoCache() {
|
||||||
|
return (Collections.synchronizedMap(new HashMap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Object get(Object key) {
|
||||||
|
return getInfoCache().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see extensions.io.FeedFetcherCache#getFeedInfo(java.net.URL)
|
||||||
|
*/
|
||||||
|
public SyndFeedInfo getFeedInfo(URL feedUrl) {
|
||||||
|
return (SyndFeedInfo) get(feedUrl.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void put(Object key, Object value) {
|
||||||
|
getInfoCache().put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see extensions.io.FeedFetcherCache#setFeedInfo(java.net.URL, extensions.io.SyndFeedInfo)
|
||||||
|
*/
|
||||||
|
public void setFeedInfo(URL feedUrl, 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(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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,353 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
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 com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.fetcher.FetcherException;
|
||||||
|
import com.sun.syndication.io.FeedException;
|
||||||
|
import com.sun.syndication.io.SyndFeedInput;
|
||||||
|
import com.sun.syndication.io.XmlReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nick Lothian
|
||||||
|
*/
|
||||||
|
public class HttpClientFeedFetcher extends AbstractFeedFetcher {
|
||||||
|
|
||||||
|
private FeedFetcherCache feedInfoCache;
|
||||||
|
private CredentialSupplier credentialSupplier;
|
||||||
|
private volatile HttpClientMethodCallbackIntf httpClientMethodCallback;
|
||||||
|
private volatile HttpClientParams httpClientParams;
|
||||||
|
|
||||||
|
public HttpClientFeedFetcher() {
|
||||||
|
super();
|
||||||
|
setHttpClientParams(new HttpClientParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cache
|
||||||
|
*/
|
||||||
|
public HttpClientFeedFetcher(FeedFetcherCache cache) {
|
||||||
|
this();
|
||||||
|
setFeedInfoCache(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public HttpClientFeedFetcher(FeedFetcherCache cache, CredentialSupplier credentialSupplier) {
|
||||||
|
this(cache);
|
||||||
|
setCredentialSupplier(credentialSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the httpClientParams.
|
||||||
|
*/
|
||||||
|
public synchronized HttpClientParams getHttpClientParams() {
|
||||||
|
return this.httpClientParams;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param httpClientParams The httpClientParams to set.
|
||||||
|
*/
|
||||||
|
public synchronized void setHttpClientParams(HttpClientParams httpClientParams) {
|
||||||
|
this.httpClientParams = httpClientParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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) {
|
||||||
|
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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public int getConnectTimeout() {
|
||||||
|
return (int) this.getHttpClientParams().getConnectionManagerTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The currently used read timeout for the URLConnection, 0 is unlimited, i.e. no timeout
|
||||||
|
*/
|
||||||
|
public synchronized void setReadTimeout(int timeout) {
|
||||||
|
httpClientParams.setSoTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeout Sets the read timeout for the URLConnection to a specified timeout, in milliseconds.
|
||||||
|
*/
|
||||||
|
public int getReadTimeout() {
|
||||||
|
return (int) this.getHttpClientParams().getSoTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientMethodCallbackIntf getHttpClientMethodCallback() {
|
||||||
|
return httpClientMethodCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setHttpClientMethodCallback(HttpClientMethodCallbackIntf httpClientMethodCallback) {
|
||||||
|
this.httpClientMethodCallback = httpClientMethodCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the feedInfoCache.
|
||||||
|
*/
|
||||||
|
public synchronized FeedFetcherCache getFeedInfoCache() {
|
||||||
|
return feedInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param feedInfoCache the feedInfoCache to set
|
||||||
|
*/
|
||||||
|
public synchronized void setFeedInfoCache(FeedFetcherCache feedInfoCache) {
|
||||||
|
this.feedInfoCache = feedInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the credentialSupplier.
|
||||||
|
*/
|
||||||
|
public synchronized CredentialSupplier getCredentialSupplier() {
|
||||||
|
return credentialSupplier;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param credentialSupplier The credentialSupplier to set.
|
||||||
|
*/
|
||||||
|
public synchronized void setCredentialSupplier(CredentialSupplier credentialSupplier) {
|
||||||
|
this.credentialSupplier = credentialSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.FeedFetcher#retrieveFeed(java.net.URL)
|
||||||
|
*/
|
||||||
|
public SyndFeed retrieveFeed(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);
|
||||||
|
|
||||||
|
if (getCredentialSupplier() != null) {
|
||||||
|
client.getState().setAuthenticationPreemptive(true);
|
||||||
|
// TODO what should realm be here?
|
||||||
|
Credentials credentials = getCredentialSupplier().getCredentials(null, feedUrl.getHost());
|
||||||
|
if (credentials != null) {
|
||||||
|
client.getState().setCredentials(null, feedUrl.getHost(), credentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.setProperty("httpclient.useragent", getUserAgent());
|
||||||
|
String urlStr = feedUrl.toString();
|
||||||
|
|
||||||
|
HttpMethod method = new GetMethod(urlStr);
|
||||||
|
method.addRequestHeader("Accept-Encoding", "gzip");
|
||||||
|
method.addRequestHeader("User-Agent", getUserAgent());
|
||||||
|
method.setFollowRedirects(true);
|
||||||
|
|
||||||
|
if (httpClientMethodCallback != null) {
|
||||||
|
synchronized (httpClientMethodCallback) {
|
||||||
|
httpClientMethodCallback.afterHttpClientMethodCreate(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FeedFetcherCache cache = getFeedInfoCache();
|
||||||
|
if (cache != null) {
|
||||||
|
// retrieve feed
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isUsingDeltaEncoding()) {
|
||||||
|
method.setRequestHeader("A-IM", "feed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the feed info from the cache
|
||||||
|
// Note that syndFeedInfo will be null if it is not in the cache
|
||||||
|
SyndFeedInfo syndFeedInfo = cache.getFeedInfo(feedUrl);
|
||||||
|
if (syndFeedInfo != null) {
|
||||||
|
method.setRequestHeader("If-None-Match", syndFeedInfo.getETag());
|
||||||
|
|
||||||
|
if (syndFeedInfo.getLastModified() instanceof String) {
|
||||||
|
method.setRequestHeader("If-Modified-Since", (String)syndFeedInfo.getLastModified());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int statusCode = client.executeMethod(method);
|
||||||
|
fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, urlStr);
|
||||||
|
handleErrorCodes(statusCode);
|
||||||
|
|
||||||
|
SyndFeed feed = getFeed(syndFeedInfo, urlStr, method, statusCode);
|
||||||
|
|
||||||
|
syndFeedInfo = buildSyndFeedInfo(feedUrl, urlStr, method, feed, statusCode);
|
||||||
|
|
||||||
|
cache.setFeedInfo(new URL(urlStr), syndFeedInfo);
|
||||||
|
|
||||||
|
// the feed may have been modified to pick up cached values
|
||||||
|
// (eg - for delta encoding)
|
||||||
|
feed = syndFeedInfo.getSyndFeed();
|
||||||
|
|
||||||
|
return feed;
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
method.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// cache is not in use
|
||||||
|
try {
|
||||||
|
int statusCode = client.executeMethod(method);
|
||||||
|
fireEvent(FetcherEvent.EVENT_TYPE_FEED_POLLED, urlStr);
|
||||||
|
handleErrorCodes(statusCode);
|
||||||
|
|
||||||
|
return getFeed(null, urlStr, method, statusCode);
|
||||||
|
} finally {
|
||||||
|
method.releaseConnection();
|
||||||
|
method.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
SyndFeedInfo syndFeedInfo;
|
||||||
|
syndFeedInfo = new SyndFeedInfo();
|
||||||
|
|
||||||
|
// this may be different to feedURL because of 3XX redirects
|
||||||
|
syndFeedInfo.setUrl(new URL(urlStr));
|
||||||
|
syndFeedInfo.setId(feedUrl.toString());
|
||||||
|
|
||||||
|
Header imHeader = method.getResponseHeader("IM");
|
||||||
|
if (imHeader != null && imHeader.getValue().indexOf("feed") >= 0 && isUsingDeltaEncoding()) {
|
||||||
|
FeedFetcherCache cache = getFeedInfoCache();
|
||||||
|
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);
|
||||||
|
if (cachedInfo != null) {
|
||||||
|
SyndFeed cachedFeed = cachedInfo.getSyndFeed();
|
||||||
|
|
||||||
|
// set the new feed to be the orginal feed plus the new items
|
||||||
|
feed = combineFeeds(cachedFeed, feed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Header lastModifiedHeader = method.getResponseHeader("Last-Modified");
|
||||||
|
if (lastModifiedHeader != null) {
|
||||||
|
syndFeedInfo.setLastModified(lastModifiedHeader.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
Header eTagHeader = method.getResponseHeader("ETag");
|
||||||
|
if (eTagHeader != null) {
|
||||||
|
syndFeedInfo.setETag(eTagHeader.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
syndFeedInfo.setSyndFeed(feed);
|
||||||
|
|
||||||
|
return syndFeedInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
|
||||||
|
InputStream stream = null;
|
||||||
|
if ((method.getResponseHeader("Content-Encoding") != null) && ("gzip".equalsIgnoreCase(method.getResponseHeader("Content-Encoding").getValue()))) {
|
||||||
|
stream = new GZIPInputStream(method.getResponseBodyAsStream());
|
||||||
|
} else {
|
||||||
|
stream = method.getResponseBodyAsStream();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
XmlReader reader = null;
|
||||||
|
if (method.getResponseHeader("Content-Type") != null) {
|
||||||
|
reader = new XmlReader(stream, method.getResponseHeader("Content-Type").getValue(), true);
|
||||||
|
} else {
|
||||||
|
reader = new XmlReader(stream, true);
|
||||||
|
}
|
||||||
|
SyndFeedInput syndFeedInput = new SyndFeedInput();
|
||||||
|
syndFeedInput.setPreserveWireFeed(isPreserveWireFeed());
|
||||||
|
|
||||||
|
return syndFeedInput.build(reader);
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SyndFeed getFeed(SyndFeedInfo syndFeedInfo, String urlStr, HttpMethod method, 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);
|
||||||
|
fireEvent(FetcherEvent.EVENT_TYPE_FEED_RETRIEVED, urlStr, feed);
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CredentialSupplier {
|
||||||
|
public Credentials getCredentials(String realm, String host);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public void afterHttpClientMethodCreate(HttpMethod method);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.fetcher.FetcherException;
|
||||||
|
import com.sun.syndication.io.FeedException;
|
||||||
|
import com.sun.syndication.io.SyndFeedInput;
|
||||||
|
import com.sun.syndication.io.XmlReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Class to retrieve syndication files via HTTP.</p>
|
||||||
|
*
|
||||||
|
* <p>If passed a {@link com.sun.syndication.fetcher.impl.FeedFetcherCache} in the
|
||||||
|
* constructor it will use conditional gets to only retrieve modified content.</p>
|
||||||
|
*
|
||||||
|
* <p>The class uses the Accept-Encoding: gzip header to retrieve gzipped feeds where
|
||||||
|
* supported by the server.</p>
|
||||||
|
*
|
||||||
|
* <p>Simple usage:
|
||||||
|
* <pre>
|
||||||
|
* // 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);
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see <a href="http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers">http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers</a>
|
||||||
|
* @see <a href="http://diveintomark.org/archives/2003/07/21/atom_aggregator_behavior_http_level">http://diveintomark.org/archives/2003/07/21/atom_aggregator_behavior_http_level</a>
|
||||||
|
* @see <a href="http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html">http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html</a>
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
private FeedFetcherCache feedInfoCache;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(FeedFetcherCache feedInfoCache) {
|
||||||
|
this();
|
||||||
|
setFeedInfoCache(feedInfoCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void retrieveAndCacheFeed(URL feedUrl, SyndFeedInfo syndFeedInfo, HttpURLConnection connection) throws IllegalArgumentException, FeedException, FetcherException, IOException {
|
||||||
|
handleErrorCodes(connection.getResponseCode());
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
// This will be 0 if the server doesn't support or isn't setting the last modified header
|
||||||
|
syndFeedInfo.setLastModified(new Long(connection.getLastModified()));
|
||||||
|
|
||||||
|
// This will be null if the server doesn't support or isn't setting the ETag header
|
||||||
|
syndFeedInfo.setETag(connection.getHeaderField("ETag"));
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Set appropriate HTTP headers, including conditional get and gzip encoding headers</p>
|
||||||
|
*
|
||||||
|
* @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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// header to retrieve feed gzipped
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
// set the user agent
|
||||||
|
connection.addRequestProperty("User-Agent", getUserAgent());
|
||||||
|
|
||||||
|
if (isUsingDeltaEncoding()) {
|
||||||
|
connection.addRequestProperty("A-IM", "feed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyndFeedInput syndFeedInput = new SyndFeedInput();
|
||||||
|
syndFeedInput.setPreserveWireFeed(isPreserveWireFeed());
|
||||||
|
|
||||||
|
return syndFeedInput.build(reader);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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(FeedFetcherCache cache) {
|
||||||
|
feedInfoCache = cache;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>An implementation of the {@link com.sun.syndication.fetcher.impl.FeedFetcherCache} interface.</p>
|
||||||
|
*
|
||||||
|
* <p>Unlike the HashMapFeedInfoCache this implementation will not grow unbound</p>
|
||||||
|
*
|
||||||
|
* @author Javier Kohen
|
||||||
|
* @author Nick Lothian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LinkedHashMapFeedInfoCache extends HashMapFeedInfoCache {
|
||||||
|
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;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1694228973357997417L;
|
||||||
|
|
||||||
|
private int maxEntries = DEFAULT_MAX_ENTRIES;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructor for HashMapFeedInfoCache</p>
|
||||||
|
*
|
||||||
|
* <p>Only use this if you want multiple instances of the cache.
|
||||||
|
* Usually {@link #getInstance()} is more appropriate.</p>
|
||||||
|
*
|
||||||
|
* @see #getInstance()
|
||||||
|
*/
|
||||||
|
public LinkedHashMapFeedInfoCache() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map createInfoCache() {
|
||||||
|
return Collections.synchronizedMap(new CacheImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized final int getMaxEntries() {
|
||||||
|
return maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized final void setMaxEntries(int maxEntries) {
|
||||||
|
this.maxEntries = maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* <p>Gets the character encoding of a response. (Note that this is different to
|
||||||
|
* the content-encoding)</p>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
src/java/com/sun/syndication/fetcher/impl/SyndFeedInfo.java
Normal file
149
src/java/com/sun/syndication/fetcher/impl/SyndFeedInfo.java
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.impl.ObjectBean;
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A class to represent a {@link com.sun.syndication.feed.synd.SyndFeed}
|
||||||
|
* and some useful information about it.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is thread safe, as expected by the different feed fetcher
|
||||||
|
* implementations.</p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
public SyndFeedInfo() {
|
||||||
|
_objBean = new ObjectBean(this.getClass(),this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a deep 'bean' clone of the object.
|
||||||
|
* <p>
|
||||||
|
* @return a clone of the object.
|
||||||
|
* @throws CloneNotSupportedException thrown if an element of the object cannot be cloned.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
return _objBean.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether some other object is "equal to" this one as defined by the Object equals() method.
|
||||||
|
* <p>
|
||||||
|
* @param other he reference object with which to compare.
|
||||||
|
* @return <b>true</b> if 'this' object is equal to the 'other' object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return _objBean.equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hashcode value for the object.
|
||||||
|
* <p>
|
||||||
|
* It follows the contract defined by the Object hashCode() method.
|
||||||
|
* <p>
|
||||||
|
* @return the hashcode of the bean object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return _objBean.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the String representation for the object.
|
||||||
|
* <p>
|
||||||
|
* @return String representation for the object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return _objBean.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 URL the feed was served from
|
||||||
|
*/
|
||||||
|
public synchronized URL getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setETag(String string) {
|
||||||
|
eTag = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setLastModified(Object o) {
|
||||||
|
lastModified = o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setUrl(URL url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized SyndFeed getSyndFeed() {
|
||||||
|
return syndFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setSyndFeed(SyndFeed feed) {
|
||||||
|
syndFeed = feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.samples;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
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 com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
import com.sun.syndication.fetcher.impl.FeedFetcherCache;
|
||||||
|
import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache;
|
||||||
|
import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
|
||||||
|
import com.sun.syndication.io.SyndFeedOutput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>It aggregates a list of RSS/Atom feeds (they can be of different types)
|
||||||
|
* into a single feed of the specified type.</p>
|
||||||
|
*
|
||||||
|
* <p>Converted from the original FeedAggregator sample</p>
|
||||||
|
*
|
||||||
|
* @author Alejandro Abdelnur
|
||||||
|
* @author Nick Lothian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FeedAggregator {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
boolean ok = false;
|
||||||
|
if (args.length>=2) {
|
||||||
|
try {
|
||||||
|
String outputType = args[0];
|
||||||
|
|
||||||
|
SyndFeed feed = new SyndFeedImpl();
|
||||||
|
feed.setFeedType(outputType);
|
||||||
|
|
||||||
|
feed.setTitle("Aggregated Feed");
|
||||||
|
feed.setDescription("Anonymous Aggregated Feed");
|
||||||
|
feed.setAuthor("anonymous");
|
||||||
|
feed.setLink("http://www.anonymous.com");
|
||||||
|
|
||||||
|
List entries = new ArrayList();
|
||||||
|
feed.setEntries(entries);
|
||||||
|
|
||||||
|
FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance();
|
||||||
|
FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache);
|
||||||
|
|
||||||
|
for (int i=1;i<args.length;i++) {
|
||||||
|
URL inputUrl = new URL(args[i]);
|
||||||
|
SyndFeed inFeed = feedFetcher.retrieveFeed(inputUrl);
|
||||||
|
entries.addAll(inFeed.getEntries());
|
||||||
|
}
|
||||||
|
|
||||||
|
SyndFeedOutput output = new SyndFeedOutput();
|
||||||
|
output.output(feed, new PrintWriter(System.out));
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
System.out.println("ERROR: "+ex.getMessage());
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("FeedAggregator aggregates different feeds into a single one.");
|
||||||
|
System.out.println("The first parameter must be the feed type for the aggregated feed.");
|
||||||
|
System.out.println(" [valid values are: rss_0.9, rss_0.91, rss_0.92, rss_0.93, ]");
|
||||||
|
System.out.println(" [ rss_0.94, rss_1.0, rss_2.0 & atom_0.3 ]");
|
||||||
|
System.out.println("The second to last parameters are the URLs of feeds to aggregate.");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
96
src/java/com/sun/syndication/fetcher/samples/FeedReader.java
Normal file
96
src/java/com/sun/syndication/fetcher/samples/FeedReader.java
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sun.syndication.fetcher.samples;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.fetcher.FetcherListener;
|
||||||
|
import com.sun.syndication.fetcher.impl.FeedFetcherCache;
|
||||||
|
import com.sun.syndication.fetcher.impl.HashMapFeedInfoCache;
|
||||||
|
import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
|
||||||
|
/**
|
||||||
|
* Reads and prints any RSS/Atom feed type. Converted from the
|
||||||
|
* original Rome sample FeedReader
|
||||||
|
* <p>
|
||||||
|
* @author Alejandro Abdelnur
|
||||||
|
* @author Nick Lothian
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FeedReader {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
boolean ok = false;
|
||||||
|
if (args.length==1) {
|
||||||
|
try {
|
||||||
|
URL feedUrl = new URL(args[0]);
|
||||||
|
FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance();
|
||||||
|
FeedFetcher fetcher = new HttpURLFeedFetcher(feedInfoCache);
|
||||||
|
|
||||||
|
FetcherEventListenerImpl listener = new FetcherEventListenerImpl();
|
||||||
|
|
||||||
|
fetcher.addFetcherEventListener(listener);
|
||||||
|
|
||||||
|
System.err.println("Retrieving feed " + feedUrl);
|
||||||
|
// Retrieve the feed.
|
||||||
|
// We will get a Feed Polled Event and then a
|
||||||
|
// Feed Retrieved event (assuming the feed is valid)
|
||||||
|
SyndFeed feed = fetcher.retrieveFeed(feedUrl);
|
||||||
|
|
||||||
|
System.err.println(feedUrl + " retrieved");
|
||||||
|
System.err.println(feedUrl + " has a title: " + feed.getTitle() + " and contains " + feed.getEntries().size() + " entries.");
|
||||||
|
// We will now retrieve the feed again. If the feed is unmodified
|
||||||
|
// and the server supports conditional gets, we will get a "Feed
|
||||||
|
// Unchanged" event after the Feed Polled event
|
||||||
|
System.err.println("Polling " + feedUrl + " again to test conditional get support.");
|
||||||
|
SyndFeed feed2 = fetcher.retrieveFeed(feedUrl);
|
||||||
|
System.err.println("If a \"Feed Unchanged\" event fired then the server supports conditional gets.");
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
System.out.println("ERROR: "+ex.getMessage());
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("FeedReader reads and prints any RSS/Atom feed type.");
|
||||||
|
System.out.println("The first parameter must be the URL of the feed to read.");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FetcherEventListenerImpl implements FetcherListener {
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.FetcherListener#fetcherEvent(com.sun.syndication.fetcher.FetcherEvent)
|
||||||
|
*/
|
||||||
|
public void fetcherEvent(FetcherEvent event) {
|
||||||
|
String eventType = event.getEventType();
|
||||||
|
if (FetcherEvent.EVENT_TYPE_FEED_POLLED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Polled. URL = " + event.getUrlString());
|
||||||
|
} else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Retrieved. URL = " + event.getUrlString());
|
||||||
|
} else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Unchanged. URL = " + event.getUrlString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
src/test/atom_1.0.xml
Normal file
78
src/test/atom_1.0.xml
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns='http://www.w3.org/2005/Atom'
|
||||||
|
xmlns:rometest='http://rome.dev.java.net/namespacetest'
|
||||||
|
xml:lang='en-us'>
|
||||||
|
<title type="html">atom_1.0.feed.title</title>
|
||||||
|
<link rel="self" type="text/html" href="http://example.com/blog/atom_1.0.xml"/>
|
||||||
|
<link rel="alternate" type="text/html" href="http://example.com/blog"/>
|
||||||
|
<link rel="alternate" type="text/plain" href="http://example.com/blog_plain"/>
|
||||||
|
<rometest:test>rometest</rometest:test>
|
||||||
|
<author>
|
||||||
|
<name>atom_1.0.feed.author.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author0@example.com</email>
|
||||||
|
</author>
|
||||||
|
<contributor>
|
||||||
|
<name>atom_1.0.feed.contributor.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author1@example.com</email>
|
||||||
|
</contributor>
|
||||||
|
<subtitle type="html">atom_1.0.feed.tagline</subtitle>
|
||||||
|
<id>http://example.com/blog/atom_1.0.xml</id>
|
||||||
|
<generator uri="http://example.com/test">atom_1.0.feed.generator</generator>
|
||||||
|
<rights>atom_1.0.feed.copyright</rights>
|
||||||
|
<updated>2000-01-01T00:00:00Z</updated>
|
||||||
|
<entry>
|
||||||
|
<title type="text">atom_1.0.feed.entry[0].title</title>
|
||||||
|
<link rel="alternate" type="text/html"
|
||||||
|
href="http://example.com/blog/entry1"/>
|
||||||
|
<link rel="alternate" type="text/plain"
|
||||||
|
href="http://example.com/blog/entry1_plain"/>
|
||||||
|
<link rel="enclosure" type="image/gif"
|
||||||
|
href="http://example.com/blog/enclosure1.gif"/>
|
||||||
|
<link rel="test" type="image/gif"
|
||||||
|
href="tag:example.com,2005:Atom-Tests:xml-base:Test0"/>
|
||||||
|
<id>http://example.com/blog/entry1</id>
|
||||||
|
<author>
|
||||||
|
<name>atom_1.0.feed.entry[0].author.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author0@example.com</email>
|
||||||
|
</author>
|
||||||
|
<contributor>
|
||||||
|
<name>atom_1.0.feed.entry[0].contributor.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author1@example.com</email>
|
||||||
|
</contributor>
|
||||||
|
<updated>2000-01-01T00:00:00Z</updated>
|
||||||
|
<published>2000-01-01T01:00:00Z</published>
|
||||||
|
<summary type="html">atom_1.0.feed.entry[0].summary</summary>
|
||||||
|
<content type="html">atom_1.0.feed.entry[0].content[0]</content>
|
||||||
|
<rometest:test>rometest</rometest:test>
|
||||||
|
<rights>atom_1.0.feed.entry[0].rights</rights>
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
<title type="text">atom_1.0.feed.entry[1].title</title>
|
||||||
|
<link rel="alternate" type="text/html"
|
||||||
|
href="http://example.com/blog/entry2"/>
|
||||||
|
<link rel="enclosure" type="image/gif"
|
||||||
|
href="http://example.com/blog/enclosure2.gif"/>
|
||||||
|
<link rel="test" type="image/gif"
|
||||||
|
href="tag:example.com,2005:Atom-Tests:xml-base:Test1"/>
|
||||||
|
<id>http://example.com/blog/entry2</id>
|
||||||
|
<author>
|
||||||
|
<name>atom_1.0.feed.entry[1].author.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author0@example.com</email>
|
||||||
|
</author>
|
||||||
|
<contributor>
|
||||||
|
<name>atom_1.0.feed.entry[1].contributor.name</name>
|
||||||
|
<uri>http://example.com</uri>
|
||||||
|
<email>author1@example.com</email>
|
||||||
|
</contributor>
|
||||||
|
<updated>2000-02-01T00:00:00Z</updated>
|
||||||
|
<published>2000-02-01T01:00:00Z</published>
|
||||||
|
<summary type="html">atom_1.0.feed.entry[1].summary</summary>
|
||||||
|
<content type="html">atom_1.0.feed.entry[1].content[0]</content>
|
||||||
|
<rometest:test>rometest</rometest:test>
|
||||||
|
</entry>
|
||||||
|
</feed>
|
419
src/test/com/sun/syndication/fetcher/impl/AbstractJettyTest.java
Normal file
419
src/test/com/sun/syndication/fetcher/impl/AbstractJettyTest.java
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.mortbay.http.BasicAuthenticator;
|
||||||
|
import org.mortbay.http.HashUserRealm;
|
||||||
|
import org.mortbay.http.HttpContext;
|
||||||
|
import org.mortbay.http.HttpServer;
|
||||||
|
import org.mortbay.http.SecurityConstraint;
|
||||||
|
import org.mortbay.http.SocketListener;
|
||||||
|
import org.mortbay.http.UserRealm;
|
||||||
|
import org.mortbay.http.handler.SecurityHandler;
|
||||||
|
import org.mortbay.jetty.servlet.ServletHandler;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.atom.Entry;
|
||||||
|
import com.sun.syndication.feed.synd.SyndEntry;
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
import com.sun.syndication.fetcher.FetcherEvent;
|
||||||
|
import com.sun.syndication.fetcher.FetcherException;
|
||||||
|
import com.sun.syndication.fetcher.FetcherListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nl
|
||||||
|
*/
|
||||||
|
public abstract class AbstractJettyTest extends TestCase {
|
||||||
|
|
||||||
|
private HttpServer server;
|
||||||
|
private int testPort = 8283;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
public AbstractJettyTest(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract FeedFetcher getFeedFetcher();
|
||||||
|
|
||||||
|
protected abstract FeedFetcher getFeedFetcher(FeedFetcherCache cache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see junit.framework.TestCase#setUp()
|
||||||
|
*/
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
setupServer();
|
||||||
|
|
||||||
|
HttpContext context = createContext();
|
||||||
|
|
||||||
|
ServletHandler servlets = createServletHandler();
|
||||||
|
context.addHandler(servlets);
|
||||||
|
|
||||||
|
server.addContext(context);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
private void setupServer() throws InterruptedException {
|
||||||
|
// Create the server
|
||||||
|
if (server != null) {
|
||||||
|
server.stop();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
server = new HttpServer();
|
||||||
|
|
||||||
|
// Create a port listener
|
||||||
|
SocketListener listener=new SocketListener();
|
||||||
|
listener.setPort(testPort);
|
||||||
|
server.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ServletHandler createServletHandler() {
|
||||||
|
ServletHandler servlets = new ServletHandler();
|
||||||
|
servlets.addServlet("FetcherTestServlet",FetcherTestServlet.SERVLET_MAPPING,"com.sun.syndication.fetcher.impl.FetcherTestServlet");
|
||||||
|
servlets.addServlet("FetcherTestServlet",FetcherTestServlet.SERVLET_MAPPING2,"com.sun.syndication.fetcher.impl.FetcherTestServlet");
|
||||||
|
return servlets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private HttpContext createContext() {
|
||||||
|
HttpContext context = new HttpContext();
|
||||||
|
context.setContextPath("/rome/*");
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see junit.framework.TestCase#tearDown()
|
||||||
|
*/
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
if (server != null) {
|
||||||
|
server.stop();
|
||||||
|
server.destroy();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FetcherEventListenerImpl implements FetcherListener {
|
||||||
|
boolean polled = false;
|
||||||
|
boolean retrieved = false;
|
||||||
|
boolean unchanged = false;
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
polled = false;
|
||||||
|
retrieved = false;
|
||||||
|
unchanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.FetcherListener#fetcherEvent(com.sun.syndication.fetcher.FetcherEvent)
|
||||||
|
*/
|
||||||
|
public void fetcherEvent(FetcherEvent event) {
|
||||||
|
String eventType = event.getEventType();
|
||||||
|
if (FetcherEvent.EVENT_TYPE_FEED_POLLED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Polled. URL = " + event.getUrlString());
|
||||||
|
polled = true;
|
||||||
|
} else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Retrieved. URL = " + event.getUrlString());
|
||||||
|
retrieved = true;
|
||||||
|
} else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) {
|
||||||
|
System.err.println("\tEVENT: Feed Unchanged. URL = " + event.getUrlString());
|
||||||
|
unchanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRetrieveFeed() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasicAuthentication() {
|
||||||
|
try {
|
||||||
|
setupServer();
|
||||||
|
|
||||||
|
HttpContext context = createContext();
|
||||||
|
|
||||||
|
URL url = this.getClass().getResource("/testuser.properties");
|
||||||
|
UserRealm ur = new HashUserRealm("test", url.getFile());
|
||||||
|
context.setRealm(ur);
|
||||||
|
|
||||||
|
BasicAuthenticator ba = new BasicAuthenticator();
|
||||||
|
context.setAuthenticator(ba);
|
||||||
|
|
||||||
|
SecurityHandler sh = new SecurityHandler();
|
||||||
|
context.addHandler(sh);
|
||||||
|
|
||||||
|
SecurityConstraint sc = new SecurityConstraint();
|
||||||
|
sc.setName("test");
|
||||||
|
sc.addRole("*");
|
||||||
|
sc.setAuthenticate(true);
|
||||||
|
context.addSecurityConstraint("/", sc);
|
||||||
|
|
||||||
|
ServletHandler servlets = createServletHandler();
|
||||||
|
context.addHandler(servlets);
|
||||||
|
|
||||||
|
server.addContext(context);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
FeedFetcher feedFetcher = getAuthenticatedFeedFetcher();
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract FeedFetcher getAuthenticatedFeedFetcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting a feed via a http 301 redirect
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void testRetrieveRedirectedFeed() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?redirect=TRUE"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test error handling
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void testErrorHandling() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?error=404"));
|
||||||
|
fail("4xx error handling did not work correctly");
|
||||||
|
} catch (FetcherException e) {
|
||||||
|
// expect this exception
|
||||||
|
assertEquals(404, e.getResponseCode());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?error=500"));
|
||||||
|
fail("5xx error handling did not work correctly");
|
||||||
|
} catch (FetcherException e) {
|
||||||
|
// expect this exception
|
||||||
|
assertEquals(500, e.getResponseCode());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUserAgent() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
//System.out.println(feedFetcher.getUserAgent());
|
||||||
|
//System.out.println(System.getProperty("rome.fetcher.version", "UNKNOWN"));
|
||||||
|
assertEquals("Rome Client (http://tinyurl.com/64t5n) Ver: " + System.getProperty("rome.fetcher.version", "UNKNOWN"), feedFetcher.getUserAgent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test events fired when there is no cache in use
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void testFetchEvents() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
FetcherEventListenerImpl listener = new FetcherEventListenerImpl();
|
||||||
|
feedFetcher.addFetcherEventListener(listener);
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertTrue(listener.polled);
|
||||||
|
assertTrue(listener.retrieved);
|
||||||
|
assertFalse(listener.unchanged);
|
||||||
|
listener.reset();
|
||||||
|
|
||||||
|
// since there is no cache, the events fired should be exactly the same if
|
||||||
|
// we re-retrieve the feed
|
||||||
|
feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertTrue(listener.polled);
|
||||||
|
assertTrue(listener.retrieved);
|
||||||
|
assertFalse(listener.unchanged);
|
||||||
|
listener.reset();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test events fired when there is a cache in use
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void testFetchEventsWithCache() {
|
||||||
|
FeedFetcherCache feedInfoCache = new HashMapFeedInfoCache();
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher(feedInfoCache);
|
||||||
|
FetcherEventListenerImpl listener = new FetcherEventListenerImpl();
|
||||||
|
feedFetcher.addFetcherEventListener(listener);
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertTrue(listener.polled);
|
||||||
|
assertTrue(listener.retrieved);
|
||||||
|
assertFalse(listener.unchanged);
|
||||||
|
listener.reset();
|
||||||
|
|
||||||
|
// Since the feed is cached, the second request should not
|
||||||
|
// actually retrieve the feed
|
||||||
|
feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertTrue(listener.polled);
|
||||||
|
assertFalse(listener.retrieved);
|
||||||
|
assertTrue(listener.unchanged);
|
||||||
|
listener.reset();
|
||||||
|
|
||||||
|
// now simulate getting the feed after it has changed
|
||||||
|
feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?refreshfeed=TRUE"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertTrue(listener.polled);
|
||||||
|
assertTrue(listener.retrieved);
|
||||||
|
assertFalse(listener.unchanged);
|
||||||
|
listener.reset();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test handling of GZipped feed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void testGZippedFeed() {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
try {
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?gzipfeed=TRUE"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPreserveWireFeed() throws Exception {
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
|
||||||
|
// first check we the WireFeed is not preserved by default
|
||||||
|
SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
assertNull(feed.originalWireFeed());
|
||||||
|
|
||||||
|
SyndEntry syndEntry = (SyndEntry)feed.getEntries().get(0);
|
||||||
|
assertNotNull(syndEntry);
|
||||||
|
assertNull(syndEntry.getWireEntry());
|
||||||
|
|
||||||
|
// now turn on WireFeed preservation
|
||||||
|
feedFetcher.setPreserveWireFeed(true);
|
||||||
|
try {
|
||||||
|
feed = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet/"));
|
||||||
|
assertNotNull(feed);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed.getTitle());
|
||||||
|
assertNotNull(feed.originalWireFeed());
|
||||||
|
|
||||||
|
syndEntry = (SyndEntry)feed.getEntries().get(0);
|
||||||
|
assertNotNull(syndEntry);
|
||||||
|
assertNotNull(syndEntry.getWireEntry());
|
||||||
|
|
||||||
|
Entry entry = (Entry) syndEntry.getWireEntry();
|
||||||
|
assertEquals("atom_1.0.feed.entry[0].rights", entry.getRights());
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
feedFetcher.setPreserveWireFeed(false); //reset
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeltaEncoding() {
|
||||||
|
FeedFetcherCache feedInfoCache = new HashMapFeedInfoCache();
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher(feedInfoCache);
|
||||||
|
try {
|
||||||
|
feedFetcher.setUsingDeltaEncoding(true);
|
||||||
|
|
||||||
|
// first retrieval should just grab the default feed
|
||||||
|
SyndFeed feed1 = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?deltaencode=TRUE&refreshfeed=TRUE"));
|
||||||
|
assertNotNull(feed1);
|
||||||
|
assertEquals("atom_1.0.feed.title", feed1.getTitle());
|
||||||
|
assertEquals(2, feed1.getEntries().size());
|
||||||
|
SyndEntry entry1 = (SyndEntry) feed1.getEntries().get(0);
|
||||||
|
assertEquals("atom_1.0.feed.entry[0].title", entry1.getTitle());
|
||||||
|
|
||||||
|
// second retrieval should get only the new item
|
||||||
|
/*
|
||||||
|
* This is breaking with Rome 0.5 ??
|
||||||
|
*/
|
||||||
|
SyndFeed feed2 = feedFetcher.retrieveFeed(new URL("http://localhost:"+testPort+"/rome/FetcherTestServlet?deltaencode=TRUE&refreshfeed=TRUE"));
|
||||||
|
assertNotNull(feed2);
|
||||||
|
assertEquals(FetcherTestServlet.DELTA_FEED_TITLE, feed2.getTitle());
|
||||||
|
assertEquals(3, feed2.getEntries().size());
|
||||||
|
entry1 = (SyndEntry) feed2.getEntries().get(0);
|
||||||
|
assertEquals(FetcherTestServlet.DELTA_FEED_ENTRY_TITLE, entry1.getTitle());
|
||||||
|
|
||||||
|
SyndEntry entry2 = (SyndEntry) feed2.getEntries().get(1);
|
||||||
|
assertEquals("atom_1.0.feed.entry[0].title", entry2.getTitle());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.*;
|
||||||
|
import java.io.File;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class DiskFeedInfoCacheTest extends TestCase {
|
||||||
|
|
||||||
|
public void testClear() throws Exception {
|
||||||
|
File cacheDir = new File("test-cache");
|
||||||
|
cacheDir.mkdir();
|
||||||
|
cacheDir.deleteOnExit();
|
||||||
|
|
||||||
|
final DiskFeedInfoCache cache = new DiskFeedInfoCache(cacheDir.getCanonicalPath());
|
||||||
|
SyndFeedInfo info = new SyndFeedInfo();
|
||||||
|
URL url = new URL("http://nowhere.com");
|
||||||
|
cache.setFeedInfo(url, info);
|
||||||
|
|
||||||
|
cache.clear();
|
||||||
|
final Object returned = cache.getFeedInfo(url);
|
||||||
|
assertTrue( returned == null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemove() throws Exception {
|
||||||
|
File cacheDir = new File("test-cache");
|
||||||
|
cacheDir.mkdir();
|
||||||
|
cacheDir.deleteOnExit();
|
||||||
|
|
||||||
|
final DiskFeedInfoCache cache = new DiskFeedInfoCache( cacheDir.getCanonicalPath() );
|
||||||
|
SyndFeedInfo info = new SyndFeedInfo();
|
||||||
|
URL url = new URL("http://nowhere.com");
|
||||||
|
cache.setFeedInfo( url, info );
|
||||||
|
|
||||||
|
SyndFeedInfo removedInfo = cache.remove( url );
|
||||||
|
assertTrue( removedInfo.equals(info) );
|
||||||
|
SyndFeedInfo shouldBeNull = cache.remove( url );
|
||||||
|
assertTrue( null == shouldBeNull );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.sun.syndication.feed.synd.SyndContent;
|
||||||
|
import com.sun.syndication.feed.synd.SyndContentImpl;
|
||||||
|
import com.sun.syndication.feed.synd.SyndEntry;
|
||||||
|
import com.sun.syndication.feed.synd.SyndEntryImpl;
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeed;
|
||||||
|
import com.sun.syndication.feed.synd.SyndFeedImpl;
|
||||||
|
import com.sun.syndication.io.FeedException;
|
||||||
|
import com.sun.syndication.io.SyndFeedOutput;
|
||||||
|
|
||||||
|
|
||||||
|
public class FetcherTestServlet extends javax.servlet.http.HttpServlet {
|
||||||
|
public static final String ETAG_1 = "ETAG-1";
|
||||||
|
public static final String ETAG_2 = "ETAG-2";
|
||||||
|
|
||||||
|
public static final String DELTA_FEED_TITLE = "Delta Encoded Feed";
|
||||||
|
public static final String DELTA_FEED_ENTRY_TITLE = "Delta Encoded Feed Entry";
|
||||||
|
|
||||||
|
public static final String SERVLET_MAPPING = "/FetcherTestServlet/*";
|
||||||
|
public static final String SERVLET_MAPPING2 = "/FetcherTestServlet2/*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IOException
|
||||||
|
* @throws
|
||||||
|
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||||
|
*/
|
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
|
||||||
|
if ("TRUE".equalsIgnoreCase(request.getParameter("redirect"))) {
|
||||||
|
// testing redirection support
|
||||||
|
response.sendRedirect("/rome/FetcherTestServlet2/");
|
||||||
|
return;
|
||||||
|
} else if (request.getParameter("error") != null) {
|
||||||
|
//response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
int errorToThrow = Integer.parseInt(request.getParameter("error"));
|
||||||
|
response.sendError(errorToThrow);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// We manually set the date headers using strings
|
||||||
|
// instead of the get/setDateHeader methods because
|
||||||
|
// those methods return longs, which has too much
|
||||||
|
// precision for the real date headers
|
||||||
|
// this is just a random date
|
||||||
|
String lastModifiedDate = "Thu, 08 Jan 2009 23:06:39 GMT";
|
||||||
|
String eTag = ETAG_1;
|
||||||
|
|
||||||
|
if ("TRUE".equalsIgnoreCase(request.getParameter("refreshfeed"))) {
|
||||||
|
lastModifiedDate = "Fri, 09 Jan 2009 12:06:39 GMT";
|
||||||
|
eTag = ETAG_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean serveFeed = checkModified(request, lastModifiedDate, eTag) || ("TRUE".equalsIgnoreCase(request.getParameter("deltaencode")));
|
||||||
|
boolean gzip = "TRUE".equalsIgnoreCase(request.getParameter("gzipfeed"));
|
||||||
|
|
||||||
|
if (serveFeed) {
|
||||||
|
String aimHeader = request.getHeader("A-IM");
|
||||||
|
boolean serveDeltaEncodedFeed = ((aimHeader != null) && (aimHeader.indexOf("feed") >=0) && "TRUE".equalsIgnoreCase(request.getParameter("deltaencode")));
|
||||||
|
if (serveDeltaEncodedFeed) {
|
||||||
|
try {
|
||||||
|
sendDeltaEncodedData(response, lastModifiedDate, request.getHeader("If-None-Match"), eTag, gzip);
|
||||||
|
} catch (FeedException e) {
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendFeedData(response, lastModifiedDate, eTag, gzip);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private boolean checkModified(HttpServletRequest request, String lastModifiedDate, String eTag) {
|
||||||
|
|
||||||
|
String requestedETag = request.getHeader("If-None-Match");
|
||||||
|
String requestedLastModified = request.getHeader("If-Modified-Since");
|
||||||
|
boolean modified = true;
|
||||||
|
boolean mustServer = false;
|
||||||
|
if (requestedETag != null) {
|
||||||
|
if (eTag.equals(requestedETag)) {
|
||||||
|
modified = false;
|
||||||
|
} else {
|
||||||
|
modified = true;
|
||||||
|
mustServer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestedLastModified != null) {
|
||||||
|
if (lastModifiedDate.equals(requestedLastModified)) {
|
||||||
|
modified = false;
|
||||||
|
} else {
|
||||||
|
modified = true;
|
||||||
|
mustServer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean serveFeed = (modified || mustServer);
|
||||||
|
return serveFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param request
|
||||||
|
* @param lastModifiedDate
|
||||||
|
* @param tag
|
||||||
|
* @param gzip
|
||||||
|
* @throws IOException
|
||||||
|
* @throws FeedException
|
||||||
|
*/
|
||||||
|
private void sendDeltaEncodedData(HttpServletResponse response, String lastModifiedDate, String requestedETag, String responseETag, boolean gzip) throws IOException, FeedException {
|
||||||
|
if (ETAG_1.equals(requestedETag) || ETAG_2.equals(requestedETag)) {
|
||||||
|
OutputStream out = null;
|
||||||
|
if (gzip) {
|
||||||
|
response.setHeader("Content-Encoding", "gzip");
|
||||||
|
out = new GZIPOutputStream(response.getOutputStream());
|
||||||
|
} else {
|
||||||
|
out = response.getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setContentType("text/xml");
|
||||||
|
response.setStatus(226);
|
||||||
|
if (gzip) {
|
||||||
|
response.setHeader("IM", "feed, gzip");
|
||||||
|
} else {
|
||||||
|
response.setHeader("IM", "feed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseETag != null) {
|
||||||
|
response.setHeader("ETag", responseETag);
|
||||||
|
}
|
||||||
|
if (lastModifiedDate != null) {
|
||||||
|
response.setHeader("Last-Modified", lastModifiedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyndFeed feed = new SyndFeedImpl();
|
||||||
|
feed.setFeedType("atom_1.0");
|
||||||
|
|
||||||
|
feed.setTitle(DELTA_FEED_TITLE);
|
||||||
|
feed.setLink("http://rome.dev.java.net");
|
||||||
|
feed.setDescription("This tests using rfc3229 delta encoding.");
|
||||||
|
|
||||||
|
List entries = new ArrayList();
|
||||||
|
SyndEntry entry;
|
||||||
|
SyndContent description;
|
||||||
|
|
||||||
|
entry = new SyndEntryImpl();
|
||||||
|
entry.setTitle(DELTA_FEED_ENTRY_TITLE);
|
||||||
|
entry.setLink("http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html");
|
||||||
|
try {
|
||||||
|
DateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
entry.setPublishedDate(dateParser.parse("2004-11-25"));
|
||||||
|
}
|
||||||
|
catch (ParseException ex) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
description = new SyndContentImpl();
|
||||||
|
description.setType("text/plain");
|
||||||
|
description.setValue("Test for RFC3229 Delta Encoding");
|
||||||
|
entry.setDescription(description);
|
||||||
|
entries.add(entry);
|
||||||
|
|
||||||
|
feed.setEntries(entries);
|
||||||
|
|
||||||
|
SyndFeedOutput output = new SyndFeedOutput();
|
||||||
|
output.output(feed, new OutputStreamWriter(out));
|
||||||
|
} else {
|
||||||
|
sendFeedData(response, lastModifiedDate, responseETag, gzip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendFeedData(HttpServletResponse response, String lastModifiedDate, String eTag, boolean gzip) throws IOException {
|
||||||
|
OutputStream out = null;
|
||||||
|
if (gzip) {
|
||||||
|
response.setHeader("Content-Encoding", "gzip");
|
||||||
|
out = new GZIPOutputStream(response.getOutputStream());
|
||||||
|
} else {
|
||||||
|
out = response.getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setContentType("text/xml");
|
||||||
|
if (eTag != null) {
|
||||||
|
response.setHeader("ETag", eTag);
|
||||||
|
}
|
||||||
|
if (lastModifiedDate != null) {
|
||||||
|
response.setHeader("Last-Modified", lastModifiedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("/atom_1.0.xml");
|
||||||
|
if (inputStream == null) {
|
||||||
|
inputStream = this.getClass().getResourceAsStream("/atom_1.0.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
try {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
out.write(line.getBytes());
|
||||||
|
line = null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class HashMapFeedInfoCacheTest extends TestCase {
|
||||||
|
|
||||||
|
public void testRemove() throws Exception {
|
||||||
|
final HashMapFeedInfoCache cache = new HashMapFeedInfoCache();
|
||||||
|
assertNotNull( cache );
|
||||||
|
|
||||||
|
final URL url = new URL("http://foo.com");
|
||||||
|
final SyndFeedInfo syndFeedInfo = new SyndFeedInfo();
|
||||||
|
syndFeedInfo.setUrl(url);
|
||||||
|
cache.setFeedInfo(url, syndFeedInfo);
|
||||||
|
|
||||||
|
final SyndFeedInfo returned = cache.remove(url);
|
||||||
|
assertTrue( returned.equals(syndFeedInfo) );
|
||||||
|
assertTrue( url.equals( returned.getUrl() ));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testClear() throws Exception {
|
||||||
|
final HashMapFeedInfoCache cache = new HashMapFeedInfoCache();
|
||||||
|
assertNotNull( cache );
|
||||||
|
|
||||||
|
final URL url = new URL("http://foo.com");
|
||||||
|
final SyndFeedInfo syndFeedInfo = new SyndFeedInfo();
|
||||||
|
syndFeedInfo.setUrl(url);
|
||||||
|
cache.setFeedInfo(url, syndFeedInfo);
|
||||||
|
|
||||||
|
//clear it
|
||||||
|
cache.clear();
|
||||||
|
|
||||||
|
//we should not get a result back
|
||||||
|
final Object returned = cache.get(url);
|
||||||
|
assertTrue( returned == null );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.Credentials;
|
||||||
|
import org.apache.commons.httpclient.UsernamePasswordCredentials;
|
||||||
|
|
||||||
|
import com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nick Lothian
|
||||||
|
*/
|
||||||
|
public class HttpClientFeedFetcherTest extends AbstractJettyTest {
|
||||||
|
|
||||||
|
public HttpClientFeedFetcherTest(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.impl.AbstractJettyTest#getFeedFetcher()
|
||||||
|
*/
|
||||||
|
protected FeedFetcher getFeedFetcher() {
|
||||||
|
return new HttpClientFeedFetcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FeedFetcher getFeedFetcher(FeedFetcherCache cache) {
|
||||||
|
return new HttpClientFeedFetcher(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.impl.AbstractJettyTest#getAuthenticatedFeedFetcher()
|
||||||
|
*/
|
||||||
|
public FeedFetcher getAuthenticatedFeedFetcher() {
|
||||||
|
return new HttpClientFeedFetcher(null, new HttpClientFeedFetcher.CredentialSupplier() {
|
||||||
|
public Credentials getCredentials(String realm, String host) {
|
||||||
|
if ("localhost".equals(host)) {
|
||||||
|
return new UsernamePasswordCredentials("username", "password");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import com.sun.syndication.fetcher.FeedFetcher;
|
||||||
|
|
||||||
|
|
||||||
|
public class HttpURLFeedFetcherTest extends AbstractJettyTest {
|
||||||
|
|
||||||
|
public HttpURLFeedFetcherTest(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.impl.AbstractJettyTest#getFeedFetcher()
|
||||||
|
*/
|
||||||
|
protected FeedFetcher getFeedFetcher() {
|
||||||
|
return new HttpURLFeedFetcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FeedFetcher getFeedFetcher(FeedFetcherCache cache) {
|
||||||
|
return new HttpURLFeedFetcher(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.sun.syndication.fetcher.impl.AbstractJettyTest#getAuthenticatedFeedFetcher()
|
||||||
|
*/
|
||||||
|
public FeedFetcher getAuthenticatedFeedFetcher() {
|
||||||
|
// setup the authenticator
|
||||||
|
java.net.Authenticator.setDefault(new TestBasicAuthenticator());
|
||||||
|
|
||||||
|
FeedFetcher feedFetcher = getFeedFetcher();
|
||||||
|
|
||||||
|
return feedFetcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import com.sun.syndication.fetcher.impl.ResponseHandler;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class ResponseHandlerTest extends TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ResponseHandlerTest.
|
||||||
|
*/
|
||||||
|
public ResponseHandlerTest(String arg0) {
|
||||||
|
super(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetCharacterEncodingString() {
|
||||||
|
assertEquals(ResponseHandler.defaultCharacterEncoding, ResponseHandler.getCharacterEncoding((String)null));
|
||||||
|
assertEquals(ResponseHandler.defaultCharacterEncoding, ResponseHandler.getCharacterEncoding("text/xml"));
|
||||||
|
assertEquals(ResponseHandler.defaultCharacterEncoding, ResponseHandler.getCharacterEncoding("text/xml;"));
|
||||||
|
assertEquals("ISO-8859-4", ResponseHandler.getCharacterEncoding("text/xml; charset=ISO-8859-4"));
|
||||||
|
assertEquals("ISO-8859-4", ResponseHandler.getCharacterEncoding("text/xml;charset=ISO-8859-4"));
|
||||||
|
assertEquals("ISO-8859-4", ResponseHandler.getCharacterEncoding("text/xml;charset=ISO-8859-4;something"));
|
||||||
|
assertEquals(ResponseHandler.defaultCharacterEncoding, ResponseHandler.getCharacterEncoding("text/xml;something"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004 Sun Microsystems, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.sun.syndication.fetcher.impl;
|
||||||
|
|
||||||
|
import java.net.Authenticator;
|
||||||
|
import java.net.PasswordAuthentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nl
|
||||||
|
*/
|
||||||
|
public class TestBasicAuthenticator extends Authenticator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see java.net.Authenticator#getPasswordAuthentication()
|
||||||
|
*/
|
||||||
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
|
if ("localhost".equals(getRequestingHost())) {
|
||||||
|
return new PasswordAuthentication("username", "password".toCharArray());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/test/testuser.properties
Normal file
1
src/test/testuser.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
username: password
|
Loading…
Reference in a new issue