blob: ed6a545cb33434df7fcfe1ed2d2ceb78274e423f [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 createObject 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.tamaya.validation;
import org.apache.tamaya.Configuration;
import org.apache.tamaya.validation.spi.ConfigDocumentationMBean;
import org.apache.tamaya.validation.spi.ModelProviderSpi;
import org.apache.tamaya.spi.ServiceContextManager;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Validator accessor to validate the current configuration.
*/
public final class ConfigModelManager {
/** The logger used. */
private static final Logger LOG = Logger.getLogger(ConfigModelManager.class.getName());
/**
* Singleton constructor.
*/
private ConfigModelManager() {
}
/**
* Access the usage statistics for the recorded uses createObject configuration.
* @param inModels the target models, not null.
* @return usage statistics
*/
public static String getConfigModelDescription(Collection<ConfigModel> inModels){
StringBuilder b = new StringBuilder();
List<ConfigModel> models = new ArrayList<>(inModels);
Collections.sort(models, (k1, k2) -> {
return k2.getName().compareTo(k2.getName());
});
b.append("TYPE OWNER NAME MANDATORY DESCRIPTION\n");
b.append("-----------------------------------------------------------------------------------------------------\n");
for(ConfigModel model:models){
switch(model.getType()){
case Parameter:
b.append("PARAM ");
break;
case Section:
b.append("SECTION ");
break;
case Group:
b.append("GROUP ");
break;
default:
break;
}
b.append(formatWithFixedLength(model.getOwner(), 10)).append(' ');
b.append(formatWithFixedLength(model.getName(), 50));
if(model.isRequired()){
b.append(formatWithFixedLength("yes", 12));
}else{
b.append(formatWithFixedLength("no", 12));
}
if(model.getDescription()!=null){
b.append(model.getDescription().replace("\n", "\\\n").replace("\"", "'")).append("\"");
}
b.append("\n");
}
return b.toString();
}
/**
* Get the validations defined, using the default classloader.
*
* @return the sections defined, never null.
* @see ServiceContextManager#getDefaultClassLoader()
*/
public static Collection<ConfigModel> getModels() {
return getModels(ServiceContextManager.getDefaultClassLoader());
}
/**
* Get the validations defined.
*
* @param classLoader the target classloader, not null.
* @return the sections defined, never null.
*/
public static Collection<ConfigModel> getModels(ClassLoader classLoader) {
List<ConfigModel> result = new ArrayList<>();
for (ModelProviderSpi model : ServiceContextManager.getServiceContext(classLoader).getServices(ModelProviderSpi.class)) {
result.addAll(model.getConfigModels());
}
return result;
}
/**
* Find the validations by checking the validation's name using the given regular expression and
* the default classloader.
*
* @param namePattern the regular expression to use, not null.
* @param targets the target types only to be returned (optional).
* @return the sections defined, never null.
* @see ServiceContextManager#getDefaultClassLoader()
*/
public static Collection<ConfigModel> findModels(String namePattern, ModelTarget... targets) {
return findModels(namePattern, ServiceContextManager.getDefaultClassLoader(), targets);
}
/**
* Find the validations by checking the validation's name using the given regular expression.
*
* @param classLoader the target classloader, not null.
* @param namePattern the regular expression to use, not null.
* @param targets the target types only to be returned (optional).
* @return the sections defined, never null.
*/
public static Collection<ConfigModel> findModels(String namePattern, ClassLoader classLoader, ModelTarget... targets) {
List<ConfigModel> result = new ArrayList<>();
for (ModelProviderSpi model : ServiceContextManager.getServiceContext(classLoader).getServices(ModelProviderSpi.class)) {
for(ConfigModel configModel : model.getConfigModels()) {
if(configModel.getName().matches(namePattern)) {
if(targets.length>0){
for(ModelTarget tgt:targets){
if(configModel.getType().equals(tgt)){
result.add(configModel);
break;
}
}
}else {
result.add(configModel);
}
}
}
}
return result;
}
/**
* Validates the given configuration.
*
* @param config the configuration to be validated against, not null.
* @return the validation results, never null.
*/
public static Collection<Validation> validate(Configuration config) {
return validate(config, false);
}
/**
* Validates the given configuration.
*
* @param config the configuration to be validated against, not null.
* @param showUndefined allows filtering for undefined configuration elements.
* @return the validation results, never null.
*/
public static Collection<Validation> validate(Configuration config, boolean showUndefined) {
List<Validation> result = new ArrayList<>();
for (ConfigModel defConf : getModels(config.getContext().getServiceContext().getClassLoader())) {
result.addAll(defConf.validate(config));
}
if(showUndefined){
Map<String,String> map = new HashMap<>(config.getProperties());
Set<String> areas = extractTransitiveAreas(map.keySet());
for (ConfigModel defConf : getModels()) {
if(ModelTarget.Section.equals(defConf.getType())){
for (Iterator<String> iter = areas.iterator();iter.hasNext();){
String area = iter.next();
if(area.matches(defConf.getName())){
iter.remove();
}
}
}
if(ModelTarget.Parameter.equals(defConf.getType())){
map.remove(defConf.getName());
}
}
outer:for(Map.Entry<String,String> entry:map.entrySet()){
for (ConfigModel defConf : getModels()) {
if(ModelTarget.Section.equals(defConf.getType())){
if(defConf.getName().endsWith(".*") && entry.getKey().matches(defConf.getName())){
// Ignore parameters that are part createObject transitive section.
continue outer;
}
}
}
result.add(Validation.createUndefined("<auto>", entry.getKey(), ModelTarget.Parameter));
}
for(String area:areas){
result.add(Validation.createUndefined("<auto>", area, ModelTarget.Section));
}
}
return result;
}
/**
* Registers the {@link ConfigDocumentationMBean} mbean for accessing config documentation into the local platform
* mbean server.
*/
public static void registerMBean() {
registerMBean(null);
}
/**
* Registers the {@link ConfigDocumentationMBean} mbean for accessing config documentation into the local platform
* mbean server.
*
* @param context allows to specify an additional MBean context, maybe {@code null}.
*/
public static void registerMBean(String context) {
try{
ConfigDocumentationMBean configMbean = ServiceContextManager.getServiceContext()
.getService(ConfigDocumentationMBean.class);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName on = context==null?new ObjectName("org.apache.tamaya.model:type=ConfigDocumentationMBean"):
new ObjectName("org.apache.tamaya.model:type=ConfigDocumentationMBean,context="+context);
try{
mbs.getMBeanInfo(on);
LOG.warning("Cannot register mbean " + on + ": already existing.");
} catch(InstanceNotFoundException e) {
LOG.info("Registering mbean " + on + "...");
mbs.registerMBean(configMbean, on);
}
} catch(Exception e){
LOG.log(Level.WARNING,
"Failed to register ConfigDocumentationMBean.", e);
}
}
private static String formatWithFixedLength(String name, int targetLength) {
targetLength = targetLength-1;
StringBuilder b = new StringBuilder();
if(name.length() > targetLength){
name = name.substring(0, targetLength);
}
b.append(name);
for(int i=0;i<(targetLength-name.length());i++){
b.append(' ');
}
b.append(' ');
return b.toString();
}
private static java.util.Set<java.lang.String> extractTransitiveAreas(Set<String> keys) {
Set<String> transitiveClosure = new HashSet<>();
for(String key:keys){
int index = key.lastIndexOf('.');
while(index>0){
String areaKey = key.substring(0,index);
transitiveClosure.add(areaKey);
index = areaKey.lastIndexOf('.');
}
}
return transitiveClosure;
}
}