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

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new b1d8df4  Swagger UI enhancements.
b1d8df4 is described below

commit b1d8df48176cddbdf7d2a92bf454e0589af92cfa
Author: JamesBognar <jamesbog...@apache.org>
AuthorDate: Sun Apr 15 14:27:11 2018 -0400

    Swagger UI enhancements.
---
 .../apache/juneau/jena/RdfBeanPropertyMeta.java    |  16 +-
 .../org/apache/juneau/jena/RdfParserSession.java   |   7 +-
 .../apache/juneau/jena/RdfSerializerSession.java   |  67 ++--
 .../apache/juneau/html/HtmlBeanPropertyMeta.java   |  33 ++
 .../java/org/apache/juneau/html/HtmlClassMeta.java |  18 +
 .../apache/juneau/html/HtmlSerializerSession.java  |  90 +++--
 .../java/org/apache/juneau/html/HtmlWriter.java    |   2 +-
 .../org/apache/juneau/html/annotation/Html.java    |   1 -
 .../apache/juneau/html/annotation/HtmlFormat.java  |  12 +
 .../java/org/apache/juneau/internal/AsciiSet.java  |  12 +
 .../org/apache/juneau/internal/ObjectUtils.java    |   3 +-
 .../org/apache/juneau/internal/StringUtils.java    |  84 ++++-
 .../jsonschema/JsonSchemaBeanPropertyMeta.java     |  13 +
 .../org/apache/juneau/xml/XmlBeanPropertyMeta.java |   9 +
 .../apache/juneau/xml/XmlSerializerSession.java    |  44 ++-
 .../main/java/org/apache/juneau/xml/XmlUtils.java  |   2 +-
 .../xmlschema/XmlSchemaSerializerSession.java      |   4 +-
 juneau-doc/src/main/javadoc/overview.html          |  26 +-
 juneau-examples/juneau-examples-rest/examples.cfg  |  15 +-
 .../juneau/examples/rest/JsonSchemaResource.java   |  22 +-
 .../apache/juneau/examples/rest/RootResources.java |   1 -
 .../juneau/examples/rest/SqlQueryResource.java     |  34 +-
 .../juneau/examples/rest/StaticFilesResource.java  |  83 ----
 .../juneau/examples/rest/TempDirResource.java      |   9 +-
 .../examples/rest/UrlEncodedFormResource.java      |  13 +-
 .../src/main/resources/examples.cfg                | 147 --------
 .../examples/rest/JsonSchemaResource_example.json  |  50 ++-
 .../examples/rest/TestMultiPartFormPostsTest.java  |   2 +-
 .../microservice/resources/ConfigResource.java     | 233 ++++++------
 .../microservice/resources/DirectoryResource.java  | 293 +++++++-------
 .../microservice/resources/LogsResource.java       | 420 +++++++++++----------
 .../my-microservice.cfg                            |  16 +-
 .../apache/juneau/rest/BasicRestInfoProvider.java  |   4 +-
 33 files changed, 926 insertions(+), 859 deletions(-)

diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
index dd1de16..d1054de 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
@@ -12,8 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.jena;
 
-import static org.apache.juneau.jena.RdfCollectionFormat.*;
-
 import java.util.*;
 
 import org.apache.juneau.*;
@@ -26,7 +24,13 @@ import org.apache.juneau.xml.*;
  */
 public class RdfBeanPropertyMeta extends BeanPropertyMetaExtended {
 
-       private RdfCollectionFormat collectionFormat = DEFAULT;
+       /**
+        * Default instance.
+        */
+       public static final RdfBeanPropertyMeta DEFAULT = new 
RdfBeanPropertyMeta();
+
+       
+       private RdfCollectionFormat collectionFormat = 
RdfCollectionFormat.DEFAULT;
        private Namespace namespace = null;
        private boolean isBeanUri;
 
@@ -42,7 +46,7 @@ public class RdfBeanPropertyMeta extends 
BeanPropertyMetaExtended {
                List<RdfSchema> schemas = bpm.findAnnotations(RdfSchema.class);
 
                for (Rdf rdf : rdfs) {
-                       if (collectionFormat == DEFAULT)
+                       if (collectionFormat == RdfCollectionFormat.DEFAULT)
                                collectionFormat = rdf.collectionFormat();
                        if (rdf.beanUri())
                                isBeanUri = true;
@@ -50,6 +54,10 @@ public class RdfBeanPropertyMeta extends 
BeanPropertyMetaExtended {
 
                namespace = RdfUtils.findNamespace(rdfs, schemas);
        }
+       
+       private RdfBeanPropertyMeta() {
+               super(null);
+       }
 
        /**
         * Returns the RDF collection format of this property from the {@link 
Rdf#collectionFormat} annotation on this bean 
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
index 3eff76c..5387a91 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
@@ -221,8 +221,11 @@ public class RdfParserSession extends ReaderParserSession {
        }
 
        private boolean isMultiValuedCollections(BeanPropertyMeta pMeta) {
-               if (pMeta != null && 
pMeta.getExtendedMeta(RdfBeanPropertyMeta.class).getCollectionFormat() != 
RdfCollectionFormat.DEFAULT)
-                       return 
pMeta.getExtendedMeta(RdfBeanPropertyMeta.class).getCollectionFormat() == 
RdfCollectionFormat.MULTI_VALUED;
+               RdfBeanPropertyMeta bpRdf = (pMeta == null ? 
RdfBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(RdfBeanPropertyMeta.class));
+               
+               if (bpRdf.getCollectionFormat() != RdfCollectionFormat.DEFAULT)
+                       return bpRdf.getCollectionFormat() == 
RdfCollectionFormat.MULTI_VALUED;
+               
                return collectionFormat == RdfCollectionFormat.MULTI_VALUED;
        }
 
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
index 06ffa7c..4e83b98 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
@@ -261,7 +261,7 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                        if (o instanceof BeanMap) {
                                BeanMap bm = (BeanMap)o;
                                Object uri = null;
-                               RdfBeanMeta rbm = 
(RdfBeanMeta)bm.getMeta().getExtendedMeta(RdfBeanMeta.class);
+                               RdfBeanMeta rbm = bRdf(bm.getMeta());
                                if (rbm.hasBeanUri())
                                        uri = rbm.getBeanUriProperty().get(bm, 
null);
                                String uri2 = getUri(uri, null);
@@ -276,7 +276,7 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                } else if (sType.isBean()) {
                        BeanMap bm = toBeanMap(o);
                        Object uri = null;
-                       RdfBeanMeta rbm = 
(RdfBeanMeta)bm.getMeta().getExtendedMeta(RdfBeanMeta.class);
+                       RdfBeanMeta rbm = bRdf(bm.getMeta());
                        if (rbm.hasBeanUri())
                                uri = rbm.getBeanUriProperty().get(bm, null);
                        String uri2 = getUri(uri, null);
@@ -284,13 +284,17 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                        serializeBeanMap(bm, (Resource)n, typeName);
 
                } else if (sType.isCollectionOrArray() || (wType != null && 
wType.isCollection())) {
+                       
                        Collection c = sort(sType.isCollection() ? 
(Collection)o : toList(sType.getInnerClass(), o));
                        RdfCollectionFormat f = collectionFormat;
-                       RdfClassMeta rcm = 
sType.getExtendedMeta(RdfClassMeta.class);
-                       if (rcm.getCollectionFormat() != 
RdfCollectionFormat.DEFAULT)
-                               f = rcm.getCollectionFormat();
-                       if (bpm != null && 
bpm.getExtendedMeta(RdfBeanPropertyMeta.class).getCollectionFormat() != 
RdfCollectionFormat.DEFAULT)
-                               f = 
bpm.getExtendedMeta(RdfBeanPropertyMeta.class).getCollectionFormat();
+                       RdfClassMeta cRdf = cRdf(sType);
+                       RdfBeanPropertyMeta bpRdf = bpRdf(bpm);
+
+                       if (cRdf.getCollectionFormat() != 
RdfCollectionFormat.DEFAULT)
+                               f = cRdf.getCollectionFormat();
+                       if (bpRdf.getCollectionFormat() != 
RdfCollectionFormat.DEFAULT)
+                               f = bpRdf.getCollectionFormat();
+                       
                        switch (f) {
                                case BAG: n = serializeToContainer(c, eType, 
m.createBag()); break;
                                case LIST: n = serializeToList(c, eType); break;
@@ -346,32 +350,34 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                List<BeanPropertyValue> l = m.getValues(isTrimNulls(), typeName 
!= null ? createBeanTypeNameProperty(m, typeName) : null);
                Collections.reverse(l);
                for (BeanPropertyValue bpv : l) {
-                       BeanPropertyMeta pMeta = bpv.getMeta();
-                       ClassMeta<?> cMeta = pMeta.getClassMeta();
-
-                       if 
(pMeta.getExtendedMeta(RdfBeanPropertyMeta.class).isBeanUri())
+                       
+                       BeanPropertyMeta bpMeta = bpv.getMeta();
+                       ClassMeta<?> cMeta = bpMeta.getClassMeta();
+                       RdfBeanPropertyMeta bpRdf = bpRdf(bpMeta);
+                       XmlBeanPropertyMeta bpXml = bpXml(bpMeta);
+                       
+                       if (bpRdf.isBeanUri())
                                continue;
 
                        String key = bpv.getName();
                        Object value = bpv.getValue();
                        Throwable t = bpv.getThrown();
                        if (t != null)
-                               onBeanGetterException(pMeta, t);
+                               onBeanGetterException(bpMeta, t);
 
                        if (canIgnoreValue(cMeta, key, value))
                                continue;
 
-                       BeanPropertyMeta bpm = bpv.getMeta();
-                       Namespace ns = 
bpm.getExtendedMeta(RdfBeanPropertyMeta.class).getNamespace();
+                       Namespace ns = bpRdf.getNamespace();
                        if (ns == null && useXmlNamespaces)
-                               ns = 
bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+                               ns = bpXml.getNamespace();
                        if (ns == null)
                                ns = juneauBpNs;
                        else if (autoDetectNamespaces)
                                addModelPrefix(ns);
 
                        Property p = model.createProperty(ns.getUri(), 
encodeElementName(key));
-                       RDFNode n = serializeAnything(value, pMeta.isUri(), 
cMeta, key, pMeta, r);
+                       RDFNode n = serializeAnything(value, bpMeta.isUri(), 
cMeta, key, bpMeta, r);
                        if (n != null)
                                r.addProperty(p, n);
                }
@@ -399,14 +405,15 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
 
        private void serializeToMultiProperties(Collection c, ClassMeta<?> 
sType, 
                        BeanPropertyMeta bpm, String attrName, Resource 
parentResource) throws Exception {
+               
                ClassMeta<?> elementType = sType.getElementType();
+               RdfBeanPropertyMeta bpRdf = bpRdf(bpm);
+               XmlBeanPropertyMeta bpXml = bpXml(bpm);
+               
                for (Object e : c) {
-                       Namespace ns = null;
-                       if (bpm != null) {
-                               ns = 
bpm.getExtendedMeta(RdfBeanPropertyMeta.class).getNamespace();
-                               if (ns == null && useXmlNamespaces)
-                                       ns = 
bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
-                       }
+                       Namespace ns = bpRdf.getNamespace();
+                       if (ns == null && useXmlNamespaces)
+                               ns = bpXml.getNamespace();
                        if (ns == null)
                                ns = juneauBpNs;
                        else if (autoDetectNamespaces)
@@ -416,4 +423,20 @@ public final class RdfSerializerSession extends 
WriterSerializerSession {
                        parentResource.addProperty(p, n2);
                }
        }
+       
+       private static RdfClassMeta cRdf(ClassMeta<?> cm) {
+               return cm.getExtendedMeta(RdfClassMeta.class);
+       }
+
+       private static XmlBeanPropertyMeta bpXml(BeanPropertyMeta pMeta) {
+               return pMeta == null ? XmlBeanPropertyMeta.DEFAULT : 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+       }
+       
+       private static RdfBeanPropertyMeta bpRdf(BeanPropertyMeta pMeta) {
+               return pMeta == null ? RdfBeanPropertyMeta.DEFAULT : 
pMeta.getExtendedMeta(RdfBeanPropertyMeta.class);
+       }
+
+       private static RdfBeanMeta bRdf(BeanMeta bm) {
+               return (RdfBeanMeta)bm.getExtendedMeta(RdfBeanMeta.class);
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
index 5380ebf..c9758d9 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
@@ -22,6 +22,11 @@ import org.apache.juneau.html.annotation.*;
 @SuppressWarnings("rawtypes")
 public final class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 
+       /**
+        * Default instance.
+        */
+       public static final HtmlBeanPropertyMeta DEFAULT = new 
HtmlBeanPropertyMeta();
+       
        private final boolean noTables, noTableHeaders;
        private final HtmlFormat format;
        private final HtmlRender render;
@@ -51,6 +56,16 @@ public final class HtmlBeanPropertyMeta extends 
BeanPropertyMetaExtended {
                this.anchorText = b.anchorText;
        }
 
+       private HtmlBeanPropertyMeta() {
+               super(null);
+               this.format = HtmlFormat.HTML;
+               this.noTables = false;
+               this.noTableHeaders = false;
+               this.render = null;
+               this.link = null;
+               this.anchorText = null;
+       }
+       
        static final class Builder {
                boolean noTables, noTableHeaders;
                HtmlFormat format = HtmlFormat.HTML;
@@ -111,6 +126,24 @@ public final class HtmlBeanPropertyMeta extends 
BeanPropertyMetaExtended {
        }
 
        /**
+        * Returns <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_CDC}.
+        * 
+        * @return <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_CDC}.
+        */
+       protected boolean isHtmlCdc() {
+               return format == HtmlFormat.HTML_CDC;
+       }
+
+       /**
+        * Returns <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_SDC}.
+        * 
+        * @return <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_SDC}.
+        */
+       protected boolean isHtmlSdc() {
+               return format == HtmlFormat.HTML_SDC;
+       }
+
+       /**
         * Returns whether this bean property should not be serialized as an 
HTML table.
         * 
         * @return
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
index 439c2ea..7de6d49 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
@@ -94,6 +94,24 @@ public class HtmlClassMeta extends ClassMetaExtended {
        }
 
        /**
+        * Returns <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_CDC}.
+        * 
+        * @return <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_CDC}.
+        */
+       protected boolean isHtmlCdc() {
+               return format == HtmlFormat.HTML_CDC;
+       }
+
+       /**
+        * Returns <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_SDC}.
+        * 
+        * @return <jk>true</jk> if {@link #getFormat()} returns {@link 
HtmlFormat#HTML_SDC}.
+        */
+       protected boolean isHtmlSdc() {
+               return format == HtmlFormat.HTML_SDC;
+       }
+
+       /**
         * Returns the {@link Html#noTables() @Html.noTables()} annotation 
defined on the class.
         * 
         * @return The value of the annotation.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 94871d3..4723e56 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -14,6 +14,7 @@ package org.apache.juneau.html;
 
 import static org.apache.juneau.html.HtmlSerializer.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ObjectUtils.*;
 import static org.apache.juneau.xml.XmlSerializerSession.ContentResult.*;
 
 import java.io.*;
@@ -240,8 +241,9 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                                type = getClassMetaForObject(o);
                }
 
-               HtmlClassMeta html = type.getExtendedMeta(HtmlClassMeta.class);
-               if (type.isMapOrBean() && ! html.isXml()) 
+               HtmlClassMeta cHtml = cHtml(type);
+               
+               if (type.isMapOrBean() && ! cHtml.isXml()) 
                        return serializeAnything(out, o, eType, elementName, 
pMeta, 0, false);
                
                return super.serializeAnything(out, o, eType, elementName, 
elementNamespace, addNamespaceUris, format, isMixed, preserveWhitespace, pMeta);
@@ -322,10 +324,10 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                                return ContentResult.CR_MIXED;
                        }
 
-                       HtmlClassMeta html = 
sType.getExtendedMeta(HtmlClassMeta.class);
-                       HtmlRender render = (pMeta == null ? null : 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getRender());
-                       if (render == null)
-                               render = html.getRender();
+                       HtmlClassMeta cHtml = cHtml(sType);
+                       HtmlBeanPropertyMeta bpHtml = bpHtml(pMeta);
+                       
+                       HtmlRender render = firstNonNull(bpHtml.getRender(), 
cHtml.getRender());
 
                        if (render != null) {
                                Object o2 = render.getContent(this, o);
@@ -337,14 +339,14 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                                }
                        }
 
-                       if (html.isXml() || (pMeta != null && 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isXml())) {
+                       if (cHtml.isXml() || bpHtml.isXml()) {
                                pop();
                                indent++;
                                super.serializeAnything(out, o, null, null, 
null, false, XmlFormat.MIXED, false, false, null);
                                indent -= xIndent+1;
                                return cr;
 
-                       } else if (html.isPlainText() || (pMeta != null && 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isPlainText())) {
+                       } else if (cHtml.isPlainText() || bpHtml.isPlainText()) 
{
                                out.write(o == null ? "null" : o.toString());
                                cr = CR_MIXED;
 
@@ -418,6 +420,8 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                ClassMeta<?> keyType = eKeyType == null ? string() : eKeyType;
                ClassMeta<?> valueType = eValueType == null ? object() : 
eValueType;
                ClassMeta<?> aType = getClassMetaForObject(m);       // The 
actual type
+               HtmlClassMeta cHtml = cHtml(aType);
+               HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
 
                int i = indent;
 
@@ -427,8 +431,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                        out.attr(getBeanTypePropertyName(sType), typeName);
 
                out.append(">").nl(i+1);
-               if (isAddKeyValueTableHeaders() && ! 
(aType.getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders()
-                               || (ppMeta != null && 
ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) {
+               if (isAddKeyValueTableHeaders() && ! (cHtml.isNoTableHeaders() 
|| bpHtml.isNoTableHeaders())) {
                        out.sTag(i+1, "tr").nl(i+2);
                        out.sTag(i+2, "th").append("key").eTag("th").nl(i+3);
                        out.sTag(i+2, "th").append("value").eTag("th").nl(i+3);
@@ -472,8 +475,11 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                out.ie(i).eTag("table").nl(i);
        }
 
-       private void serializeBeanMap(XmlWriter out, BeanMap<?> m, ClassMeta<?> 
eType,
-                       BeanPropertyMeta ppMeta) throws Exception {
+       private void serializeBeanMap(XmlWriter out, BeanMap<?> m, ClassMeta<?> 
eType, BeanPropertyMeta ppMeta) throws Exception {
+
+               HtmlClassMeta cHtml = cHtml(m.getClassMeta());
+               HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
+
                int i = indent;
 
                out.oTag(i, "table");
@@ -483,8 +489,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                        out.attr(getBeanTypePropertyName(m.getClassMeta()), 
typeName);
 
                out.append('>').nl(i);
-               if (isAddKeyValueTableHeaders() && ! 
(m.getClassMeta().getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders()
-                               || (ppMeta != null && 
ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) {
+               if (isAddKeyValueTableHeaders() && ! (cHtml.isNoTableHeaders() 
|| bpHtml.isNoTableHeaders())) {
                        out.sTag(i+1, "tr").nl(i+1);
                        out.sTag(i+2, "th").append("key").eTag("th").nl(i+2);
                        out.sTag(i+2, "th").append("value").eTag("th").nl(i+2);
@@ -544,15 +549,17 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
        }
 
        @SuppressWarnings({ "rawtypes", "unchecked" })
-       private void serializeCollection(XmlWriter out, Object in, ClassMeta<?> 
sType,
-                       ClassMeta<?> eType, String name, BeanPropertyMeta 
ppMeta) throws Exception {
+       private void serializeCollection(XmlWriter out, Object in, ClassMeta<?> 
sType, ClassMeta<?> eType, String name, BeanPropertyMeta ppMeta) throws 
Exception {
 
-               ClassMeta<?> seType = sType.getElementType();
-               if (seType == null)
-                       seType = object();
+               HtmlClassMeta cHtml = cHtml(sType);
+               HtmlBeanPropertyMeta bpHtml = bpHtml(ppMeta);
 
                Collection c = (sType.isCollection() ? (Collection)in : 
toList(sType.getInnerClass(), in));
 
+               boolean isCdc = cHtml.isHtmlCdc() || bpHtml.isHtmlCdc();
+               boolean isSdc = cHtml.isHtmlSdc() || bpHtml.isHtmlSdc();
+               boolean isDc = isCdc || isSdc;
+               
                int i = indent;
                if (c.isEmpty()) {
                        out.appendln(i, "<ul></ul>");
@@ -567,14 +574,13 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
 
                c = sort(c);
 
-               HtmlBeanPropertyMeta hbpMeta = (ppMeta == null ? null : 
ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class));
                String btpn = getBeanTypePropertyName(eType);
 
                // Look at the objects to see how we're going to handle them.  
Check the first object to see how we're going to
                // handle this.
                // If it's a map or bean, then we'll create a table.
                // Otherwise, we'll create a list.
-               Object[] th = getTableHeaders(c, hbpMeta);
+               Object[] th = getTableHeaders(c, bpHtml);
 
                if (th != null) {
 
@@ -662,17 +668,22 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                        out.ie(i).eTag("table").nl(i);
 
                } else {
-                       out.oTag(i, "ul");
+                       out.oTag(i, isDc ? "p" : "ul");
                        if (! type2.equals("array"))
                                out.attr(btpn, type2);
                        out.append('>').nl(i+1);
+                       boolean isFirst = true;
                        for (Object o : c) {
-                               out.oTag(i+1, "li");
+                               if (isDc && ! isFirst)
+                                       out.append(isCdc ? ", " : " ");
+                               if (! isDc)
+                                       out.oTag(i+1, "li");
                                String style = getStyle(this, ppMeta, o);
                                String link = getLink(ppMeta);
-                               if (style != null)
+                               if (style != null && ! isDc)
                                        out.attr("style", style);
-                               out.cTag();
+                               if (! isDc)
+                                       out.cTag();
                                if (link != null)
                                        out.oTag(i+2, "a").attrUri("href", 
link.replace("{#}", asString(o))).cTag();
                                ContentResult cr = serializeAnything(out, o, 
eType.getElementType(), name, null, 1, false);
@@ -680,21 +691,22 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                                        out.eTag("a");
                                if (cr == CR_ELEMENTS)
                                        out.ie(i+1);
-                               out.eTag("li").nl(i+1);
+                               if (! isDc)
+                                       out.eTag("li").nl(i+1);
+                               isFirst = false;
                        }
-                       out.ie(i).eTag("ul").nl(i);
+                       out.ie(i).eTag(isDc ? "p" : "ul").nl(i);
                }
        }
 
        private static HtmlRender<?> getRender(HtmlSerializerSession session, 
BeanPropertyMeta pMeta, Object value) {
                if (pMeta == null)
                        return null;
-               HtmlBeanPropertyMeta hpMeta = 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class);
-               HtmlRender<?> render = hpMeta.getRender();
+               HtmlRender<?> render = bpHtml(pMeta).getRender();
                if (render != null)
                        return render;
                ClassMeta<?> cMeta = session.getClassMetaForObject(value);
-               render = cMeta == null ? null : 
cMeta.getExtendedMeta(HtmlClassMeta.class).getRender();
+               render = cMeta == null ? null : cHtml(cMeta).getRender();
                return render;
        }
 
@@ -711,6 +723,14 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
        private static String getAnchorText(BeanPropertyMeta pMeta) {
                return pMeta == null ? null : 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).getAnchorText();
        }
+       
+       private static HtmlClassMeta cHtml(ClassMeta<?> cm) {
+               return cm.getExtendedMeta(HtmlClassMeta.class);
+       }
+
+       private static HtmlBeanPropertyMeta bpHtml(BeanPropertyMeta pMeta) {
+               return pMeta == null ? HtmlBeanPropertyMeta.DEFAULT : 
pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class);
+       }
 
        /*
         * Returns the table column headers for the specified collection of 
objects.
@@ -718,7 +738,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
         * 2-dimensional tables are used for collections of objects that all 
have the same set of property names.
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
-       private Object[] getTableHeaders(Collection c, HtmlBeanPropertyMeta 
hbpMeta) throws Exception {
+       private Object[] getTableHeaders(Collection c, HtmlBeanPropertyMeta 
bpHtml) throws Exception {
                if (c.size() == 0)
                        return null;
                c = sort(c);
@@ -744,10 +764,12 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                        return null;
                if (cm.getInnerClass().isAnnotationPresent(HtmlLink.class))
                        return null;
-               HtmlClassMeta h = cm.getExtendedMeta(HtmlClassMeta.class);
-               if (h.isNoTables() || (hbpMeta != null && hbpMeta.isNoTables()))
+               
+               HtmlClassMeta cHtml = cHtml(cm);
+               
+               if (cHtml.isNoTables() || bpHtml.isNoTables())
                        return null;
-               if (h.isNoTableHeaders() || (hbpMeta != null && 
hbpMeta.isNoTableHeaders()))
+               if (cHtml.isNoTableHeaders() || bpHtml.isNoTableHeaders())
                        return new Object[0];
                if (canIgnoreValue(cm, null, o1))
                        return null;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
index 8372924..5a4e100 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlWriter.java
@@ -79,7 +79,7 @@ public class HtmlWriter extends XmlWriter {
                                else if (test == ' ')
                                        append("<sp> </sp>");
                                else
-                                       
append("<sp>&#x").append(toHex(test)).append(";</sp>");
+                                       
append("<sp>&#x").append(toHex4(test)).append(";</sp>");
                        }
                        else if (Character.isISOControl(test))
                                append("&#" + (int) test + ";");
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/Html.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/Html.java
index d18cf64..5e3a7d8 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/Html.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/Html.java
@@ -49,7 +49,6 @@ public @interface Html {
 
        /**
         * Specifies what format to use for the HTML element.
-        * 
         */
        HtmlFormat format() default HtmlFormat.HTML;
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
index eef5c1e..5d0a8e6 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
@@ -30,6 +30,18 @@ public enum HtmlFormat {
        HTML,
        
        /**
+        * Object is serialized to HTML.
+        * <br>When serializing lists, use comma-delimited format instead of 
list.
+        */
+       HTML_CDC,
+
+       /**
+        * Object is serialized to HTML.
+        * <br>When serializing lists, use space-delimited format instead of 
list.
+        */
+       HTML_SDC,
+
+       /**
         * Object is serialized to XML.
         * <br>Useful when creating beans that model HTML elements.
         */
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
index 4efa915..fb7ab30 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
@@ -44,6 +44,18 @@ public final class AsciiSet {
        }
 
        /**
+        * Copies an existing {@link AsciiSet} so that you can augment it with 
additional values.
+        * 
+        * @return A builder initialized to the same characters in the copied 
set.
+        */
+       public AsciiSet.Builder copy() {
+               Builder b = new Builder();
+               for (int i = 0; i < 128; i++)
+                       b.store[i] = store[i];
+               return b;
+       }
+
+       /**
         * Builder class for {@link AsciiSet} objects.
         */
        public static class Builder {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
index 21032cd..98adf11 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
@@ -252,7 +252,8 @@ public final class ObjectUtils {
         * @param t
         * @return The first non-null value, or <jk>null</jk> if the array is 
null or empty or contains only <jk>null</jk> values.
         */
-       public static <T> T firstNonNull(T[] t) {
+       @SafeVarargs
+       public static <T> T firstNonNull(T... t) {
                if (t != null)
                        for (T tt : t)
                                if (tt != null)
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 694a01b..8805c10 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -49,6 +49,12 @@ public final class StringUtils {
        // Characters that do not need to be URL-encoded
        private static final AsciiSet unencodedChars = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars("-_.!~*'()\\").build();
 
+       // Characters that really do not need to be URL-encoded
+       private static final AsciiSet unencodedCharsLax = unencodedChars.copy()
+               .chars(":@$,")  // reserved, but can't be confused in a query 
parameter.
+               .chars("{}|\\^[]`")  // unwise characters.
+               .build();
+
        // Maps BASE64 characters to 6-bit nibbles.
        private static final byte[] base64m2 = new byte[128];
        static {
@@ -909,12 +915,29 @@ public final class StringUtils {
        }
 
        /**
+        * Converts the specified number into a 2 hexadecimal characters.
+        * 
+        * @param num The number to convert to hex.
+        * @return A <code><jk>char</jk>[2]</code> containing the specified 
characters.
+        */
+       public static final char[] toHex2(int num) {
+               if (num < 0 || num > 255)
+                       throw new NumberFormatException("toHex2 can only be 
used on numbers between 0 and 255");
+               char[] n = new char[2];
+               int a = num%16;
+               n[1] = (char)(a > 9 ? 'A'+a-10 : '0'+a);
+               a = (num/16)%16;
+               n[0] = (char)(a > 9 ? 'A'+a-10 : '0'+a);
+               return n;
+       }
+
+       /**
         * Converts the specified number into a 4 hexadecimal characters.
         * 
         * @param num The number to convert to hex.
         * @return A <code><jk>char</jk>[4]</code> containing the specified 
characters.
         */
-       public static final char[] toHex(int num) {
+       public static final char[] toHex4(int num) {
                char[] n = new char[4];
                int a = num%16;
                n[3] = (char)(a > 9 ? 'A'+a-10 : '0'+a);
@@ -928,6 +951,25 @@ public final class StringUtils {
        }
 
        /**
+        * Converts the specified number into a 8 hexadecimal characters.
+        * 
+        * @param num The number to convert to hex.
+        * @return A <code><jk>char</jk>[8]</code> containing the specified 
characters.
+        */
+       public static final char[] toHex8(long num) {
+               char[] n = new char[8];
+               long a = num%16;
+               n[7] = (char)(a > 9 ? 'A'+a-10 : '0'+a);
+               int base = 16;
+               for (int i = 1; i < 8; i++) {
+                       a = (num/base)%16;
+                       base <<= 4;
+                       n[7-i] = (char)(a > 9 ? 'A'+a-10 : '0'+a);
+               }
+               return n;
+       }
+
+       /**
         * Tests two strings for equality, but gracefully handles nulls.
         * 
         * @param s1 String 1.
@@ -1090,6 +1132,10 @@ public final class StringUtils {
        /**
         * Parses an ISO8601 string into a date.
         * 
+        * <p>
+        * Supports any of the following formats:
+        * <br><code>yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddThh, 
yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS</code>
+        * 
         * @param date The date string.
         * @return The parsed date.
         * @throws IllegalArgumentException
@@ -1252,7 +1298,7 @@ public final class StringUtils {
        public static String unicodeSequence(char c) {
                StringBuilder sb = new StringBuilder(6);
                sb.append('\\').append('u');
-               for (char cc : toHex(c))
+               for (char cc : toHex4(c))
                        sb.append(cc);
                return sb.toString();
        }
@@ -1655,6 +1701,40 @@ public final class StringUtils {
        }
        
        /**
+        * Same as {@link #urlEncode(String)} except only excapes characters 
that absolutely need to be escaped.
+        * 
+        * @param s The string to escape.
+        * @return The encoded string, or <jk>null</jk> if input is 
<jk>null</jk>.
+        */
+       public static String urlEncodeLax(String s) {
+               if (s == null)
+                       return null;
+               boolean needsEncode = false;
+               for (int i = 0; i < s.length() && ! needsEncode; i++)
+                       needsEncode |= (! 
unencodedCharsLax.contains(s.charAt(i)));
+               if (needsEncode) {
+                       StringBuilder sb = new StringBuilder(s.length()*2);
+                       for (int i = 0; i < s.length(); i++) {
+                               char c = s.charAt(i);
+                               if (unencodedCharsLax.contains(c))
+                                       sb.append(c);
+                               else if (c == ' ')
+                                       sb.append("+");
+                               else if (c <= 127)
+                                       sb.append('%').append(toHex2(c));
+                               else
+                                       try {
+                                               
sb.append(URLEncoder.encode(""+c, "UTF-8"));  // Yuck.
+                                       } catch (UnsupportedEncodingException 
e) {
+                                               // Not possible.
+                                       } 
+                       }
+                       s = sb.toString();
+               }
+               return s;
+       }
+
+       /**
         * Splits a string into equally-sized parts.
         * 
         * @param s The string to split.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
index 5a0ae79..7b915b1 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/jsonschema/JsonSchemaBeanPropertyMeta.java
@@ -23,6 +23,11 @@ import org.apache.juneau.parser.*;
  */
 public class JsonSchemaBeanPropertyMeta extends BeanPropertyMetaExtended {
 
+       /**
+        * Default instance.
+        */
+       public static final JsonSchemaBeanPropertyMeta DEFAULT = new 
JsonSchemaBeanPropertyMeta();
+
        private String type, format, description;
        private Object example;
 
@@ -41,6 +46,14 @@ public class JsonSchemaBeanPropertyMeta extends 
BeanPropertyMetaExtended {
                if (bpm.getSetter() != null)
                        
findInfo(bpm.getSetter().getAnnotation(JsonSchema.class));
        }
+       
+       private JsonSchemaBeanPropertyMeta() {
+               super(null);
+               this.type = null;
+               this.format = null;
+               this.description = null;
+               this.example = null;
+       }
 
        private void findInfo(JsonSchema js) {
                if (js == null)
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
index a8f0c05..c2103c1 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
@@ -23,6 +23,11 @@ import org.apache.juneau.xml.annotation.*;
  */
 public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended {
 
+       /**
+        * Default instance.
+        */
+       public static final XmlBeanPropertyMeta DEFAULT = new 
XmlBeanPropertyMeta();
+
        private Namespace namespace = null;
        private XmlFormat xmlFormat = XmlFormat.DEFAULT;
        private String childName;
@@ -45,6 +50,10 @@ public class XmlBeanPropertyMeta extends 
BeanPropertyMetaExtended {
                if (namespace == null)
                        namespace = 
bpm.getBeanMeta().getClassMeta().getExtendedMeta(XmlClassMeta.class).getNamespace();
        }
+       
+       private XmlBeanPropertyMeta() {
+               super(null);
+       }
 
        /**
         * Returns the XML namespace associated with this bean property.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index 1c563a9..b20d34d 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -181,7 +181,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                aType = push(null, o, null);
 
                if (aType != null) {
-                       Namespace ns = 
aType.getExtendedMeta(XmlClassMeta.class).getNamespace();
+                       Namespace ns = cXml(aType).getNamespace();
                        if (ns != null) {
                                if (ns.uri != null)
                                        addNamespace(ns);
@@ -200,7 +200,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                                bm = toBeanMap(o);
                        } else if (aType.isDelegate()) {
                                ClassMeta<?> innerType = 
((Delegate<?>)o).getClassMeta();
-                               Namespace ns = 
innerType.getExtendedMeta(XmlClassMeta.class).getNamespace();
+                               Namespace ns = cXml(innerType).getNamespace();
                                if (ns != null) {
                                        if (ns.uri != null)
                                                addNamespace(ns);
@@ -211,7 +211,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                                if (innerType.isBean()) {
                                        for (BeanPropertyMeta bpm : 
innerType.getBeanMeta().getPropertyMetas()) {
                                                if (bpm.canRead()) {
-                                                       ns = 
bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+                                                       ns = 
bpXml(bpm).getNamespace();
                                                        if (ns != null && 
ns.uri != null)
                                                                
addNamespace(ns);
                                                }
@@ -238,7 +238,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                        if (bm != null) {
                                for (BeanPropertyValue p : 
bm.getValues(isTrimNulls())) {
 
-                                       Namespace ns = 
p.getMeta().getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+                                       Namespace ns = 
bpXml(p.getMeta()).getNamespace();
                                        if (ns != null && ns.uri != null)
                                                addNamespace(ns);
 
@@ -362,7 +362,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                } else if (sType.isBoolean()) {
                        type = BOOLEAN;
                } else if (sType.isMapOrBean()) {
-                       isCollapsed = 
sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED;
+                       isCollapsed = cXml(sType).getFormat() == COLLAPSED;
                        type = OBJECT;
                } else if (sType.isCollectionOrArray()) {
                        isCollapsed = (format == COLLAPSED && ! 
addNamespaceUris);
@@ -382,9 +382,9 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
 
                if (enableNamespaces) {
                        if (elementNamespace == null)
-                               elementNamespace = 
sType.getExtendedMeta(XmlClassMeta.class).getNamespace();
+                               elementNamespace = cXml(sType).getNamespace();
                        if (elementNamespace == null)
-                               elementNamespace = 
aType.getExtendedMeta(XmlClassMeta.class).getNamespace();
+                               elementNamespace = cXml(aType).getNamespace();
                        if (elementNamespace != null && elementNamespace.uri == 
null)
                                elementNamespace = null;
                        if (elementNamespace == null)
@@ -546,7 +546,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
 
                List<BeanPropertyValue> lp = m.getValues(isTrimNulls());
 
-               XmlBeanMeta xbm = bm.getExtendedMeta(XmlBeanMeta.class);
+               XmlBeanMeta xbm = bXml(bm);
 
                Set<String>
                        attrs = xbm.getAttrPropertyNames(),
@@ -576,7 +576,8 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                                        if (canIgnoreValue(cMeta, key, value))
                                                continue;
 
-                                       Namespace ns = (enableNamespaces && 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
+                                       XmlBeanPropertyMeta bpXml = 
bpXml(pMeta);
+                                       Namespace ns = (enableNamespaces && 
bpXml.getNamespace() != elementNs ? bpXml.getNamespace() : null);
 
                                        if (pMeta.isUri()  ) {
                                                out.attrUri(ns, key, value);
@@ -643,8 +644,8 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                                                out.appendIf(! isCollapsed, 
'>').nlIf(! isMixed, indent);
                                        }
 
-                                       XmlBeanPropertyMeta xbpm = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-                                       serializeAnything(out, value, cMeta, 
key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
+                                       XmlBeanPropertyMeta bpXml = 
bpXml(pMeta);
+                                       serializeAnything(out, value, cMeta, 
key, bpXml.getNamespace(), false, bpXml.getXmlFormat(), isMixed, false, pMeta);
                                }
                        }
                }
@@ -685,9 +686,6 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
        private XmlWriter serializeCollection(XmlWriter out, Object in, 
ClassMeta<?> sType,
                        ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean 
isMixed) throws Exception {
 
-               ClassMeta<?> seType = sType.getElementType();
-               if (seType == null)
-                       seType = object();
                ClassMeta<?> eeType = eType.getElementType();
 
                Collection c = (sType.isCollection() ? (Collection)in : 
toList(sType.getInnerClass(), in));
@@ -700,9 +698,9 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                Namespace eNs = null;
 
                if (ppMeta != null) {
-                       XmlBeanPropertyMeta xbpm = 
ppMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
-                       eName = xbpm.getChildName();
-                       eNs = xbpm.getNamespace();
+                       XmlBeanPropertyMeta bpXml = bpXml(ppMeta);
+                       eName = bpXml.getChildName();
+                       eNs = bpXml.getNamespace();
                }
 
                for (Iterator i = c.iterator(); i.hasNext();) {
@@ -712,6 +710,18 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                return out;
        }
 
+       private static XmlClassMeta cXml(ClassMeta<?> cm) {
+               return cm.getExtendedMeta(XmlClassMeta.class);
+       }
+
+       private static XmlBeanPropertyMeta bpXml(BeanPropertyMeta pMeta) {
+               return pMeta == null ? XmlBeanPropertyMeta.DEFAULT : 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+       }
+
+       private static XmlBeanMeta bXml(BeanMeta bm) {
+               return (XmlBeanMeta)bm.getExtendedMeta(XmlBeanMeta.class);
+       }
+
        static enum JsonType {
                
STRING("string"),BOOLEAN("boolean"),NUMBER("number"),ARRAY("array"),OBJECT("object"),NULL("null");
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
index 96fcb3e..cc01222 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlUtils.java
@@ -473,7 +473,7 @@ public final class XmlUtils {
        // Converts an integer to a hexadecimal string padded to 4 places.
        private static final Writer appendPaddedHexChar(Writer out, int num) 
throws IOException {
                out.append("_x");
-               for (char c : toHex(num))
+               for (char c : toHex4(num))
                        out.append(c);
                return out.append('_');
        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
index 6c68c4e..05f5f95 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xmlschema/XmlSchemaSerializerSession.java
@@ -351,8 +351,8 @@ public class XmlSchemaSerializerSession extends 
XmlSerializerSession {
 
                                        for (BeanPropertyMeta pMeta : 
bm.getPropertyMetas()) {
                                                if (pMeta.canRead()) {
-                                                       XmlFormat pMetaFormat = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
-                                                       if (pMetaFormat != 
XmlFormat.ATTR)
+                                                       XmlFormat bpXml = 
pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+                                                       if (bpXml != 
XmlFormat.ATTR)
                                                                
hasChildElements = true;
                                                }
                                        }
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 931684b..0264198 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -9051,7 +9051,7 @@
                        Both kinds of objects are accessible through the 
following method:
                </p>
                <ul>
-                       <li class='jm'>{@link 
org.apache.juneau.svl.VarResolverSession#getSessionObject(Class, String)}
+                       <li class='jm'>{@link 
org.apache.juneau.svl.VarResolverSession#getSessionObject(Class, String, 
boolean)}
                </ul>
                <p>
                        Var resolvers can be cloned and extended by using the 
{@link org.apache.juneau.svl.VarResolver#builder()} method.
@@ -21358,6 +21358,12 @@
                        <li>
                                New option for dyna-property (i.e. 
<code><ja>@Bean</ja>(<js>"*"</js>)</code>) using a method that returns a 
collection of extra keys.
                                <br>See new options #4 on {@link 
org.apache.juneau.annotation.BeanProperty#name()}
+                       <li>
+                               New formats for the {@link 
org.apache.juneau.html.annotation.Html#format() @Html.format()} annotation:
+                               <ul>
+                                       <li class='jf'>{@link 
org.apache.juneau.html.annotation.HtmlFormat#HTML_CDC} - Format collections as 
comma-delimited lists.
+                                       <li class='jf'>{@link 
org.apache.juneau.html.annotation.HtmlFormat#HTML_SDC} - Format collections as 
space-delimited lists.
+                               </ul> 
                </ul>
                
                <h5 class='topic w800'>juneau-dto</h5>
@@ -21460,9 +21466,23 @@
                                        <li class='jc'>{@link 
org.apache.juneau.parser.ReaderParser} - The reader parser matching the request 
content type.
                                        <li class='jc'>{@link 
org.apache.juneau.parser.InputStreamParser} - The input stream parser matching 
the request content type.
                                </ul>
+                       <li>
+                               The <code>$F</code> variable can now be used as 
a initialization time variable.
+                               <br>For example, the 
<code>AtomFeedResource</code> example pulls a bean example from a file on the 
classpath:
+                               <p class='bcode'>
+       <ja>@RestResource</ja>(
+               path=<js>"/atom"</js>,
+               title=<js>"Sample ATOM feed resource"</js>,
+               properties={
+                       <ja>@Property</ja>(name=<jsf>BEAN_examples</jsf>, 
value=<js>"{'org.apache.juneau.dto.atom.Feed': 
$F{AtomFeedResource_example.json}}"</js>)
+               },
+               ...
+       )
+                               </p>
+                               <br>It should be noted that you cannot use the 
<code>$F</code> variable to retrieve localized versions of files (since you're 
not within
+                               the scope of a client request.
                </ul>
-               
-               <h5 class='topic w800'>juneau-rest-client</h5>
+       
                <ul class='spaced-list'>
                        <li>
                                Made improvements to the builder API for 
defining SSL support.
diff --git a/juneau-examples/juneau-examples-rest/examples.cfg 
b/juneau-examples/juneau-examples-rest/examples.cfg
index e869a35..23d9f06 100755
--- a/juneau-examples/juneau-examples-rest/examples.cfg
+++ b/juneau-examples/juneau-examples-rest/examples.cfg
@@ -95,12 +95,10 @@ logDir = ./target/logs
 logFile = microservice.%g.log
 
 # Whether to append to the existing log file or create a new one.
-# Default is false.
-append = 
+append = false
 
 # The SimpleDateFormat format to use for dates.
-# Default is "yyyy.MM.dd hh:mm:ss".
-dateFormat = 
+dateFormat = yyyy.MM.dd hh:mm:ss
 
 # The log message format.
 # The value can contain any of the following variables:
@@ -112,17 +110,14 @@ dateFormat =
 #      {msg} - The log message.
 #      {threadid} - The thread ID.
 #      {exception} - The localized exception message.
-# Default is "[{date} {level}] {msg}%n".
-format =
+format = [{date} {level}] {msg}%n
 
 # The maximum log file size.
 # Suffixes available for numbers.
 # See Config.getInt(String,int) for details.
-# Default is 1M.
-limit = 10M
+limit = 1M
 
 # Max number of log files.
-# Default is 1.
 count = 5
 
 # Default log levels.
@@ -138,12 +133,10 @@ levels =
 
 # Only print unique stack traces once and then refer to them by a simple 8 
character hash identifier.
 # Useful for preventing log files from filling up with duplicate stack traces.
-# Default is false.
 useStackTraceHashes = true
 
 # The default level for the console logger.
 # Values are serialized Level POJOs (SEVERE, WARNING, INFO, CONFIG, FINE, 
FINER, FINEST)
-# Default is WARNING.
 consoleLevel = WARNING
 
 # The default level for the file logger.
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/JsonSchemaResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/JsonSchemaResource.java
index 50b20d9..07839df 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/JsonSchemaResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/JsonSchemaResource.java
@@ -12,6 +12,7 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.examples.rest;
 
+import static org.apache.juneau.BeanContext.*;
 import static org.apache.juneau.http.HttpMethodName.*;
 
 import org.apache.juneau.dto.jsonschema.*;
@@ -27,6 +28,9 @@ import org.apache.juneau.rest.widget.*;
        messages="nls/JsonSchemaResource",
        title="Sample JSON-Schema document",
        description="Sample resource that shows how to generate JSON-Schema 
documents",
+       properties={
+               @Property(name=BEAN_examples, 
value="{'org.apache.juneau.dto.jsonschema.Schema': 
$F{JsonSchemaResource_example.json}}")
+       },
        htmldoc=@HtmlDoc(
                widgets={
                        ContentTypeMenuItem.class,
@@ -82,17 +86,21 @@ public class JsonSchemaResource extends 
BasicRestServletJena {
                }
        }
 
-       /** GET request handler */
-       @RestMethod(name=GET, path="/", summary="Get the JSON-Schema document")
+       @RestMethod(
+               name=GET, 
+               path="/", 
+               summary="Get the JSON-Schema document"
+       )
        public Schema getSchema() throws Exception {
                return schema;
        }
 
-       /**
-        * PUT request handler.
-        * Replaces the schema document with the specified content, and then 
mirrors it as the response.
-        */
-       @RestMethod(name=PUT, path="/", summary="Overwrite the JSON-Schema 
document")
+       @RestMethod(
+               name=PUT, 
+               path="/", 
+               summary="Overwrite the JSON-Schema document",
+               description="Replaces the schema document with the specified 
content, and then mirrors it as the response."
+       )
        public Schema setSchema(@Body Schema schema) throws Exception {
                this.schema = schema;
                return schema;
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
index e5697a3..8d5290b 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
@@ -76,7 +76,6 @@ import org.apache.juneau.rest.widget.*;
                LogsResource.class,
                DockerRegistryResource.class,
                PredefinedLabelsResource.class,
-               StaticFilesResource.class,
                DebugResource.class,
                ShutdownResource.class
        }
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
index f34839a..78e6e21 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
@@ -43,7 +43,7 @@ import org.apache.juneau.rest.widget.*;
                },
                navlinks={
                        "up: request:/..",
-                       "options: servlet:/..",
+                       "options: servlet:/?method=OPTIONS",
                        "$W{ThemeMenuItem}",
                        "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
                },
@@ -93,9 +93,15 @@ public class SqlQueryResource extends BasicRestServlet {
                }
        }
 
-       /** GET request handler - Display the query entry page. */
-       @RestMethod(name=GET, path="/", summary="Display the query entry page")
-       public Div doGet(RestRequest req, @Query("sql") String sql) {
+       @RestMethod(
+               name=GET, 
+               path="/", 
+               summary="Display the query entry page"
+       )
+       public Div doGet(
+                       @Query(name="sql", description="Text to prepopulate the 
SQL query field with.", example="select * from sys.systables") String sql
+               ) {
+               
                return div(
                        script("text/javascript",
                                "\n     // Quick and dirty function to allow 
tabs in textarea."
@@ -117,9 +123,9 @@ public class SqlQueryResource extends BasicRestServlet {
                        form("servlet:/").method(POST).target("buf").children(
                                table(
                                        tr(
-                                               th("Position (1-10000):"),
+                                               th("Position 
(1-10000):").style("white-space:nowrap"),
                                                
td(input().name("pos").type("number").value(1)),
-                                               th("Limit (1-10000):"),
+                                               th("Limit 
(1-10000):").style("white-space:nowrap"),
                                                
td(input().name("limit").type("number").value(100)),
                                                td(button("submit", "Submit"), 
button("reset", "Reset"))
                                        ),
@@ -136,9 +142,19 @@ public class SqlQueryResource extends BasicRestServlet {
                );
        }
 
-       /** POST request handler - Execute the query. */
-       @RestMethod(name=POST, path="/", summary="Execute one or more queries")
-       public List<Object> doPost(@Body PostInput in) throws BadRequest {
+       @RestMethod(
+               name=POST, 
+               path="/", 
+               summary="Execute one or more queries",
+               swagger= {
+                       "responses:{",
+                               "200:{ description:'Query results.\nEach entry 
in the array is a result of one query.\nEach result can be a result set (for 
queries) or update count (for updates).', 
'x-example':[[{col1:'val1'},{col2:'val2'},{col3:'val3'}],123]}",
+                       "}",
+               }
+       )
+       public List<Object> doPost(
+                       @Body(description="Query input", example="{sql:'select 
* from sys.systables',pos:1,limit:100}") PostInput in
+               ) throws BadRequest {
 
                List<Object> results = new LinkedList<>();
 
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
deleted file mode 100644
index a501b45..0000000
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// 
***************************************************************************************************************************
-// * 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.juneau.examples.rest;
-
-import static org.apache.juneau.http.HttpMethodName.*;
-
-import org.apache.juneau.dto.*;
-import org.apache.juneau.dto.swagger.*;
-import org.apache.juneau.dto.swagger.ui.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.widget.*;
-
-/**
- * Sample resource that shows how to generate ATOM feeds.
- */
-@RestResource(
-       path="/staticFiles",
-       title="SwaggerUI testbed",
-       description="Sample resource that shows how to use static files.",
-       htmldoc=@HtmlDoc(
-               widgets={
-                       ContentTypeMenuItem.class,
-                       ThemeMenuItem.class
-               },
-               navlinks={
-                       "up: request:/..",
-                       "options: servlet:/?method=OPTIONS",
-                       "$W{ContentTypeMenuItem}",
-                       "$W{ThemeMenuItem}",
-                       "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{staticFilesResource}.java"
-               }
-       ),
-       staticFiles= {
-               // Serve up files in /files under the child URI /static
-               "static:files"
-       },
-       swagger={
-               "info: {",
-                       "contact:{name:'Juneau 
Developer',email:'d...@juneau.apache.org'},",
-                       "license:{name:'Apache 
2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'},",
-                       "version:'2.0',",
-                       "termsOfService:'You are on your own.'",
-               "},",
-               "externalDocs:{description:'Apache 
Juneau',url:'http://juneau.apache.org'}"
-       }
-)
-public class StaticFilesResource extends BasicRestServletJena {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * GET request handler
-        */
-       @RestMethod(name=GET, path="/", summary="Get the sample ATOM feed")
-       public LinkString[] getFiles() throws Exception {
-               return new LinkString[] {
-                       new LinkString("petstore.html","static/petstore.html")
-               };
-       }
-       
-       @RestMethod(name=GET, path="/swagger", summary="Normal")
-       public Swagger testSwagger() throws Exception {
-               Swagger s = getContext().getClasspathResource(Swagger.class, 
MediaType.JSON, "files/petstore.json", null);
-               return s;
-       }
-       
-       @RestMethod(name=GET, path="/swagger2", summary="SwaggerUI", 
pojoSwaps=SwaggerUI.class)
-       public Swagger testSwagger2() throws Exception {
-               Swagger s = getContext().getClasspathResource(Swagger.class, 
MediaType.JSON, "files/petstore.json", null);
-               return s;
-       }
-}
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java
index 79d3fb3..baa58a8 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java
@@ -15,6 +15,7 @@ package org.apache.juneau.examples.rest;
 import static org.apache.juneau.dto.html5.HtmlBuilder.*;
 import static org.apache.juneau.http.HttpMethodName.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
+import static org.apache.juneau.microservice.resources.DirectoryResource.*;
 
 import java.io.*;
 
@@ -57,10 +58,10 @@ import org.apache.juneau.utils.*;
                }
        ),
        properties={
-               @Property(name="DirectoryResource.rootDir", 
value="$C{TempDirResource/dir,$S{java.io.tmpdir}}"),
-               @Property(name="DirectoryResource.allowViews", value="true"),
-               @Property(name="DirectoryResource.allowDeletes", value="true"),
-               @Property(name="DirectoryResource.allowPuts", value="false")
+               @Property(name=DIRECTORY_RESOURCE_rootDir, 
value="$C{TempDirResource/dir,$S{java.io.tmpdir}}"),
+               @Property(name=DIRECTORY_RESOURCE_allowViews, value="true"),
+               @Property(name=DIRECTORY_RESOURCE_allowDeletes, value="true"),
+               @Property(name=DIRECTORY_RESOURCE_allowUploads, value="false")
        },
        swagger={
                "info: {",
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
index 8013dd0..2da113e 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
@@ -67,6 +67,7 @@ public class UrlEncodedFormResource extends BasicRestServlet {
                path="/",
                htmldoc=@HtmlDoc(
                        script={
+                               "INHERIT",
                                "// Load results from IFrame into this 
document.",
                                "function loadResults(buff) {",
                                "       var doc = buff.contentDocument || 
buff.contentWindow.document;",
@@ -81,16 +82,16 @@ public class UrlEncodedFormResource extends 
BasicRestServlet {
                        
form().id("form").action("servlet:/").method(POST).target("buff").children(
                                table(
                                        tr(
-                                               th(req.getMessage("aString")),
-                                               
td(input().name("aString").type("text"))
+                                               
th(req.getMessage("aString")).style("white-space:nowrap"),
+                                               
td(input().name("aString").type("text").size(50))
                                        ),
                                        tr(
-                                               th(req.getMessage("aNumber")),
-                                               
td(input().name("aNumber").type("number"))
+                                               
th(req.getMessage("aNumber")).style("white-space:nowrap"),
+                                               
td(input().name("aNumber").type("number").size(50))
                                        ),
                                        tr(
-                                               th(req.getMessage("aDate")),
-                                               
td(input().name("aDate").type("datetime"), br(), "ISO8601", br(), 
code("2001-07-04T15:30:45Z"))
+                                               
th(req.getMessage("aDate")).style("white-space:nowrap"),
+                                               
td(input().name("aDate").type("datetime-local").size(50).value("2001-07-04T15:30:45"),
 br(), "ISO8601", code("")).style("white-space:nowrap")
                                        ),
                                        tr(
                                                
td().colspan(2).style("text-align:right").children(
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/resources/examples.cfg 
b/juneau-examples/juneau-examples-rest/src/main/resources/examples.cfg
deleted file mode 100755
index d6d0e6a..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/resources/examples.cfg
+++ /dev/null
@@ -1,147 +0,0 @@
-# 
***************************************************************************************************************************
-# * 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. 
                                             *
-# 
***************************************************************************************************************************
-
-#================================================================================
-# Basic configuration file for SaaS microservices
-# Subprojects can use this as a starting point.
-#================================================================================
-
-#================================================================================
-# REST settings
-#================================================================================
-[REST]
-
-port = 10000
-
-# Authentication:  NONE, BASIC.
-authType = NONE
-
-# The BASIC auth username, password, and realm
-loginUser = 
-loginPassword = 
-authRealm = 
-
-# Stylesheet to use for HTML views.
-# The default options are:
-#  - styles/juneau.css
-#  - styles/devops.css
-# Other stylesheets can be referenced relative to the servlet package or 
working
-#      directory.
-stylesheet = styles/devops.css
-
-# What to do when the config file is saved.
-# Possible values:
-#      NOTHING - Don't do anything. 
-#      RESTART_SERVER - Restart the Jetty server.
-#      RESTART_SERVICE - Shutdown and exit with code '3'.
-saveConfigAction = RESTART_SERVER
-
-# Enable SSL support.
-useSsl = false
-
-#================================================================================
-# Bean properties on the org.eclipse.jetty.util.ssl.SslSocketFactory class
-#--------------------------------------------------------------------------------
-# Ignored if REST/useSsl is false.
-# Specify any of the following fields:
-#      allowRenegotiate (boolean)
-#      certAlias (String)
-#      crlPath (String)
-#      enableCRLDP (boolean)
-#      enableOCSP (boolean)
-#      excludeCipherSuites (String[]) 
-#      excludeProtocols (String[])
-#      includeCipherSuites (String[])
-#      includeProtocols (String...)
-#      keyManagerPassword (String)
-#      keyStore (String)
-#      keyStorePassword (String)
-#      keyStorePath (String)
-#      keyStoreProvider (String)
-#      keyStoreType (String)
-#      maxCertPathLength (int)
-#      needClientAuth (boolean)
-#      ocspResponderURL (String)
-#      protocol (String)
-#      provider (String)
-#      secureRandomAlgorithm (String)
-#      sessionCachingEnabled (boolean) 
-#      sslKeyManagerFactoryAlgorithm (String)
-#      sslSessionCacheSize (int)
-#      sslSessionTimeout (int)
-#      trustAll (boolean)
-#      trustManagerFactoryAlgorithm (String)
-#      trustStore (String)
-#      trustStorePassword (String)
-#      trustStoreProvider (String)
-#      trustStoreType (String)
-#      validateCerts (boolean)
-#      validatePeerCerts (boolean)
-#      wantClientAuth (boolean)                        
-#================================================================================
-[REST-SslContextFactory]
-keyStorePath = client_keystore.jks
-keyStorePassword* = {HRAaRQoT}
-excludeCipherSuites = TLS_DHE.*, TLS_EDH.*
-excludeProtocols = SSLv3
-allowRenegotiate = false
-
-#================================================================================
-# Logger settings
-# See FileHandler Java class for details.
-#================================================================================
-[Logging]
-logDir = logs
-logFile = sample.%g.log
-dateFormat = yyyy.MM.dd hh:mm:ss
-format = [{date} {level}] {msg}%n
-append = false
-limit = 10M
-count = 5
-levels = { org.apache.juneau:'INFO' }
-useStackTraceHashes = true
-consoleLevel = WARNING
-
-#================================================================================
-# System properties
-#--------------------------------------------------------------------------------
-# These are arbitrary system properties that can be set during startup.
-#================================================================================
-[SystemProperties]
-
-# Configure Jetty for StdErrLog Logging
-org.eclipse.jetty.util.log.class = org.eclipse.jetty.util.log.StrErrLog
-
-# Jetty logging level
-org.eclipse.jetty.LEVEL = WARN
-
-#================================================================================
-# DockerRegistryResource properties
-#================================================================================
-[DockerRegistry]
-url = http://docker.apache.org:5000/v1
-
-#================================================================================
-# SqlQueryResource properties
-#================================================================================
-[SqlQueryResource]
-driver = org.apache.derby.jdbc.EmbeddedDriver
-connectionUrl = jdbc:derby:C:/testDB;create=true
-allowTempUpdates = true
-includeRowNums = true
-
-#================================================================================
-# Source code location
-#================================================================================
-[Source]
-gitHub = 
https://github.com/apache/juneau/blob/master/juneau-examples-rest/src/main/java
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
 
b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/JsonSchemaResource_example.json
similarity index 70%
copy from 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
copy to 
juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/JsonSchemaResource_example.json
index eef5c1e..d5bdc95 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlFormat.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/JsonSchemaResource_example.json
@@ -2,7 +2,7 @@
 // * 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                
                                              * 
+// * with the License.  You may obtain a copy of the License at                
                                              *
 // *                                                                           
                                              *
 // *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
 // *                                                                           
                                              *
@@ -10,28 +10,26 @@
 // * "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.juneau.html.annotation;
-
-/**
- * Possible values for the {@link Html#format()} annotation.
- */
-public enum HtmlFormat {
-       
-       /**
-        * Object is serialized to a String using the <code>toString()</code> 
method and written directly to output.
-        * <br>Useful when you want to serialize custom HTML.
-        */
-       PLAIN_TEXT,
-       
-       /**
-        * Object is serialized to HTML.
-        * <br>This is the default value for serialization.
-        */
-       HTML,
-       
-       /**
-        * Object is serialized to XML.
-        * <br>Useful when creating beans that model HTML elements.
-        */
-       XML
-}
+{
+       id: 'http://example.com/sample-schema#',
+       '$schema': 'http://json-schema.org/draft-04/schema#',
+       title: 'Example Schema',
+       type: 'object',
+       properties: {
+               firstName: {
+                       type: 'string'
+               },
+               lastName: {
+                       type: 'string'
+               },
+               age: {
+                       description: 'Age in years',
+                       type: 'integer',
+                       minimum: 0
+               }
+       },
+       required: [
+               'firstName',
+               'lastName'
+       ]
+}
\ No newline at end of file
diff --git 
a/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java
 
b/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java
index 7f43edd..96c847c 100644
--- 
a/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java
+++ 
b/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java
@@ -41,7 +41,7 @@ public class TestMultiPartFormPostsTest extends RestTestcase {
                HttpEntity entity = 
MultipartEntityBuilder.create().addBinaryBody(f.getName(), f).build();
                client.doPost(URL + "/upload", entity);
 
-               String downloaded = client.doGet(URL + "/file/" + f.getName() + 
"?method=VIEW").getResponseAsString();
+               String downloaded = client.doGet(URL + "/" + f.getName() + 
"?method=VIEW").getResponseAsString();
                assertEquals("test!", downloaded);
        }
 }
\ No newline at end of file
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
index 0803bab..27863c1 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
@@ -41,29 +41,32 @@ import org.apache.juneau.rest.exception.*;
                }
        )
 )
+@SuppressWarnings("javadoc")
 public class ConfigResource extends BasicRestServlet {
        private static final long serialVersionUID = 1L;
-
-       /**
-        * [GET /] - Show contents of config file.
-        * 
-        * @return The config file.
-        * @throws Exception
-        */
-       @RestMethod(name=GET, path="/", description="Show contents of config 
file.")
-       public ObjectMap getConfig() throws Exception {
+       
+       @RestMethod(
+               name=GET, 
+               path="/", 
+               summary="Get config file contents",
+               description="Show contents of config file as an ObjectMap.",
+               swagger={
+                       "responses:{",
+                               "200:{ description:'Config file as a map of map 
of objects.', 
'x-example':{'':{defaultKey:'defaultValue'},'Section1':{key1:'val1',key2:123}}}",
+                       "}",
+               }
+       )
+       public ObjectMap getConfig() {
                return getServletConfig().getConfig().asMap();
        }
 
-       /**
-        * [GET /edit] - Show config file edit page.
-        * 
-        * @param req The HTTP request.
-        * @return The config file as a reader resource.
-        * @throws Exception
-        */
-       @RestMethod(name=GET, path="/edit", description="Edit config file.")
-       public Form getConfigEditForm(RestRequest req) throws Exception {
+       @RestMethod(
+               name=GET, 
+               path="/edit",
+               summary="Render form entry page for editing config file",
+               description="Renders a form entry page for editing the raw text 
of a config file."
+       )
+       public Form getConfigEditForm() {
                return 
form().id("form").action("servlet:/").method("POST").enctype("application/x-www-form-urlencoded").children(
                        div()._class("data").children(
                                table(
@@ -78,140 +81,158 @@ public class ConfigResource extends BasicRestServlet {
                );
        }
 
-       /**
-        * [GET /{section}] - Show config file section.
-        * 
-        * @param section The section name.
-        * @return The config file section.
-        * @throws Exception
-        */
-       @RestMethod(name=GET, path="/{section}",
-               description="Show config file section.",
+       @RestMethod(
+               name=GET, 
+               path="/{section}",
+               summary="Get config file section contents",
+               description="Show contents of config file section as an 
ObjectMap.",
                swagger={
-                       "parameters:[",
-                               "{name:'section',in:'path',description:'Section 
name.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'Config file section as a 
map of objects.', 'x-example':{key1:'val1',key2:123}}",
+                       "}",
                }
        )
-       public ObjectMap getConfigSection(@Path("section") String section) 
throws Exception {
+       public ObjectMap getConfigSection(
+                       @Path(name="section", description="Section name in 
config file.", example="REST") String section
+               ) throws SectionNotFound, BadConfig {
+               
                return getSection(section);
        }
 
-       /**
-        * [GET /{section}/{key}] - Show config file entry.
-        * 
-        * @param section The section name.
-        * @param key The section key.
-        * @return The value of the config file entry.
-        * @throws Exception
-        */
-       @RestMethod(name=GET, path="/{section}/{key}",
-               description="Show config file entry.",
+       @RestMethod(
+               name=GET, 
+               path="/{section}/{key}",
+               summary="Get config file entry value",
+               description="Show value of config file entry as a simple 
string.",
                swagger={
-                       "parameters:[",
-                               "{name:'section',in:'path',description:'Section 
name.'},",
-                               "{name:'key',in:'path',description:'Entry 
name.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'Entry value.', 
'x-example':'servlet:/htdocs/themes/dark.css'}",
+                       "}",
                }
        )
-       public String getConfigEntry(@Path("section") String section, 
@Path("key") String key) throws Exception {
+       public String getConfigEntry(
+                       @Path(name="section", description="Section name in 
config file.", example="REST") String section,
+                       @Path(name="key", description="Key name in section.", 
example="theme") String key
+               ) throws SectionNotFound, BadConfig {
+               
                return getSection(section).getString(key);
        }
 
-       /**
-        * [POST /] - Sets contents of config file from a FORM post.
-        * 
-        * @param contents The new contents of the config file.
-        * @return The new config file contents.
-        * @throws Exception
-        */
-       @RestMethod(name=POST, path="/",
-               description="Sets contents of config file from a FORM post.",
+       @RestMethod(
+               name=POST, 
+               path="/",
+               summary="Update config file contents",
+               description="Update the contents of the config file from a FORM 
post.",
                swagger={
-                       "parameters:[",
-                               
"{name:'contents',in:'formData',description:'New contents in INI file 
format.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'Config file section as a 
map of objects.', 'x-example':{key1:'val1',key2:123}}",
+                       "}",
                }
        )
-       public ObjectMap setConfigContentsFormPost(@FormData("contents") String 
contents) throws Exception {
+       public ObjectMap setConfigContentsFormPost(
+                       @FormData(name="contents", description="New contents in 
INI file format.") String contents
+               ) throws Exception {
+
                return setConfigContents(new StringReader(contents));
        }
 
-       /**
-        * [PUT /] - Sets contents of config file.
-        * 
-        * @param contents The new contents of the config file.
-        * @return The new config file contents.
-        * @throws Exception
-        */
-       @RestMethod(name=PUT, path="/",
-               description="Sets contents of config file.",
+       @RestMethod(
+               name=PUT, 
+               path="/",
+               summary="Update config file contents",
+               description="Update the contents of the config file from raw 
text.",
                swagger={
-                       "parameters:[",
-                               "{in:'body',description:'New contents in INI 
file format.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'Config file section as a 
map of objects.', 'x-example':{key1:'val1',key2:123}}",
+                       "}",
                }
        )
-       public ObjectMap setConfigContents(@Body Reader contents) throws 
Exception {
+       public ObjectMap setConfigContents(
+                       @Body(description="New contents in INI file format.") 
Reader contents
+               ) throws Exception {
+               
                return getServletConfig().getConfig().load(contents, 
true).asMap();
        }
 
-       /**
-        * [PUT /{section}] - Add or overwrite a config file section.
-        * 
-        * @param section The section name.
-        * @param contents The new contents of the config file section.
-        * @return The new section.
-        * @throws Exception
-        */
-       @RestMethod(name=PUT, path="/{section}",
+       @RestMethod(
+               name=PUT, 
+               path="/{section}",
+               summary="Update config section contents",
                description="Add or overwrite a config file section.",
                swagger={
-                       "parameters:[",
-                               "{name:'section',in:'path',description:'Section 
name.'},",
-                               "{in:'body',description:'New contents for 
section as a simple map with string keys and values.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'Config file section as a 
map of objects.', 'x-example':{key1:'val1',key2:123}}",
+                       "}",
                }
        )
-       public ObjectMap setConfigSection(@Path("section") String section, 
@Body Map<String,Object> contents) throws Exception {
+       public ObjectMap setConfigSection(
+                       @Path(name="section", description="Section name in 
config file.", example="REST") String section,
+                       @Body(
+                               description="New contents of config section as 
a simple map of key/value pairs.", 
+                               
example="{theme:'servlet:/htdocs/themes/dark.css'}"
+                       ) Map<String,Object> contents
+               ) throws Exception {
+               
                getServletConfig().getConfig().setSection(section, null, 
contents);
                return getSection(section);
        }
 
-       /**
-        * [PUT /{section}/{key}] - Add or overwrite a config file entry.
-        * 
-        * @param section The section name.
-        * @param key The section key.
-        * @param value The new value.
-        * @return The new value.
-        * @throws NotFound Thrown if config section not found.
-        * @throws BadRequest Thrown if contents are not valid.
-        */
-       @RestMethod(name=PUT, path="/{section}/{key}",
+       @RestMethod(
+               name=PUT, 
+               path="/{section}/{key}",
+               summary="Update config entry value",
                description="Add or overwrite a config file entry.",
                swagger={
-                       "parameters:[",
-                               "{name:'section',in:'path',description:'Section 
name.'},",
-                               "{name:'key',in:'path',description:'Entry 
name.'},",
-                               "{in:'body',description:'New value as a 
string.'}",
-                       "]"
+                       "responses:{",
+                               "200:{ description:'The updated value.', 
'x-example':'servlet:/htdocs/themes/dark.css'}",
+                       "}",
                }
        )
-       public String setConfigSection(@Path("section") String section, 
@Path("key") String key, @Body String value) throws NotFound, BadRequest {
+       public String setConfigValue(
+                       @Path(name="section", description="Section name in 
config file.", example="REST") String section,
+                       @Path(name="key", description="Key name in section.", 
example="theme") String key,
+                       @Body(description="New value for entry.", 
example="servlet:/htdocs/themes/dark.css") String value
+               ) throws SectionNotFound, BadConfig {
+               
                getServletConfig().getConfig().set(section + '/' + key, value);
                return getSection(section).getString(key);
        }
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper beans
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @ResponseInfo(description="Section not found.")
+       private class SectionNotFound extends NotFound {
+               private static final long serialVersionUID = 1L;
+
+               SectionNotFound() {
+                       super("Section not found.");
+               }
+       }
+       
+       @ResponseInfo(description="The configuration file contained syntax 
errors and could not be parsed.")
+       private class BadConfig extends InternalServerError {
+               private static final long serialVersionUID = 1L;
+
+               BadConfig(Exception e) {
+                       super(e, "The configuration file contained syntax 
errors and could not be parsed.");
+               }
+       }
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper methods
+       
//-----------------------------------------------------------------------------------------------------------------
 
-       private ObjectMap getSection(String name) throws NotFound, BadRequest {
+       private ObjectMap getSection(String name) throws SectionNotFound, 
BadConfig {
                ObjectMap m;
                try {
                        m = 
getServletConfig().getConfig().getSectionAsMap(name);
                } catch (ParseException e) {
-                       throw new BadRequest(e, "Invalid input");
+                       throw new BadConfig(e);
                }
                if (m == null)
-                       throw new NotFound("Section not found.");
+                       throw new SectionNotFound();
                return m;
        }
 }
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
index 9d50ec6..2e10ed3 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
@@ -12,22 +12,19 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice.resources;
 
-import static java.util.logging.Level.*;
 import static org.apache.juneau.html.HtmlDocSerializer.*;
 import static org.apache.juneau.http.HttpMethodName.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
 
 import java.io.*;
-import java.net.URI;
 import java.util.*;
-import java.util.logging.*;
 
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.dto.*;
+import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.converters.*;
 import org.apache.juneau.rest.exception.*;
 import org.apache.juneau.rest.helper.*;
 import org.apache.juneau.transforms.*;
@@ -51,7 +48,7 @@ import org.apache.juneau.utils.*;
  *     <li>
  *             <l>DirectoryResource.allowViews</l> - If <jk>true</jk>, allows 
view and download access to files.
  *     <li>
- *             <l>DirectoryResource.allowPuts</l> - If <jk>true</jk>, allows 
files to be created or overwritten.
+ *             <l>DirectoryResource.allowUploads</l> - If <jk>true</jk>, 
allows files to be created or overwritten.
  *     <li>
  *             <l>DirectoryResource.allowDeletes</l> - If <jk>true</jk>, 
allows files to be deleted.
  * </ul>
@@ -67,102 +64,126 @@ import org.apache.juneau.utils.*;
        ),
        allowedMethodParams="*",
        properties={
-               @Property(name=HTML_uriAnchorText, value="PROPERTY_NAME"),
-               @Property(name="DirectoryResource.rootDir", value="")
+               @Property(name=HTML_uriAnchorText, value="PROPERTY_NAME")
        }
 )
 @SuppressWarnings("javadoc")
 public class DirectoryResource extends BasicRestServlet {
        private static final long serialVersionUID = 1L;
 
+       
//-------------------------------------------------------------------------------------------------------------------
+       // Configurable properties
+       
//-------------------------------------------------------------------------------------------------------------------
+
+       private static final String PREFIX = "DirectoryResource.";
+       
+       /**
+        * Configuration property:  Root directory.
+        */
+       public static final String DIRECTORY_RESOURCE_rootDir = PREFIX + 
"rootDir.s";
+       
+       /**
+        * Configuration property:  Allow view and downloads on files.
+        */
+       public static final String DIRECTORY_RESOURCE_allowViews = PREFIX + 
"allowViews.b";
+       
+       /**
+        * Configuration property:  Allow deletes on files.
+        */
+       public static final String DIRECTORY_RESOURCE_allowDeletes = PREFIX + 
"allowDeletes.b";
+       
+       /**
+        * Configuration property:  Allow uploads on files.
+        */
+       public static final String DIRECTORY_RESOURCE_allowUploads = PREFIX + 
"allowUploads.b";
+
+
+       
//-------------------------------------------------------------------------------------------------------------------
+       // Instance
+       
//-------------------------------------------------------------------------------------------------------------------
+       
        private File rootDir;     // The root directory
 
        // Settings enabled through servlet init parameters
-       boolean allowDeletes, allowPuts, allowViews;
-
-       private static Logger logger = 
Logger.getLogger(DirectoryResource.class.getName());
+       boolean allowDeletes, allowUploads, allowViews;
 
        @RestHook(INIT)
        public void init(RestContextBuilder b) throws Exception { 
                RestContextProperties p = b.getProperties();
-               rootDir = new File(p.getString("DirectoryResource.rootDir"));
-               allowViews = p.getBoolean("DirectoryResource.allowViews", 
false);
-               allowDeletes = p.getBoolean("DirectoryResource.allowDeletes", 
false);
-               allowPuts = p.getBoolean("DirectoryResource.allowPuts", false);
-       }
-
-       /**
-        * Returns the root directory defined by the 'rootDir' init parameter.
-        * 
-        * <p>
-        * Subclasses can override this method to provide their own root 
directory.
-        * 
-        * @return The root directory.
-        */
-       protected File getRootDir() {
-               if (rootDir == null) {
-                       rootDir = new 
File(getProperties().getString("rootDir"));
-                       if (! rootDir.exists())
-                               if (! rootDir.mkdirs())
-                                       throw new RuntimeException("Could not 
create root dir");
-               }
-               return rootDir;
+               rootDir = new File(p.getString(DIRECTORY_RESOURCE_rootDir));
+               allowViews = p.getBoolean(DIRECTORY_RESOURCE_allowViews, false);
+               allowDeletes = p.getBoolean(DIRECTORY_RESOURCE_allowDeletes, 
false);
+               allowUploads = p.getBoolean(DIRECTORY_RESOURCE_allowUploads, 
false);
        }
 
        @RestMethod(
                name=GET, 
                path="/*",
-               summary="View files on directory",
-               description="Returns a listing of all files in the specified 
directory.",
-               converters={Queryable.class},
+               summary="View information on file or directory",
+               description="Returns information about the specified file or 
directory.",
                htmldoc=@HtmlDoc(
-                       nav={"<h5>Folder:  $RA{fullPath}</h5>"}
+                       nav={"<h5>Folder:  $RA{fullPath}</h5>"},
+                       nowrap=true
                )
        )
-       public FileListing listFiles(RestRequest req, @PathRemainder String 
path) throws NotFound, Exception {
+       public FileResource getFile(RestRequest req, @PathRemainder String 
path) throws NotFound, Exception {
 
-               File dir = getDir(path);
+               File dir = getFile(path);
                req.setAttribute("fullPath", dir.getAbsolutePath());
-               
-               FileListing l = new FileListing();
-               for (File fc : dir.listFiles()) 
-                       l.add(new FileResource(fc, (path != null ? (path + '/') 
: "") + urlEncode(fc.getName())));
-               return l;
+
+               return new FileResource(dir, path, true);
        }
        
        @RestMethod(
-               name=GET, 
-               path="/file/*",
-               summary="View information about file",
-               description="Returns detailed information about the specified 
file.",
-               htmldoc=@HtmlDoc(
-                       nav={"<h5>File:  $RA{fullPath}</h5>"}
-               )
+               name="VIEW", 
+               path="/*",
+               summary="View contents of file",
+               description="View the contents of a file.\nContent-Type is set 
to 'text/plain'."
        )
-       public FileResource getFileInfo(RestRequest req, @PathRemainder String 
path) throws NotFound, Exception {
-               
-               File f = getFile(path);
-               req.setAttribute("fullPath", f.getAbsolutePath());
-               
-               return new FileResource(f, path);
+       public FileContents viewFile(RestResponse res, @PathRemainder String 
path) throws NotFound, MethodNotAllowed {
+               if (! allowViews)
+                       throw new MethodNotAllowed("VIEW not enabled");
+
+               res.setContentType("text/plain");
+               try {
+                       return new FileContents(getFile(path));
+               } catch (FileNotFoundException e) {
+                       throw new NotFound("File not found");
+               }
+       }
+       
+       @RestMethod(
+               name="DOWNLOAD", 
+               path="/*",
+               summary="Download file",
+               description="Download the contents of a file.\nContent-Type is 
set to 'application/octet-stream'."
+       )
+       public FileContents downloadFile(RestResponse res, @PathRemainder 
String path) throws NotFound, MethodNotAllowed {
+               if (! allowViews)
+                       throw new MethodNotAllowed("DOWNLOAD not enabled");
+
+               res.setContentType("application/octet-stream");
+               try {
+                       return new FileContents(getFile(path));
+               } catch (FileNotFoundException e) {
+                       throw new NotFound("File not found");
+               }
        }
 
        @RestMethod(
                name=DELETE, 
-               path="/file/*",
+               path="/*",
                summary="Delete file",
                description="Delete a file on the file system."
        )
        public RedirectToRoot deleteFile(@PathRemainder String path) throws 
MethodNotAllowed {
-               if (! allowDeletes)
-                       throw new MethodNotAllowed("DELETE not enabled");
                deleteFile(getFile(path));
                return new RedirectToRoot();
        }
 
        @RestMethod(
                name=PUT, 
-               path="/file/*",
+               path="/*",
                summary="Add or replace file",
                description="Add or overwrite a file on the file system."
        )
@@ -171,7 +192,7 @@ public class DirectoryResource extends BasicRestServlet {
                @PathRemainder String path
        ) throws InternalServerError {
                
-               if (! allowPuts)
+               if (! allowUploads)
                        throw new MethodNotAllowed("PUT not enabled");
 
                File f = getFile(path);
@@ -185,68 +206,12 @@ public class DirectoryResource extends BasicRestServlet {
                return new RedirectToRoot();
        }
 
-       @RestMethod(
-               name="VIEW", 
-               path="/file/*",
-               summary="View contents of file",
-               description="View the contents of a file."
-       )
-       public FileContents doView(RestResponse res, @PathRemainder String 
path) throws NotFound, MethodNotAllowed {
-               if (! allowViews)
-                       throw new MethodNotAllowed("VIEW not enabled");
-
-               res.setContentType("text/plain");
-               try {
-                       return new FileContents(getFile(path));
-               } catch (FileNotFoundException e) {
-                       throw new NotFound("File not found");
-               }
-       }
-       
-       @RestMethod(
-               name="DOWNLOAD", 
-               path="/file/*",
-               summary="Download file",
-               description="Download the contents of a file"
-       )
-       public FileContents doDownload(RestResponse res, @PathRemainder String 
path) throws NotFound, MethodNotAllowed {
-               if (! allowViews)
-                       throw new MethodNotAllowed("DOWNLOAD not enabled");
-
-               res.setContentType("application/octet-stream");
-               try {
-                       return new FileContents(getFile(path));
-               } catch (FileNotFoundException e) {
-                       throw new NotFound("File not found");
-               }
-       }
-
-       private File getFile(String path) throws NotFound {
-               File f = new File(rootDir.getAbsolutePath() + '/' + path);
-               if (f.exists() && f.isFile())
-                       return f;
-               throw new NotFound("File not found.");
-       }
-       
-       private File getDir(String path) throws NotFound {
-               if (path == null)
-                       return rootDir;
-               File f = new File(rootDir.getAbsolutePath() + '/' + path);
-               if (f.exists() && f.isDirectory())
-                       return f;
-               throw new NotFound("Directory not found.");
-       }
-
        
//-----------------------------------------------------------------------------------------------------------------
        // Helper beans
        
//-----------------------------------------------------------------------------------------------------------------
 
-       @SuppressWarnings("serial")
-       @ResponseInfo(description="Directory listing")
-       static class FileListing extends ArrayList<FileResource> {}
-       
        @ResponseInfo(schema="{schema:{type:'string',format:'binary'}}", 
description="Contents of file")
-       static class FileContents extends FileReader {
+       static class FileContents extends FileInputStream {
                public FileContents(File file) throws FileNotFoundException {
                        super(file);
                }
@@ -263,31 +228,28 @@ public class DirectoryResource extends BasicRestServlet {
        }
 
        @ResponseInfo(description="File or directory details")
+       @Bean(properties="type,name,size,lastModified,actions,files")
        public class FileResource {
-               private File f;
-               private String path;
+               private final File f;
+               private final String path;
+               private final String uri;
+               private final boolean includeChildren;
 
-               public FileResource(File f, String path) {
+               public FileResource(File f, String path, boolean 
includeChildren) {
                        this.f = f;
                        this.path = path;
-               }
-
-               // Bean property getters
-
-               public URI getUri() {
-                       if (f.isDirectory())
-                               return URI.create("servlet:/"+path);
-                       return URI.create("servlet:/file/"+path);
+                       this.uri = "servlet:/"+(path == null ? "" : path);
+                       this.includeChildren = includeChildren;
                }
 
                public String getType() {
                        return (f.isDirectory() ? "dir" : "file");
                }
 
-               public String getName() {
-                       return f.getName();
+               public LinkString getName() {
+                       return new LinkString(f.getName(), uri);
                }
-
+               
                public long getSize() {
                        return f.isDirectory() ? f.listFiles().length : 
f.length();
                }
@@ -297,31 +259,70 @@ public class DirectoryResource extends BasicRestServlet {
                        return new Date(f.lastModified());
                }
 
+               @Html(format=HtmlFormat.HTML_CDC)
                public List<Action> getActions() throws Exception {
                        List<Action> l = new ArrayList<>();
                        if (allowViews && f.canRead() && ! f.isDirectory()) {
-                               l.add(new Action("view", getUri().toString() + 
"?method=VIEW"));
-                               l.add(new Action("download", 
getUri().toString() + "?method=DOWNLOAD"));
+                               l.add(new Action("view", uri + "?method=VIEW"));
+                               l.add(new Action("download", uri + 
"?method=DOWNLOAD"));
                        }
                        if (allowDeletes && f.canWrite() && ! f.isDirectory())
-                               l.add(new Action("delete", getUri().toString() 
+ "?method=DELETE"));
+                               l.add(new Action("delete", uri + 
"?method=DELETE"));
                        return l;
                }
+               
+               public Set<FileResource> getFiles() {
+                       if (f.isFile() || ! includeChildren)
+                               return null;
+                       Set<FileResource> s = new TreeSet<>(new 
FileResourceComparator());
+                       for (File fc : f.listFiles()) 
+                               s.add(new FileResource(fc, (path != null ? 
(path + '/') : "") + urlEncode(fc.getName()), false));
+                       return s;
+               }
+       }
+       
+       static final class FileResourceComparator implements 
Comparator<FileResource>, Serializable {
+               private static final long serialVersionUID = 1L;
+               @Override /* Comparator */
+               public int compare(FileResource o1, FileResource o2) {
+                       int c = o1.getType().compareTo(o2.getType());
+                       return c != 0 ? c : 
o1.getName().compareTo(o2.getName());
+               }
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Returns the root directory.
+        * 
+        * @return The root directory.
+        */
+       protected File getRootDir() {
+               return rootDir;
+       }
+
+       private File getFile(String path) throws NotFound {
+               if (path == null)
+                       return rootDir;
+               File f = new File(rootDir.getAbsolutePath() + '/' + path);
+               if (f.exists())
+                       return f;
+               throw new NotFound("File not found.");
        }
 
-       /** Utility method */
        private void deleteFile(File f) {
-               try {
-                       if (f.isDirectory()) {
-                               File[] files = f.listFiles();
-                               if (files != null) {
-                                       for (File fc : files)
-                                               deleteFile(fc);
-                               }
+               if (! allowDeletes)
+                       throw new MethodNotAllowed("DELETE not enabled");
+               if (f.isDirectory()) {
+                       File[] files = f.listFiles();
+                       if (files != null) {
+                               for (File fc : files)
+                                       deleteFile(fc);
                        }
-                       f.delete();
-               } catch (Exception e) {
-                       logger.log(WARNING, "Cannot delete file '" + 
f.getAbsolutePath() + "'", e);
                }
+               if (! f.delete())
+                       throw new Forbidden("Could not delete file {0}", 
f.getAbsolutePath()) ;
        }
 }
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
index 1446dfe..66751d0 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
@@ -12,19 +12,18 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice.resources;
 
-import static org.apache.juneau.html.HtmlDocSerializer.*;
+import static org.apache.juneau.html.HtmlSerializer.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
-import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.http.HttpMethodName.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
-import java.net.URI;
 import java.nio.charset.*;
 import java.util.*;
 
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.config.*;
-import org.apache.juneau.dto.LinkString;
+import org.apache.juneau.dto.*;
+import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.converters.*;
@@ -41,6 +40,11 @@ import org.apache.juneau.transforms.*;
        description="Log files from this service",
        properties={
                @Property(name=HTML_uriAnchorText, value="PROPERTY_NAME"),
+               @Property(name=LogsResource.LOGS_RESOURCE_logDir, 
value="$C{Logging/logDir}"),
+               @Property(name=LogsResource.LOGS_RESOURCE_allowDeletes, 
value="$C{Logging/allowDeletes,true}"),
+               @Property(name=LogsResource.LOGS_RESOURCE_logFormat, 
value="$C{Logging/format}"),
+               @Property(name=LogsResource.LOGS_RESOURCE_dateFormat, 
value="$C{Logging/dateFormat}"),
+               @Property(name=LogsResource.LOGS_RESOURCE_useStackTraceHashes, 
value="$C{Logging/useStackTraceHashes}")
        },
        allowedMethodParams="*",
        pojoSwaps={
@@ -48,119 +52,98 @@ import org.apache.juneau.transforms.*;
                DateSwap.ISO8601DT.class  // Serialize Date objects as ISO8601 
strings.
        }
 )
+@SuppressWarnings("javadoc")
 public class LogsResource extends BasicRestServlet {
        private static final long serialVersionUID = 1L;
 
-       private File logDir;
-       private LogEntryFormatter leFormatter;
+       
//-------------------------------------------------------------------------------------------------------------------
+       // Configurable properties
+       
//-------------------------------------------------------------------------------------------------------------------
 
-       private final FileFilter filter = new FileFilter() {
-               @Override /* FileFilter */
-               public boolean accept(File f) {
-                       return f.isDirectory() || f.getName().endsWith(".log");
-               }
-       };
+       private static final String PREFIX = "LogsResource.";
+       
+       /**
+        * Configuration property:  Root directory.
+        */
+       public static final String LOGS_RESOURCE_logDir = PREFIX + "logDir.s";
+       
+       /**
+        * Configuration property:  Allow deletes on files.
+        */
+       public static final String LOGS_RESOURCE_allowDeletes = PREFIX + 
"allowDeletes.b";
+       
+       /**
+        * Configuration property:  Log entry format.
+        */
+       public static final String LOGS_RESOURCE_logFormat = PREFIX + 
"logFormat.s";
 
        /**
-        * Initializes the log directory and formatter.
-        * 
-        * @param builder The resource config.
-        * @throws Exception
+        * Configuration property:  Log entry format.
         */
-       @RestHook(INIT) 
-       public void init(RestContextBuilder builder) throws Exception {
-               Config c = builder.getConfig();
+       public static final String LOGS_RESOURCE_dateFormat = PREFIX + 
"dateFormat.s";
 
-               logDir = new File(c.getString("Logging/logDir", "."));
+       /**
+        * Configuration property:  Log entry format.
+        */
+       public static final String LOGS_RESOURCE_useStackTraceHashes = PREFIX + 
"useStackTraceHashes.b";
+
+       
//-------------------------------------------------------------------------------------------------------------------
+       // Instance
+       
//-------------------------------------------------------------------------------------------------------------------
+       
+       private File logDir;
+       private LogEntryFormatter leFormatter;
+       boolean allowDeletes;
+
+
+       @RestHook(INIT) 
+       public void init(RestContextBuilder b) throws Exception {
+               RestContextProperties p = b.getProperties();
+               logDir = new File(p.getString(LOGS_RESOURCE_logDir));
+               allowDeletes = p.getBoolean(LOGS_RESOURCE_allowDeletes);
                leFormatter = new LogEntryFormatter(
-                       c.getString("Logging/format", "[{date} {level}] 
{msg}%n"),
-                       c.getString("Logging/dateFormat", "yyyy.MM.dd 
hh:mm:ss"),
-                       c.getBoolean("Logging/useStackTraceHashes")
+                       p.getString(LOGS_RESOURCE_logFormat, "[{date} {level}] 
{msg}%n"),
+                       p.getString(LOGS_RESOURCE_dateFormat, "yyyy.MM.dd 
hh:mm:ss"),
+                       p.getBoolean(LOGS_RESOURCE_useStackTraceHashes, true)
                );
        }
 
-       /**
-        * [GET /*] - Get file details or directory listing.
-        * 
-        * @param req The HTTP request
-        * @param res The HTTP response
-        * @param properties The writable properties for setting the 
descriptions.
-        * @param path The log file path.
-        * @return The log file.
-        * @throws Exception
-        */
        @RestMethod(
-               name=GET,
+               name=GET, 
                path="/*",
-               swagger= {
-                       "responses:{",
-                               "200: {description:'OK'},",
-                               "404: {description:'Not Found'}",
-                       "}"
-               }
+               summary="View information on file or directory",
+               description="Returns information about the specified file or 
directory.",
+               htmldoc=@HtmlDoc(
+                       nav={"<h5>Folder:  $RA{fullPath}</h5>"},
+                       nowrap=true
+               )
        )
-       public Object getFileOrDirectory(RestRequest req, RestResponse res, 
RequestProperties properties, @PathRemainder String path) throws Exception {
+       public FileResource getFile(RestRequest req, @PathRemainder String 
path) throws NotFound, Exception {
 
-               File f = getFile(path);
-
-               if (f.isDirectory()) {
-                       Set<FileResource> l = new TreeSet<>(new 
FileResourceComparator());
-                       File[] files = f.listFiles(filter);
-                       if (files != null) {
-                               for (File fc : files) {
-                                       URI fUrl = new URI("servlet:/" + 
fc.getName());
-                                       l.add(new FileResource(fc, fUrl));
-                               }
-                       }
-                       return l;
-               }
+               File dir = getFile(path);
+               req.setAttribute("fullPath", dir.getAbsolutePath());
 
-               return new FileResource(f, new URI("servlet:/"));
+               return new FileResource(dir, path, allowDeletes, true);
        }
 
-       /**
-        * [VIEW /*] - Retrieve the contents of a log file.
-        * 
-        * @param req The HTTP request.
-        * @param res The HTTP response.
-        * @param path The log file path.
-        * @param properties The writable properties for setting the 
descriptions.
-        * @param highlight If <code>true</code>, add color highlighting based 
on severity.
-        * @param start Optional start timestamp.  Don't print lines logged 
before the specified timestamp.  Example:  "&amp;start=2014-01-23 11:25:47".
-        * @param end Optional end timestamp.  Don't print lines logged after 
the specified timestamp.  Example:  "&amp;end=2014-01-23 11:25:47".
-        * @param thread Optional thread name filter.  Only show log entries 
with the specified thread name.  Example: "&amp;thread=pool-33-thread-1".
-        * @param loggers Optional logger filter.  Only show log entries if 
they were produced by one of the specified loggers (simple class name).  
Example: "&amp;loggers=(LinkIndexService,LinkIndexRestService)".
-        * @param severity Optional severity filter.  Only show log entries 
with the specified severity.  Example: "&amp;severity=(ERROR,WARN)".
-        * @throws NotFound File not found.
-        * @throws MethodNotAllowed View not available on directories.
-        * @throws IOException Could not write file.
-        */
        @RestMethod(
                name="VIEW",
                path="/*",
-               swagger= {
-                       "responses:{",
-                               "200: {description:'OK'},",
-                               "404: {description:'Not Found'}",
-                       "}"
-               }
+               summary="View contents of log file",
+               description="View the contents of a log file."
        )
        public void viewFile(
-                       RestRequest req, 
                        RestResponse res, 
                        @PathRemainder String path, 
-                       RequestProperties properties, 
-                       @Query("highlight") boolean highlight, 
-                       @Query("start") String start, 
-                       @Query("end") String end, 
-                       @Query("thread") String thread, 
-                       @Query("loggers") String[] loggers, 
-                       @Query("severity") String[] severity
+                       @Query(name="highlight", description="Add severity 
color highlighting.", example="true") boolean highlight, 
+                       @Query(name="start", description="Start timestamp 
(ISO8601, full or partial).\nDon't print lines logged before the specified 
timestamp.\nUse any of the following formats: yyyy, yyyy-MM, yyyy-MM-dd, 
yyyy-MM-ddThh, yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS", 
example="2014-01-23T11:25:47") String start, 
+                       @Query(name="end", description="End timestamp (ISO8601, 
full or partial).\nDon't print lines logged after the specified timestamp.\nUse 
any of the following formats: yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddThh, 
yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS", 
example="2014-01-24") String end, 
+                       @Query(name="thread", description="Thread name 
filter.\nOnly show log entries with the specified thread name.", 
example="thread-pool-33-thread-1") String thread, 
+                       @Query(name="loggers", description="Logger filter 
(simple class name).\nOnly show log entries if they were produced by one of the 
specified loggers.", example="['LinkIndexService','LinkIndexRestService']") 
String[] loggers, 
+                       @Query(name="severity", description="Severity 
filter.\nOnly show log entries with the specified severity.", 
example="['ERROR','WARN']") String[] severity
                ) throws NotFound, MethodNotAllowed, IOException {
 
                File f = getFile(path);
-               if (f.isDirectory())
-                       throw new MethodNotAllowed("View not available on 
directories");
 
                Date startDate = parseISO8601Date(start), endDate = 
parseISO8601Date(end);
 
@@ -203,168 +186,189 @@ public class LogsResource extends BasicRestServlet {
                }
        }
 
-       /**
-        * [VIEW /*] - Retrieve the contents of a log file as parsed entries.
-        * 
-        * @param req The HTTP request.
-        * @param path The log file path.
-        * @param start Optional start timestamp.  Don't print lines logged 
before the specified timestamp.  Example:  "&amp;start=2014-01-23 11:25:47".
-        * @param end Optional end timestamp.  Don't print lines logged after 
the specified timestamp.  Example:  "&amp;end=2014-01-23 11:25:47".
-        * @param thread Optional thread name filter.  Only show log entries 
with the specified thread name.  Example: "&amp;thread=pool-33-thread-1".
-        * @param loggers Optional logger filter.  Only show log entries if 
they were produced by one of the specified loggers (simple class name).  
Example: "&amp;loggers=(LinkIndexService,LinkIndexRestService)".
-        * @param severity Optional severity filter.  Only show log entries 
with the specified severity.  Example: "&amp;severity=(ERROR,WARN)".
-        * @return The parsed contents of the log file.
-        * @throws NotFound File not found.
-        * @throws MethodNotAllowed View not available on directories.
-        * @throws IOException Could not read file.
-        */
        @RestMethod(
                name="PARSE",
                path="/*",
                converters=Queryable.class,
-               swagger= {
-                       "responses:{",
-                               "200: {description:'OK'},",
-                               "404: {description:'Not Found'}",
-                       "}"
+               summary="View parsed contents of file",
+               description="View the parsed contents of a file.",
+               htmldoc=@HtmlDoc(
+                       nav={"<h5>Folder:  $RA{fullPath}</h5>"}
+               ),
+               swagger={
+                       "parameters:[",
+                                Queryable.SWAGGER_PARAMS,
+                       "]"
                }
        )
        public LogParser viewParsedEntries(
-                       RestRequest req, 
+                       RestRequest req,
                        @PathRemainder String path, 
-                       @Query("start") String start,
-                       @Query("end") String end, 
-                       @Query("thread") String thread, 
-                       @Query("loggers") String[] loggers, 
-                       @Query("severity") String[] severity
-               ) throws NotFound, MethodNotAllowed, IOException {
+                       @Query(name="start", description="Start timestamp 
(ISO8601, full or partial).\nDon't print lines logged before the specified 
timestamp.\nUse any of the following formats: yyyy, yyyy-MM, yyyy-MM-dd, 
yyyy-MM-ddThh, yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS", 
example="2014-01-23T11:25:47") String start, 
+                       @Query(name="end", description="End timestamp (ISO8601, 
full or partial).\nDon't print lines logged after the specified timestamp.\nUse 
any of the following formats: yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddThh, 
yyyy-MM-ddThh:mm, yyyy-MM-ddThh:mm:ss, yyyy-MM-ddThh:mm:ss.SSS", 
example="2014-01-24") String end, 
+                       @Query(name="thread", description="Thread name 
filter.\nOnly show log entries with the specified thread name.", 
example="thread-pool-33-thread-1") String thread, 
+                       @Query(name="loggers", description="Logger filter 
(simple class name).\nOnly show log entries if they were produced by one of the 
specified loggers.", example="['LinkIndexService','LinkIndexRestService']") 
String[] loggers, 
+                       @Query(name="severity", description="Severity 
filter.\nOnly show log entries with the specified severity.", 
example="['ERROR','WARN']") String[] severity
+               ) throws NotFound, IOException {
 
                File f = getFile(path);
-               Date startDate = parseISO8601Date(start), endDate = 
parseISO8601Date(end);
+               req.setAttribute("fullPath", f.getAbsolutePath());
 
-               if (f.isDirectory())
-                       throw new MethodNotAllowed("View not available on 
directories");
+               Date startDate = parseISO8601Date(start), endDate = 
parseISO8601Date(end);
 
                return getLogParser(f, startDate, endDate, thread, loggers, 
severity);
        }
 
-       /**
-        * [DOWNLOAD /*] - Download file.
-        * 
-        * @param res The HTTP response.
-        * @param path The log file path.
-        * @return The contents of the log file.
-        * @throws NotFound File not found.
-        * @throws MethodNotAllowed Download not available on directories.
-        */
        @RestMethod(
-               name="DOWNLOAD",
+               name="DOWNLOAD", 
                path="/*",
-               swagger= {
-                       "responses:{",
-                               "200: {description:'OK'},",
-                               "404: {description:'Not Found'}",
-                       "}"
-               }
+               summary="Download file",
+               description="Download the contents of a file.\nContent-Type is 
set to 'application/octet-stream'."
        )
-       public Object downloadFile(RestResponse res, @PathRemainder String 
path) throws NotFound, MethodNotAllowed {
-
-               File f = getFile(path);
-
-               if (f.isDirectory())
-                       throw new MethodNotAllowed("Download not available on 
directories");
-
+       public FileContents downloadFile(RestResponse res, @PathRemainder 
String path) throws NotFound, MethodNotAllowed {
                res.setContentType("application/octet-stream");
-               res.setContentLength((int)f.length());
-               
                try {
-                       return new FileInputStream(f);
+                       return new FileContents(getFile(path));
                } catch (FileNotFoundException e) {
                        throw new NotFound("File not found");
                }
        }
 
-       /**
-        * [DELETE /*] - Delete a file.
-        * 
-        * @param path The log file path.
-        * @return A redirect object to the root.
-        * @throws NotFound File not found.
-        * @throws BadRequest Delete not available on directories.
-        * @throws Forbidden Could not delete file.
-        */
        @RestMethod(
-               name=DELETE,
+               name=DELETE, 
                path="/*",
-               swagger= {
-                       "responses:{",
-                               "200: {description:'OK'},",
-                               "404: {description:'Not Found'}",
-                       "}"
-               }
+               summary="Delete log file",
+               description="Delete a log file on the file system."
        )
-       public Object deleteFile(@PathRemainder String path) throws NotFound, 
BadRequest, Forbidden {
+       public RedirectToRoot deleteFile(@PathRemainder String path) throws 
MethodNotAllowed {
+               deleteFile(getFile(path));
+               return new RedirectToRoot();
+       }
 
-               File f = getFile(path);
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper beans
+       
//-----------------------------------------------------------------------------------------------------------------
 
-               if (f.isDirectory())
-                       throw new BadRequest("Delete not available on 
directories.");
+       @ResponseInfo(schema="{schema:{type:'string',format:'binary'}}", 
description="Contents of file")
+       static class FileContents extends FileInputStream {
+               public FileContents(File file) throws FileNotFoundException {
+                       super(file);
+               }
+       }
+       
+       @ResponseInfo(description="Redirect to root page on success")
+       static class RedirectToRoot extends RedirectToServletRoot {}
+
+       @ResponseInfo(description="File action")
+       public static class Action extends LinkString {
+               public Action(String name, String uri, Object...uriArgs) {
+                       super(name, uri, uriArgs);
+               }
+       }
 
-               if (f.canWrite())
-                       if (! f.delete())
-                               throw new Forbidden("Could not delete file.");
+       @ResponseInfo(description="File or directory details")
+       @Bean(properties="type,name,size,lastModified,actions,files")
+       public static class FileResource {
+               private final File f;
+               private final String path;
+               private final String uri;
+               private final boolean includeChildren, allowDeletes;
 
-               return new Redirect(path + "/..");
-       }
+               public FileResource(File f, String path, boolean allowDeletes, 
boolean includeChildren) {
+                       this.f = f;
+                       this.path = path;
+                       this.uri = "servlet:/"+(path == null ? "" : path);
+                       this.includeChildren = includeChildren;
+                       this.allowDeletes = allowDeletes;
+               }
 
-       private static BufferedReader getReader(File f) throws IOException {
-               return new BufferedReader(new InputStreamReader(new 
FileInputStream(f), Charset.defaultCharset()));
+               public String getType() {
+                       return (f.isDirectory() ? "dir" : "file");
+               }
+
+               public LinkString getName() {
+                       return new LinkString(f.getName(), uri);
+               }
+
+               public long getSize() {
+                       return f.isDirectory() ? f.listFiles().length : 
f.length();
+               }
+
+               @Swap(DateSwap.ISO8601DTP.class)
+               public Date getLastModified() {
+                       return new Date(f.lastModified());
+               }
+
+               @Html(format=HtmlFormat.HTML_CDC)
+               public List<Action> getActions() throws Exception {
+                       List<Action> l = new ArrayList<>();
+                       if (f.canRead() && ! f.isDirectory()) {
+                               l.add(new Action("view", uri + "?method=VIEW"));
+                               l.add(new Action("highlighted", uri + 
"?method=VIEW&highlight=true"));
+                               l.add(new Action("parsed", uri + 
"?method=PARSE"));
+                               l.add(new Action("download", uri + 
"?method=DOWNLOAD"));
+                               if (allowDeletes)
+                                       l.add(new Action("delete", uri + 
"?method=DELETE"));
+                       }
+                       return l;
+               }
+               
+               public Set<FileResource> getFiles() {
+                       if (f.isFile() || ! includeChildren)
+                               return null;
+                       Set<FileResource> s = new TreeSet<>(FILE_COMPARATOR);
+                       for (File fc : f.listFiles(FILE_FILTER)) 
+                               s.add(new FileResource(fc, (path != null ? 
(path + '/') : "") + urlEncode(fc.getName()), allowDeletes, false));
+                       return s;
+               }
+
+               static final FileFilter FILE_FILTER = new FileFilter() {
+                       @Override /* FileFilter */
+                       public boolean accept(File f) {
+                               return f.isDirectory() || 
f.getName().endsWith(".log");
+                       }
+               };
+
+               static final Comparator<FileResource> FILE_COMPARATOR = new 
Comparator<FileResource>() {
+                       @Override /* Comparator */
+                       public int compare(FileResource o1, FileResource o2) {
+                               int c = o1.getType().compareTo(o2.getType());
+                               return c != 0 ? c : 
o1.getName().compareTo(o2.getName());
+                       }
+               };
        }
 
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
        private File getFile(String path) throws NotFound {
-               if (path != null && path.indexOf("..") != -1)
-                       throw new NotFound("File not found.");
-               File f = (path == null ? logDir : new 
File(logDir.getAbsolutePath() + '/' + path));
-               if (filter.accept(f))
+               if (path == null)
+                       return logDir;
+               File f = new File(logDir.getAbsolutePath() + '/' + path);
+               if (f.exists())
                        return f;
                throw new NotFound("File not found.");
        }
-
-       /**
-        * File bean.
-        */
-       @SuppressWarnings("javadoc")
-       public static class FileResource {
-               final File f;
-               public final String type;
-               public final Object name;
-               public final Long size;
-               @Swap(DateSwap.DateTimeMedium.class) public Date lastModified;
-               public URI view, highlighted, parsed, download, delete;
-
-               public FileResource(File f, URI uri) throws Exception {
-                       this.f = f;
-                       this.type = (f.isDirectory() ? "dir" : "file");
-                       this.name = f.isDirectory() ? new 
LinkString(f.getName(), uri.toString()) : f.getName();
-                       this.size = f.isDirectory() ? null : f.length();
-                       this.lastModified = new Date(f.lastModified());
-                       if (f.canRead() && ! f.isDirectory()) {
-                               this.view = new URI(uri + "?method=VIEW");
-                               this.highlighted = new URI(uri + 
"?method=VIEW&highlight=true");
-                               this.parsed = new URI(uri + "?method=PARSE");
-                               this.download = new URI(uri + 
"?method=DOWNLOAD");
-                               this.delete = new URI(uri + "?method=DELETE");
+       
+       private void deleteFile(File f) {
+               if (! allowDeletes)
+                       throw new MethodNotAllowed("DELETE not enabled");
+               if (f.isDirectory()) {
+                       File[] files = f.listFiles();
+                       if (files != null) {
+                               for (File fc : files)
+                                       deleteFile(fc);
                        }
                }
+               if (! f.delete())
+                       throw new Forbidden("Could not delete file {0}", 
f.getAbsolutePath()) ;
        }
 
-       static final class FileResourceComparator implements 
Comparator<FileResource>, Serializable {
-               private static final long serialVersionUID = 1L;
-               @Override /* Comparator */
-               public int compare(FileResource o1, FileResource o2) {
-                       int c = o1.type.compareTo(o2.type);
-                       return c != 0 ? c : 
o1.f.getName().compareTo(o2.f.getName());
-               }
+       private static BufferedReader getReader(File f) throws IOException {
+               return new BufferedReader(new InputStreamReader(new 
FileInputStream(f), Charset.defaultCharset()));
        }
 
        private Object getReader(File f, final Date start, final Date end, 
final String thread, final String[] loggers, final String[] severity) throws 
IOException {
diff --git 
a/juneau-microservice/juneau-microservice-template/my-microservice.cfg 
b/juneau-microservice/juneau-microservice-template/my-microservice.cfg
index 10c9306..5fde384 100755
--- a/juneau-microservice/juneau-microservice-template/my-microservice.cfg
+++ b/juneau-microservice/juneau-microservice-template/my-microservice.cfg
@@ -94,12 +94,10 @@ logDir = logs
 logFile = microservice.%g.log
 
 # Whether to append to the existing log file or create a new one.
-# Default is false.
-append = 
+append = false
 
 # The SimpleDateFormat format to use for dates.
-# Default is "yyyy.MM.dd hh:mm:ss".
-dateFormat = 
+dateFormat = yyyy.MM.dd hh:mm:ss
 
 # The log message format.
 # The value can contain any of the following variables:
@@ -111,17 +109,14 @@ dateFormat =
 #      {msg} - The log message.
 #      {threadid} - The thread ID.
 #      {exception} - The localized exception message.
-# Default is "[{date} {level}] {msg}%n".
-format =
+format = [{date} {level}] {msg}%n
 
 # The maximum log file size.
 # Suffixes available for numbers.
 # See Config.getInt(String,int) for details.
-# Default is 1M.
-limit = 10M
+limit = 1M
 
 # Max number of log files.
-# Default is 1.
 count = 5
 
 # Default log levels.
@@ -137,17 +132,14 @@ levels =
 
 # Only print unique stack traces once and then refer to them by a simple 8 
character hash identifier.
 # Useful for preventing log files from filling up with duplicate stack traces.
-# Default is false.
 useStackTraceHashes = true
 
 # The default level for the console logger.
 # Values are serialized Level POJOs (SEVERE, WARNING, INFO, CONFIG, FINE, 
FINER, FINEST)
-# Default is WARNING.
 consoleLevel = WARNING
 
 # The default level for the file logger.
 # Values are serialized Level POJOs (SEVERE, WARNING, INFO, CONFIG, FINE, 
FINER, FINEST)
-# Default is INFO.
 fileLevel = INFO
 
 
#=======================================================================================================================
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
index ae8d387..566f366 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
@@ -623,13 +623,13 @@ public class BasicRestInfoProvider implements 
RestInfoProvider {
                        String paramName = piri.getString("name");
                        String s = 
req.getPartSerializer().serialize(HttpPartType.valueOf(in.toUpperCase()), 
example);
                        if ("query".equals(in))
-                               s = "?" + paramName + "=" + s;
+                               s = "?" + urlEncodeLax(paramName) + "=" + 
urlEncodeLax(s);
                        else if ("formData".equals(in))
                                s = paramName + "=" + s;
                        else if ("header".equals(in))
                                s = paramName + ": " + s;
                        else if ("path".equals(in))
-                               s = 
sm.getPathPattern().replace("{"+paramName+"}", s);
+                               s = 
sm.getPathPattern().replace("{"+paramName+"}", urlEncodeLax(s));
                        examples.put("example", s);
                }
                

-- 
To stop receiving notification emails like this one, please contact
jamesbog...@apache.org.

Reply via email to