blob: 9b4b62b0c0e72c7021b8f3607eb1e3698cd8780c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. 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.
*/
package org.apache.myfaces.extensions.validator.core;
import org.apache.myfaces.extensions.validator.core.factory.DefaultFactoryFinder;
import org.apache.myfaces.extensions.validator.core.factory.FactoryFinder;
import org.apache.myfaces.extensions.validator.core.initializer.component.ComponentInitializer;
import org.apache.myfaces.extensions.validator.core.initializer.configuration.StaticConfiguration;
import org.apache.myfaces.extensions.validator.core.initializer.configuration.StaticConfigurationNames;
import org.apache.myfaces.extensions.validator.core.interceptor.MetaDataExtractionInterceptor;
import org.apache.myfaces.extensions.validator.core.interceptor.PropertyValidationInterceptor;
import org.apache.myfaces.extensions.validator.core.interceptor.RendererInterceptor;
import org.apache.myfaces.extensions.validator.core.interceptor.ValidationExceptionInterceptor;
import org.apache.myfaces.extensions.validator.core.metadata.MetaDataEntry;
import org.apache.myfaces.extensions.validator.core.recorder.ProcessedInformationRecorder;
import org.apache.myfaces.extensions.validator.core.validation.SkipValidationEvaluator;
import org.apache.myfaces.extensions.validator.core.validation.parameter.ViolationSeverityInterpreter;
import org.apache.myfaces.extensions.validator.core.validation.strategy.ValidationStrategy;
import org.apache.myfaces.extensions.validator.internal.UsageCategory;
import org.apache.myfaces.extensions.validator.internal.UsageInformation;
import org.apache.myfaces.extensions.validator.util.ClassUtils;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import static org.apache.myfaces.extensions.validator.util.WebXmlUtils.getInitParameter;
/**
* @author Gerhard Petracek
* @since 1.x.1
*/
@UsageInformation(UsageCategory.API)
public class ExtValContext
{
private final Logger logger = Logger.getLogger(getClass().getName());
private static ExtValContext extValContext;
//don't try to resolve it dynamically e.g. via InformationProviderBean - there's a mojarra issue
private static final String CUSTOM_EXTVAL_CONTEXT_CLASS_NAME =
ExtValContext.class.getName().replace(".core.", ".custom.");
private static final String CUSTOM_EXTVAL_MODULE_CONFIGURATION_RESOLVER_CLASS_NAME =
ExtValModuleConfigurationResolver.class.getName().replace(".core.", ".custom.");
private ViolationSeverityInterpreter violationSeverityInterpreter;
private FactoryFinder factoryFinder = DefaultFactoryFinder.getInstance();
private Map<String, RendererInterceptor> rendererInterceptors =
new ConcurrentHashMap<String, RendererInterceptor>();
private List<String> deniedInterceptors = new CopyOnWriteArrayList<String>();
private List<ProcessedInformationRecorder> processedInformationRecorders =
new CopyOnWriteArrayList<ProcessedInformationRecorder>();
private SkipValidationEvaluator skipValidationEvaluator;
private Map<String, Object> globalProperties = new HashMap<String, Object>();
/**
* Storage for all configuration objects.
* @since r4
*/
private Map<Class<? extends ExtValModuleConfiguration>, ExtValModuleConfiguration> extValConfig =
new ConcurrentHashMap<Class<? extends ExtValModuleConfiguration>, ExtValModuleConfiguration>();
/**
* Configured Module Configuration resolver.
* @since r4
*/
private ExtValModuleConfigurationResolver defaultModuleConfigurationResolver;
private Map<StaticConfigurationNames, List<StaticConfiguration<String, String>>> staticConfigMap
= new HashMap<StaticConfigurationNames, List<StaticConfiguration<String, String>>>();
private ExtValContextInternals contextHelper;
private ExtValContextInvocationOrderAwareInternals invocationOrderAwareContextHelper;
protected ExtValContext()
{
this.contextHelper = new ExtValContextInternals();
this.invocationOrderAwareContextHelper = new ExtValContextInvocationOrderAwareInternals(this.contextHelper);
retrieveModuleConfigurationResolver();
}
/**
* Retrieves the Module Configuration Resolver. Code looks first for a resolver located under the ExtVal Custom
* package (org.apache.myfaces.extensions.validator.custom.ExtValModuleConfigurationResolver) and then for a
* web.xml defined initialization parameter org.apache.myfaces.extensions.validator.core.
* ExtValModuleConfigurationResolver
* for a fully qualified class name. The resolver configured by the web.xml overrides the custom defined one.
*/
private void retrieveModuleConfigurationResolver()
{
Object customExtValModuleConfigurationResolver =
ClassUtils.tryToInstantiateClassForName(CUSTOM_EXTVAL_MODULE_CONFIGURATION_RESOLVER_CLASS_NAME);
if(customExtValModuleConfigurationResolver instanceof ExtValModuleConfigurationResolver)
{
this.defaultModuleConfigurationResolver =
(ExtValModuleConfigurationResolver)customExtValModuleConfigurationResolver;
}
String customExtValModuleConfigurationResolverClassName =
getInitParameter(null, ExtValModuleConfigurationResolver.class.getName());
if(customExtValModuleConfigurationResolverClassName != null)
{
customExtValModuleConfigurationResolver =
ClassUtils.tryToInstantiateClassForName(customExtValModuleConfigurationResolverClassName);
if(customExtValModuleConfigurationResolver instanceof ExtValModuleConfigurationResolver)
{
this.defaultModuleConfigurationResolver =
(ExtValModuleConfigurationResolver)customExtValModuleConfigurationResolver;
}
}
}
public static ExtValContext getContext()
{
if (extValContext == null)
{
extValContext = new ExtValContext();
tryToCreateCustomExtValContext();
}
return extValContext;
}
private static void tryToCreateCustomExtValContext()
{
Object customExtValContext = ClassUtils.tryToInstantiateClassForName(CUSTOM_EXTVAL_CONTEXT_CLASS_NAME);
if (customExtValContext instanceof ExtValContext)
{
extValContext = (ExtValContext) customExtValContext;
}
}
public void setViolationSeverityInterpreter(ViolationSeverityInterpreter violationSeverityInterpreter)
{
setViolationSeverityInterpreter(violationSeverityInterpreter, true);
}
public void setViolationSeverityInterpreter(
ViolationSeverityInterpreter violationSeverityInterpreter, boolean forceOverride)
{
if (this.violationSeverityInterpreter == null || forceOverride)
{
if (violationSeverityInterpreter != null)
{
this.logger.info(violationSeverityInterpreter.getClass() + " is used");
}
this.violationSeverityInterpreter = violationSeverityInterpreter;
}
}
public ViolationSeverityInterpreter getViolationSeverityInterpreter()
{
ViolationSeverityInterpreter requestScopedInterpreter = this.contextHelper
.getRequestScopedViolationSeverityInterpreter();
if(requestScopedInterpreter != null)
{
return requestScopedInterpreter;
}
return this.violationSeverityInterpreter;
}
/*
* FactoryFinder
*/
public FactoryFinder getFactoryFinder()
{
return this.factoryFinder;
}
public void setFactoryFinder(FactoryFinder factoryFinder)
{
if (factoryFinder != null)
{
this.factoryFinder = factoryFinder;
}
}
/*
* SkipValidationEvaluator
*/
public void setSkipValidationEvaluator(SkipValidationEvaluator skipValidationEvaluator)
{
setSkipValidationEvaluator(skipValidationEvaluator, true);
}
public void setSkipValidationEvaluator(SkipValidationEvaluator skipValidationEvaluator, boolean forceOverride)
{
if (this.skipValidationEvaluator == null || forceOverride)
{
if (skipValidationEvaluator != null)
{
this.logger.info(skipValidationEvaluator.getClass() + " is used");
}
this.skipValidationEvaluator = skipValidationEvaluator;
}
}
public SkipValidationEvaluator getSkipValidationEvaluator()
{
if (this.skipValidationEvaluator == null)
{
return new SkipValidationEvaluator()
{
public boolean skipValidation(FacesContext facesContext, UIComponent uiComponent,
ValidationStrategy validationStrategy, MetaDataEntry entry)
{
return false;
}
};
}
return this.skipValidationEvaluator;
}
/*
* RendererInterceptors
*/
public List<RendererInterceptor> getRendererInterceptors()
{
return new ArrayList<RendererInterceptor>(this.rendererInterceptors.values());
}
public boolean registerRendererInterceptor(RendererInterceptor rendererInterceptor)
{
synchronized (ExtValContext.class)
{
if (this.deniedInterceptors.contains(rendererInterceptor.getInterceptorId()))
{
return false;
}
this.rendererInterceptors.put(rendererInterceptor.getInterceptorId(), rendererInterceptor);
}
return true;
}
public void deregisterRendererInterceptor(Class<? extends RendererInterceptor> rendererInterceptorClass)
{
RendererInterceptor rendererInterceptor = ClassUtils.tryToInstantiateClass(rendererInterceptorClass);
synchronized (ExtValContext.class)
{
this.rendererInterceptors.remove(rendererInterceptor.getInterceptorId());
}
}
//if an interceptor hasn't been registered so far, it should be denied at future registrations
public void denyRendererInterceptor(Class<? extends RendererInterceptor> rendererInterceptorClass)
{
RendererInterceptor rendererInterceptor = ClassUtils.tryToInstantiateClass(rendererInterceptorClass);
synchronized (ExtValContext.class)
{
if (!this.deniedInterceptors.contains(rendererInterceptor.getInterceptorId()))
{
this.deniedInterceptors.add(rendererInterceptor.getInterceptorId());
}
}
deregisterRendererInterceptor(rendererInterceptorClass);
}
/*
* ComponentInitializers
*/
public void addComponentInitializer(ComponentInitializer componentInitializer)
{
this.invocationOrderAwareContextHelper.lazyInitComponentInitializers();
this.invocationOrderAwareContextHelper.addComponentInitializer(componentInitializer);
}
public List<ComponentInitializer> getComponentInitializers()
{
this.invocationOrderAwareContextHelper.lazyInitComponentInitializers();
return this.invocationOrderAwareContextHelper.getComponentInitializers();
}
/*
* ValidationExceptionInterceptors
*/
public void addValidationExceptionInterceptor(ValidationExceptionInterceptor validationExceptionInterceptor)
{
this.invocationOrderAwareContextHelper.lazyInitValidationExceptionInterceptors();
this.invocationOrderAwareContextHelper.addValidationExceptionInterceptor(validationExceptionInterceptor);
}
public List<ValidationExceptionInterceptor> getValidationExceptionInterceptors()
{
this.invocationOrderAwareContextHelper.lazyInitValidationExceptionInterceptors();
return this.invocationOrderAwareContextHelper.getValidationExceptionInterceptors();
}
/*
* PropertyValidationInterceptors
*/
public void addPropertyValidationInterceptor(PropertyValidationInterceptor propertyValidationInterceptor)
{
this.invocationOrderAwareContextHelper.lazyInitPropertyValidationInterceptors();
this.invocationOrderAwareContextHelper.addPropertyValidationInterceptor(propertyValidationInterceptor);
}
/**
* @return all global validation interceptors
*/
public List<PropertyValidationInterceptor> getPropertyValidationInterceptors()
{
this.invocationOrderAwareContextHelper.lazyInitPropertyValidationInterceptors();
return this.invocationOrderAwareContextHelper.getPropertyValidationInterceptors();
}
public List<PropertyValidationInterceptor> getPropertyValidationInterceptorsFor(Class moduleKey)
{
this.invocationOrderAwareContextHelper.lazyInitPropertyValidationInterceptors();
return this.invocationOrderAwareContextHelper.getPropertyValidationInterceptorsFor(moduleKey);
}
/*
* MetaDataExtractionInterceptors
*/
public void addMetaDataExtractionInterceptor(MetaDataExtractionInterceptor metaDataExtractionInterceptor)
{
this.invocationOrderAwareContextHelper.lazyInitMetaDataExtractionInterceptors();
this.invocationOrderAwareContextHelper.addMetaDataExtractionInterceptor(metaDataExtractionInterceptor);
}
/**
* @return all global meta-data extraction interceptors
*/
public List<MetaDataExtractionInterceptor> getMetaDataExtractionInterceptors()
{
this.invocationOrderAwareContextHelper.lazyInitMetaDataExtractionInterceptors();
return this.invocationOrderAwareContextHelper.getMetaDataExtractionInterceptors();
}
public List<MetaDataExtractionInterceptor> getMetaDataExtractionInterceptorsFor(Class moduleKey)
{
Map<String, Object> properties = new HashMap<String, Object>();
if(moduleKey != null)
{
properties.put(ValidationModuleKey.class.getName(), moduleKey);
}
return getMetaDataExtractionInterceptorsWith(properties);
}
public List<MetaDataExtractionInterceptor> getMetaDataExtractionInterceptorsWith(Map<String, Object> properties)
{
this.invocationOrderAwareContextHelper.lazyInitMetaDataExtractionInterceptors();
return this.invocationOrderAwareContextHelper.getMetaDataExtractionInterceptorsWith(properties);
}
/*
* ProcessedInformationRecorders
*/
public List<ProcessedInformationRecorder> getProcessedInformationRecorders()
{
return this.processedInformationRecorders;
}
public void addProcessedInformationRecorder(ProcessedInformationRecorder processedInformationRecorder)
{
this.processedInformationRecorders.add(processedInformationRecorder);
}
/*
* InformationProviderBean
*/
public InformationProviderBean getInformationProviderBean()
{
return this.contextHelper.getInformationProviderBean();
}
/*
* StaticConfiguration
*/
public List<StaticConfiguration<String, String>> getStaticConfiguration(StaticConfigurationNames name)
{
if (!this.staticConfigMap.containsKey(name))
{
List<StaticConfiguration<String, String>> staticConfigList =
new ArrayList<StaticConfiguration<String, String>>();
this.staticConfigMap.put(name, staticConfigList);
}
return this.staticConfigMap.get(name);
}
public void addStaticConfiguration(StaticConfigurationNames name, StaticConfiguration<String, String> staticConfig)
{
synchronized (this)
{
List<StaticConfiguration<String, String>> staticConfigList;
if (!this.staticConfigMap.containsKey(name))
{
staticConfigList = new ArrayList<StaticConfiguration<String, String>>();
this.staticConfigMap.put(name, staticConfigList);
}
this.staticConfigMap.get(name).add(staticConfig);
}
}
/*
* Global properties
*/
public boolean addGlobalProperty(String name, Object value)
{
return addGlobalProperty(name, value, true);
}
public boolean addGlobalProperty(String name, Object value, boolean forceOverride)
{
if (this.globalProperties.containsKey(name))
{
if (!forceOverride)
{
return false;
}
this.logger.info("override global property '" + name + "'");
}
if(JsfProjectStage.is(JsfProjectStage.Development))
{
this.logger.info("global property [" + name + "] added");
}
this.globalProperties.put(name, value);
return true;
}
public Object getGlobalProperty(String name)
{
return this.globalProperties.get(name);
}
/**
* Retrieves the configuration object of the type specified by the parameter 'targetType'. The type should be
* direct descendants of ExtValModuleConfiguration and the same as the type you used to register the configuration
* (see addModuleConfiguration).
*
* @param targetType Key that specifies the type of configuration, should be direct descendants of
* ExtValModuleConfiguration
* @param <T>
* @return configuration object
* @since r4
*/
public <T extends ExtValModuleConfiguration> T getModuleConfiguration(Class<T> targetType)
{
ExtValModuleConfiguration result = this.extValConfig.get(targetType);
//noinspection unchecked
return (T)result;
}
/**
* Registers the configuration object specified in the parameter 'config' for the type 'key' within ExtVal
* overriding
* possible another registration.
*
* @param key Key that specifies the type of configuration, should be direct descendants of
* ExtValModuleConfiguration
* @param extValConfig Configuration object to register
* @return true
* @since r4
*/
public boolean addModuleConfiguration(Class<? extends ExtValModuleConfiguration> key,
ExtValModuleConfiguration extValConfig)
{
return addModuleConfiguration(key, extValConfig, true);
}
/**
* Registers the configuration object specified in the parameter 'config' for the type 'key' within ExtVal. When a
* configuration object already exist for the key and we don't specify to override it (parameter forceOverride) the
* configuration isn't registered.
* @param key Key that specifies the type of configuration, should be direct descendants of
* ExtValModuleConfiguration
* @param config Configuration object to register
* @param forceOverride Do we override another custom configuration that is already registered.
* @return true is the configuration is registered or false if already existed and no ofrce override specified.
* @since r4
*/
public boolean addModuleConfiguration(Class<? extends ExtValModuleConfiguration> key,
ExtValModuleConfiguration config,
boolean forceOverride)
{
// Check if it already exists.
if (this.extValConfig.containsKey(key))
{
if (!forceOverride)
{
// Don't forced, so not registered
return false;
}
this.logger.info("override config for key '" + config.getClass() + "'");
}
//anonymous class are only supported for test-cases and
//there we don't need a custom config defined in the web.xml
// or from a resolver
if(!config.getClass().isAnonymousClass())
{
config = tryToLoadCustomConfigImplementation(config);
}
// Store the configuration
this.extValConfig.put(key, config);
// Logging when in Development Stage.
if(JsfProjectStage.is(JsfProjectStage.Development))
{
this.logger.info("config for key [" + config.getClass() + "] added");
}
return true;
}
/**
* Tries to load a custom configuration implementation by looking for a web.xml initialization parameter. The method
* returns the configuration object that should be used. That is, the custom defined version or the specified
* as parameter of the method.
*
* @param config The configuration object for which we try to load another version
* @return The custom configuration object or parameter specified if no alternative found.
* @since r4
*/
private ExtValModuleConfiguration tryToLoadCustomConfigImplementation(ExtValModuleConfiguration config)
{
// Get the parent if the parameter. For the default version, like DefaultExtValCoreConfiguration, it is
// the abstract base class.
Class configClass = config.getClass().getSuperclass();
// To be on the safe side that the parent still belongs to the configuration part of ExtVal.
if(!ExtValModuleConfiguration.class.isAssignableFrom(configClass))
{
return config;
}
@SuppressWarnings({"unchecked"})
Class<? extends ExtValModuleConfiguration> configDefinitionClass =
(Class<? extends ExtValModuleConfiguration>)configClass;
// If we have a resolver, use it to retrieve the configuration
if(this.defaultModuleConfigurationResolver != null)
{
config = this.defaultModuleConfigurationResolver.getCustomConfiguration(configDefinitionClass);
}
// Lookup the web.xml initialization parameter
String customConfigClassName = getInitParameter(null, configDefinitionClass.getName());
if(customConfigClassName != null)
{
// If specified, see if it exists and can be used.
Object customConfig = ClassUtils.tryToInstantiateClassForName(customConfigClassName);
if(customConfig instanceof ExtValModuleConfiguration)
{
return (ExtValModuleConfiguration)customConfig;
}
}
// return the parameter or resolver one.
return config;
}
}