/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.configuration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import org.olat.core.gui.control.Event; import org.olat.core.logging.LogDelegator; import org.olat.core.logging.OLog; import org.olat.core.logging.StartupException; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.event.GenericEventListener; /** * Description:<br> * Default/abstract class with helper methods. <br> * The abstract olat module features reading and writing of configuration * properties to config and properties files. The idea is that the system can be * configured with default values either in the config xml file or by providing * reasonable default values directly in the module code. * <p> * The developer should provide a GUI for each value that can be configured at * runtime without the need of rebooting the entire system. But this is up to * the programmer who implements the setter methods for the config values. * <p> * The getter methods will load the configuration in the following order: * <ol> * <li> * <code>olatdata/system/configuration/fully.qualified.ClassName.properties</code> * </li> * <li>falling back to<code>olat.local.properties</code></li> * <li>falling back to<code>olat.properties</code></li> * <li>falling back to default value defined in method call</li> * </ol> * The class does also provide save methods. Setting a config parameter will * store it always in the user space config file * <code>olatdata/system/configuration/fully.qualified.ClassName.properties</code> * and not in the olat.properties, those are only the default values in case no * other configuration. * <p> * To work properly in a cluster environment, the module will fire a * ModuleConfiguratoinChangedEvent at the end of each save cycle. This event is * catched automatically and the abstract method initFromChangedProperties() is * called. * <p> * For more information on how the storing mechanism of the configuration works * please have a look at the PersistedProperties class. * <p> * If you want to use the properties mechanism in a spring loaded class, use the * PersistedProperties class directly to read/write your application * configuration. * <p> * Initial Date: 01.10.2007 <br> * * @author Felix Jost, http://www.goodsolutions.ch * @author Florian Gnägi, http://www.frentix.com */ public abstract class AbstractOLATModule extends LogDelegator implements GenericEventListener { protected PersistedProperties moduleConfigProperties; private Properties moduleDefaultConfig; public static final Map<Class<?>,AtomicInteger> starts = new HashMap<Class<?>,AtomicInteger>(); public AbstractOLATModule() { if(!starts.containsKey(this.getClass())) { starts.put(this.getClass(), new AtomicInteger(1)); } else { starts.get(this.getClass()).incrementAndGet(); } } public static void printStats() { OLog logger = Tracing.createLoggerFor(AbstractOLATModule.class); for(Map.Entry<Class<?>, AtomicInteger> entry:starts.entrySet()) { if(entry.getValue().get() > 1) { logger.info(entry.getValue().get() + " :: " + entry.getKey()); } } } /** * Initialize the module. Called by the spring framework at startup time */ public final void init(Properties moduleConfig) { // Default module configuration from xml file this.moduleDefaultConfig = moduleConfig; // Let the module set the default properties if (moduleConfigProperties == null) throw new StartupException("PersistedProperties is null. You have to set Persisted properties in spring config when you use abstractOlatModule!"); initDefaultProperties(); init(); } public abstract void init(); public abstract void setPersistedProperties(PersistedProperties persistedProperties); /** * Called during module initialization to read the default values from the * configuration and set them as config properties default. */ protected abstract void initDefaultProperties(); /** * Called whenever the properties configuraton changed (e.g. on this or on another * cluster node). The properties have been reloaded prior to when this method is executed. */ protected abstract void initFromChangedProperties(); /** * Return an int value for certain config-parameter. If the parameter does * not exist, return the defaultValue. * * @param parameterName * @param defaultValue * @param * @return The int value. */ protected int getIntConfigParameter(String parameterName, int defaultValue) { String stringValue = moduleDefaultConfig.getProperty(parameterName); if (StringHelper.containsNonWhitespace(stringValue)) { try { return Integer.parseInt(stringValue.trim()); } catch (Exception ex) { logWarn("Cannot parse to integer conf-parameter '" + parameterName + "', value=" + stringValue, null); } } logInfo("Take default value for integer conf-parameter '" + parameterName + "', value=" + defaultValue, null); return defaultValue; } /** * Return a string value for certain config-parameter. If the parameter does * not exist, return the defaultValue. * * @param parameterName * @param defaultValue * @param * @return The string value. Can be empty, but never null */ protected String getStringConfigParameter(String parameterName, String defaultValue, boolean allowEmptyString) { String stringValue = moduleDefaultConfig.getProperty(parameterName); if (stringValue != null) { if (allowEmptyString || StringHelper.containsNonWhitespace(stringValue)) { return stringValue.trim(); } } logInfo("Take default value for String conf-parameter '" + parameterName + "', value=" + defaultValue, null); return defaultValue; } /** * Return a boolean value for certain config-parameter. If the paramter does * not exist, return the defaultValue. 'true' and 'enabled' return <code>true</code>, 'false' and 'disabled' return <code>false</code>. * * @param parameterName * @param defaultValue * @return */ protected boolean getBooleanConfigParameter(String parameterName, boolean defaultValue) { String stringValue = moduleDefaultConfig.getProperty(parameterName); if ((stringValue != null) && (stringValue.trim().equalsIgnoreCase("TRUE") || stringValue.trim().equalsIgnoreCase("ENABLED"))) { return true; } if ((stringValue != null) && (stringValue.trim().equalsIgnoreCase("FALSE") || stringValue.trim().equalsIgnoreCase("DISABLED"))) { return false; } logInfo("Take default Boolean conf-parameter '" + parameterName + "', value=" + stringValue + ", only true/false supported => take default value.", null); return defaultValue; } // // Delegate methods used to get and set the values and default in the user // configuration using the persisted properties. // /** * Return a string value for certain propertyName-parameter. * * @param propertyName * @param allowEmptyString * true: empty strings are valid values; false: emtpy strings are * discarded * @return the value from the configuration or the default value or ""/NULL * (depending on allowEmptyString flag) */ protected String getStringPropertyValue(String propertyName, boolean allowEmptyString) { // delegate to new property based config style return moduleConfigProperties.getStringPropertyValue(propertyName, allowEmptyString); } /** * Set a string property * * @param propertyName * The key * @param value * The Value * @param saveConfiguration * true: will save property and fire event; false: will not save, * but set a dirty flag */ protected void setStringProperty(String propertyName, String value, boolean saveConfiguration) { // delegate to new property based config style moduleConfigProperties.setStringProperty(propertyName, value, saveConfiguration); logAudit("change system property: " + propertyName, value); } /** * Retrun an int value for a certain propertyName * * @param propertyName * @return the value from the configuration or the default value or 0 */ protected int getIntPropertyValue(String propertyName) { // delegate to new property based config style return moduleConfigProperties.getIntPropertyValue(propertyName); } /** * Set an int property * * @param propertyName * The key * @param value * The Value * @param saveConfiguration * true: will save property and fire event; false: will not save, * but set a dirty flag */ protected void setIntProperty(String propertyName, int value, boolean saveConfiguration) { // delegate to new property based config style moduleConfigProperties.setIntProperty(propertyName, value, saveConfiguration); logAudit("change system property: " + propertyName, Integer.toString(value)); } /** * Return a boolean value for certain propertyName * * @param propertyName * @return the value from the configuration or the default value or false */ protected boolean getBooleanPropertyValue(String propertyName) { // delegate to new property based config style return moduleConfigProperties.getBooleanPropertyValue(propertyName); } /** * Set a boolean property * * @param propertyName * The key * @param value * The Value * @param saveConfiguration * true: will save property and fire event; false: will not save, * but set a dirty flag */ protected void setBooleanProperty(String propertyName, boolean value, boolean saveConfiguration) { // delegate to new property based config style moduleConfigProperties.setBooleanProperty(propertyName, value, saveConfiguration); logAudit("change system property: " + propertyName, Boolean.toString(value)); } /** * Save the properties configuration to disk and notify other nodes about * change. This is only done when there are dirty changes, otherwhile the * method call does nothing. */ protected void savePropertiesAndFireChangedEvent() { // delegate to new property based config style moduleConfigProperties.savePropertiesAndFireChangedEvent(); } /** * Clear the properties and save the empty properties to the file system. */ protected void clearAndSaveProperties() { // delegate to new property based config style moduleConfigProperties.clearAndSaveProperties(); } /** * Set a default value for a string property * @param propertyName * @param value */ protected void setStringPropertyDefault(String key, String value){ // delegate to new property based config style moduleConfigProperties.setStringPropertyDefault(key, value); } /** * Set a default value for a boolean property * @param propertyName * @param value */ protected void setBooleanPropertyDefault(String key, boolean value){ // delegate to new property based config style moduleConfigProperties.setBooleanPropertyDefault(key, value); } /** * Set a default value for an integer property * @param propertyName * @param value */ protected void setIntPropertyDefault(String key, int value){ // delegate to new property based config style moduleConfigProperties.setIntPropertyDefault(key, value); } /** * @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event) */ public void event(Event event) { if (event instanceof PersistedPropertiesChangedEvent) { PersistedPropertiesChangedEvent persistedPropertiesEvent = (PersistedPropertiesChangedEvent) event; if (!persistedPropertiesEvent.isEventOnThisNode()) { // Reload the module configuration from disk, only when event not fired by this node moduleConfigProperties.loadPropertiesFromFile(); } // Call abstract method to initialize after the property changed, even when changes // were triggered by this node. initFromChangedProperties(); } } }