You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2018/12/17 19:54:44 UTC

[incubator-tamaya-sandbox] 01/09: Added experimental documentation generator tool.

This is an automated email from the ASF dual-hosted git repository.

anatole pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-tamaya-sandbox.git

commit 9f6fda17dfdf7e776c3291e712b0eb89bf1a909b
Author: Anatole Tresch <at...@gmail.com>
AuthorDate: Sat Dec 1 01:34:47 2018 +0100

    Added experimental documentation generator tool.
---
 documentation/bnd.bnd                              |  32 +++
 documentation/pom.xml                              |  72 ++++++
 .../org/apache/tamaya/doc/ConfigDocumenter.java    | 174 ++++++++++++++
 .../main/java/org/apache/tamaya/doc/DocFormat.java |  25 ++
 .../java/org/apache/tamaya/doc/DocumentedArea.java | 264 +++++++++++++++++++++
 .../apache/tamaya/doc/DocumentedConfiguration.java | 186 +++++++++++++++
 .../org/apache/tamaya/doc/DocumentedProperty.java  | 188 +++++++++++++++
 .../apache/tamaya/doc/annot/ConfigAreaSpec.java    |  94 ++++++++
 .../apache/tamaya/doc/annot/ConfigAreaSpecs.java   |  38 +++
 .../tamaya/doc/annot/ConfigPropertySpec.java       |  63 +++++
 .../tamaya/doc/annot/ConfigPropertySpecs.java      |  38 +++
 .../org/apache/tamaya/doc/annot/ConfigSpec.java    |  48 ++++
 .../apache/tamaya/doc/formats/HtmlDocFormat.java   | 127 ++++++++++
 .../apache/tamaya/doc/formats/TextDocFormat.java   | 124 ++++++++++
 .../apache/tamaya/validation/ConfigValidator.java  |  26 ++
 .../java/org/apache/tamaya/validation/Finding.java |  54 +++++
 .../apache/tamaya/validation/ValidationCheck.java  | 194 +++++++++++++++
 .../apache/tamaya/validation/ValidationResult.java |  29 +++
 documentation/src/main/resources/img/tamaya.png    | Bin 0 -> 2015 bytes
 .../AnnotBasedStandaloneConfigDocumentation.java   |  62 +++++
 .../apache/tamaya/doc/AnnotatedDocConfigBean.java  |  95 ++++++++
 .../apache/tamaya/doc/ConfigDocumenterTest.java    |  46 ++++
 pom.xml                                            |   1 +
 23 files changed, 1980 insertions(+)

diff --git a/documentation/bnd.bnd b/documentation/bnd.bnd
new file mode 100644
index 0000000..ef18c20
--- /dev/null
+++ b/documentation/bnd.bnd
@@ -0,0 +1,32 @@
+-buildpath: \
+	osgi.annotation; version=6.0.0,\
+	osgi.core; version=6.0,\
+	osgi.cmpn; version=6.0
+
+-testpath: \
+	${junit}
+
+javac.source: 1.8
+javac.target: 1.8
+
+Automatic-Module-Name: org.apache.tamaya.doc
+Bundle-Version: ${version}.${tstamp}
+Bundle-Name: Apache Tamaya - Documentation Generator
+Bundle-SymbolicName: org.apache.tamaya.doc
+Bundle-Description: Apacha Tamaya Configuration - Documentation Generator
+Bundle-Category: Implementation
+Bundle-Copyright: (C) Apache Foundation
+Bundle-License: Apache Licence version 2
+Bundle-Vendor: Apache Software Foundation
+Bundle-ContactAddress: dev-tamaya@incubator.apache.org
+Bundle-DocURL: http://tamaya.apache.org
+Export-Package: \
+	org.apache.tamaya.doc,\
+    org.apache.tamaya.doc.spi
+Import-Package: \
+    org.apache.tamaya,\
+    org.apache.tamaya.spi
+Export-Service: \
+    org.apache.tamaya.doc.spi.ConfigDocumentationMBean,\
+    org.apache.tamaya.doc.spi.ModelProviderSpi
+
diff --git a/documentation/pom.xml b/documentation/pom.xml
new file mode 100644
index 0000000..8ac628f
--- /dev/null
+++ b/documentation/pom.xml
@@ -0,0 +1,72 @@
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.tamaya.ext</groupId>
+        <artifactId>tamaya-sandbox</artifactId>
+        <version>0.4-incubating-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>tamaya-doc_alpha</artifactId>
+    <name>Apache Tamaya Modules - Documentation</name>
+    <description>This extension module provides functionality to document your configuration options easily.
+    </description>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.reflections</groupId>
+            <artifactId>reflections</artifactId>
+            <version>0.9.9-RC1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.j2html</groupId>
+            <artifactId>j2html</artifactId>
+            <version>1.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-core</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-injection-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>java-hamcrest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java b/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java
new file mode 100644
index 0000000..6c00d16
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java
@@ -0,0 +1,174 @@
+/*
+ * 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.doc;
+
+import org.apache.tamaya.doc.annot.*;
+import org.reflections.Reflections;
+import org.reflections.scanners.FieldAnnotationsScanner;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to read and store the current configuration documentation.
+ */
+public final class ConfigDocumenter {
+
+    private DocumentedConfiguration docs = new DocumentedConfiguration();
+
+    /**
+     * Read documentation from the given classes.
+     * @param clazzes the classes to read, not null.
+     */
+    public void readClasses(Class... clazzes){
+        FilterBuilder filterBuilder = new FilterBuilder();
+        List<URL> urls = new ArrayList<>(clazzes.length);
+        for(Class clazz:clazzes){
+//            filterBuilder.exclude(".*");
+            filterBuilder.include(clazz.getName()+".*");
+            urls.add(ClasspathHelper.forClass(clazz));
+        }
+        ConfigurationBuilder configBuilder = new ConfigurationBuilder()
+                .setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner())
+                .setUrls(urls)
+                .filterInputsBy(filterBuilder);
+        Reflections reflections = new Reflections(configBuilder);
+        readSpecs(reflections);
+    }
+
+    /**
+     * Read documentation from the classes managed by the given classloader.
+     * @param classLoader the classloader, not null.
+     */
+    public void readClasses(ClassLoader classLoader){
+        ConfigurationBuilder configBuilder = new ConfigurationBuilder()
+                .setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner())
+                .setUrls(ClasspathHelper.forClassLoader(classLoader));
+        Reflections reflections = new Reflections(configBuilder);
+        readSpecs(reflections);
+    }
+
+    /**
+     * Read documentation from the given packages.
+     * @param packages the package names, not null.
+     */
+    public void readPackages(String... packages) {
+        ConfigurationBuilder configBuilder = new ConfigurationBuilder();
+        FilterBuilder filterBuilder = new FilterBuilder();
+        for (String p : packages) {
+            filterBuilder.includePackage(p);
+        }
+        configBuilder.filterInputsBy(filterBuilder);
+        configBuilder.setUrls(ClasspathHelper.forJavaClassPath());
+        configBuilder.setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner());
+        Reflections reflections = new Reflections(configBuilder);
+        readSpecs(reflections);
+    }
+
+    /**
+     * Access the collected configuration documentation.
+     * @return the documentation, not null.
+     */
+    public DocumentedConfiguration getDocumentation(){
+        return docs;
+    }
+
+    private void readSpecs(Reflections reflections){
+        // types
+        for(Class type:reflections.getTypesAnnotatedWith(ConfigSpec.class)){
+            readConfigSpec(type);
+        }
+        for(Class type:reflections.getTypesAnnotatedWith(ConfigAreaSpec.class)){
+            readAreaSpec(type);
+        }
+        for(Class type:reflections.getTypesAnnotatedWith(ConfigAreaSpecs.class)){
+            readAreaSpecs(type);
+        }
+        for(Class type:reflections.getTypesAnnotatedWith(ConfigPropertySpec.class)){
+            readPropertySpec(type);
+        }
+        for(Class type:reflections.getTypesAnnotatedWith(ConfigPropertySpecs.class)){
+            readPropertySpecs(type);
+        }
+        // Fields
+        for(Field f:reflections.getFieldsAnnotatedWith(ConfigAreaSpec.class)){
+            readAreaSpec(f);
+        }
+        for(Field f:reflections.getFieldsAnnotatedWith(ConfigAreaSpecs.class)){
+            readAreaSpecs(f);
+        }
+        for(Field f:reflections.getFieldsAnnotatedWith(ConfigPropertySpec.class)){
+            readPropertySpec(f);
+        }
+        for(Field f:reflections.getFieldsAnnotatedWith(ConfigPropertySpecs.class)){
+            readPropertySpecs(f);
+        }
+        // Methods
+        for(Method m:reflections.getMethodsAnnotatedWith(ConfigAreaSpec.class)){
+            readAreaSpec(m);
+        }
+        for(Method m:reflections.getMethodsAnnotatedWith(ConfigAreaSpecs.class)){
+            readAreaSpecs(m);
+        }
+        for(Method m:reflections.getMethodsAnnotatedWith(ConfigPropertySpec.class)){
+            readPropertySpec(m);
+        }
+        for(Method m:reflections.getMethodsAnnotatedWith(ConfigPropertySpecs.class)){
+            readPropertySpecs(m);
+        }
+    }
+
+    private void readConfigSpec(Class type){
+        docs.init((ConfigSpec)type.getAnnotation(ConfigSpec.class), type);
+    }
+
+    private void readAreaSpecs(AnnotatedElement elem){
+        ConfigAreaSpecs areaSpecs = elem.getAnnotation(ConfigAreaSpecs.class);
+        for (ConfigAreaSpec areaSpec:areaSpecs.value()){
+            docs.addGroup(new DocumentedArea(areaSpec, elem));
+        }
+    }
+
+    private void readPropertySpecs(AnnotatedElement elem){
+        ConfigPropertySpecs propertySpecs = elem.getAnnotation(ConfigPropertySpecs.class);
+        for (ConfigPropertySpec propertySpec:propertySpecs.value()){
+            docs.addProperty(new DocumentedProperty(propertySpec, elem));
+        }
+    }
+
+    private void readAreaSpec(AnnotatedElement elem){
+        docs.addGroup(new DocumentedArea(elem.getAnnotation(ConfigAreaSpec.class), elem));
+    }
+
+    private void readPropertySpec(AnnotatedElement elem){
+        docs.addProperty(new DocumentedProperty(elem.getAnnotation(ConfigPropertySpec.class), elem));
+    }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java
new file mode 100644
index 0000000..0f5e14a
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java
@@ -0,0 +1,25 @@
+/*
+ * 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.doc;
+
+import java.util.function.Function;
+
+public interface DocFormat<T> extends Function<DocumentedConfiguration, T> {
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java
new file mode 100644
index 0000000..e2215b4
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java
@@ -0,0 +1,264 @@
+/*
+ * 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.inject.api.ConfigDefaultSections;
+import org.apache.tamaya.inject.spi.InjectionUtils;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+public final class DocumentedArea {
+
+    private final AnnotatedElement owner;
+    private ConfigAreaSpec configArea;
+    private String path;
+    private String description;
+    private PropertyValue.ValueType groupType;
+    private Map<String, DocumentedProperty> properties = new HashMap<>();
+    private Map<String, DocumentedArea> areas = new HashMap<>();
+    private Class<?> valueType;
+
+    private int minCardinality;
+    private int maxCardinality;
+    private Set<DocumentedArea> dependsOnGroups = new TreeSet<>();
+    private Set<DocumentedProperty> dependsOnProperties = new TreeSet<>();
+
+    public DocumentedArea(ConfigAreaSpec areaSpec, AnnotatedElement owner){
+        this.owner = owner;
+        this.configArea = areaSpec;
+        if(!areaSpec.path().isEmpty()) {
+            this.path = areaSpec.path();
+        }else{
+           this.path = evaluatePath(owner);
+        }
+        if(!areaSpec.description().isEmpty()) {
+            this.description = areaSpec.description();
+        }
+        this.groupType = areaSpec.areaType();
+        if(areaSpec.max()>0) {
+            this.maxCardinality = areaSpec.max();
+        }
+        if(areaSpec.min()>0) {
+            this.minCardinality = areaSpec.min();
+        }
+        if(Object.class!=areaSpec.valueType()) {
+            this.valueType = areaSpec.valueType();
+        }else{
+            if(owner instanceof Class){
+                this.valueType = ((Class)owner);
+            }
+        }
+        for(ConfigPropertySpec ps:areaSpec.properties()) {
+            this.properties.put(ps.name(), new DocumentedProperty(ps, owner));
+        }
+    }
+
+    private String evaluatePath(AnnotatedElement owner) {
+        if(owner instanceof Field) {
+            return String.join(", ", InjectionUtils.getKeys((Field) owner));
+        }else if(owner instanceof Method) {
+            return String.join(", ", InjectionUtils.getKeys((Method) owner));
+        }else if(owner instanceof Class) {
+            ConfigDefaultSections sectionsAnnot = owner.getAnnotation(ConfigDefaultSections.class);
+            if(sectionsAnnot!=null){
+                return String.join(", ", sectionsAnnot.value());
+            }
+            return ((Class)owner).getName()+", "+((Class)owner).getSimpleName();
+        }
+        return "<root>";
+    }
+
+    void resolve(DocumentedConfiguration documentation){
+        if(configArea !=null){
+            for(String key: configArea.dependsOnAreas()){
+                this.dependsOnGroups.add(documentation.getGroup(key));
+            }
+            for(String key: configArea.dependsOnProperties()){
+                this.dependsOnProperties.add(documentation.getProperty(key));
+            }
+        }
+    }
+
+    public DocumentedArea addGroup(DocumentedArea group){
+        this.areas.put(group.path, group);
+        return this;
+    }
+
+    public DocumentedArea addProperty(DocumentedProperty property){
+        this.properties.put(property.getName(), property);
+        return this;
+    }
+
+    public AnnotatedElement getOwner() {
+        return owner;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public PropertyValue.ValueType getGroupType() {
+        return groupType;
+    }
+
+    public Map<String, DocumentedProperty> getProperties() {
+        return properties;
+    }
+
+    public List<DocumentedProperty> getPropertiesSorted() {
+        List<DocumentedProperty> result = new ArrayList<>(properties.values());
+        result.sort(Comparator.comparing(DocumentedProperty::getName));
+        return result;
+    }
+
+    public Map<String, DocumentedArea> getAreas() {
+        return areas;
+    }
+
+    public List<DocumentedArea> getAreasSorted() {
+        List<DocumentedArea> result = new ArrayList<>(areas.values());
+        result.sort(Comparator.comparing(DocumentedArea::getPath));
+        return result;
+    }
+
+    public Class<?> getValueType() {
+        return valueType;
+    }
+
+    public int getMinCardinality() {
+        return minCardinality;
+    }
+
+    public int getMaxCardinality() {
+        return maxCardinality;
+    }
+
+    public List<DocumentedArea> getDependsOnAreas() {
+        List<DocumentedArea> result = new ArrayList<>(dependsOnGroups);
+        result.sort(Comparator.comparing(DocumentedArea::getPath));
+        return result;
+    }
+
+    public List<DocumentedProperty> getDependsOnProperties() {
+        List<DocumentedProperty> result = new ArrayList<>(dependsOnProperties);
+        result.sort(Comparator.comparing(DocumentedProperty::getName));
+        return result;
+    }
+
+    public DocumentedArea path(String path) {
+        this.path = path;
+        return this;
+    }
+
+    public DocumentedArea description(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public DocumentedArea groupType(PropertyValue.ValueType groupType) {
+        this.groupType = groupType;
+        return this;
+    }
+
+    public DocumentedArea properties(Map<String, DocumentedProperty> properties) {
+        this.properties = properties;
+        return this;
+    }
+
+    public DocumentedArea groups(Map<String, DocumentedArea> groups) {
+        this.areas = groups;
+        return this;
+    }
+
+    public DocumentedArea valueType(Class<?> valueType) {
+        this.valueType = valueType;
+        return this;
+    }
+
+    public DocumentedArea minCardinality(int minCardinality) {
+        this.minCardinality = minCardinality;
+        return this;
+    }
+
+    public DocumentedArea maxCardinality(int maxCardinality) {
+        this.maxCardinality = maxCardinality;
+        return this;
+    }
+
+    public DocumentedArea dependsOnGroups(Set<DocumentedArea> dependsOnGroups) {
+        this.dependsOnGroups = dependsOnGroups;
+        return this;
+    }
+
+    public DocumentedArea dependsOnProperties(Set<DocumentedProperty> dependsOnProperties) {
+        this.dependsOnProperties = dependsOnProperties;
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        DocumentedArea that = (DocumentedArea) o;
+
+        return Objects.equals(this.dependsOnGroups, that.dependsOnGroups) &&
+                Objects.equals(this.dependsOnProperties, that.dependsOnProperties) &&
+                Objects.equals(this.description, that.description) &&
+                Objects.equals(this.areas, that.areas) &&
+                Objects.equals(this.groupType, that.groupType) &&
+                Objects.equals(this.maxCardinality, that.maxCardinality) &&
+                Objects.equals(this.minCardinality, that.minCardinality) &&
+                Objects.equals(this.path, that.path) &&
+                Objects.equals(this.properties, that.properties) &&
+                Objects.equals(this.valueType, that.valueType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dependsOnGroups, dependsOnProperties, description, areas, groupType, maxCardinality,
+                minCardinality, path, properties, valueType);
+    }
+
+    @Override
+    public String toString() {
+        return "ConfigGroup{" +
+                "path='" + path + '\'' +
+                ", description='" + description + '\'' +
+                ", areaType=" + groupType +
+                ", properties=" + properties +
+                ", areas=" + areas +
+                ", valueType=" + valueType +
+                ", minCardinality=" + minCardinality +
+                ", maxCardinality=" + maxCardinality +
+                ", dependsOnAreas=" + dependsOnGroups +
+                ", dependsOnProperties=" + dependsOnProperties +
+                '}';
+    }
+
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java
new file mode 100644
index 0000000..2596d84
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java
@@ -0,0 +1,186 @@
+/*
+ * 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigSpec;
+
+import java.util.*;
+
+/**
+ * Documentation of an application configuration.
+ */
+public final class DocumentedConfiguration {
+
+    private String name;
+    private String version;
+    private Class ownerClass;
+
+    private Map<String, DocumentedProperty> properties = new TreeMap<>();
+    private Map<String, DocumentedArea> groups = new TreeMap<>();
+
+    /**
+     * Creates a new empty configuration documentation.
+     */
+    public DocumentedConfiguration(){}
+
+    /**
+     * Creates a new configuration documentation and initializes it with the values from the annotation given.
+     * @param annotation the spec annotation, not null.
+     */
+    public DocumentedConfiguration(ConfigSpec annotation){
+        init(annotation, null);
+    }
+
+    /**
+     * Initializes the instance with the given annotation.
+     * @param annotation the annotation , not null.
+     * @param ownerClass the annotaed class.
+     */
+    public void init(ConfigSpec annotation, Class ownerClass) {
+        this.name = annotation.name();
+        this.version = annotation.version();
+        this.ownerClass = ownerClass;
+    }
+
+    /**
+     * Get the current configuration name.
+     * @return the name, or '<undefined>'.
+     */
+    public String getName() {
+        if(name==null){
+            return "<undefined>";
+        }
+        return name;
+    }
+
+    /**
+     * Get the current configuration version.
+     * @return the version, or '<undefined>'.
+     */
+    public String getVersion() {
+        if(version==null){
+            return "<undefined>";
+        }
+        return version;
+    }
+
+    /**
+     * Get the annotated class.
+     * @return the class, or null.
+     */
+    public Class getOwnerClass() {
+        return ownerClass;
+    }
+
+    /**
+     * Get the documented properties.
+     * @return the properties, never null.
+     */
+    public Map<String, DocumentedProperty> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Get the documented properties.
+     * @return the properties, never null.
+     */
+    public List<DocumentedArea> getAllAreasSorted() {
+        List<DocumentedArea> areas = new ArrayList<>();
+        areas.addAll(getAreas().values());
+        areas.sort(this::compareAreas);
+        return areas;
+    }
+
+    private int compareAreas(DocumentedArea area1, DocumentedArea area2) {
+        return area1.getPath().compareTo(area2.getPath());
+    }
+
+    /**
+     * Get the documented properties.
+     * @return the properties, never null.
+     */
+    public List<DocumentedProperty> getAllPropertiesSorted() {
+        List<DocumentedProperty> props = new ArrayList<>();
+        props.addAll(getProperties().values());
+        for(DocumentedArea area:getAreas().values()) {
+            props.addAll(area.getProperties().values());
+        }
+        props.sort(this::compareProperties);
+        return props;
+    }
+
+    private int compareProperties(DocumentedProperty property, DocumentedProperty property1) {
+        return property.getName().compareTo(property1.getName());
+    }
+
+    /**
+     * Get the documented areas.
+     * @return the areas, never null.
+     */
+    public Map<String, DocumentedArea> getAreas() {
+        return groups;
+    }
+
+    public DocumentedArea getGroup(String path) {
+        return null;
+    }
+
+    public DocumentedProperty getProperty(String path) {
+        return null;
+    }
+
+    public DocumentedConfiguration addProperty(DocumentedProperty property){
+        this.properties.put(property.getName(), property);
+        return this;
+    }
+
+    public DocumentedConfiguration addGroup(DocumentedArea group){
+        this.groups.put(group.getPath(), group);
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DocumentedConfiguration)) return false;
+
+        DocumentedConfiguration that = (DocumentedConfiguration) o;
+
+        if (!name.equals(that.name)) return false;
+        return version.equals(that.version);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "DocumentedConfiguration{" +
+                "name='" + name + '\'' +
+                ", version='" + version + '\'' +
+                ", properties=" + properties +
+                ", groups=" + groups +
+                '}';
+    }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java
new file mode 100644
index 0000000..b270aea
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java
@@ -0,0 +1,188 @@
+/*
+ * 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.spi.InjectionUtils;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.*;
+
+public final class DocumentedProperty {
+
+    private final AnnotatedElement owner;
+    private ConfigPropertySpec propertySpec;
+    private String name;
+    private String defaultValue;
+    private boolean required;
+    private String description;
+    private Class<?> valueType;
+
+    private Set<DocumentedArea> dependsOnGroups = new TreeSet<>();
+    private Set<DocumentedProperty> dependsOnProperties = new TreeSet<>();
+
+
+    public DocumentedProperty(ConfigPropertySpec annot, AnnotatedElement owner){
+        this.owner = owner;
+        this.propertySpec = annot;
+        if(!annot.name().isEmpty()) {
+            this.name = annot.name();
+        }else{
+            if(owner instanceof Field) {
+                this.name = String.join(", ", InjectionUtils.getKeys((Field) owner));
+            }else if(owner instanceof Method) {
+                this.name = String.join(", ", InjectionUtils.getKeys((Method) owner));
+            }
+        }
+        this.description = annot.description();
+        this.valueType = annot.valueType();
+        if(String.class.equals(this.valueType)){
+            if(owner instanceof Field){
+                Field f = (Field)owner;
+                this.valueType = f.getType();
+            }else if(owner instanceof Method){
+                Method m = (Method)owner;
+                this.valueType = m.getParameterTypes()[0];
+            }
+        }
+        Config configAnnot = owner.getAnnotation(Config.class);
+        if(configAnnot!=null){
+            this.required = configAnnot.required();
+            this.defaultValue = configAnnot.defaultValue();
+        }
+    }
+
+    void resolve(DocumentedConfiguration documentation){
+        if(propertySpec !=null){
+            for(String key: propertySpec.dependsOnAreas()){
+                this.dependsOnGroups.add(documentation.getGroup(key));
+            }
+            for(String key: propertySpec.dependsOnProperties()){
+                this.dependsOnProperties.add(documentation.getProperty(key));
+            }
+        }
+    }
+
+    public AnnotatedElement getOwner() {
+        return owner;
+    }
+
+    public String getDefaultValue() {
+        if(defaultValue==null){
+            return "";
+        }
+        return defaultValue;
+    }
+
+    public boolean isRequired() {
+        return required;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        if(description==null){
+            return "";
+        }
+        return description;
+    }
+
+    public Class<?> getValueType() {
+        return valueType;
+    }
+
+    public Set<DocumentedArea> getDependsOnGroups() {
+        return dependsOnGroups;
+    }
+
+    public Set<DocumentedProperty> getDependsOnProperties() {
+        return dependsOnProperties;
+    }
+
+    public DocumentedProperty path(String path) {
+        this.name = path;
+        return this;
+    }
+
+    public DocumentedProperty description(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public DocumentedProperty valueType(Class<?> valueType) {
+        this.valueType = valueType;
+        return this;
+    }
+
+    public DocumentedProperty dependsOnGroups(Collection<DocumentedArea> dependsOnGroups) {
+        this.dependsOnGroups.addAll(dependsOnGroups);
+        return this;
+    }
+
+    public DocumentedProperty dependsOnGroups(DocumentedArea... dependsOnGroups) {
+        this.dependsOnGroups.addAll(Arrays.asList(dependsOnGroups));
+        return this;
+    }
+
+    public DocumentedProperty dependsOnProperties(Collection<DocumentedProperty> dependsOnProperties) {
+        this.dependsOnProperties.addAll(dependsOnProperties);
+        return this;
+    }
+
+    public DocumentedProperty dependsOnProperties(DocumentedProperty... dependsOnProperties) {
+        this.dependsOnProperties.addAll(Arrays.asList(dependsOnProperties));
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        DocumentedProperty that = (DocumentedProperty) o;
+
+        return Objects.equals(this.dependsOnGroups, that.dependsOnGroups) &&
+                Objects.equals(this.dependsOnProperties, that.dependsOnProperties) &&
+                Objects.equals(this.description, that.description) &&
+                Objects.equals(this.name, that.name) &&
+                Objects.equals(this.valueType, that.valueType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dependsOnGroups, dependsOnProperties, description, name, valueType);
+    }
+
+    @Override
+    public String toString() {
+        return "ConfigGroup{" +
+                "name='" + name + '\'' +
+                ", description='" + description + '\'' +
+                ", valueType=" + valueType +
+                ", dependsOnAreas=" + dependsOnGroups +
+                ", dependsOnProperties=" + dependsOnProperties +
+                '}';
+    }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java
new file mode 100644
index 0000000..589cc6c
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java
@@ -0,0 +1,94 @@
+/*
+ * 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.doc.annot;
+
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation allows to specify a configuration area.
+ */
+@Repeatable(ConfigAreaSpecs.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigAreaSpec {
+    /**
+     * The areas configuration area path, e.g. {@code foo.bar}. An empty String means the root
+     * of the configuration tree.
+     * @return the path, not null.
+     */
+    String path()default"";
+
+    /**
+     * Define a description of the area.
+     * @return the description.
+     */
+    String description() default "";
+
+    /**
+     * Defines the value type that is represented by a given area path, e.g. all properties on level
+     * {@code foo.bar.Server} are mapped to some client classes {@code a.b.ServerConfig}.
+     * @return the type mapping, if any.
+     */
+    Class<?> valueType() default Object.class;
+
+    /**
+     * Allows to specifiy the node type of an area:
+     * <ul>
+     *     <li><b>ARRAY: </b> is an array of child items.</li>
+     *     <li><b>MAP: </b> is an map of named child items.</li>
+     * </ul>
+     * @return the area group type, default is {@link org.apache.tamaya.spi.PropertyValue.ValueType#MAP}.
+     */
+    PropertyValue.ValueType areaType() default PropertyValue.ValueType.MAP;
+
+    /**
+     * The minimal cardinality required. A cardinality > 0 means that the corresponding area must be present
+     * in your configuration. This can be ensured/checked by a configuration validation system.
+     * @return the minimal cardinality.
+     */
+    int min() default 0;
+
+    /**
+     * The maximal cardinality allowed. A cardinality > 0 means that the corresponding area must not be present
+     * in your configuration more than the configured times. This can be ensured/checked by a configuration validation
+     * system.
+     * @return the maximal cardinality.
+     */
+    int max() default 0;
+
+    /**
+     * The properties managed in this area.
+     * @return the properties managed within this area.
+     */
+    ConfigPropertySpec[] properties() default {};
+
+    /**
+     * Allows to define that this area is only required, if any of these configured areas are present in your config.
+     * @return the area dependencies. This can be ensured/checked by a configuration validation
+     */
+    String[] dependsOnAreas() default {};
+
+    /**
+     * Allows to define that this area is only required, if any of these configured properties are present in your config.
+     * @return the area dependencies. This can be ensured/checked by a configuration validation
+     */
+    String[] dependsOnProperties() default {};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java
new file mode 100644
index 0000000..5735c16
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java
@@ -0,0 +1,38 @@
+/*
+ * 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is the container annotation for the repeatable {@link ConfigAreaSpec} annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigAreaSpecs {
+
+    /**
+     * The contained child annotations.
+     * @return the contained annotations.
+     */
+    ConfigAreaSpec[] value()default{};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java
new file mode 100644
index 0000000..0e77e05
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java
@@ -0,0 +1,63 @@
+/*
+ * 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigPropertySpec {
+
+//    boolean id() default false;
+
+    /**
+     * The property name. The full property key is a combination of the parent
+     * @return
+     */
+    String name() default "";
+
+    /**
+     * Define a description of the property.
+     * @return the description.
+     */
+    String description() default "";
+
+    /**
+     * The property's value type, by default {@link String}.
+     * @return the property's value type.
+     */
+    Class<?> valueType() default String.class;
+
+    /**
+     * Allows to define that this property is only required, if any of these configured areas are present in your config.
+     * @return the area dependencies. This can be ensured/checked by a configuration validation
+     */
+    String[] dependsOnAreas() default {};
+
+    /**
+     * Allows to define that this property is only required, if any of these configured properties are present in your config.
+     * @return the area dependencies. This can be ensured/checked by a configuration validation
+     */
+    String[] dependsOnProperties() default {};
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java
new file mode 100644
index 0000000..a8fd4f0
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java
@@ -0,0 +1,38 @@
+/*
+ * 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is the container annotation for the repeatable {@link ConfigPropertySpec} annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigPropertySpecs {
+
+    /**
+     * The contained child annotations.
+     * @return the contained annotations.
+     */
+    ConfigPropertySpec[] value()default{};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java
new file mode 100644
index 0000000..5f0c798
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java
@@ -0,0 +1,48 @@
+/*
+ * 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.doc.annot;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to specify the main configuration properties.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConfigSpec {
+    String name() default "";
+    String version() default "";
+
+    /**
+     * Define a description of the configuration.
+     * @return the description.
+     */
+    String description() default "";
+
+    /**
+     * Should loading of the configuration fail, if a validation failed, default is {@code false}, where
+     * a warning is logged only.
+     * @return true to fail config load on configuration validation failures.
+     */
+    boolean failOnErrors() default false;
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java
new file mode 100644
index 0000000..b37bf80
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java
@@ -0,0 +1,127 @@
+/*
+ * 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.doc.formats;
+
+import j2html.tags.ContainerTag;
+import org.apache.tamaya.doc.DocFormat;
+import org.apache.tamaya.doc.DocumentedArea;
+import org.apache.tamaya.doc.DocumentedConfiguration;
+import org.apache.tamaya.doc.DocumentedProperty;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static j2html.TagCreator.*;
+
+public class HtmlDocFormat implements DocFormat<String> {
+    @Override
+    public String apply(DocumentedConfiguration documentedConfiguration) {
+        List<ContainerTag> areaTags = new ArrayList<>();
+        for(DocumentedArea area:documentedConfiguration.getAllAreasSorted()) {
+            areaTags.addAll(createAreaEntries(area, null));
+        }
+        ContainerTag propertiesTable = createPropertiesTable(documentedConfiguration.getAllPropertiesSorted(), null);
+
+        ContainerTag head = createHead(documentedConfiguration);
+        ContainerTag body = body()
+                .with(
+                    h1("Configuration Documentation (" + documentedConfiguration.getName() + ")"),
+                    p("Version: " + documentedConfiguration.getVersion()),
+                    h2("Documented Areas")).with(areaTags)
+                .with(
+                    h2("Documented Properties"),
+                    propertiesTable);
+        String result = html(head, body).render();
+        writeResultToFile(result);
+        return result;
+    }
+
+    private void writeResultToFile(String result) {
+        File file = new File("./doc.html");
+        FileWriter w;
+        try {
+            w = new FileWriter(file);
+            w.append(result);
+            w.flush();
+            w.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private ContainerTag createHead(DocumentedConfiguration config) {
+        return head(title("Tamaya Configuration - " + config.getName() + " " +
+                        config.getVersion()),
+                meta().withCharset("utf-8"),
+                meta().withName("viewport").withContent("width=device-width, initial-scale=0.9, shrink-to-fit=yes"),
+                link().withRel("stylesheet")
+                        .withHref("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css")
+                        .attr("integrity","sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm")
+                        .attr("crossorigin","anonymous"));
+    }
+
+    private ContainerTag createPropertiesTable(List<DocumentedProperty> properties, String parentArea) {
+        List<ContainerTag> propertyRows = new ArrayList<>();
+        for (DocumentedProperty prop : properties) {
+            propertyRows.add(tr(
+                    td(pre(prop.getName().replace(", ", "\n"))).attr("scope","row"),
+                    td(Object.class==prop.getValueType()?"":prop.getValueType().getName()),
+                    td(prop.getDescription()),
+                    td(i(prop.getDefaultValue()))
+            ));
+        }
+        ContainerTag propertiesTable = table(
+                thead(tr(
+                        th("Key(s)").attr("scope","col"),
+                        th("Valuetype").attr("scope","col"),
+                        th("Description").attr("width", "75%").attr("scope","col"),
+                        th("Default").attr("scope","col").attr("width", "15%")
+                )).withClass("thead-dark")
+        ).with(propertyRows.toArray(new ContainerTag[propertyRows.size()]))
+                .withClass("table table-striped table-hover table-bordered table-sm")
+                .attr("width", "90%");
+        return propertiesTable;
+    }
+
+    private List<ContainerTag> createAreaEntries(DocumentedArea area, String parentArea) {
+        List<ContainerTag> result = new ArrayList<>();
+        if(parentArea==null){
+            result.add(h4(area.getPath()));
+        }else{
+            result.add(h4(parentArea + "."+ area.getPath()));
+        }
+        result.add(ul(
+                li(b("Group Type: "), text(area.getGroupType().toString()),
+                li(b("Valuetype: "), text(area.getValueType().getName()))
+                        .withCondHidden(Object.class==area.getValueType()),
+                li(b("Description: "), text(area.getDescription()))
+                        .withCondHidden(area.getDescription()==null),
+                li(b("Properties: ")).with(createPropertiesTable(area.getPropertiesSorted(), area.getPath()))
+                        .withCondHidden(area.getProperties().isEmpty())
+        )));
+        for(DocumentedArea subArea:area.getAreasSorted()){
+            result.addAll(createAreaEntries(subArea, area.getPath()));
+        }
+        return result;
+    }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java
new file mode 100644
index 0000000..4172cb9
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java
@@ -0,0 +1,124 @@
+/*
+ * 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.doc.formats;
+
+import org.apache.tamaya.doc.DocFormat;
+import org.apache.tamaya.doc.DocumentedConfiguration;
+import org.apache.tamaya.doc.DocumentedArea;
+import org.apache.tamaya.doc.DocumentedProperty;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class TextDocFormat implements DocFormat<String> {
+    @Override
+    public String apply(DocumentedConfiguration documentedConfiguration) {
+        StringBuilder b = new StringBuilder();
+        b.append("Configuration:\n");
+        b.append("  Spec    : ").append(documentedConfiguration.getName()).append('\n');
+        b.append("  Version : ").append(documentedConfiguration.getVersion()).append('\n');
+        if(documentedConfiguration.getOwnerClass()!=null){
+            b.append("  Owner   : ").append(documentedConfiguration.getOwnerClass().getName()).append('\n');
+        }
+        if(!documentedConfiguration.getAreas().isEmpty()) {
+            b.append("Areas: ").append('\n');
+            for (DocumentedArea area : documentedConfiguration.getAreas().values()) {
+                printArea("  ", area, b);
+            }
+        }
+        if(!documentedConfiguration.getProperties().isEmpty()) {
+            b.append("Properties: ").append('\n');
+            for (DocumentedProperty prop : documentedConfiguration.getProperties().values()) {
+                printProperty("  ", prop, b);
+            }
+        }
+        return b.toString();
+    }
+
+    private void printArea(String inset, DocumentedArea area, StringBuilder b) {
+        if(!area.getPath().isEmpty()) {
+            b.append(inset).append("- name     : ").append(area.getPath()).append("\n");
+        }else{
+            b.append(inset).append("- name     : NONE\n");
+        }
+//        b.append(inset).    append("  Type     : area\n");
+        if(area.getOwner()!=null){
+            b.append(inset).append("  Owner    : ").append(printOwner(area.getOwner())).append('\n');
+        }
+        if(area.getDescription()!=null) {
+            b.append(inset).append("  Descr    : ").append(area.getDescription()).append('\n');
+        }
+        b.append(inset).    append("  Areatype : ").append(area.getGroupType()).append('\n');
+        if(area.getMinCardinality()!=0) {
+            b.append(inset).append("  Min      : ").append(area.getMinCardinality()).append('\n');
+        }
+        if(area.getMaxCardinality()!=0) {
+            b.append(inset).append("  Max      : ").append(area.getMaxCardinality()).append('\n');
+        }
+        if(area.getValueType()!=Object.class) {
+            b.append(inset).append("  Value    : ").append(area.getValueType().getName()).append('\n');
+        }
+        if(!area.getProperties().isEmpty()) {
+            b.append(inset).append("  Properties : ").append('\n');
+            for (DocumentedProperty prop : area.getProperties().values()) {
+                printProperty(inset + "    ", prop, b);
+            }
+        }
+        if(!area.getAreas().isEmpty()) {
+            b.append(inset).append("  Areas   : ").append('\n');
+            for (DocumentedArea childArea : area.getAreas().values()) {
+                printArea(inset + "  ", childArea, b);
+            }
+        }
+    }
+
+    private void printProperty(String inset, DocumentedProperty prop, StringBuilder b) {
+        b.append(inset).append("- Name     : ").append(prop.getName()).append("\n");
+//        b.append(inset).append("  Type     : property\n");
+        if(prop.getOwner()!=null){
+            b.append(inset).append("  Owner    : ").append(printOwner(prop.getOwner())).append('\n');
+        }
+        if(prop.getDescription()!=null) {
+            b.append(inset).append("  Descr    : ").append(prop.getDescription()).append('\n');
+        }
+        b.append(inset).append("  Value    : ").append(prop.getValueType().getName()).append('\n');
+    }
+
+    private String printOwner(AnnotatedElement owner) {
+        if(owner instanceof Type){
+            return ((Type)owner).getTypeName();
+        }else if(owner instanceof Field){
+            Field f = (Field)owner;
+            return f.getDeclaringClass().getName()+ '#' + f.getName()+": " + f.getType().getName();
+        }
+        else if(owner instanceof Method){
+            Method m = (Method)owner;
+            return m.getDeclaringClass().getName()+ '#' + m.getName()+
+                    "("+String.join(", ", Stream.of(m.getParameterTypes()).map(c -> c.getName())
+                    .collect(Collectors.toList())) + "): " +
+                    m.getReturnType().getName();
+        }else{
+            return String.valueOf(owner);
+        }
+    }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java b/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java
new file mode 100644
index 0000000..9a6b548
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java
@@ -0,0 +1,26 @@
+/*
+ * 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 java.util.function.Function;
+
+public interface ConfigValidator extends Function<Configuration, ValidationResult> {
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/Finding.java b/documentation/src/main/java/org/apache/tamaya/validation/Finding.java
new file mode 100644
index 0000000..d75c677
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/Finding.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+/**
+ * Enum type describing the different validation results supported.
+ */
+public enum Finding {
+    /**
+     * The validated item is valid
+     */
+    VALID,
+    /**
+     * The validated item is deprecated.
+     */
+    DEPRECATED,
+    /**
+     * The validated item is correct, but the createValue is worth a warning.
+     */
+    WARNING,
+    /**
+     * A required parameter or section is missing.
+     */
+    MISSING,
+    /**
+     * The validated item has an invalid createValue.
+     */
+    ERROR;
+
+    /**
+     * Method to quickly evaluate if the current state is an error state.
+     *
+     * @return true, if the state is not ERROR or MISSING.
+     */
+    boolean isError() {
+        return this.ordinal() == MISSING.ordinal() || this.ordinal() == ERROR.ordinal();
+    }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java b/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java
new file mode 100644
index 0000000..2fb40a8
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java
@@ -0,0 +1,194 @@
+/*
+ * 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.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+
+import java.lang.annotation.Annotation;
+import java.util.Objects;
+
+/**
+ * Models a partial configuration configModel result.
+ */
+public final class ValidationCheck {
+    /**
+     * The configModel result.
+     */
+    private final Finding result;
+    /**
+     * The configModel message.
+     */
+    private final String message;
+
+    private final Annotation spec;
+
+    /**
+     * Get the checks annotation.
+     * @return the annotation.
+     */
+    public Class getSpecType(){
+        return spec.getClass();
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the specification item, not null.
+     * @return a new validation result containing valid parts createObject the given model.
+     */
+    public static ValidationCheck createValid(Annotation spec) {
+        return new ValidationCheck(spec, Finding.VALID, null);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @return a new validation result containing missing parts createObject the given model.
+     */
+    public static ValidationCheck createMissing(Annotation spec) {
+        return new ValidationCheck(spec, Finding.MISSING, null);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @param message Additional message to be shown (optional).
+     * @return a new validation result containing missing parts createObject the given model with a message.
+     */
+    public static ValidationCheck createMissing(Annotation spec, String message) {
+        return new ValidationCheck(spec, Finding.MISSING, message);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @param error error message to addNode.
+     * @return a new validation result containing erroneous parts createObject the given model with the given error message.
+     */
+    public static ValidationCheck createError(Annotation spec, String error) {
+        return new ValidationCheck(spec, Finding.ERROR, error);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @param warning warning message to addNode.
+     * @return a new validation result containing warning parts createObject the given model with the given warning message.
+     */
+    public static ValidationCheck createWarning(Annotation spec, String warning) {
+        return new ValidationCheck(spec, Finding.WARNING, warning);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @param alternativeUsage allows setting a message to indicate non-deprecated replacement, maybe null.
+     * @return a new validation result containing deprecated parts createObject the given model with an optional message.
+     */
+    public static ValidationCheck createDeprecated(Annotation spec, String alternativeUsage) {
+        return new ValidationCheck(spec, Finding.DEPRECATED, alternativeUsage != null ? "Use instead: " + alternativeUsage : null);
+    }
+
+    /**
+     * Creates a new ValidationResult.
+     *
+     * @param spec the validated specification, not null.
+     * @return a new validation result containing deprecated parts createObject the given model.
+     */
+    public static ValidationCheck createDeprecated(Annotation spec) {
+        return new ValidationCheck(spec, Finding.DEPRECATED, null);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param spec the validated specification, not null.
+     * @param result     the configModel result, not null.
+     * @param message    the detail message.
+     * @return new validation result.
+     */
+    public static ValidationCheck create(Annotation spec, Finding result, String message) {
+        return new ValidationCheck(spec, result, message);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param spec the validated specification, not null.
+     * @param result     the configModel result, not null.
+     * @param message    the detail message.
+     */
+    private ValidationCheck(Annotation spec, Finding result, String message) {
+        this.message = message;
+        this.spec = Objects.requireNonNull(spec);
+        this.result = Objects.requireNonNull(result);
+    }
+
+    /**
+     * Get the configModel section.
+     *
+     * @return the section, never null.
+     */
+    public Annotation getSpec() {
+        return spec;
+    }
+
+    /**
+     * Get the configModel result.
+     *
+     * @return the result, never null.
+     */
+    public Finding getResult() {
+        return result;
+    }
+
+    /**
+     * Get the detail message.
+     *
+     * @return the detail message, or null.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public String toString() {
+        String finalMessage = "";
+        if (message != null) {
+            finalMessage = " -> " + message;
+        }
+        if(spec instanceof ConfigPropertySpec){
+            ConfigPropertySpec pspec = (ConfigPropertySpec)spec;
+            return result + ": " + pspec.name() + " (property)"+finalMessage + '\n';
+        }
+        else if(spec instanceof ConfigAreaSpec){
+            ConfigAreaSpec gspec = (ConfigAreaSpec)spec;
+            return result + ": " + gspec.path() + " (group)"+finalMessage + '\n';
+        }
+        return result + ": " + spec + ")"+finalMessage + '\n';
+    }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java b/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java
new file mode 100644
index 0000000..6ff8a4c
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java
@@ -0,0 +1,29 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+
+public class ValidationResult {
+
+    public Collection<ValidationCheck> getRules(){
+        return Collections.emptyList();
+    }
+}
diff --git a/documentation/src/main/resources/img/tamaya.png b/documentation/src/main/resources/img/tamaya.png
new file mode 100644
index 0000000..16df5d7
Binary files /dev/null and b/documentation/src/main/resources/img/tamaya.png differ
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java b/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java
new file mode 100644
index 0000000..223b3d4
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java
@@ -0,0 +1,62 @@
+package org.apache.tamaya.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.doc.annot.ConfigSpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.api.ConfigDefaultSections;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.net.URL;
+
+@ConfigSpec(
+        name="Test",
+        version="0.1.0",
+        description = "Tomcat Configuration based on Tamaya"
+)
+@ConfigAreaSpec(
+        path = "kubernetes",
+        description = "Kubernetes Settings",
+        areaType = PropertyValue.ValueType.MAP,
+        max = 1
+)
+@ConfigAreaSpec(
+        path = "kubernetes.security",
+        description = "Kubernetes Security Settings",
+        areaType = PropertyValue.ValueType.MAP,
+        max = 1
+)
+@ConfigAreaSpec(
+        path = "kubernetes.cluster",
+        description = "Kubernetes Cluster Options",
+        areaType = PropertyValue.ValueType.MAP,
+        max = 1
+)
+@ConfigAreaSpec(
+        path = "<root>",
+        description = "Main Options",
+        areaType = PropertyValue.ValueType.MAP,
+        properties = {
+                @ConfigPropertySpec(name="log", description ="Log the server startup in detail, default: false.",
+                        valueType = Boolean.class),
+                @ConfigPropertySpec(name="refresh", description = "Refresh interval in millis, default: 1000ms",
+                        valueType = Long.class),
+        }
+)
+public interface AnnotBasedStandaloneConfigDocumentation {
+
+    @ConfigAreaSpec(
+            description = "Tomcat Server Endpoints",
+            min = 1,
+            areaType = PropertyValue.ValueType.ARRAY)
+    @ConfigDefaultSections("servers")
+    class Server{
+        @ConfigPropertySpec(description = "The server name.")
+        @Config(required = true)
+        private String name;
+        @ConfigPropertySpec(description = "The server url.")
+        @Config(required = true)
+        private URL url;
+    }
+
+}
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java b/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java
new file mode 100644
index 0000000..184c4ce
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java
@@ -0,0 +1,95 @@
+/*
+ * 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.tamaya.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.doc.annot.ConfigSpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.api.DynamicValue;
+import org.apache.tamaya.inject.api.NoConfig;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An example showing some basic annotations, using an interface to be proxied by the
+ * configuration system, nevertheless extending the overall Configuration interface.
+ * Created by Anatole on 15.02.14.
+ */
+@ConfigSpec(
+        name="Test",
+        version="0.1.0"
+)
+@ConfigAreaSpec(
+        path="",
+        description = "Default root configuration",
+        areaType = PropertyValue.ValueType.MAP
+)
+public class AnnotatedDocConfigBean {
+
+    @ConfigPropertySpec(description = "Tests a multi key resolution")
+    @Config(value = {"foo.bar.myprop", "mp", "common.testdata.myProperty"}, defaultValue = "ET")
+    public String myParameter;
+
+    @ConfigPropertySpec(description = "Tests a simple String description")
+    @Config("simple_value")
+    public String simpleValue;
+
+    @ConfigPropertySpec(description = "Another test value without any explicit definition")
+    @Config
+    String anotherValue;
+
+    @ConfigPropertySpec(description = "An explicit config parameter value.")
+    @Config("[host.name]")
+    private String hostName;
+
+    @ConfigPropertySpec(description = "An non String typed instance.")
+    @Config("host.name")
+    private DynamicValue<String> dynamicHostname;
+
+    @NoConfig
+    public String javaVersion;
+
+    public String getAnotherValue(){
+        return anotherValue;
+    }
+
+    public String getHostName(){
+        return hostName;
+    }
+
+    public DynamicValue<String> getDynamicValue(){
+        return dynamicHostname;
+    }
+
+    @NoConfig
+    private List<String> events = new ArrayList<>();
+
+    // verify we don't try to inject final fields
+    public static final String CONSTANT = "a constant";
+
+
+    @Config("java.version")
+    void setJavaVersion(String version){
+        this.javaVersion = version;
+    }
+
+}
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java b/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java
new file mode 100644
index 0000000..3b6bdcd
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java
@@ -0,0 +1,46 @@
+package org.apache.tamaya.doc;
+
+import org.apache.tamaya.doc.formats.HtmlDocFormat;
+import org.apache.tamaya.doc.formats.TextDocFormat;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ConfigDocumenterTest {
+
+    @Test
+    public void getDocumentationAndPrint_ConfigBean() {
+        ConfigDocumenter reader = new ConfigDocumenter();
+        reader.readClasses(AnnotatedDocConfigBean.class);
+        DocumentedConfiguration documentation = reader.getDocumentation();
+        assertNotNull(documentation);
+        System.out.println(new TextDocFormat().apply(documentation));
+    }
+
+    @Test
+    public void getDocumentationAndPrint_AnnotationType() {
+        ConfigDocumenter reader = new ConfigDocumenter();
+        reader.readClasses(AnnotBasedStandaloneConfigDocumentation.class);
+        DocumentedConfiguration documentation = reader.getDocumentation();
+        assertNotNull(documentation);
+        System.out.println(new TextDocFormat().apply(documentation));
+    }
+
+    @Test
+    public void getDocumentationAndPrint_Package() {
+        ConfigDocumenter reader = new ConfigDocumenter();
+        reader.readPackages("org.apache.tamaya.doc");
+        DocumentedConfiguration documentation = reader.getDocumentation();
+        assertNotNull(documentation);
+        System.out.println(new TextDocFormat().apply(documentation));
+    }
+
+    @Test
+    public void getDocumentationAndPrint_Package_html() {
+        ConfigDocumenter reader = new ConfigDocumenter();
+        reader.readPackages("org.apache.tamaya.doc");
+        DocumentedConfiguration documentation = reader.getDocumentation();
+        assertNotNull(documentation);
+        System.out.println(new HtmlDocFormat().apply(documentation));
+    }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 715ca1d..167366d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -809,6 +809,7 @@ under the License.
         <module>uom</module>
         <module>vertx</module>
         <module>configjsr</module>
+        <module>documentation</module>
         <!-- Once the API is officially available ... -->
         <!--   module>configjsr</module-->
     </modules>