/** * Copyright 2010 Marko Lavikainen * * Licensed 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. */ package net.contextfw.web.application.configuration; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.contextfw.web.application.DocumentProcessor; import net.contextfw.web.application.PropertyProvider; import net.contextfw.web.application.SystemPropertyProvider; import net.contextfw.web.application.development.DefaultXMLResponseLogger; import net.contextfw.web.application.development.XMLResponseLogger; import net.contextfw.web.application.internal.configuration.BasicSettableProperty; import net.contextfw.web.application.internal.configuration.BindablePropertyImpl; import net.contextfw.web.application.internal.configuration.KeyValue; import net.contextfw.web.application.internal.configuration.Property; import net.contextfw.web.application.internal.configuration.ReloadableClassPropertyImpl; import net.contextfw.web.application.internal.configuration.SelfKeyValueSetPropertyImpl; import net.contextfw.web.application.internal.configuration.SelfSettableProperty; import net.contextfw.web.application.internal.configuration.SetPropertyImpl; import net.contextfw.web.application.internal.configuration.StringSetPropertyImpl; import net.contextfw.web.application.internal.configuration.TemporalPropertyImpl; import net.contextfw.web.application.lifecycle.DefaultLifecycleListener; import net.contextfw.web.application.lifecycle.DefaultRequestInvocationFilter; import net.contextfw.web.application.lifecycle.LifecycleListener; import net.contextfw.web.application.lifecycle.RequestInvocationFilter; import net.contextfw.web.application.scope.DefaultWebApplicationStorage; import net.contextfw.web.application.scope.WebApplicationStorage; import net.contextfw.web.application.serialize.AttributeJsonSerializer; import net.contextfw.web.application.serialize.AttributeSerializer; import com.google.gson.JsonDeserializer; import com.google.gson.JsonSerializer; /** * This class defines the global system properties which are set during initialization. * * <h3>Note</h3> * * <p> * This class is immutable thus modifying or adding properties returns always a new instance * of <code>Configuration</code>. * </p> * */ public class Configuration { private static final String KEY_NAMESPACE = "contextfw.namespace"; private static final String KEY_ATTRIBUTE_SERIALIZER = "contextfw.attributeSerializer"; private static final String KEY_JSON_SERIALIZER = "contextfw.jsonSerializer"; private static final String KEY_JSON_DESERIALIZER = "contextfw.jsonDeserializer"; private static final String KEY_ATTRIBUTE_JSON_SERIALIZER = "contextfw.attributeJsonSerializer"; private static final String KEY_REMOVAL_SCHEDULE_PERIOD = "contextfw.removalSchedulePeriod"; private static final String KEY_MAX_INACTIVITY = "contextfw.maxInactivity"; private static final String KEY_INITIAL_MAX_INACTIVITY = "contextfw.initialMaxInactivity"; private static final String KEY_VIEW_COMPONENT_ROOT_PACKAGE = "contextfw.viewComponentRootPackage"; private static final String KEY_RESOURCE_PATH = "contextfw.resourcePath"; private static final String KEY_LIFECYCLE_LISTENER = "contextfw.lifecycleListener"; private static final String KEY_REQUEST_INVOCATION_FILTER = "contextfw.requestInvocationFilter"; private static final String KEY_PROPERTY_PROVIDER = "contextfw.propertyProvider"; private static final String KEY_XSL_POST_PROCESSOR = "contextfw.xslPostProcessor"; private static final String KEY_XML_PARAM_NAME = "contextfw.xmlParamName"; private static final String KEY_RESOURCES_PREFIX = "contextfw.resourcesPrefix"; private static final String KEY_LOG_XML = "contextfw.logXML"; private static final String KEY_XML_RESPONSE_LOGGER = "contextfw.xmlResponseLogger"; private static final String KEY_RELOADABLE_ROOT_PACKAGE = "contextfw.reloadableRootPackage"; private static final String KEY_DEVELOPMENT_MODE = "contextfw.developmentMode"; private static final String KEY_CLASS_RELOADING_ENABLED = "contextfw.classReloadingEnabled"; private static final String KEY_WEB_APPLICATION_STORAGE = "contextfw.webApplicationStorage"; private static final String KEY_HOST = "contextfw.host"; private static final String KEY_VERSION = "contextfw.version"; /** * Creates the default configuration. * * <p> * This is the recommended way to initialize properties. * </p> */ public static Configuration getDefaults() { return new Configuration() .set(DEVELOPMENT_MODE, true) .set(CLASS_RELOADING_ENABLED, true) .set(LOG_XML, true) .set(RESOURCES_PREFIX, "/resources") .set(XML_PARAM_NAME, null) .set(XML_RESPONSE_LOGGER.asInstance(new DefaultXMLResponseLogger())) .set(PROPERTY_PROVIDER, new SystemPropertyProvider()) .set(REQUEST_INVOCATION_FILTER, new DefaultRequestInvocationFilter()) .set(LIFECYCLE_LISTENER.as(DefaultLifecycleListener.class)) .set(WEB_APPLICATION_STORAGE.as(DefaultWebApplicationStorage.class)) .set(RESOURCE_PATH, new HashSet<String>()) .set(VIEW_COMPONENT_ROOT_PACKAGE, new HashSet<String>()) .set(INITIAL_MAX_INACTIVITY.inSeconds(30)) .set(REMOVAL_SCHEDULE_PERIOD.inMinutes(1)) .set(MAX_INACTIVITY.inMinutes(2)) .set(NAMESPACE, new HashSet<KeyValue<String, String>>()) .set(ATTRIBUTE_JSON_SERIALIZER, new HashSet<KeyValue<Class<?>, Class<? extends AttributeJsonSerializer<?>>>>()) .set(JSON_SERIALIZER, new HashSet<KeyValue<Class<?>, Class<? extends JsonSerializer<?>>>>()) .set(JSON_DESERIALIZER, new HashSet<KeyValue<Class<?>, Class<? extends JsonDeserializer<?>>>>()) .set(ATTRIBUTE_SERIALIZER, new HashSet<KeyValue<Class<?>, Class<? extends AttributeSerializer<?>>>>()); } /** * Defines whether system is run in development mode or not. * * <p> * In development mode resource changes are actively tracked during each page load or update. * </p> * <p> * Default: <code>true</code> * </p> */ public static final SettableProperty<Boolean> DEVELOPMENT_MODE = createProperty(Boolean.class, KEY_DEVELOPMENT_MODE); /** * Defines whether page components should be reloaded when changed. * * <p> * It reloading is enabled page components are loaded through different * class loader and when changes are made that class loader it disposed * and new one is created. * </p> * * <p> * Note, class reloading works only for those classes which are not in * <code>Singleton</code>-scope. Also, this setting has effect only * on development mode. * </p> * * <p> * Default: <code>true</code> * </p> */ public static final SettableProperty<Boolean> CLASS_RELOADING_ENABLED = createProperty(Boolean.class, KEY_CLASS_RELOADING_ENABLED); /** * Defines whether the XML-representation of page load or update are logged. Only suitable * during development mode. * * <p> * Default: <code>true</code> * </p> */ public static final SettableProperty<Boolean> LOG_XML = createProperty(Boolean.class, KEY_LOG_XML); /** * Defines the prefix for javascript- and css-files that are loaded with each page. * * <p> * Default: <code>/resources</code> * </p> */ public static final SettableProperty<String> RESOURCES_PREFIX = createProperty(String.class, KEY_RESOURCES_PREFIX); /** * A voluntary property for setting host name * * This property may be used by third-party plugins and is used as common * host identifier. * */ public static final SettableProperty<String> HOST = createProperty(String.class, KEY_HOST); /** * A voluntary property for setting application version * <p> * This property may be used by third-party plugins and is used as common * version identifier. * </p> */ public static final SettableProperty<String> VERSION = createProperty(String.class, KEY_VERSION); /** * Besides property <code>LOG_XML</code> it is possible to see the the page XML-representation * on web client. * * <p> * This property defines an URL-parameter that is used to trigger the behavior. Note that the * parameter value is irrelevant, the existence of the parameter is enough. * </p> * <p> * If the value of this property is set to <code>null</code> this feature is disabled. * </p> * <p> * Default: <code>xml</code> * </p> */ public static final SettableProperty<String> XML_PARAM_NAME = createProperty(String.class, KEY_XML_PARAM_NAME); /** * Defines the provider that is used to inject system properties to the system. * * <p> * This property takes a sub class of {@link PropertyProvider}. * </p> * <p> * Default: {@link SystemPropertyProvider} * </p> */ public static final SettableProperty<PropertyProvider> PROPERTY_PROVIDER = createProperty(PropertyProvider.class, KEY_PROPERTY_PROVIDER); /** * Binds a lifecycle listener to the system */ public static final BindableProperty<LifecycleListener> LIFECYCLE_LISTENER = createBindableProperty(LifecycleListener.class, KEY_LIFECYCLE_LISTENER); /** * Binds a web application storage to the system */ public static final BindableProperty<WebApplicationStorage> WEB_APPLICATION_STORAGE = createBindableProperty(WebApplicationStorage.class, KEY_WEB_APPLICATION_STORAGE); /** * Binds a request invocation filter to the system */ // This cannot be a bindable property, because it is needed immediately during // initiallizing public static final SettableProperty<RequestInvocationFilter> REQUEST_INVOCATION_FILTER = createProperty(RequestInvocationFilter.class, KEY_REQUEST_INVOCATION_FILTER); /** * Binds a response logger for XML. * * <p> * By default XML is logged by normal logging mechanism, but * it is possible to override using this property * </p> */ public static final BindableProperty<XMLResponseLogger> XML_RESPONSE_LOGGER = createBindableProperty(XMLResponseLogger.class, KEY_XML_RESPONSE_LOGGER); /** * Binds a XSL-postprocessor to the system */ public static final BindableProperty<DocumentProcessor> XSL_POST_PROCESSOR = createBindableProperty(DocumentProcessor.class, KEY_XSL_POST_PROCESSOR); /** * Defines the root paths that contains components' css- and javascript-resources. * * <p> * Determined paths and their sub folder are scanned for all resources and are returned as part * of a page. * </p> * * <p> * The value of the property can mean class package or directory. By default * value interpreted as package, but by adding a prefix <code>file:</code> value is * interpreted as directory path. * </p> */ public static final AddableProperty<Set<String>, String> RESOURCE_PATH = new StringSetPropertyImpl(KEY_RESOURCE_PATH); /** * Defines the root package from within view packages are scanned. * * <p> * Default: No default value * </p> */ public static final AddableProperty<Set<String>, String> VIEW_COMPONENT_ROOT_PACKAGE = new StringSetPropertyImpl(KEY_VIEW_COMPONENT_ROOT_PACKAGE); /** * Defines root package from within reloadable classes are scanned. * * <p> * This setting has effect only if class reloading is enabled. Also note * that all view-component root packages are automatically included to * reloadable packages. * </p> * * <p> * Default: No default value * </p> * */ public static final ReloadableClassProperty RELOADABLE_CLASSES = new ReloadableClassPropertyImpl(KEY_RELOADABLE_ROOT_PACKAGE); /** * Defines the initial maximum inactivity until page scope is expired. * * <p> * This property is used to expire page scope early for bots and web clients incapable of using * javascript, to make sure that resources are freed. * </p> * * <p> * Recommended values range from 30 seconds to 5 minutes. For mobile clients it is preferred * to use higher values. * </p> * * <p> * Default: 30 seconds * </p> */ public static final TemporalProperty INITIAL_MAX_INACTIVITY = createTemporalProperty(KEY_INITIAL_MAX_INACTIVITY); /** * Defines the maximum inactivity until page scope is expired. * * <p> * This property is used to expire page scope if no activity is taken in page for given maximum time. * In normal circumstances page scope is expired automatically when page is unloaded. However, * in cases of network failure or misuse expiration may never be triggered. * </p> * * <p> * The values of this property can range from minutes to hours depending on need. If inactivity is * defined low (< 10 minutes) system is quite safe from misuse but is more intolerable to temporary * network failures and requires constant refreshing. If higher values are used (> 1 hour) it is * recommended to use bandwidth throttling stategies to prevent misuse. * </p> * * <p> * Default: 2 minutes * </p> */ public static final TemporalProperty MAX_INACTIVITY = createTemporalProperty(KEY_MAX_INACTIVITY); /** * Defines the the period how often expired page scopes are purged from memory. * * <p> * There should be no need to touch this property. * </p> * <p> * Default: 1 minute * </p> */ public static final TemporalProperty REMOVAL_SCHEDULE_PERIOD = createTemporalProperty(KEY_REMOVAL_SCHEDULE_PERIOD); /** * Defines additional namespaces to be used in XSL-templates. * * <p> * If XSL-templates are using additional namespaces they must be registered here. * The namespaces are added to the master template. * </p> */ public static final SelfKeyValueSetProperty<String, String> NAMESPACE = new SelfKeyValueSetPropertyImpl<String, String>(KEY_NAMESPACE); /** * Binds a new AttributeJsonSerialiser to the system */ public static final SelfKeyValueSetProperty<Class<?>, Class<? extends AttributeJsonSerializer<?>>> ATTRIBUTE_JSON_SERIALIZER = new SelfKeyValueSetPropertyImpl<Class<?>, Class<? extends AttributeJsonSerializer<?>>>(KEY_ATTRIBUTE_JSON_SERIALIZER ); /** * Binds a new Json deserialiser to the system */ public static final SelfKeyValueSetProperty<Class<?>, Class<? extends JsonDeserializer<?>>> JSON_DESERIALIZER = new SelfKeyValueSetPropertyImpl<Class<?>, Class<? extends JsonDeserializer<?>>>(KEY_JSON_DESERIALIZER); /** * Binds a new Json serializer to the system */ public static final SelfKeyValueSetProperty<Class<?>, Class<? extends JsonSerializer<?>>> JSON_SERIALIZER = new SelfKeyValueSetPropertyImpl<Class<?>, Class<? extends JsonSerializer<?>>>(KEY_JSON_SERIALIZER); /** * Binds a new attribute serializer */ public static final SelfKeyValueSetProperty<Class<?>, Class<? extends AttributeSerializer<?>>> ATTRIBUTE_SERIALIZER = new SelfKeyValueSetPropertyImpl<Class<?>, Class<? extends AttributeSerializer<?>>>(KEY_ATTRIBUTE_SERIALIZER); private final Map<String, Object> values; /** * Constructs a new property. Not recommended for normal usage. */ public Configuration() { values = new HashMap<String, Object>(); } private <T> Configuration(Map<String, Object> values, Property<T> property, T value) { this.values = new HashMap<String, Object>(); this.values.putAll(values); this.values.put(property.getKey(), property.validate(value)); } /** * Returns the value of given property */ @SuppressWarnings("unchecked") public <T> T get(Property<T> property) { return property.validate((T) values.get(property.getKey())); } /** * Returns the value of given property or default if property is null */ @SuppressWarnings("unchecked") public <T> T getOrElse(Property<T> property, T def) { T value = (T) values.get(property.getKey()); return value != null ? property.validate(value) : def; } /** * Set a new property. * * <p> * Previosly set property get overriden. * </p> */ public <T> Configuration set(SettableProperty<T> property, T value) { return new Configuration(values, property, value); } /** * Set a new property. * * <p> * Previosly set property get overriden. * </p> */ public <T extends Collection<V>, V> Configuration set(AddableProperty<T, V> property, T value) { return new Configuration(values, property, value); } /** * Set a new property. * * <p> * Previously set property get overriden. * </p> */ public <T extends Collection<V>, V> Configuration set(SelfAddableProperty<T, V> property, T value) { return new Configuration(values, property, value); } /** * Set a new property. * * <p> * Previously set property get overriden. * </p> */ public <T> Configuration set(SelfSettableProperty<T> property) { return new Configuration(values, property, property.getValue()); } /** * Adds a new property */ public <T extends Collection<V>, V> Configuration add(AddableProperty<T, V> property, V value) { return new Configuration(values, property, property.add(get(property), value)); } /** * Adds a new property */ public <T extends Collection<V>, V> Configuration add(SelfAddableProperty<T, V> property) { return new Configuration(values, property, property.add(get(property), property.getValue())); } public static <T> SettableProperty<T> createProperty(Class<T> type, String key) { return new BasicSettableProperty<T>(key); } public static <T> BindableProperty<T> createBindableProperty(Class<T> type, String key) { return new BindablePropertyImpl<T>(key); } public static TemporalProperty createTemporalProperty(String key) { return new TemporalPropertyImpl(key); } public static <T> AddableProperty<Set<T>, T> createAddableProperty(Class<T> type, String key) { return new SetPropertyImpl<T>(key); } }