/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <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 the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <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> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.core.configuration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.olat.core.gui.control.Event; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; /** * * This replace the AbstractOLATModule with an annotation * based module which is better loaded by Spring in cae of heavy * cycle in the dependency tree of the spring beans.<br> * To get a property from olat.properties or olat.local.properties, * use the @Value annotation of Spring Framework and please * set a default value:<br> * @Value("${my.prop:defaultValue}")<br> * * * * Initial date: 08.08.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public abstract class AbstractSpringModule implements GenericEventListener, InitializingBean, DisposableBean { private static final OLog log = Tracing.createLoggerFor(AbstractSpringModule.class); @Value("${userdata.dir}") private String userDataDirectory; private final PersistedProperties moduleConfigProperties; public static final Map<Class<?>,AtomicInteger> starts = new HashMap<Class<?>,AtomicInteger>(); public AbstractSpringModule(CoordinatorManager coordinatorManager) { moduleConfigProperties = new PersistedProperties(coordinatorManager, this); if(!starts.containsKey(this.getClass())) { starts.put(this.getClass(), new AtomicInteger(1)); } else { starts.get(this.getClass()).incrementAndGet(); } } public AbstractSpringModule(CoordinatorManager coordinatorManager, boolean secured) { moduleConfigProperties = new PersistedProperties(coordinatorManager, this, secured); if(!starts.containsKey(this.getClass())) { starts.put(this.getClass(), new AtomicInteger(1)); } else { starts.get(this.getClass()).incrementAndGet(); } } public AbstractSpringModule(CoordinatorManager coordinatorManager, String path, boolean secured) { moduleConfigProperties = new PersistedProperties(coordinatorManager, this, path, secured); 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(AbstractSpringModule.class); for(Map.Entry<Class<?>, AtomicInteger> entry:starts.entrySet()) { if(entry.getValue().get() > 1) { logger.info(entry.getValue().get() + " :: " + entry.getKey()); } } } @Override public void afterPropertiesSet() { moduleConfigProperties.setUserDataDirectory(userDataDirectory); moduleConfigProperties.init(); initDefaultProperties(); init(); } @Override public void destroy() { moduleConfigProperties.destroy(); } public abstract void init(); /** * Called during module initialization to read the default values from the * configuration and set them as config properties default. */ protected void initDefaultProperties() { //override if needed } /** * 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(); protected Properties createPropertiesFromPersistedProperties() { return moduleConfigProperties.createPropertiesFromPersistedProperties(); } protected Set<Object> getPropertyKeys() { return moduleConfigProperties.getPropertyKeys(); } /** * 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); log.audit("change system property: " + propertyName, value); } /** * Set a string which must not be logged * @param propertyName * @param value * @param saveConfiguration */ protected void setSecretStringProperty(String propertyName, String value, boolean saveConfiguration) { // delegate to new property based config style moduleConfigProperties.setStringProperty(propertyName, value, saveConfiguration); log.audit("change system property: " + propertyName, "*********"); } /** * 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); log.audit("change system property: " + propertyName, Integer.toString(value)); } protected void removeProperty(String propertyName, boolean saveConfiguration) { moduleConfigProperties.removeProperty(propertyName, saveConfiguration); log.audit("remove system property: " + propertyName, null); } /** * 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); log.audit("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) */ @Override 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(); } } }