001package votorola.a.diff.harvest.run; // Copyright 2012. Christian Weilbach.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
002
003import java.io.InputStream;
004import java.util.concurrent.atomic.AtomicInteger;
005import java.util.concurrent.atomic.AtomicReference;
006
007import votorola.a.diff.harvest.PipermailHarvester;
008
009/**
010 * Wrapper class around Runnable to work with asynchronous HTTP responses from
011 * {@linkplain HarvestRunner}. Implement this and then
012 * {@linkplain HarvestRunner#scheduleLast(Fetcher) schedule} it. Once the
013 * scheduler can fetch the URL it {@linkplain #setInputStream(InputStream) sets
014 * the input stream} and executes this job in its thread pool.
015 * 
016 * All you have to care about is implementing {@linkplain Runnable#run()}
017 * properly and schedule cascading jobs from there in the same way. See
018 * {@linkplain PipermailHarvester} for reference.
019 */
020public abstract class AbstractFetcher implements Fetcher {
021
022    /**
023     * Jobs are equal if and only if
024     * 
025     * <pre *>
026     * this.url.equals(job.url)
027     * </pre>
028     * 
029     * @return whether the jobs are equal
030     */
031    @Override
032    public boolean equals(final Object o) {
033        return (o instanceof AbstractFetcher)
034                && ((AbstractFetcher) o).archiveUrl.equals(archiveUrl)
035                && ((AbstractFetcher) o).path.equals(path);
036    }
037    
038    /**
039     * Obey equals contract.
040     */
041    @Override
042    public int hashCode() {
043        int hash = archiveUrl.hashCode();
044        hash = hash*37 + path.hashCode();
045        return hash;
046    }
047
048    /**
049     * Constructor depending on split URL parameters.
050     * 
051     * @param archiveUrl
052     *            url of this archive, unique
053     * @param path
054     *            path and query of this archive
055     */
056    protected AbstractFetcher(final String archiveUrl, final String path) {
057        this.path = path;
058        this.archiveUrl = archiveUrl;
059    }
060
061    /**
062     * Access to the base-url of the archive.
063     * 
064     * @return base-url
065     */
066    public String archiveUrl() {
067        return archiveUrl;
068    }
069
070    private final String archiveUrl;
071
072    /**
073     * @return URL of this job. This is final and cannot change.
074     */
075    public String path() {
076        return path;
077    }
078
079    private final String path;
080
081    /**
082     * Base-url and path combined to complete url.
083     * 
084     * @return url
085     */
086    public String url() {
087        return archiveUrl + path;
088    }
089
090    /**
091     * Supposed to be final after being set by HarvestRunner.
092     */
093    private final AtomicReference<InputStream> inputStream = new AtomicReference<InputStream>();
094
095    /**
096     * Used by {@linkplain HarvestRunner} to set the data stream returned by the
097     * request of the url.
098     */
099    public void setInputStream(final InputStream is) {
100        inputStream.set(is);
101    }
102
103    /**
104     * @return Once the HTTP response is complete, use this
105     *         {@linkplain InputStream} to read the response entity.
106     */
107    protected InputStream getInputStream() {
108        return inputStream.get();
109    }
110
111    private final AtomicInteger statusCode = new AtomicInteger();
112
113    /**
114     * Set HTTP status code, e.g. 404 for not found.
115     * 
116     * @param code
117     */
118    public void setStatusCode(int code) {
119        statusCode.set(code);
120    }
121
122    /**
123     * Access HTTP error code, e.g. 404 for not found.
124     * 
125     * @return code
126     */
127    public int getStatusCode() {
128        return statusCode.get();
129    }
130}