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 org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023: import org.apache.roller.config.PingConfig;
024: import org.apache.roller.pojos.PingTargetData;
025: import org.apache.roller.pojos.WebsiteData;
026: import org.apache.xmlrpc.XmlRpcClient;
027: import org.apache.xmlrpc.XmlRpcException;
028:
029: import java.io.IOException;
030: import java.net.MalformedURLException;
031: import java.net.UnknownHostException;
032: import java.util.*;
033:
034: /**
035: * Utility for sending a weblog update ping.
036: *
037: * This implements the <code>WeblogUpdates.ping<code> XML-RPC call described at
038: * <a href="http://www.xmlrpc.com/weblogsCom">www.xmlrpc.com</a>
039: * as well as some variants required to interoperate with certain
040: * buggy but popular ping targets.
041: *
042: *
043: * @author <a href="mailto:anil@busybuddha.org">Anil Gangolli</a>
044: * @author llavandowska (for code refactored from the now-defunct <code>RollerXmlRpcClient</code>)
045: */
046: public class WeblogUpdatePinger {
047: public static final Log logger = LogFactory
048: .getLog(WeblogUpdatePinger.class);
049:
050: /**
051: * Conveys a ping result.
052: */
053: public static class PingResult {
054: boolean error;
055: String message;
056:
057: public PingResult(Boolean error, String message) {
058: this .error = error != null ? error.booleanValue() : false;
059: this .message = message != null ? message : "";
060: }
061:
062: public boolean isError() {
063: return error;
064: }
065:
066: public void setError(boolean error) {
067: this .error = error;
068: }
069:
070: public String getMessage() {
071: return message;
072: }
073:
074: public void setMessage(String message) {
075: this .message = message;
076: }
077:
078: public String toString() {
079: return "PingResult{" + "error=" + error + ", message='"
080: + message + "'" + "}";
081: }
082: }
083:
084: // Inhibit construction
085: private WeblogUpdatePinger() {
086: }
087:
088: /**
089: * Send a weblog update ping.
090: *
091: * @param pingTarget the target site to ping
092: * @param website the website that changed (from which the ping originates)
093: * @return the result message string sent by the server.
094: * @throws IOException
095: * @throws XmlRpcException
096: */
097: public static PingResult sendPing(PingTargetData pingTarget,
098: WebsiteData website) throws IOException, XmlRpcException {
099: String websiteUrl = website.getAbsoluteURL();
100: String pingTargetUrl = pingTarget.getPingUrl();
101: Set variantOptions = PingConfig
102: .getVariantOptions(pingTargetUrl);
103:
104: // Set up the ping parameters.
105: Vector params = new Vector();
106: if (!variantOptions.contains("noname")) {
107: // ping variant for icerocket and anyone with similar bug, where we must omit the blog name.
108: params.addElement(website.getName());
109: }
110: params.addElement(websiteUrl);
111: if (logger.isDebugEnabled()) {
112: logger
113: .debug("Executing ping to '"
114: + pingTargetUrl
115: + "' for website '"
116: + websiteUrl
117: + "' ("
118: + website.getName()
119: + ")"
120: + (variantOptions.isEmpty() ? ""
121: : " with variant options "
122: + variantOptions));
123: }
124:
125: // Send the ping.
126: XmlRpcClient client = new XmlRpcClient(pingTargetUrl);
127: PingResult pingResult = parseResult(client.execute(
128: "weblogUpdates.ping", params));
129:
130: if (logger.isDebugEnabled())
131: logger.debug("Ping result is: " + pingResult);
132: return pingResult;
133: }
134:
135: private static PingResult parseResult(Object obj) {
136: // Deal with the fact that some buggy ping targets may not respond with the proper struct type.
137: if (obj == null)
138: return new PingResult(null, null);
139: try {
140: // normal case: response is a struct (represented as a Hashtable) with Boolean flerror and String fields.
141: Hashtable result = (Hashtable) obj;
142: return new PingResult((Boolean) result.get("flerror"),
143: (String) result.get("message"));
144: } catch (Exception ex) {
145: // exception case: The caller responded with an unexpected type, though parsed at the basic XML RPC level.
146: // This effectively assumes flerror = false, and sets message = obj.toString();
147: if (logger.isDebugEnabled())
148: logger.debug("Invalid ping result of type: "
149: + obj.getClass().getName()
150: + "; proceeding with stand-in representative.");
151: return new PingResult(null, obj.toString());
152: }
153: }
154:
155: /**
156: * Decide if the given exception appears to warrant later retrial attempts.
157: *
158: * @param ex an exception thrown by the <coce>sendPing</code> operation
159: * @return true if the error warrants retrial
160: */
161: public static boolean shouldRetry(Exception ex) {
162: // Determine if error appears transient (warranting retrial)
163: // We give most errors the "benefit of the doubt" by considering them transient
164: // This picks out a few that we consider non-transient
165: if (ex instanceof UnknownHostException) {
166: // User probably mistyped the url in the custom target.
167: return false;
168: } else if (ex instanceof MalformedURLException) {
169: // This should never happen due to validations but if we get here, retrial won't fix it.
170: return false;
171: }
172: return true;
173: }
174:
175: }
|