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.config;
020:
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023: import org.apache.roller.RollerException;
024: import org.apache.roller.business.pings.PingTargetManager;
025: import org.apache.roller.business.RollerFactory;
026: import org.apache.roller.pojos.PingTargetData;
027:
028: import java.util.*;
029: import java.util.regex.Matcher;
030: import java.util.regex.Pattern;
031:
032: // This may need to move to a different package, but it seems appropriate here in the current structure.
033: // Previous placement in the presentation.pings package introduced the undesirable dependency of the
034: // business package on the presentation package.
035:
036: /**
037: * Thin wrapper around RollerConfig and RollerRuntimeConfig for centralizing access to the many configurable
038: * settings for pings.
039: *
040: * @author <a href="mailto:anil@busybuddha.org">Anil Gangolli</a>
041: */
042: public class PingConfig {
043: private static final Log logger = LogFactory
044: .getLog(PingConfig.class);
045:
046: // Config property for maximim ping attempts.
047: static final String MAX_PING_ATTEMPTS_PROP = "pings.maxPingAttempts";
048: private static final int MAX_PING_ATTEMPTS_DEFAULT = 3;
049: private static final int MAX_PING_ATTEMPTS_MIN = 1;
050: private static final int MAX_PING_ATTEMPTS_MAX = 10;
051:
052: // Config property for queue processing interval
053: private static final String QUEUE_PROCESSING_INTERVAL_PROP = "pings.queueProcessingIntervalMins";
054: private static final int QUEUE_PROCESSING_INTERVAL_DEFAULT = 5;
055: private static final int QUEUE_PROCESSING_INTERVAL_MIN = 0;
056: private static final int QUEUE_PROCESSING_INTERVAL_MAX = 120;
057:
058: // PingConfig property for logging pings (not actually performing them). Used for debugging.
059: private static final String PINGS_LOG_ONLY_PROP = "pings.logOnly";
060: private static final boolean PINGS_LOG_ONLY_DEFAULT = false;
061:
062: // PingConfig property for controlling whether or not to allow custom ping targets
063: // ("Weblog:Custom Ping Targets" page and actions). If absent, this defaults to false.
064: // with the enabledProperty behavior in editor-menu.xml.
065: // NOTE: If this property name is changed, editor-menu.xml must also be adjusted.
066: private static final String PINGS_DISALLOW_CUSTOM_TARGETS_PROP = "pings.disallowCustomTargets";
067: private static final boolean PINGS_DISALLOW_CUSTOM_TARGETS_DEFAULT = false;
068:
069: // PingConfig property for controlling whether or not to allow usage of pings
070: // ("Weblog:Pings" page and actions). If absent, this defaults to false
071: // NOTE: If this property name is changed, editor-menu.xml must also be adjusted.
072: private static final String PINGS_DISABLE_PING_USAGE_PROP = "pings.disablePingUsage";
073: private static final boolean PINGS_DISABLE_PING_USAGE_DEFAULT = false;
074:
075: // PingConfig property for controlling suspending the processing of pings. If true,
076: // new auto ping requests are not queued, any existing queued requests are not processed,
077: // and sending a manual ping results in a message saying pings have been disabled.
078: // NOTE: This is a "runtime" property settable on the Admin:PingConfig page, default is false.
079: private static final String PINGS_SUSPEND_PING_PROCESSING_PROP = "pings.suspendPingProcessing";
080:
081: // PingConfig property determining the initial common ping targets. If the list of common
082: // ping targets is empty on startup, the value of this property is used to populate initial values.
083: // The value takes the form of comma-separated ping targets where each ping target is specified in
084: // the form {{name}{pingurl}}. If an administrator wants to disable this initialization, in order to
085: // maintain an empty list of common targets, the administrator can disable the initialization by
086: // commenting out this property in the config file.
087: private static final String PINGS_INITIAL_COMMON_TARGETS_PROP = "pings.initialCommonTargets";
088:
089: // PingConfig property determining the known WeblogUpdates.ping variants/bugs
090: // in popular ping targets, which we are used when invoking pings on those targets.
091: // The value takes the form of a comma separated list of ping target urls and
092: // variant options, where each one is in the form {{pingurl}{option[[,option]...]}}.
093: private static final String PINGS_VARIANT_OPTIONS_PROP = "pings.variantOptions";
094: // Map of configured ping variants. Maps a ping target hostname to a set of
095: // Strings representing variant options to be used when pinging this target.
096: // This was introduced in order to support certain buggy (but popular) ping
097: // targets that implement minor variants of the WeblogUpdates.ping call.
098: // This is initialized once at startup, and referenced when pings are made.
099: private static final Map configuredVariants = new HashMap();
100:
101: // Inhibit construction
102: private PingConfig() {
103: }
104:
105: /**
106: * Get the maximum number of ping attempts that should be made for each ping queue entry before we give up. If we
107: * get apparently transient failures while trying to perform the ping, the entry is requeued for processing on later
108: * passes through the queue until this number of attempts has been reached.
109: *
110: * @return the configured (or default) maximum number of ping attempts
111: */
112: public static int getMaxPingAttempts() {
113: return getIntegerProperty(MAX_PING_ATTEMPTS_PROP,
114: MAX_PING_ATTEMPTS_DEFAULT, MAX_PING_ATTEMPTS_MIN,
115: MAX_PING_ATTEMPTS_MAX);
116: }
117:
118: /**
119: * Get the ping queue processing interval in minutes.
120: *
121: * @return the configured (or default) queue processing interval in minutes.
122: */
123: public static int getQueueProcessingIntervalMins() {
124: return getIntegerProperty(QUEUE_PROCESSING_INTERVAL_PROP,
125: QUEUE_PROCESSING_INTERVAL_DEFAULT,
126: QUEUE_PROCESSING_INTERVAL_MIN,
127: QUEUE_PROCESSING_INTERVAL_MAX);
128: }
129:
130: /**
131: * Get the logs only setting. Get configuration value determining whether pings are to be logged only (not sent).
132: * This configuration setting is used for development and debugging.
133: *
134: * @return the configured (or default) value of the logs only setting.
135: */
136: public static boolean getLogPingsOnly() {
137: return getBooleanProperty(PINGS_LOG_ONLY_PROP,
138: PINGS_LOG_ONLY_DEFAULT);
139: }
140:
141: /**
142: * Determine whether the configuration disallows custom ping targets. If this is true, users are not allowed to
143: * create or edit custom ping targets, and any auto ping configs that use them are ignored.
144: *
145: * @return the configured (or default) value of the "disallow custom targets" setting.
146: */
147: public static boolean getDisallowCustomTargets() {
148: return getBooleanProperty(PINGS_DISALLOW_CUSTOM_TARGETS_PROP,
149: PINGS_DISALLOW_CUSTOM_TARGETS_DEFAULT);
150: }
151:
152: /**
153: * Determine whether the configuration disables ping usage (configuration of auto pings and sending of manual
154: * pings). If this is true, all auto ping configus are removed at startup, the Weblog:Pings UI and the associated
155: * actions are disabled.
156: *
157: * @return the configured (or default) value of the enable ping usage setting.
158: */
159: public static boolean getDisablePingUsage() {
160: return getBooleanProperty(PINGS_DISABLE_PING_USAGE_PROP,
161: PINGS_DISABLE_PING_USAGE_DEFAULT);
162: }
163:
164: /**
165: * Determine whether ping processing is suspended. If this is true, new auto ping requests are not
166: * queued, any existing queued requests are not processed, and sending a manual ping results in a message saying
167: * pings have been disabled.
168: *
169: * @return the configured (or default) value of the suspend ping processing setting.
170: */
171: public static boolean getSuspendPingProcessing() {
172: return RollerRuntimeConfig
173: .getBooleanProperty(PINGS_SUSPEND_PING_PROCESSING_PROP);
174: }
175:
176: // Pattern used to parse common ping targets as well as ping variants.
177: // Each initial commmon ping target is specified in the format {{name}{url}}
178: // Ping variants are also specified in a nested brace format {{url}{options}}
179: private static final Pattern NESTED_BRACE_PAIR = Pattern
180: .compile("\\{\\{(.*?)\\}\\{(.*?)\\}\\}");
181:
182: /**
183: * Initialize the common ping targets from the configuration properties. If the current list of common ping targets
184: * is empty, and the <code>PINGS_INITIAL_COMMON_TARGETS_PROP</code> property is present in the configuration then,
185: * this method will use that value to initialize the common targets. This is called on each server startup.
186: * <p/>
187: * Note: this is expected to be called during initialization with transaction demarcation being handled by the
188: * caller.
189: *
190: * @see org.apache.roller.ui.core.RollerContext#contextInitialized(javax.servlet.ServletContextEvent)
191: */
192: public static void initializeCommonTargets() throws RollerException {
193: String configuredVal = RollerConfig
194: .getProperty(PINGS_INITIAL_COMMON_TARGETS_PROP);
195: if (configuredVal == null || configuredVal.trim().length() == 0) {
196: if (logger.isDebugEnabled()) {
197: logger
198: .debug("No (or empty) value of "
199: + PINGS_INITIAL_COMMON_TARGETS_PROP
200: + " present in the configuration. Skipping initialization of commmon targets.");
201: }
202: return;
203: }
204: PingTargetManager pingTargetMgr = RollerFactory.getRoller()
205: .getPingTargetManager();
206: if (!pingTargetMgr.getCommonPingTargets().isEmpty()) {
207: if (logger.isDebugEnabled()) {
208: logger
209: .debug("Some common ping targets are present in the database already. Skipping initialization.");
210: }
211: return;
212: }
213:
214: String[] configuredTargets = configuredVal.trim().split(",");
215: for (int i = 0; i < configuredTargets.length; i++) {
216: // Trim space around the target spec
217: String this Target = configuredTargets[i].trim();
218: // skip empty ones
219: if (this Target.length() == 0)
220: continue;
221: // parse the ith target and store it
222: Matcher m = NESTED_BRACE_PAIR.matcher(this Target);
223: if (m.matches() && m.groupCount() == 2) {
224: String name = m.group(1).trim();
225: String url = m.group(2).trim();
226: logger.info("Creating common ping target '" + name
227: + "' from configuration properties.");
228: PingTargetData pingTarget = new PingTargetData(null,
229: name, url, null, false);
230: pingTargetMgr.savePingTarget(pingTarget);
231: } else {
232: logger
233: .error("Unable to parse configured initial ping target '"
234: + this Target
235: + "'. Skipping this target. Check your setting of the property "
236: + PINGS_INITIAL_COMMON_TARGETS_PROP);
237: }
238: }
239: }
240:
241: /**
242: * Initialize known ping variants from the configuration.
243: */
244: public static void initializePingVariants() {
245: String configuredVal = RollerConfig
246: .getProperty(PINGS_VARIANT_OPTIONS_PROP);
247: if (configuredVal == null || configuredVal.trim().length() == 0) {
248: if (logger.isDebugEnabled()) {
249: logger
250: .debug("No (or empty) value of "
251: + PINGS_VARIANT_OPTIONS_PROP
252: + " present in the configuration. Skipping initialization of ping variants.");
253: }
254: return;
255: }
256: String[] variants = configuredVal.trim().split(",");
257: for (int i = 0; i < variants.length; i++) {
258: String this Variant = variants[i].trim();
259: if (this Variant.length() == 0)
260: continue;
261: Matcher m = NESTED_BRACE_PAIR.matcher(this Variant);
262: if (m.matches() && m.groupCount() == 2) {
263: String url = m.group(1).trim();
264: String optionsList = m.group(2).trim();
265: Set variantOptions = new HashSet();
266: String[] options = optionsList.split(",");
267: for (int j = 0; j < options.length; j++) {
268: String option = options[j].trim().toLowerCase();
269: if (option.length() > 0) {
270: variantOptions.add(option);
271: }
272: }
273: if (!variantOptions.isEmpty()) {
274: configuredVariants.put(url, variantOptions);
275: } else {
276: logger
277: .warn("Ping variant entry for url '"
278: + url
279: + "' has an empty variant options list. Ignored.");
280: }
281: } else {
282: logger
283: .error("Unable to parse configured ping variant '"
284: + this Variant
285: + "'. Skipping this variant. Check your setting of the property "
286: + PINGS_VARIANT_OPTIONS_PROP);
287: }
288: }
289: }
290:
291: /**
292: * Get the set of variant options configured for the given ping target url.
293: *
294: * @param pingTargetUrl
295: * @return the set of variant options configured for the given ping target url, or
296: * the empty set if there are no variants configured.
297: */
298: public static Set getVariantOptions(String pingTargetUrl) {
299: Set variantOptions = (Set) configuredVariants
300: .get(pingTargetUrl);
301: if (variantOptions == null) {
302: variantOptions = Collections.EMPTY_SET;
303: }
304: return variantOptions;
305: }
306:
307: // TODO: Refactor functionality below to RollerConfig?
308:
309: /**
310: * Get the value of an integer configuration property.
311: *
312: * @param propName the property name
313: * @param defaultValue the default value if the property is not present
314: * @param min the minimum allowed value
315: * @param max the maximum allowed value
316: * @return the value as an integer; the default value if no configured value is present or if the configured value
317: * is out of the specified range.
318: */
319: private static int getIntegerProperty(String propName,
320: int defaultValue, int min, int max) {
321: String configuredVal = RollerConfig.getProperty(propName);
322: if (configuredVal == null) {
323: if (logger.isDebugEnabled()) {
324: logger
325: .debug("PingConfig property '"
326: + propName
327: + "' is not present in the configuration. Using default value: "
328: + defaultValue);
329: }
330: return defaultValue;
331: }
332:
333: int val;
334: try {
335: val = Integer.parseInt(configuredVal);
336: } catch (NumberFormatException ex) {
337: logger
338: .error("ERROR: PingConfig property '"
339: + propName
340: + "' is not an integer value. Using default value: "
341: + defaultValue);
342: return defaultValue;
343: }
344:
345: if (val < min || val > max) {
346: logger.error("ERROR: PingConfig property '" + propName
347: + "' is outside the required range (" + min + ", "
348: + max + "). Using default value: " + defaultValue);
349: return defaultValue;
350: }
351:
352: return val;
353: }
354:
355: /**
356: * Get the value of a boolean property with specified default.
357: *
358: * @param propName the property name
359: * @param defaultValue the default value if the property is not present
360: * @return the configured value or the default if it the configured value is not present.
361: */
362: private static boolean getBooleanProperty(String propName,
363: boolean defaultValue) {
364: String configuredVal = RollerConfig.getProperty(propName);
365: if (configuredVal == null) {
366: if (logger.isDebugEnabled()) {
367: logger
368: .debug("PingConfig property '"
369: + propName
370: + "' is not present in the configuration. Using default value: "
371: + defaultValue);
372: }
373: return defaultValue;
374: }
375: return Boolean.valueOf(configuredVal).booleanValue();
376: }
377:
378: }
|