| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. The ASF licenses this file to You |
| * under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| |
| package org.apache.roller.weblogger.config; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.roller.weblogger.WebloggerException; |
| import org.apache.roller.weblogger.business.pings.PingTargetManager; |
| import org.apache.roller.weblogger.business.WebloggerFactory; |
| import org.apache.roller.weblogger.pojos.PingTarget; |
| |
| import java.util.*; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| // This may need to move to a different package, but it seems appropriate here in the current structure. |
| // Previous placement in the presentation.pings package introduced the undesirable dependency of the |
| // business package on the presentation package. |
| |
| /** |
| * Thin wrapper around WebloggerConfig and WebloggerRuntimeConfig for centralizing access to the many configurable |
| * settings for pings. |
| * |
| * @author <a href="mailto:anil@busybuddha.org">Anil Gangolli</a> |
| */ |
| public final class PingConfig { |
| private static final Log LOGGER = LogFactory.getLog(PingConfig.class); |
| |
| |
| // Config property for maximum ping attempts. |
| static final String MAX_PING_ATTEMPTS_PROP = "pings.maxPingAttempts"; |
| private static final int MAX_PING_ATTEMPTS_DEFAULT = 3; |
| private static final int MAX_PING_ATTEMPTS_MIN = 1; |
| private static final int MAX_PING_ATTEMPTS_MAX = 10; |
| |
| // Config property for queue processing interval |
| private static final String QUEUE_PROCESSING_INTERVAL_PROP = "pings.queueProcessingIntervalMins"; |
| private static final int QUEUE_PROCESSING_INTERVAL_DEFAULT = 5; |
| private static final int QUEUE_PROCESSING_INTERVAL_MIN = 0; |
| private static final int QUEUE_PROCESSING_INTERVAL_MAX = 120; |
| |
| // PingConfig property for logging pings (not actually performing them). Used for debugging. |
| private static final String PINGS_LOG_ONLY_PROP = "pings.logOnly"; |
| private static final boolean PINGS_LOG_ONLY_DEFAULT = false; |
| |
| // PingConfig property for controlling whether or not to allow usage of pings |
| // ("Weblog:Pings" page and actions). If absent, this defaults to false |
| // NOTE: If this property name is changed, editor-menu.xml must also be adjusted. |
| private static final String PINGS_DISABLE_PING_USAGE_PROP = "pings.disablePingUsage"; |
| private static final boolean PINGS_DISABLE_PING_USAGE_DEFAULT = false; |
| |
| // PingConfig property for controlling suspending the processing of pings. If true, |
| // new auto ping requests are not queued, any existing queued requests are not processed, |
| // and sending a manual ping results in a message saying pings have been disabled. |
| // NOTE: This is a "runtime" property settable on the Admin:PingConfig page, default is false. |
| private static final String PINGS_SUSPEND_PING_PROCESSING_PROP = "pings.suspendPingProcessing"; |
| |
| // PingConfig property determining the initial common ping targets. If the list of common |
| // ping targets is empty on startup, the value of this property is used to populate initial values. |
| // The value takes the form of comma-separated ping targets where each ping target is specified in |
| // the form {{name}{pingurl}}. If an administrator wants to disable this initialization, in order to |
| // maintain an empty list of common targets, the administrator can disable the initialization by |
| // commenting out this property in the config file. |
| private static final String PINGS_INITIAL_COMMON_TARGETS_PROP = "pings.initialCommonTargets"; |
| |
| // PingConfig property determining the known WeblogUpdates.ping variants/bugs |
| // in popular ping targets, which we are used when invoking pings on those targets. |
| // The value takes the form of a comma separated list of ping target urls and |
| // variant options, where each one is in the form {{pingurl}{option[[,option]...]}}. |
| private static final String PINGS_VARIANT_OPTIONS_PROP = "pings.variantOptions"; |
| |
| // Map of configured ping variants. Maps a ping target hostname to a set of |
| // Strings representing variant options to be used when pinging this target. |
| // This was introduced in order to support certain buggy (but popular) ping |
| // targets that implement minor variants of the WeblogUpdates.ping call. |
| // This is initialized once at startup, and referenced when pings are made. |
| private static final Map<String, Set<String>> CONFIGURED_VARIANTS = new HashMap<>(); |
| |
| // Pattern used to parse common ping targets as well as ping variants. |
| // Each initial commmon ping target is specified in the format {{name}{url}} |
| // Ping variants are also specified in a nested brace format {{url}{options}} |
| private static final Pattern NESTED_BRACE_PAIR = Pattern.compile("\\{\\{(.*?)\\}\\{(.*?)\\}\\}"); |
| |
| |
| // Inhibit construction |
| private PingConfig() { |
| } |
| |
| /** |
| * Get the maximum number of ping attempts that should be made for each ping queue entry before we give up. If we |
| * get apparently transient failures while trying to perform the ping, the entry is requeued for processing on later |
| * passes through the queue until this number of attempts has been reached. |
| * |
| * @return the configured (or default) maximum number of ping attempts |
| */ |
| public static int getMaxPingAttempts() { |
| return getIntegerProperty(MAX_PING_ATTEMPTS_PROP, MAX_PING_ATTEMPTS_DEFAULT, |
| MAX_PING_ATTEMPTS_MIN, MAX_PING_ATTEMPTS_MAX); |
| } |
| |
| /** |
| * Get the ping queue processing interval in minutes. |
| * |
| * @return the configured (or default) queue processing interval in minutes. |
| */ |
| public static int getQueueProcessingIntervalMins() { |
| return getIntegerProperty(QUEUE_PROCESSING_INTERVAL_PROP, QUEUE_PROCESSING_INTERVAL_DEFAULT, |
| QUEUE_PROCESSING_INTERVAL_MIN, QUEUE_PROCESSING_INTERVAL_MAX); |
| } |
| |
| |
| /** |
| * Get the logs only setting. Get configuration value determining whether pings are to be logged only (not sent). |
| * This configuration setting is used for development and debugging. |
| * |
| * @return the configured (or default) value of the logs only setting. |
| */ |
| public static boolean getLogPingsOnly() { |
| return getBooleanProperty(PINGS_LOG_ONLY_PROP, PINGS_LOG_ONLY_DEFAULT); |
| } |
| |
| /** |
| * Determine whether the configuration disables ping usage (configuration of auto pings and sending of manual |
| * pings). If this is true, all auto ping configus are removed at startup, the Weblog:Pings UI and the associated |
| * actions are disabled. |
| * |
| * @return the configured (or default) value of the enable ping usage setting. |
| */ |
| public static boolean getDisablePingUsage() { |
| return getBooleanProperty(PINGS_DISABLE_PING_USAGE_PROP, PINGS_DISABLE_PING_USAGE_DEFAULT); |
| } |
| |
| /** |
| * Determine whether ping processing is suspended. If this is true, new auto ping requests are not |
| * queued, any existing queued requests are not processed, and sending a manual ping results in a message saying |
| * pings have been disabled. |
| * |
| * @return the configured (or default) value of the suspend ping processing setting. |
| */ |
| public static boolean getSuspendPingProcessing() { |
| return WebloggerRuntimeConfig.getBooleanProperty(PINGS_SUSPEND_PING_PROCESSING_PROP); |
| } |
| |
| /** |
| * Initialize the common ping targets from the configuration properties. If the current list of common ping targets |
| * is empty, and the <code>PINGS_INITIAL_COMMON_TARGETS_PROP</code> property is present in the configuration then, |
| * this method will use that value to initialize the common targets. This is called on each server startup. |
| * <p/> |
| * Note: this is expected to be called during initialization with transaction demarcation being handled by the |
| * caller. |
| * |
| * @see org.apache.roller.weblogger.ui.core.RollerContext#contextInitialized(javax.servlet.ServletContextEvent) |
| */ |
| public static void initializeCommonTargets() throws WebloggerException { |
| String configuredVal = WebloggerConfig.getProperty(PINGS_INITIAL_COMMON_TARGETS_PROP); |
| if (configuredVal == null || configuredVal.isBlank()) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("No (or empty) value of " + PINGS_INITIAL_COMMON_TARGETS_PROP + " present in the configuration. Skipping initialization of commmon targets."); |
| } |
| return; |
| } |
| PingTargetManager pingTargetMgr = WebloggerFactory.getWeblogger().getPingTargetManager(); |
| if (!pingTargetMgr.getCommonPingTargets().isEmpty()) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("Some common ping targets are present in the database already. Skipping initialization."); |
| } |
| return; |
| } |
| |
| String[] configuredTargets = configuredVal.trim().split(","); |
| for (int i = 0; i < configuredTargets.length; i++) { |
| // Trim space around the target spec |
| String thisTarget = configuredTargets[i].trim(); |
| // skip empty ones |
| if (thisTarget.length() == 0) { |
| continue; |
| } |
| // parse the ith target and store it |
| Matcher m = NESTED_BRACE_PAIR.matcher(thisTarget); |
| if (m.matches() && m.groupCount() == 2) { |
| String name = m.group(1).trim(); |
| String url = m.group(2).trim(); |
| LOGGER.info("Creating common ping target '" + name + "' from configuration properties."); |
| PingTarget pingTarget = new PingTarget(null, name, url, false); |
| pingTargetMgr.savePingTarget(pingTarget); |
| } else { |
| LOGGER.error("Unable to parse configured initial ping target '" + thisTarget + "'. Skipping this target. Check your setting of the property " + PINGS_INITIAL_COMMON_TARGETS_PROP); |
| } |
| } |
| } |
| |
| /** |
| * Initialize known ping variants from the configuration. |
| */ |
| public static void initializePingVariants() { |
| String configuredVal = WebloggerConfig.getProperty(PINGS_VARIANT_OPTIONS_PROP); |
| if (configuredVal == null || configuredVal.isBlank()) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("No (or empty) value of " + PINGS_VARIANT_OPTIONS_PROP + " present in the configuration. Skipping initialization of ping variants."); |
| } |
| return; |
| } |
| String[] variants = configuredVal.trim().split(","); |
| for (int i = 0; i < variants.length; i++) { |
| String thisVariant = variants[i].trim(); |
| if (thisVariant.length() == 0) { |
| continue; |
| } |
| Matcher m = NESTED_BRACE_PAIR.matcher(thisVariant); |
| if (m.matches() && m.groupCount() == 2) { |
| String url = m.group(1).trim(); |
| String optionsList = m.group(2).trim(); |
| Set<String> variantOptions = new HashSet<>(); |
| String[] options = optionsList.split(","); |
| for (int j = 0; j < options.length; j++) { |
| String option = options[j].trim().toLowerCase(); |
| if (option.length() > 0) { |
| variantOptions.add(option); |
| } |
| } |
| if (!variantOptions.isEmpty()) { |
| CONFIGURED_VARIANTS.put(url, variantOptions); |
| } else { |
| LOGGER.warn("Ping variant entry for url '" + url |
| + "' has an empty variant options list. Ignored."); |
| } |
| } else { |
| LOGGER.error("Unable to parse configured ping variant '" |
| + thisVariant + "'. Skipping this variant. Check your setting of the property " |
| + PINGS_VARIANT_OPTIONS_PROP); |
| } |
| } |
| } |
| |
| /** |
| * Get the set of variant options configured for the given ping target url. |
| * |
| * @param pingTargetUrl |
| * @return the set of variant options configured for the given ping target url, or |
| * the empty set if there are no variants configured. |
| */ |
| public static Set<String> getVariantOptions(String pingTargetUrl) { |
| Set<String> variantOptions = CONFIGURED_VARIANTS.get(pingTargetUrl); |
| if (variantOptions == null) { |
| variantOptions = Collections.emptySet(); |
| } |
| return variantOptions; |
| } |
| |
| |
| // TODO: Refactor functionality below to WebloggerConfig? |
| |
| /** |
| * Get the value of an integer configuration property. |
| * |
| * @param propName the property name |
| * @param defaultValue the default value if the property is not present |
| * @param min the minimum allowed value |
| * @param max the maximum allowed value |
| * @return the value as an integer; the default value if no configured value is present or if the configured value |
| * is out of the specified range. |
| */ |
| private static int getIntegerProperty(String propName, int defaultValue, int min, int max) { |
| String configuredVal = WebloggerConfig.getProperty(propName); |
| if (configuredVal == null) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("PingConfig property '" + propName |
| + "' is not present in the configuration. Using default value: " + defaultValue); |
| } |
| return defaultValue; |
| } |
| |
| int val; |
| try { |
| val = Integer.parseInt(configuredVal); |
| } catch (NumberFormatException ex) { |
| LOGGER.error("ERROR: PingConfig property '" + propName |
| + "' is not an integer value. Using default value: " + defaultValue); |
| return defaultValue; |
| } |
| |
| if (val < min || val > max) { |
| LOGGER.error("ERROR: PingConfig property '" + propName |
| + "' is outside the required range (" |
| + min + ", " + max + "). Using default value: " + defaultValue); |
| return defaultValue; |
| } |
| |
| return val; |
| } |
| |
| /** |
| * Get the value of a boolean property with specified default. |
| * |
| * @param propName the property name |
| * @param defaultValue the default value if the property is not present |
| * @return the configured value or the default if it the configured value is not present. |
| */ |
| private static boolean getBooleanProperty(String propName, boolean defaultValue) { |
| String configuredVal = WebloggerConfig.getProperty(propName); |
| if (configuredVal == null) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("PingConfig property '" + propName |
| + "' is not present in the configuration. Using default value: " + defaultValue); |
| } |
| return defaultValue; |
| } |
| return Boolean.valueOf(configuredVal); |
| } |
| |
| |
| } |