001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.business.pings;
020:
021: import java.util.Iterator;
022: import java.util.List;
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.apache.roller.RollerException;
026: import org.apache.roller.business.RollerFactory;
027: import org.apache.roller.config.PingConfig;
028: import org.apache.roller.config.RollerRuntimeConfig;
029: import org.apache.roller.pojos.PingQueueEntryData;
030: import org.apache.roller.pojos.PingTargetData;
031: import org.apache.roller.pojos.WebsiteData;
032:
033: /**
034: * Ping Queue Processor. Singleton encapsulating logic for processing the weblog update ping queue.
035: *
036: * @author <a href="mailto:anil@busybuddha.org">Anil Gangolli</a>
037: */
038: public class PingQueueProcessor {
039:
040: private static final Log logger = LogFactory
041: .getLog(PingQueueProcessor.class);
042:
043: private static PingQueueProcessor theInstance;
044:
045: private PingQueueManager pingQueueMgr;
046:
047: public static PingQueueProcessor getInstance() {
048: return theInstance;
049: }
050:
051: private PingQueueProcessor() throws RollerException {
052: pingQueueMgr = RollerFactory.getRoller().getPingQueueManager();
053: }
054:
055: /**
056: * Initialize the singleton. This is called during <code>RollerContext</code> initialization.
057: *
058: * @throws RollerException
059: */
060: public static synchronized void init() throws RollerException {
061: if (theInstance != null) {
062: logger
063: .warn("Ignoring duplicate initialization of PingQueueProcessor!");
064: return;
065: }
066: theInstance = new PingQueueProcessor();
067: if (logger.isDebugEnabled())
068: logger.debug("Ping queue processor initialized.");
069: }
070:
071: /**
072: * Process the ping queue. Performs one pass through the ping queue, processing every entry once. On ping failure
073: * an entry is requeued for processing on subsequent passes until the configured maximum number of attempts is
074: * reached.
075: */
076: public synchronized void processQueue() {
077: if (PingConfig.getSuspendPingProcessing()) {
078: logger
079: .info("Ping processing has been suspended. Skipping current round of ping queue processing.");
080: return;
081: }
082:
083: String absoluteContextUrl = RollerRuntimeConfig
084: .getAbsoluteContextURL();
085: if (absoluteContextUrl == null) {
086: logger
087: .warn("WARNING: Skipping current ping queue processing round because we cannot yet determine the site's absolute context url.");
088: return;
089: }
090:
091: // TODO: Group by ping target and ping all sites for that target?
092: // We're currently not taking advantage of grouping by ping target site and then sending
093: // all of the pings for that target at once. If it becomes an efficiency issue, we should do
094: // that.
095:
096: try {
097: if (logger.isDebugEnabled())
098: logger.debug("Started processing ping queue.");
099: // Get all of the entries
100: List entries = pingQueueMgr.getAllQueueEntries();
101:
102: // Process each entry
103: for (Iterator i = entries.iterator(); i.hasNext();) {
104: PingQueueEntryData pingQueueEntry = (PingQueueEntryData) i
105: .next();
106: processQueueEntry(pingQueueEntry);
107: }
108: if (logger.isDebugEnabled())
109: logger.debug("Finished processing ping queue.");
110: } catch (Exception ex) {
111: logger
112: .error(
113: "Unexpected exception processing ping queue! Aborting this pass of ping queue processing.",
114: ex);
115: }
116: }
117:
118: /**
119: * Process an individual ping queue entry.
120: *
121: * @param pingQueueEntry the ping queue entry
122: * @throws RollerException only if there are problems processing the queue. Exceptions from sending pings are
123: * handled, not thrown.
124: */
125: private void processQueueEntry(PingQueueEntryData pingQueueEntry)
126: throws RollerException {
127: if (logger.isDebugEnabled())
128: logger.debug("Processing ping queue entry: "
129: + pingQueueEntry);
130:
131: PingTargetData pingTarget = pingQueueEntry.getPingTarget();
132: WebsiteData website = pingQueueEntry.getWebsite();
133: boolean pingSucceeded = false;
134: if (PingConfig.getLogPingsOnly()) {
135: // Just log the ping and pretend it succeeded.
136: logger.info("Logging simulated ping for ping queue entry "
137: + pingQueueEntry);
138: pingSucceeded = true;
139: } else {
140: // Actually process the ping
141: try {
142: // Send the ping
143: WeblogUpdatePinger.sendPing(pingTarget, website);
144: // Consider successful ping transmission if we didn't get an exception. We don't care here
145: // about the result of the ping if it was transmitted.
146: pingSucceeded = true;
147: } catch (Exception ex) {
148: // Handle the ping error, either removing or requeuing the ping queue entry.
149: handlePingError(pingQueueEntry, ex);
150: }
151: }
152: // We do this outside of the previous try-catch because we don't want an exception here to be considered a ping error.
153: if (pingSucceeded) {
154: if (logger.isDebugEnabled())
155: logger.debug("Processed ping: " + pingQueueEntry);
156: pingQueueMgr.removeQueueEntry(pingQueueEntry);
157: }
158: }
159:
160: /**
161: * Handle any ping error.
162: *
163: * @param pingQueueEntry the ping queue entry
164: * @param ex the exception that occurred on the ping attempt
165: * @throws RollerException
166: */
167: private void handlePingError(PingQueueEntryData pingQueueEntry,
168: Exception ex) throws RollerException {
169:
170: if ((pingQueueEntry.incrementAttempts() < PingConfig
171: .getMaxPingAttempts())
172: && WeblogUpdatePinger.shouldRetry(ex)) {
173: // We have attempts remaining, and it looks like we should retry,
174: // so requeue the entry for processing on subsequent rounds
175: logger.debug("Error on ping attempt ("
176: + pingQueueEntry.getAttempts() + ") for "
177: + pingQueueEntry + ": [" + ex.getMessage()
178: + "]. Will re-queue for later attempts.");
179: logger.debug("Error on last ping attempt was: ", ex);
180: pingQueueMgr.saveQueueEntry(pingQueueEntry);
181: } else {
182: // Remove the entry
183: logger.warn("Error on ping attempt ("
184: + pingQueueEntry.getAttempts() + ") for "
185: + pingQueueEntry + ": [" + ex.getMessage()
186: + "]. Entry will be REMOVED from ping queue.");
187: logger.debug("Error on last ping attempt was: ", ex);
188: pingQueueMgr.removeQueueEntry(pingQueueEntry);
189: // TODO: mark ping target invalid?
190: }
191: }
192:
193: }
|