0001: package org.methodize.nntprss.rss;
0002:
0003: /* -----------------------------------------------------------
0004: * nntp//rss - a bridge between the RSS world and NNTP clients
0005: * Copyright (c) 2002, 2003 Jason Brome. All Rights Reserved.
0006: *
0007: * email: nntprss@methodize.org
0008: * mail: Methodize Solutions
0009: * PO Box 3865
0010: * Grand Central Station
0011: * New York NY 10163
0012: *
0013: * This file is part of nntp//rss
0014: *
0015: * nntp//rss is free software; you can redistribute it
0016: * and/or modify it under the terms of the GNU General
0017: * Public License as published by the Free Software Foundation;
0018: * either version 2 of the License, or (at your option) any
0019: * later version.
0020: *
0021: * This program is distributed in the hope that it will be
0022: * useful, but WITHOUT ANY WARRANTY; without even the implied
0023: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
0024: * PURPOSE. See the GNU General Public License for more
0025: * details.
0026: *
0027: * You should have received a copy of the GNU General Public
0028: * License along with this program; if not, write to the
0029: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0030: * Boston, MA 02111-1307 USA
0031: * ----------------------------------------------------- */
0032:
0033: import java.io.BufferedInputStream;
0034: import java.io.ByteArrayInputStream;
0035: import java.io.ByteArrayOutputStream;
0036: import java.io.FileNotFoundException;
0037: import java.io.IOException;
0038: import java.io.InputStream;
0039: import java.io.PushbackInputStream;
0040: import java.net.ConnectException;
0041: import java.net.HttpURLConnection;
0042: import java.net.MalformedURLException;
0043: import java.net.NoRouteToHostException;
0044: import java.net.SocketException;
0045: import java.net.URL;
0046: import java.net.UnknownHostException;
0047: import java.security.MessageDigest;
0048: import java.util.ArrayList;
0049: import java.util.Calendar;
0050: import java.util.Date;
0051: import java.util.HashSet;
0052: import java.util.List;
0053: import java.util.Map;
0054: import java.util.Set;
0055: import java.util.zip.GZIPInputStream;
0056:
0057: import javax.xml.parsers.DocumentBuilder;
0058:
0059: import org.apache.log4j.Logger;
0060: import org.apache.log4j.Priority;
0061: import org.methodize.nntprss.rss.db.ChannelManagerDAO;
0062: import org.methodize.nntprss.rss.parser.LooseParser;
0063: import org.methodize.nntprss.util.AppConstants;
0064: import org.methodize.nntprss.util.Base64;
0065: import org.methodize.nntprss.util.XMLHelper;
0066: import org.w3c.dom.Document;
0067: import org.w3c.dom.Element;
0068: import org.w3c.dom.NodeList;
0069: import org.xml.sax.SAXParseException;
0070: import sun.net.www.http.HttpClient;
0071:
0072: /**
0073: * @author Jason Brome <jason@methodize.org>
0074: * @version $Id: Channel.java,v 1.9 2003/03/22 17:21:27 jasonbrome Exp $
0075: */
0076: public class Channel implements Runnable {
0077:
0078: public static final int STATUS_OK = 0;
0079: public static final int STATUS_NOT_FOUND = 1;
0080: public static final int STATUS_INVALID_CONTENT = 2;
0081: public static final int STATUS_CONNECTION_TIMEOUT = 3;
0082: public static final int STATUS_UNKNOWN_HOST = 4;
0083: public static final int STATUS_NO_ROUTE_TO_HOST = 5;
0084: public static final int STATUS_SOCKET_EXCEPTION = 6;
0085:
0086: private static final int PUSHBACK_BUFFER_SIZE = 4;
0087:
0088: private static final int HTTP_CONNECTION_TIMEOUT = 1000 * 60 * 5;
0089:
0090: private Logger log = Logger.getLogger(Channel.class);
0091:
0092: private String author;
0093: private String name;
0094: private URL url;
0095: private int id;
0096:
0097: private String title;
0098: private String link;
0099: private String description;
0100:
0101: private Date lastPolled;
0102: private long lastModified;
0103: private String lastETag;
0104:
0105: private Date created;
0106:
0107: private int firstArticleNumber = 1;
0108: private int lastArticleNumber = 0;
0109: private int totalArticles = 0;
0110:
0111: private String rssVersion;
0112:
0113: private String managingEditor;
0114:
0115: private boolean historical = true;
0116: private boolean enabled = true;
0117: private boolean parseAtAllCost = false;
0118:
0119: // Publishing related
0120: private boolean postingEnabled = false;
0121: private String publishAPI = null;
0122: private Map publishConfig = null;
0123:
0124: private int status = STATUS_OK;
0125:
0126: private ChannelManager channelManager;
0127: private ChannelManagerDAO channelManagerDAO;
0128:
0129: private transient boolean polling = false;
0130: private transient boolean connected = false;
0131:
0132: public static final long DEFAULT_POLLING_INTERVAL = 0;
0133:
0134: private long pollingIntervalSeconds = DEFAULT_POLLING_INTERVAL;
0135:
0136: private HttpURLConnection httpCon = null;
0137:
0138: public Channel(String name, String urlString)
0139: throws MalformedURLException {
0140: this .name = name;
0141: this .url = new URL(urlString);
0142:
0143: channelManager = ChannelManager.getChannelManager();
0144: channelManagerDAO = channelManager.getChannelManagerDAO();
0145: }
0146:
0147: /**
0148: * Returns the name.
0149: * @return String
0150: */
0151: public String getName() {
0152: return name;
0153: }
0154:
0155: /**
0156: * Returns the url.
0157: * @return String
0158: */
0159: public String getUrl() {
0160: return url.toString();
0161: }
0162:
0163: private String stripControlChars(String string) {
0164: StringBuffer strippedString = new StringBuffer();
0165: for (int charCount = 0; charCount < string.length(); charCount++) {
0166: char c = string.charAt(charCount);
0167: if (c >= 32) {
0168: strippedString.append(c);
0169: }
0170: }
0171: return strippedString.toString();
0172: }
0173:
0174: /**
0175: * Retrieves the latest RSS doc from the remote site
0176: */
0177: public synchronized void poll() {
0178: // Use method-level variable
0179: // Guard against change in history mid-poll
0180: polling = true;
0181:
0182: boolean keepHistory = historical;
0183:
0184: lastPolled = new Date();
0185:
0186: try {
0187: // HttpURLConnection httpCon =
0188: httpCon = (HttpURLConnection) url.openConnection();
0189: httpCon.setDoInput(true);
0190: httpCon.setDoOutput(false);
0191: httpCon.setRequestMethod("GET");
0192: httpCon.setRequestProperty("User-Agent", AppConstants
0193: .getUserAgent());
0194: httpCon.setRequestProperty("Accept-Encoding", "gzip");
0195:
0196: // ETag
0197: if (lastETag != null) {
0198: httpCon.setRequestProperty("If-None-Match", lastETag);
0199: }
0200:
0201: // Last Modified
0202: if (lastModified != 0) {
0203: httpCon.setIfModifiedSince(lastModified);
0204: }
0205:
0206: InputStream is = null;
0207: try {
0208: httpCon.connect();
0209: connected = true;
0210: is = httpCon.getInputStream();
0211: } catch (ConnectException ce) {
0212: if (log.isDebugEnabled()) {
0213: log.debug("Channel=" + name
0214: + " - Connection Timeout, skipping");
0215: }
0216: status = STATUS_CONNECTION_TIMEOUT;
0217: } catch (UnknownHostException ue) {
0218: if (log.isDebugEnabled()) {
0219: log.debug("Channel=" + name
0220: + " - Unknown Host Exception, skipping");
0221: }
0222: status = STATUS_UNKNOWN_HOST;
0223: } catch (NoRouteToHostException re) {
0224: if (log.isDebugEnabled()) {
0225: log
0226: .debug("Channel="
0227: + name
0228: + " - No Route To Host Exception, skipping");
0229: }
0230: status = STATUS_NO_ROUTE_TO_HOST;
0231: } catch (SocketException se) {
0232: if (log.isDebugEnabled()) {
0233: log.debug("Channel=" + name
0234: + " - Socket Exception, skipping");
0235: }
0236: status = STATUS_SOCKET_EXCEPTION;
0237: }
0238:
0239: // Only process if ok - if not ok (e.g. not modified), don't do anything
0240: if (connected
0241: && httpCon.getResponseCode() == HttpURLConnection.HTTP_OK) {
0242: String contentEncoding = httpCon.getContentEncoding();
0243: if (contentEncoding != null
0244: && contentEncoding.equals("gzip")) {
0245: is = new GZIPInputStream(is);
0246: }
0247:
0248: PushbackInputStream pbis = new PushbackInputStream(is,
0249: PUSHBACK_BUFFER_SIZE);
0250: skipBOM(pbis);
0251: BufferedInputStream bis = new BufferedInputStream(pbis);
0252: DocumentBuilder db = AppConstants.newDocumentBuilder();
0253:
0254: try {
0255: Document rssDoc = null;
0256: if (!parseAtAllCost) {
0257: rssDoc = db.parse(bis);
0258: } else {
0259: // Parse-at-all-costs selected
0260: // Read in document to local array - may need to parse twice
0261: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0262: byte[] buf = new byte[1024];
0263: int bytesRead = bis.read(buf);
0264: while (bytesRead > -1) {
0265: if (bytesRead > 0) {
0266: bos.write(buf, 0, bytesRead);
0267: }
0268: bytesRead = bis.read(buf);
0269: }
0270: bos.flush();
0271: bos.close();
0272:
0273: byte[] rssDocBytes = bos.toByteArray();
0274:
0275: try {
0276: // Try the XML document parser first - just in case
0277: // the doc is well-formed
0278: rssDoc = db.parse(new ByteArrayInputStream(
0279: rssDocBytes));
0280: } catch (SAXParseException spe) {
0281: if (log.isDebugEnabled()) {
0282: log
0283: .debug("XML parse failed, trying tidy");
0284: }
0285: // Fallback to parse-at-all-costs parser
0286: rssDoc = LooseParser
0287: .parse(new ByteArrayInputStream(
0288: rssDocBytes));
0289: }
0290: }
0291:
0292: Element rootElm = rssDoc.getDocumentElement();
0293:
0294: if (rootElm.getNodeName().equals("rss")) {
0295: rssVersion = rootElm.getAttribute("version");
0296: } else if (rootElm.getNodeName().equals("rdf:RDF")) {
0297: rssVersion = "RDF";
0298: }
0299:
0300: Element rssDocElm = (Element) rootElm
0301: .getElementsByTagName("channel").item(0);
0302:
0303: // Read header...
0304: title = XMLHelper.getChildElementValue(rssDocElm,
0305: "title");
0306: // XXX Currently assign channelTitle to author
0307: author = title;
0308:
0309: link = XMLHelper.getChildElementValue(rssDocElm,
0310: "link");
0311: description = XMLHelper.getChildElementValue(
0312: rssDocElm, "description");
0313: managingEditor = XMLHelper.getChildElementValue(
0314: rssDocElm, "managingEditor");
0315:
0316: // Check for items within channel element and outside
0317: // channel element
0318: NodeList itemList = rssDocElm
0319: .getElementsByTagName("item");
0320:
0321: if (itemList.getLength() == 0) {
0322: itemList = rootElm.getElementsByTagName("item");
0323: }
0324:
0325: Calendar retrievalDate = Calendar.getInstance();
0326: retrievalDate.add(Calendar.SECOND, -itemList
0327: .getLength());
0328:
0329: // Get current item signatures
0330: Set currentSignatures = channelManagerDAO
0331: .getItemSignatures(this .id);
0332:
0333: Set newSignatures = null;
0334: if (!keepHistory) {
0335: newSignatures = new HashSet();
0336: }
0337:
0338: // Calculate signature
0339: MessageDigest md = MessageDigest.getInstance("MD5");
0340:
0341: for (int itemCount = itemList.getLength() - 1; itemCount >= 0; itemCount--) {
0342: Element itemElm = (Element) itemList
0343: .item(itemCount);
0344: String title = XMLHelper.getChildElementValue(
0345: itemElm, "title", "");
0346: String link = XMLHelper.getChildElementValue(
0347: itemElm, "link", "");
0348: String comments = XMLHelper
0349: .getChildElementValue(itemElm,
0350: "comments", "");
0351:
0352: // Fix for content:encoded section of RSS 1.0/2.0
0353: String description = XMLHelper
0354: .getChildElementValue(itemElm,
0355: "content:encoded");
0356:
0357: if ((description == null)
0358: || (description.length() == 0)) {
0359: description = XMLHelper
0360: .getChildElementValue(itemElm,
0361: "description", "");
0362: }
0363:
0364: String signatureStr = null;
0365: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0366:
0367: // Used trimmed forms of content, ignore whitespace changes
0368: bos.write(title.trim().getBytes());
0369: bos.write(link.trim().getBytes());
0370: bos.write(description.trim().getBytes());
0371: bos.flush();
0372: bos.close();
0373:
0374: byte[] signatureSource = bos.toByteArray();
0375:
0376: md.reset();
0377: byte[] signature = md.digest(signatureSource);
0378:
0379: signatureStr = Base64.encodeBytes(signature);
0380:
0381: if (!keepHistory) {
0382: newSignatures.add(signatureStr);
0383: }
0384:
0385: if (!currentSignatures.contains(signatureStr)) {
0386: // New item, lets add...
0387: currentSignatures.add(signatureStr);
0388:
0389: Item item = new Item(++lastArticleNumber,
0390: signatureStr);
0391: item.setChannel(this );
0392:
0393: if (title.length() > 0) {
0394: item.setTitle(title);
0395: } else {
0396: // We need to create a initial title from the description, because
0397: // we do have a description, don't we???
0398: String strippedDesc = stripControlChars(XMLHelper
0399: .stripTags(description));
0400: int length = strippedDesc.length() > 64 ? 64
0401: : strippedDesc.length();
0402: item.setTitle(strippedDesc.substring(0,
0403: length));
0404: }
0405: item.setDescription(description);
0406: item.setLink(link);
0407: item.setComments(comments);
0408:
0409: // FIXME what to do about date?
0410: item.setDate(retrievalDate.getTime());
0411: // Add 1 second - to introduce some distinction date-wise
0412: // between items
0413: retrievalDate.add(Calendar.SECOND, 1);
0414:
0415: // persist to database...
0416: channelManagerDAO.saveItem(item);
0417: totalArticles++;
0418: }
0419: }
0420:
0421: if (!keepHistory) {
0422: currentSignatures.removeAll(newSignatures);
0423: // We're left with the old items that have to be purged...
0424: if (currentSignatures.size() > 0) {
0425: channelManagerDAO.deleteItemsBySignature(
0426: this , currentSignatures);
0427: totalArticles -= currentSignatures.size();
0428: }
0429: }
0430:
0431: // Update last modified / etag from headers
0432: lastETag = httpCon.getHeaderField("ETag");
0433: lastModified = httpCon.getHeaderFieldDate(
0434: "Last-Modified", 0);
0435:
0436: status = STATUS_OK;
0437: } catch (SAXParseException spe) {
0438: if (log.isEnabledFor(Priority.WARN)) {
0439: log
0440: .warn("Channel="
0441: + name
0442: + " - Error parsing RSS document - check feed");
0443: }
0444: status = STATUS_INVALID_CONTENT;
0445: }
0446:
0447: bis.close();
0448:
0449: // end if response code == HTTP_OK
0450: } else if (connected
0451: && httpCon.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
0452: if (log.isDebugEnabled()) {
0453: log.debug("Channel=" + name
0454: + " - HTTP_NOT_MODIFIED, skipping");
0455: }
0456: }
0457:
0458: // Update channel in database...
0459: channelManagerDAO.updateChannel(this );
0460:
0461: httpCon.disconnect();
0462:
0463: } catch (FileNotFoundException fnfe) {
0464: if (log.isEnabledFor(Priority.WARN)) {
0465: log
0466: .warn("Channel="
0467: + name
0468: + " - File not found returned by web server - check feed");
0469: }
0470: status = STATUS_NOT_FOUND;
0471: } catch (Exception e) {
0472: if (log.isEnabledFor(Priority.WARN)) {
0473: log.warn("Channel=" + name
0474: + " - Exception while polling channel", e);
0475: }
0476: } finally {
0477: connected = false;
0478: httpCon = null;
0479: polling = false;
0480: }
0481:
0482: }
0483:
0484: /**
0485: * Simple channel validation - ensures URL
0486: * is valid, XML document is returned, and
0487: * document has an rss root element with a
0488: * version, or rdf root element,
0489: */
0490: public static boolean isValid(URL url) {
0491: boolean valid = false;
0492: try {
0493: HttpURLConnection httpCon = (HttpURLConnection) url
0494: .openConnection();
0495: httpCon.setDoInput(true);
0496: httpCon.setDoOutput(false);
0497: httpCon.setRequestMethod("GET");
0498: httpCon.setRequestProperty("User-Agent", AppConstants
0499: .getUserAgent());
0500: httpCon.setRequestProperty("Accept-Encoding", "gzip");
0501: httpCon.connect();
0502: InputStream is = httpCon.getInputStream();
0503:
0504: // Only process if ok - if not ok (e.g. not modified), don't do anything
0505: if (httpCon.getResponseCode() == HttpURLConnection.HTTP_OK) {
0506: String contentEncoding = httpCon.getContentEncoding();
0507: if (contentEncoding != null
0508: && contentEncoding.equals("gzip")) {
0509: is = new GZIPInputStream(is);
0510: }
0511:
0512: PushbackInputStream pbis = new PushbackInputStream(is,
0513: PUSHBACK_BUFFER_SIZE);
0514: skipBOM(pbis);
0515:
0516: BufferedInputStream bis = new BufferedInputStream(pbis);
0517: DocumentBuilder db = AppConstants.newDocumentBuilder();
0518: Document rssDoc = db.parse(bis);
0519: Element rootElm = rssDoc.getDocumentElement();
0520: String rssVersion = rootElm.getAttribute("version");
0521: if ((rootElm.getNodeName().equals("rss") && rssVersion != null)
0522: || rootElm.getNodeName().equals("rdf:RDF")) {
0523: valid = true;
0524: }
0525: }
0526:
0527: httpCon.disconnect();
0528:
0529: } catch (Exception e) {
0530: // e.printStackTrace();
0531: }
0532: return valid;
0533: }
0534:
0535: private static void skipBOM(PushbackInputStream is)
0536: throws IOException {
0537: byte[] header = new byte[PUSHBACK_BUFFER_SIZE];
0538: int bytesRead = is.read(header);
0539: if (header[0] == 0 && header[1] == 0
0540: && (header[2] & 0xff) == 0xFE
0541: && (header[3] & 0xff) == 0xFF) {
0542: // UTF-32, big-endian
0543: } else if ((header[0] & 0xff) == 0xFF
0544: && (header[1] & 0xff) == 0xFE && header[2] == 0
0545: && header[3] == 0) {
0546: // UTF-32, little-endian
0547: } else if ((header[0] & 0xff) == 0xFE
0548: && (header[1] & 0xff) == 0xFF) {
0549: is.unread(header, 2, 2);
0550: // UTF-16, big-endian
0551: } else if ((header[0] & 0xff) == 0xFF
0552: && (header[1] & 0xff) == 0xFE) {
0553: is.unread(header, 2, 2);
0554: // UTF-16, little-endian
0555: } else if ((header[0] & 0xff) == 0xEF
0556: && (header[1] & 0xff) == 0xBB
0557: && (header[2] & 0xff) == 0xBF) {
0558: // UTF-8
0559: is.unread(header, 3, 1);
0560: } else {
0561: is.unread(header, 0, PUSHBACK_BUFFER_SIZE);
0562: }
0563: }
0564:
0565: public void save() {
0566: // Update channel in database...
0567: channelManagerDAO.updateChannel(this );
0568: }
0569:
0570: /**
0571: * Validates the channel
0572: */
0573: public boolean isValid() {
0574: return isValid(url);
0575: }
0576:
0577: /**
0578: * Returns the firstArticleNumber.
0579: * @return long
0580: */
0581: public int getFirstArticleNumber() {
0582: return firstArticleNumber;
0583: }
0584:
0585: /**
0586: * Returns the lastArticleNumber.
0587: * @return long
0588: */
0589: public int getLastArticleNumber() {
0590: return lastArticleNumber;
0591: }
0592:
0593: /**
0594: * Sets the firstArticleNumber.
0595: * @param firstArticleNumber The firstArticleNumber to set
0596: */
0597: public void setFirstArticleNumber(int firstArticleNumber) {
0598: this .firstArticleNumber = firstArticleNumber;
0599: }
0600:
0601: /**
0602: * Sets the lastArticleNumber.
0603: * @param lastArticleNumber The lastArticleNumber to set
0604: */
0605: public void setLastArticleNumber(int lastArticleNumber) {
0606: this .lastArticleNumber = lastArticleNumber;
0607: }
0608:
0609: /**
0610: * Returns the author.
0611: * @return String
0612: */
0613: public String getAuthor() {
0614: return author;
0615: }
0616:
0617: /**
0618: * Sets the author.
0619: * @param author The author to set
0620: */
0621: public void setAuthor(String author) {
0622: this .author = author;
0623: }
0624:
0625: /**
0626: * Returns the id.
0627: * @return int
0628: */
0629: public int getId() {
0630: return id;
0631: }
0632:
0633: /**
0634: * Sets the id.
0635: * @param id The id to set
0636: */
0637: public void setId(int id) {
0638: this .id = id;
0639: }
0640:
0641: /**
0642: * Returns the lastPolled.
0643: * @return Date
0644: */
0645: public Date getLastPolled() {
0646: return lastPolled;
0647: }
0648:
0649: /**
0650: * Sets the lastPolled.
0651: * @param lastPolled The lastPolled to set
0652: */
0653: public void setLastPolled(Date lastPolled) {
0654: this .lastPolled = lastPolled;
0655: }
0656:
0657: public boolean isAwaitingPoll() {
0658: // Need intelligent algorithm to handle this...
0659: // Currently just poll once an hour
0660: boolean awaitingPoll = false;
0661:
0662: if (lastPolled != null) {
0663: long currentTimeMillis = System.currentTimeMillis();
0664:
0665: long pollingInterval = this .pollingIntervalSeconds;
0666: if (pollingInterval == DEFAULT_POLLING_INTERVAL) {
0667: pollingInterval = channelManager
0668: .getPollingIntervalSeconds();
0669: }
0670:
0671: if ((currentTimeMillis - lastPolled.getTime()) > (pollingInterval * 1000)) {
0672: awaitingPoll = true;
0673: }
0674:
0675: } else {
0676: awaitingPoll = true;
0677: }
0678:
0679: return awaitingPoll;
0680: }
0681:
0682: /**
0683: * @see java.lang.Runnable#run()
0684: */
0685: public void run() {
0686: if (log.isInfoEnabled()) {
0687: log.info("Polling channel " + name);
0688: }
0689:
0690: poll();
0691:
0692: if (log.isInfoEnabled()) {
0693: log.info("Finished polling channel " + name);
0694: }
0695:
0696: }
0697:
0698: /**
0699: * Returns the lastETag.
0700: * @return String
0701: */
0702: public String getLastETag() {
0703: return lastETag;
0704: }
0705:
0706: /**
0707: * Returns the lastModified.
0708: * @return long
0709: */
0710: public long getLastModified() {
0711: return lastModified;
0712: }
0713:
0714: /**
0715: * Sets the lastETag.
0716: * @param lastETag The lastETag to set
0717: */
0718: public void setLastETag(String lastETag) {
0719: this .lastETag = lastETag;
0720: }
0721:
0722: /**
0723: * Sets the lastModified.
0724: * @param lastModified The lastModified to set
0725: */
0726: public void setLastModified(long lastModified) {
0727: this .lastModified = lastModified;
0728: }
0729:
0730: /**
0731: * Returns the created.
0732: * @return Date
0733: */
0734: public Date getCreated() {
0735: return created;
0736: }
0737:
0738: /**
0739: * Sets the created.
0740: * @param created The created to set
0741: */
0742: public void setCreated(Date created) {
0743: this .created = created;
0744: }
0745:
0746: /**
0747: * Returns the rssVersion.
0748: * @return String
0749: */
0750: public String getRssVersion() {
0751: return rssVersion;
0752: }
0753:
0754: /**
0755: * Sets the rssVersion.
0756: * @param rssVersion The rssVersion to set
0757: */
0758: public void setRssVersion(String rssVersion) {
0759: this .rssVersion = rssVersion;
0760: }
0761:
0762: /**
0763: * Sets the url.
0764: * @param url The url to set
0765: */
0766: public void setUrl(URL url) {
0767: if (!this .url.equals(url)) {
0768: this .url = url;
0769:
0770: // If we change the URL, then reset the
0771: // polling characteristics associated with the channel
0772: this .lastModified = 0;
0773: this .lastETag = null;
0774: this .lastPolled = null;
0775: }
0776:
0777: }
0778:
0779: /**
0780: * Returns the historical.
0781: * @return boolean
0782: */
0783: public boolean isHistorical() {
0784: return historical;
0785: }
0786:
0787: /**
0788: * Sets the historical.
0789: * @param historical The historical to set
0790: */
0791: public void setHistorical(boolean historical) {
0792: this .historical = historical;
0793: }
0794:
0795: /**
0796: * Returns the totalArticles.
0797: * @return int
0798: */
0799: public int getTotalArticles() {
0800: return totalArticles;
0801: }
0802:
0803: /**
0804: * Sets the totalArticles.
0805: * @param totalArticles The totalArticles to set
0806: */
0807: public void setTotalArticles(int totalArticles) {
0808: this .totalArticles = totalArticles;
0809: }
0810:
0811: /**
0812: * Returns the status.
0813: * @return int
0814: */
0815: public int getStatus() {
0816: return status;
0817: }
0818:
0819: /**
0820: * Sets the status.
0821: * @param status The status to set
0822: */
0823: public void setStatus(int status) {
0824: this .status = status;
0825: }
0826:
0827: /**
0828: * Returns the description.
0829: * @return String
0830: */
0831: public String getDescription() {
0832: return description;
0833: }
0834:
0835: /**
0836: * Returns the link.
0837: * @return String
0838: */
0839: public String getLink() {
0840: return link;
0841: }
0842:
0843: /**
0844: * Returns the title.
0845: * @return String
0846: */
0847: public String getTitle() {
0848: return title;
0849: }
0850:
0851: /**
0852: * Sets the description.
0853: * @param description The description to set
0854: */
0855: public void setDescription(String description) {
0856: this .description = description;
0857: }
0858:
0859: /**
0860: * Sets the link.
0861: * @param link The link to set
0862: */
0863: public void setLink(String link) {
0864: this .link = link;
0865: }
0866:
0867: /**
0868: * Sets the title.
0869: * @param title The title to set
0870: */
0871: public void setTitle(String title) {
0872: this .title = title;
0873: }
0874:
0875: /**
0876: * Returns the enabled.
0877: * @return boolean
0878: */
0879: public boolean isEnabled() {
0880: return enabled;
0881: }
0882:
0883: /**
0884: * Sets the enabled.
0885: * @param enabled The enabled to set
0886: */
0887: public void setEnabled(boolean enabled) {
0888: this .enabled = enabled;
0889: }
0890:
0891: /**
0892: * Returns the managingEditor.
0893: * @return String
0894: */
0895: public String getManagingEditor() {
0896: return managingEditor;
0897: }
0898:
0899: /**
0900: * Sets the managingEditor.
0901: * @param managingEditor The managingEditor to set
0902: */
0903: public void setManagingEditor(String managingEditor) {
0904: this .managingEditor = managingEditor;
0905: }
0906:
0907: /**
0908: * Returns the postingEnabled.
0909: * @return boolean
0910: */
0911: public boolean isPostingEnabled() {
0912: return postingEnabled;
0913: }
0914:
0915: /**
0916: * Sets the postingEnabled.
0917: * @param postingEnabled The postingEnabled to set
0918: */
0919: public void setPostingEnabled(boolean postingEnabled) {
0920: this .postingEnabled = postingEnabled;
0921: }
0922:
0923: /**
0924: * Returns the parseAtAllCost.
0925: * @return boolean
0926: */
0927: public boolean isParseAtAllCost() {
0928: return parseAtAllCost;
0929: }
0930:
0931: /**
0932: * Sets the parseAtAllCost.
0933: * @param parseAtAllCost The parseAtAllCost to set
0934: */
0935: public void setParseAtAllCost(boolean parseAtAllCost) {
0936: this .parseAtAllCost = parseAtAllCost;
0937: }
0938:
0939: /**
0940: * Returns the publishAPI.
0941: * @return String
0942: */
0943: public String getPublishAPI() {
0944: return publishAPI;
0945: }
0946:
0947: /**
0948: * Sets the publishAPI.
0949: * @param publishAPI The publishAPI to set
0950: */
0951: public void setPublishAPI(String publishAPI) {
0952: this .publishAPI = publishAPI;
0953: }
0954:
0955: /**
0956: * Returns the polling.
0957: * @return boolean
0958: */
0959: public boolean isPolling() {
0960: return polling;
0961: }
0962:
0963: /**
0964: * Checks any current http connection.
0965: *
0966: * Called from the Channel Poller's thread - will invoke
0967: * disconnect on the HttpUrlConnection is the poll has
0968: * exceeded 5 minutes. This'll cause an exception to
0969: * be thrown in the polling thread, within the poll method.
0970: *
0971: * It'd be preferable to use the timeout setting feature
0972: * within HttpUrlConnection, but this is only a parameter
0973: * in the 1.4 release of the JRE.
0974: */
0975:
0976: public void checkConnection() {
0977: if (polling
0978: && httpCon != null
0979: && connected
0980: && ((lastPolled == null) || ((System
0981: .currentTimeMillis() - lastPolled.getTime()) > HTTP_CONNECTION_TIMEOUT))) {
0982: try {
0983: if (log.isDebugEnabled()) {
0984: log
0985: .debug("Timeout exceeded, attempting to disconnect HttpUrlConnection");
0986: }
0987:
0988: httpCon.disconnect();
0989: connected = false;
0990: } catch (Exception e) {
0991: if (log.isDebugEnabled()) {
0992: log
0993: .debug("Error disconnecting HttpUrlConnection in checkConnection");
0994: }
0995: } finally {
0996: // httpCon = null;
0997: }
0998: }
0999: }
1000:
1001: /**
1002: * Returns the publishConfig.
1003: * @return Map
1004: */
1005: public Map getPublishConfig() {
1006: return publishConfig;
1007: }
1008:
1009: /**
1010: * Sets the publishConfig.
1011: * @param publishConfig The publishConfig to set
1012: */
1013: public void setPublishConfig(Map publishConfig) {
1014: this .publishConfig = publishConfig;
1015: }
1016:
1017: /**
1018: * Returns the pollingIntervalSeconds.
1019: * @return long
1020: */
1021: public long getPollingIntervalSeconds() {
1022: return pollingIntervalSeconds;
1023: }
1024:
1025: /**
1026: * Sets the pollingIntervalSeconds.
1027: * @param pollingIntervalSeconds The pollingIntervalSeconds to set
1028: */
1029: public void setPollingIntervalSeconds(long pollingIntervalSeconds) {
1030: this.pollingIntervalSeconds = pollingIntervalSeconds;
1031: }
1032:
1033: }
|