Improve debugging support in microservice.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/720e7ff6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/720e7ff6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/720e7ff6

Branch: refs/heads/master
Commit: 720e7ff6eece9535023cb6706bfc083e17d84b7c
Parents: ab8f0fa
Author: JamesBognar <jamesbog...@apache.org>
Authored: Sun Sep 24 15:54:11 2017 -0400
Committer: JamesBognar <jamesbog...@apache.org>
Committed: Sun Sep 24 15:54:11 2017 -0400

----------------------------------------------------------------------
 juneau-doc/src/main/javadoc/overview.html       |  71 ++-
 .../juneau-examples-rest/examples.cfg           | 140 ++++--
 juneau-examples/juneau-examples-rest/jetty.xml  |  15 +-
 .../juneau/examples/rest/DirectoryResource.java |   3 +-
 .../juneau/examples/rest/RootResources.java     |   1 +
 .../examples/rest/SampleRemoteableServlet.java  |   8 +-
 .../rest/addressbook/AddressBookResource.java   |   7 +-
 .../apache/juneau/microservice/JettyLogger.java | 198 +++++++++
 .../juneau/microservice/Microservice.java       | 199 ++++++++-
 .../apache/juneau/microservice/Resource.java    |  12 +-
 .../juneau/microservice/ResourceGroup.java      |  12 +-
 .../juneau/microservice/ResourceJenaGroup.java  |  12 +-
 .../juneau/microservice/RestMicroservice.java   | 429 +++++++------------
 .../microservice/resources/DebugResource.java   |  74 ++++
 .../resources/DirectoryResource.java            |   3 +-
 .../microservice/resources/LogsResource.java    |   3 +-
 .../juneau-microservice-template/jetty.xml      |  17 +-
 .../my-microservice.cfg                         |  78 +++-
 .../juneau-microservice-test/jetty.xml          |  15 +-
 .../juneau-microservice-test.cfg                |  39 +-
 .../juneau/rest/test/AcceptCharsetResource.java |   8 +-
 .../juneau/rest/test/ContentResource.java       |   6 +-
 .../apache/juneau/rest/test/ParamsResource.java |   5 +-
 .../java/org/apache/juneau/rest/test/Root.java  |   4 +-
 .../rest/test/ThirdPartyProxyResource.java      |  62 ++-
 .../apache/juneau/rest/test/HeadersTest.java    |   5 +-
 .../juneau/rest/test/TestMicroservice.java      |  17 +-
 .../juneau/rest/test/ThirdPartyProxyTest.java   |  43 +-
 .../java/org/apache/juneau/rest/CallMethod.java |  10 +-
 .../org/apache/juneau/rest/RestCallHandler.java |   2 +-
 .../java/org/apache/juneau/rest/RestConfig.java | 113 +++++
 .../org/apache/juneau/rest/RestContext.java     | 257 ++---------
 .../org/apache/juneau/rest/RestException.java   |   5 +-
 .../juneau/rest/RestResourceResolver.java       |   2 +-
 .../apache/juneau/rest/RestServletDefault.java  |   7 +-
 .../apache/juneau/rest/annotation/FormData.java |   2 +-
 .../apache/juneau/rest/annotation/Query.java    |   2 +-
 .../juneau/rest/annotation/RestMethod.java      |  47 ++
 .../juneau/rest/annotation/RestResource.java    | 142 +++++-
 .../java/org/apache/juneau/rest/package.html    |  22 +-
 40 files changed, 1403 insertions(+), 694 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-doc/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 2ea98dc..2304c63 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -5439,12 +5439,12 @@
                        }
                ),
 
+               <jc>// Allow INIT as a method parameter.</jc>
+               allowMethodParam=<js>"*"</js>,
+       
                <jc>// Properties that get applied to all serializers and 
parsers.</jc>
                properties={
                        
-                       <jc>// Allow INIT as a method parameter.</jc>
-                       
<ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>),
-       
                        <jc>// Use single quotes.</jc>
                        
<ja>@Property</ja>(name=<jsf>SERIALIZER_quoteChar</jsf>, value=<js>"'"</js>),
                        
@@ -6122,7 +6122,7 @@
                                </p>
                                <p>
                                        The ability to overload methods is 
enabled through the 
-                                       {@link 
org.apache.juneau.rest.RestContext#REST_allowMethodParam} property.
+                                       {@link 
org.apache.juneau.rest.annotation.RestResource#allowMethodParam()} setting.
                                </p>
                        </div>
                </div>  
@@ -6163,10 +6163,8 @@
                                <js>"options: servlet:/?method=OPTIONS"</js>
                        }
                ),
-               properties={ 
-                       <jc>// Allow us to use method=POST from a browser.</jc> 
-                       
<ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>) 
-               } 
+               <jc>// Allow us to use method=POST from a browser.</jc> 
+               allowMethodParam=<js>"*"</js> 
        ) 
        <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> 
RemoteableServlet { 
        
@@ -7448,6 +7446,7 @@
                <h6 class='topic'>juneau-dto</h6>
                <ul class='spaced-list'>
                </ul>
+               
        </div>
 
        <!-- 
===========================================================================================================
 -->
@@ -7845,13 +7844,35 @@
                                {@link 
org.apache.juneau.rest.RestResourceResolver} instances are now inherited from 
parent resources to child resources
                                unless explicitly overridden at the child level.
                                <br>It's also been changed to an interface.
-                       <li>
-                               New setting: {@link 
org.apache.juneau.rest.RestContext#REST_resourceResolver}.  
-                               <br>Allows you to specify a resource resolver 
on the servlet context to make it easier to work with
-                               dependency injection frameworks.
-                       <li>
-                               New annotation: {@link 
org.apache.juneau.rest.annotation.RestResource#contextPath()}.
-                               <br>Allows you to override the context path 
value inherited from the servlet container.
+                       <li>New annotations on {@link 
org.apache.juneau.rest.annotation.RestResource @RestResource}:
+                               <ul>
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#resourceResolver() 
resourceResolver()}
+                                               <br>Allows you to specify a 
resource resolver on the servlet context to make it easier to work with
+                                               dependency injection frameworks.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#contextPath() contextPath()} - 
+                                               <br>Allows you to override the 
context path value inherited from the servlet container.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#allowHeaderParams() 
allowHeaderParams()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_allowHeaderParams</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#allowMethodParam() 
allowMethodParam()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_allowMethodParam</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#allowBodyParam() 
allowBodyParam()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_allowBodyParam</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#renderResponseStackTraces() 
renderResponseStackTraces()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_xxx</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#useStackTraceHashes() 
useStackTraceHashes()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_useStackTraceHashes</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#defaultCharset() 
defaultCharset()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_defaultCharset</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#paramFormat() paramFormat()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_paramFormat</code> setting.
+                               </ul>
+                       <li>New annotations on {@link 
org.apache.juneau.rest.annotation.RestMethod @RestMethod}:
+                               <ul>
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestMethod#defaultCharset() defaultCharset()} 
- 
+                                               <br>Replaces the 
<code>RestContext.REST_defaultCharset</code> setting.
+                                       <li>{@link 
org.apache.juneau.rest.annotation.RestMethod#paramFormat() paramFormat()} - 
+                                               <br>Replaces the 
<code>RestContext.REST_paramFormat</code> setting.
+                               </ul>
                        <li>
                                The following implementation classes can now be 
defined as non-static inner classes of servlets/resources:
                                <ul>
@@ -7884,12 +7905,32 @@
                <h6 class='topic'>juneau-microservice</h6>
                <ul class='spaced-list'>
                        <li>
+                               The microservice has been significantly 
modified to be configured via a <code>jetty.xml</code> file
+                               for maximum flexibility instead of the 
hodge-podge of support in the config file.
+                               <br>Top-level servlets should now be defined in 
the provided <code>jetty.xml</code> file.
+                       <li>
                                New methods on {@link 
org.apache.juneau.microservice.RestMicroservice}:
                                <ul>
                                        <li>{@link 
org.apache.juneau.microservice.RestMicroservice#addServlet(Servlet,String) 
addServlet(Servlet,String)}
                                        <li>{@link 
org.apache.juneau.microservice.RestMicroservice#addServletAttribute(String,Object)
 addServletAttribute(String,Object)}
                                        <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getServer() getServer()}
+                                       <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getInstance() getInstance()}
+                                       <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getPort() getPort()}
+                                       <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getContextPath() 
getContextPath()}
+                                       <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getProtocol() getProtocol()}
+                                       <li>{@link 
org.apache.juneau.microservice.RestMicroservice#getHostName() getHostName()}
                                </ul>
+                       <li>
+                               New methods on {@link 
org.apache.juneau.microservice.Microservice}:
+                               <ul>
+                                       <li>{@link 
org.apache.juneau.microservice.Microservice#getInstance() getInstance()}
+                               </ul>
+                       <li>
+                               New class {@link 
org.apache.juneau.microservice.JettyLogger} for directing Jetty logging to the
+                               java.util.logging framework.
+                       <li>
+                               New class {@link 
org.apache.juneau.microservice.resources.DebugResource} for viewing and 
generating
+                               Jetty thread dumps through REST calls.
                </ul>
 
                <h6 class='topic'>org.apache.juneau.rest.examples</h6>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/examples.cfg
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/examples.cfg 
b/juneau-examples/juneau-examples-rest/examples.cfg
index b54711b..4a8e528 100755
--- a/juneau-examples/juneau-examples-rest/examples.cfg
+++ b/juneau-examples/juneau-examples-rest/examples.cfg
@@ -11,18 +11,40 @@
 # * specific language governing permissions and limitations under the License. 
                                             *
 # 
***************************************************************************************************************************
 
-#================================================================================
+#=======================================================================================================================
 # Basic configuration file for SaaS microservices
 # Subprojects can use this as a starting point.
-#================================================================================
+#=======================================================================================================================
 
-#================================================================================
+# 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
+
+#=======================================================================================================================
+# Jetty settings
+#=======================================================================================================================
+[Jetty]
+
+# Path of the jetty.xml file used to configure the Jetty server.
+config = jetty.xml
+
+# Resolve Juneau variables in the jetty.xml file.
+resolveVars = true
+
+# Port to use for the jetty server.
+# You can specify multiple ports.  The first available will be used.  '0' 
indicates to try a random port.
+# The resulting available port gets set as the system property "availablePort" 
which can be referenced in the 
+# jetty.xml file as "$S{availablePort}" (assuming resolveVars is enabled).
+port = 10000,0,0,0
+
+#=======================================================================================================================
 # REST settings
-#================================================================================
+#=======================================================================================================================
 [REST]
 
-jettyXml = jetty.xml
-
 # Stylesheet to use for HTML views.
 # The default options are:
 #  - styles/juneau.css
@@ -31,53 +53,107 @@ jettyXml = jetty.xml
 #      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
-
-#================================================================================
+#=======================================================================================================================
 # Logger settings
+#-----------------------------------------------------------------------------------------------------------------------
 # See FileHandler Java class for details.
-#================================================================================
+#=======================================================================================================================
 [Logging]
-logDir = $S{user.dir}/target/logs
-logFile = sample.%g.log
-dateFormat = yyyy.MM.dd hh:mm:ss
-format = [{date} {level}] {msg}%n
-append = false
+
+# The directory where to create the log file.
+# Default is "."
+logDir = ./target/logs
+
+# The name of the log file to create for the main logger.
+# The logDir and logFile make up the pattern that's passed to the FileHandler
+# constructor.
+# If value is not specified, then logging to a file will not be set up.
+logFile = microservice.%g.log
+
+# Whether to append to the existing log file or create a new one.
+# Default is false.
+append = 
+
+# The SimpleDateFormat format to use for dates.
+# Default is "yyyy.MM.dd hh:mm:ss".
+dateFormat = 
+
+# The log message format.
+# The value can contain any of the following variables:
+#      {date} - The date, formatted per dateFormat.
+#      {class} - The class name.
+#      {method} - The method name.
+#      {logger} - The logger name.
+#      {level} - The log level name.
+#      {msg} - The log message.
+#      {threadid} - The thread ID.
+#      {exception} - The localized exception message.
+# Default is "[{date} {level}] {msg}%n".
+format =
+
+# The maximum log file size.
+# Suffixes available for numbers.
+# See ConfigFile.getInt(String,int) for details.
+# Default is 1M.
 limit = 10M
+
+# Max number of log files.
+# Default is 1.
 count = 5
-levels = { org.apache.juneau:'INFO' }
+
+# Default log levels.
+# Format is lax-JSON.
+# Keys are logger names.
+# Values are serialized Level POJOs (SEVERE, WARNING, INFO, CONFIG, FINE, 
FINER, FINEST)
+levels = 
+       { 
+               '': 'WARNING', 
+               org.apache.juneau: 'WARNING', 
+               org.eclipse.jetty: 'WARNING' 
+       }
+
+# 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
+
+#=======================================================================================================================
 # 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
+# org.eclipse.jetty.util.log.class = org.eclipse.jetty.util.log.StrErrLog
+
+# Configure Jetty to log using java-util logging
+org.eclipse.jetty.util.log.class = org.apache.juneau.microservice.JettyLogger
 
 # Jetty logging level
 org.eclipse.jetty.LEVEL = WARN
 
-derby.stream.error.file = $S{user.dir}/target/logs/derby.log
+derby.stream.error.file = $C{Logging/logDir}/derby-errors.log
 
-#================================================================================
+#=======================================================================================================================
 # DockerRegistryResource properties
-#================================================================================
+#=======================================================================================================================
 [DockerRegistry]
 url = http://docker.apache.org:5000/v1
 
-#================================================================================
+#=======================================================================================================================
 # SqlQueryResource properties
-#================================================================================
+#=======================================================================================================================
 [SqlQueryResource]
 driver = org.apache.derby.jdbc.EmbeddedDriver
 directory = $S{user.dir}/target/derby/testDB
@@ -85,9 +161,9 @@ connectionUrl = 
jdbc:derby:$C{SqlQueryResource/directory};create=true
 allowTempUpdates = true
 includeRowNums = true
 
-#================================================================================
+#=======================================================================================================================
 # Source code location
-#================================================================================
+#=======================================================================================================================
 [Source]
 gitHub = 
https://github.com/apache/incubator-juneau/blob/master/juneau-examples-rest/src/main/java
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/jetty.xml
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/jetty.xml 
b/juneau-examples/juneau-examples-rest/jetty.xml
index 2cac2e9..d3ff1aa 100644
--- a/juneau-examples/juneau-examples-rest/jetty.xml
+++ b/juneau-examples/juneau-examples-rest/jetty.xml
@@ -24,7 +24,7 @@
                                        <Arg>
                                                <Ref refid="ExampleServer" />
                                        </Arg>
-                                       <Set name="port">10000</Set>
+                                       <Set 
name="port">$S{availablePort,8080}</Set>
                                </New>
                        </Item>
                </Array>
@@ -58,7 +58,18 @@
                        </Set>
                </New>
        </Set>
-       
+
+       <Set name="requestLog">
+               <New id="RequestLogImpl" 
class="org.eclipse.jetty.server.NCSARequestLog">
+                       <Set name="filename"><Property name="jetty.logs" 
default="$C{Logging/logDir,logs}"/>/jetty-requests.log</Set>
+                       <Set name="filenameDateFormat">yyyy_MM_dd</Set>
+                       <Set name="LogTimeZone">GMT</Set>
+                       <Set name="retainDays">90</Set>
+                       <Set name="append">false</Set>
+                       <Set name="LogLatency">true</Set>
+               </New>
+       </Set>
+
     <Get name="ThreadPool">
         <Set name="minThreads" type="int">10</Set>
         <Set name="maxThreads" type="int">100</Set>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
index 0b3799f..e72b17e 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
@@ -17,7 +17,6 @@ import static org.apache.juneau.rest.annotation.HookEvent.*;
 import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 import java.net.*;
@@ -51,9 +50,9 @@ import org.apache.juneau.utils.*;
                        "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
                }
        ),
+       allowMethodParam="*",
        properties={
                @Property(name=HTML_uriAnchorText, value=PROPERTY_NAME),
-               @Property(name=REST_allowMethodParam, value="*"),
                @Property(name="rootDir", value="$S{java.io.tmpdir}"),
                @Property(name="allowViews", value="false"),
                @Property(name="allowDeletes", value="false"),

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RootResources.java
----------------------------------------------------------------------
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 9ee9abe..5520b5b 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
@@ -68,6 +68,7 @@ import org.apache.juneau.rest.widget.*;
                ConfigResource.class,
                LogsResource.class,
                DockerRegistryResource.class,
+               DebugResource.class,
                ShutdownResource.class
        }
 )

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
----------------------------------------------------------------------
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
index 1306d97..a5b0aa0 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
@@ -12,8 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.examples.rest;
 
-import static org.apache.juneau.rest.RestContext.*;
-
 import java.util.*;
 
 import org.apache.juneau.examples.addressbook.*;
@@ -44,10 +42,8 @@ import org.apache.juneau.rest.remoteable.*;
                        "</div>"
                }
        ),
-       properties={
-               // Allow us to use method=POST from a browser.
-               @Property(name=REST_allowMethodParam, value="*")
-       },
+       // Allow us to use method=POST from a browser.
+       allowMethodParam="*",
        config="$S{juneau.configFile}"  // So we can resolve $C{Source/gitHub} 
above.
 )
 public class SampleRemoteableServlet extends RemoteableServlet {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index 5569388..8776322 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -16,7 +16,6 @@ import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
 import static org.apache.juneau.jena.RdfCommonContext.*;
 import static org.apache.juneau.jena.RdfSerializerContext.*;
-import static org.apache.juneau.rest.RestContext.*;
 
 import java.util.*;
 
@@ -82,12 +81,12 @@ import org.apache.juneau.utils.*;
                footer="$W{PoweredByJuneau}"
        ),
 
+       // Allow INIT as a method parameter.
+       allowMethodParam="*",
+       
        // Properties that get applied to all serializers and parsers.
        properties={
 
-               // Allow INIT as a method parameter.
-               @Property(name=REST_allowMethodParam, value="*"),
-
                // Use single quotes.
                @Property(name=SERIALIZER_quoteChar, value="'"),
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/JettyLogger.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/JettyLogger.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/JettyLogger.java
new file mode 100644
index 0000000..1c011e8
--- /dev/null
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/JettyLogger.java
@@ -0,0 +1,198 @@
+// 
***************************************************************************************************************************
+// * 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.microservice;
+
+import static java.util.logging.Level.*;
+import java.util.logging.*;
+
+import org.apache.juneau.internal.*;
+import org.eclipse.jetty.util.log.AbstractLogger;
+
+/**
+ * Implementation of Jetty {@link Logger} based on {@link 
java.util.logging.Logger}.
+ * 
+ * <p>
+ * Allows Jetty to log to the Java Util logging framework (and thus to the 
main log file defined in the 
+ * <cc>[Logging]</cc> section).
+ * 
+ * <p>
+ * Can be used by setting the following system property in the microservice 
config file.
+ * 
+ * <p class='bcode'>
+ *     <cs>[SystemProperties]</cs>
+ *     
+ *     <cc># Configure Jetty to log using java-util logging</cc>
+ *     <ck>org.eclipse.jetty.util.log.class</ck> = 
org.apache.juneau.microservice.JettyLogger
+ * </p>
+ * 
+ */
+public class JettyLogger extends AbstractLogger {
+       private final static boolean SHOW_SOURCE = 
SystemUtils.getFirstBoolean(true, "org.eclipse.jetty.util.log.SOURCE", 
"org.eclipse.jetty.util.log.javautil.SOURCE");
+
+       private Level configuredLevel;
+       private Logger logger;
+
+       /**
+        * Default constructor.
+        * 
+        * <p>
+        * Returns the logger with name 
<js>"org.eclipse.jetty.util.log.javautil"</js>.
+        */
+       public JettyLogger() {
+               this("org.eclipse.jetty.util.log.javautil");
+       }
+
+       /**
+        * Normal constructor.
+        * 
+        * @param name The logger name.
+        */
+       public JettyLogger(String name) {
+               logger = Logger.getLogger(name);
+               configuredLevel = logger.getLevel();
+       }
+
+       @Override
+       public String getName() {
+               return logger.getName();
+       }
+
+       @Override
+       public void warn(String msg, Object... args) {
+               if (isLoggable(WARNING))
+                       log(WARNING, format(msg, args), null);
+       }
+
+       @Override
+       public void warn(Throwable thrown) {
+               if (isLoggable(WARNING))
+                       log(WARNING, "", thrown);
+       }
+
+       @Override
+       public void warn(String msg, Throwable thrown) {
+               if (isLoggable(WARNING))
+                       log(WARNING, msg, thrown);
+       }
+
+       @Override
+       public void info(String msg, Object... args) {
+               if (isLoggable(INFO))
+                       log(INFO, format(msg, args), null);
+       }
+
+       @Override
+       public void info(Throwable thrown) {
+               if (isLoggable(INFO))
+                       log(INFO, "", thrown);
+       }
+
+       @Override
+       public void info(String msg, Throwable thrown) {
+               if (isLoggable(INFO))
+                       log(INFO, msg, thrown);
+       }
+
+       @Override
+       public boolean isDebugEnabled() {
+               return isLoggable(FINE);
+       }
+
+       @Override
+       public void setDebugEnabled(boolean enabled) {
+               if (enabled) {
+                       configuredLevel = logger.getLevel();
+                       logger.setLevel(FINE);
+               } else {
+                       logger.setLevel(configuredLevel);
+               }
+       }
+
+       @Override
+       public void debug(String msg, Object... args) {
+               if (isLoggable(FINE))
+                       log(FINE, format(msg, args), null);
+       }
+
+       @Override
+       public void debug(String msg, long arg) {
+               if (isLoggable(FINE))
+                       log(FINE, format(msg, arg), null);
+       }
+
+       @Override
+       public void debug(Throwable thrown) {
+               if (isLoggable(FINE))
+                       log(FINE, "", thrown);
+       }
+
+       @Override
+       public void debug(String msg, Throwable thrown) {
+               if (isLoggable(FINE))
+                       log(FINE, msg, thrown);
+       }
+
+       @Override
+       protected org.eclipse.jetty.util.log.Logger newLogger(String fullname) {
+               return new JettyLogger(fullname);
+       }
+
+       @Override
+       public void ignore(Throwable ignored) {
+               if (isLoggable(FINEST))
+                       log(FINEST, org.eclipse.jetty.util.log.Log.IGNORED, 
ignored);
+       }
+
+       private static String format(String msg, Object... args) {
+               msg = String.valueOf(msg); 
+               if (args.length == 0)
+                       return msg;
+               StringBuilder sb = new StringBuilder();
+               int start = 0;
+               for (Object arg : args) {
+                       int bi = msg.indexOf("{}", start);
+                       if (bi < 0) {
+                               sb.append(msg.substring(start)).append(" 
").append(arg);
+                               start = msg.length();
+                       } else {
+                               sb.append(msg.substring(start, 
bi)).append(String.valueOf(arg));
+                               start = bi + 2;
+                       }
+               }
+               sb.append(msg.substring(start));
+               return sb.toString();
+       }
+
+       private void log(Level level, String msg, Throwable thrown) {
+               LogRecord r = new LogRecord(level, msg);
+               if (thrown != null)
+                       r.setThrown(thrown);
+               r.setLoggerName(logger.getName());
+               if (SHOW_SOURCE) {
+                       StackTraceElement[] stack = new 
Throwable().getStackTrace();
+                       for (int i = 0; i < stack.length; i++) {
+                               StackTraceElement e = stack[i];
+                               if 
(!e.getClassName().equals(getClass().getName())) {
+                                       r.setSourceClassName(e.getClassName());
+                                       
r.setSourceMethodName(e.getMethodName());
+                                       break;
+                               }
+                       }
+               }
+               logger.log(r);
+       }
+       
+       private boolean isLoggable(Level level) {
+               return logger.isLoggable(level);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
index 1e7ca26..c60187b 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
@@ -12,16 +12,20 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice;
 
+import static org.apache.juneau.internal.FileUtils.*;
 import static org.apache.juneau.internal.IOUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import java.util.jar.*;
+import java.util.logging.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.ini.*;
 import org.apache.juneau.internal.*;
+import org.apache.juneau.microservice.resources.*;
 import org.apache.juneau.svl.*;
 import org.apache.juneau.svl.vars.*;
 import org.apache.juneau.utils.*;
@@ -101,21 +105,47 @@ import org.apache.juneau.utils.*;
  */
 public abstract class Microservice {
 
-       private static Args args;
-       private static ConfigFile cf;
-       private static ManifestFile mf;
+       private static volatile Microservice INSTANCE;
+
+       private Logger logger;
+       private Args args;
+       private ConfigFile cf;
+       private ManifestFile mf;
+       private VarResolver vr;
 
        private String cfPath;
 
        /**
+        * Returns the Microservice instance.  
+        * <p>
+        * This method only works if there's only one Microservice instance in 
a JVM.  
+        * Otherwise, it's just overwritten by the last call to {@link 
#Microservice(String...)}.
+        * 
+        * @return The Microservice instance, or <jk>null</jk> if there isn't 
one.
+        */
+       public static Microservice getInstance() {
+               synchronized(Microservice.class) {
+                       return INSTANCE;
+               }
+       }
+       
+       /**
         * Constructor.
         *
         * @param args Command line arguments.
         * @throws Exception
         */
        protected Microservice(String...args) throws Exception {
-               Microservice.args = new Args(args);
+               setInstance(this);
+               this.args = new Args(args);
        }
+       
+       private static void setInstance(Microservice m) {
+               synchronized(Microservice.class) {
+                       INSTANCE = m;
+               }
+       }
+
 
        /**
         * Specifies the path of the config file for this microservice.
@@ -158,8 +188,8 @@ public abstract class Microservice {
         *
         * @param cf The config file for this application, or <jk>null</jk> if 
no config file is needed.
         */
-       public static void setConfig(ConfigFile cf) {
-               Microservice.cf = cf;
+       public void setConfig(ConfigFile cf) {
+               this.cf = cf;
        }
 
        /**
@@ -176,8 +206,8 @@ public abstract class Microservice {
         *
         * @param mf The manifest file of this microservice.
         */
-       public static void setManifest(Manifest mf) {
-               Microservice.mf = new ManifestFile(mf);
+       public void setManifest(Manifest mf) {
+               this.mf = new ManifestFile(mf);
        }
 
        /**
@@ -189,7 +219,7 @@ public abstract class Microservice {
         */
        public Microservice setManifestContents(String...contents) throws 
IOException {
                String s = StringUtils.join(contents, "\n") + "\n";
-               Microservice.mf = new ManifestFile(new Manifest(new 
ByteArrayInputStream(s.getBytes("UTF-8"))));
+               this.mf = new ManifestFile(new Manifest(new 
ByteArrayInputStream(s.getBytes("UTF-8"))));
                return this;
        }
 
@@ -199,8 +229,8 @@ public abstract class Microservice {
         * @param f The manifest file of this microservice.
         * @throws IOException If a problem occurred while trying to read the 
manifest file.
         */
-       public static void setManifest(File f) throws IOException {
-               Microservice.mf = new ManifestFile(f);
+       public void setManifest(File f) throws IOException {
+               this.mf = new ManifestFile(f);
        }
 
        /**
@@ -210,8 +240,8 @@ public abstract class Microservice {
         * @param c The class whose jar file contains the manifest to use for 
this microservice.
         * @throws IOException If a problem occurred while trying to read the 
manifest file.
         */
-       public static void setManifest(Class<?> c) throws IOException {
-               Microservice.mf = new ManifestFile(c);
+       public void setManifest(Class<?> c) throws IOException {
+               this.mf = new ManifestFile(c);
        }
 
        /**
@@ -285,7 +315,7 @@ public abstract class Microservice {
         *
         * @return The command-line arguments passed into the application.
         */
-       protected static Args getArgs() {
+       public Args getArgs() {
                return args;
        }
 
@@ -382,7 +412,7 @@ public abstract class Microservice {
         *
         * @return The config file for this application, or <jk>null</jk> if no 
config file is configured.
         */
-       protected static ConfigFile getConfig() {
+       public ConfigFile getConfig() {
                return cf;
        }
 
@@ -406,11 +436,30 @@ public abstract class Microservice {
         *
         * @return The manifest file from the main jar, or <jk>null</jk> if the 
manifest file could not be retrieved.
         */
-       protected static ManifestFile getManifest() {
+       public ManifestFile getManifest() {
                return mf;
        }
 
+       /**
+        * Returns the variable resolver for resolving variables in strings and 
files.
+        * <p>
+        * See the {@link #createVarResolver()} method for the list of 
available resolution variables.
+        * 
+        * @return The VarResolver used by this Microservice, or <jk>null</jk> 
if it was never created.
+        */
+       public VarResolver getVarResolver() {
+               return vr;
+       }
 
+       /**
+        * Returns the logger for this microservice.
+        * 
+        * @return The logger for this microservice.
+        */
+       public Logger getLogger() {
+               return logger;
+       }
+       
        
//--------------------------------------------------------------------------------
        // Abstract lifecycle methods.
        
//--------------------------------------------------------------------------------
@@ -492,9 +541,11 @@ public abstract class Microservice {
                        }
                }
 
+               vr = createVarResolver().build();
+               
                if (cfPath != null)
                        System.setProperty("juneau.configFile", cfPath);
-
+               
                // 
--------------------------------------------------------------------------------
                // Set system properties.
                // 
--------------------------------------------------------------------------------
@@ -504,6 +555,16 @@ public abstract class Microservice {
                                System.setProperty(key, 
cf.get("SystemProperties", key));
 
                // 
--------------------------------------------------------------------------------
+               // Initialize logging.
+               // 
--------------------------------------------------------------------------------
+               try {
+                       initLogging();
+               } catch (Exception e) {
+                       // If logging can be initialized, just print a stack 
trace and continue.
+                       e.printStackTrace();
+               }
+
+               // 
--------------------------------------------------------------------------------
                // Add a config file change listener.
                // 
--------------------------------------------------------------------------------
                cf.addListener(new ConfigFileListener() {
@@ -550,6 +611,110 @@ public abstract class Microservice {
        }
 
        /**
+        * Initialize the logging for this microservice.
+        * 
+        * <p>
+        * Subclasses can override this method to provide customized logging.
+        * 
+        * <p>
+        * The default implementation uses the <cs>Logging</cs> section in the 
config file to set up logging:
+        * <p class='bcode'>
+        *      
<cc>#================================================================================
+        *      # Logger settings
+        *      # See FileHandler Java class for details.
+        *      
#================================================================================</cc>
+        *      <cs>[Logging]</cs>
+        *
+        *      <cc># The directory where to create the log file.
+        *      # Default is ".".</cc>
+        *      <ck>logDir</ck> = logs
+        *
+        *      <cc># The name of the log file to create for the main logger.
+        *      # The logDir and logFile make up the pattern that's passed to 
the FileHandler
+        *      # constructor.
+        *      # If value is not specified, then logging to a file will not be 
set up.</cc>
+        *      <ck>logFile</ck> = microservice.%g.log
+        *
+        *      <cc># Whether to append to the existing log file or create a 
new one.
+        *      # Default is false.</cc>
+        *      <ck>append</ck> =
+        *
+        *      <cc># The SimpleDateFormat format to use for dates.
+        *      # Default is "yyyy.MM.dd hh:mm:ss".</cc>
+        *      <ck>dateFormat</ck> =
+        *
+        *      <cc># The log message format.
+        *      # The value can contain any of the following variables:
+        *      #       {date} - The date, formatted per dateFormat.
+        *      #       {class} - The class name.
+        *      #       {method} - The method name.
+        *      #       {logger} - The logger name.
+        *      #       {level} - The log level name.
+        *      #       {msg} - The log message.
+        *      #       {threadid} - The thread ID.
+        *      #       {exception} - The localized exception message.
+        *      # Default is "[{date} {level}] {msg}%n".</cc>
+        *      <ck>format</ck> =
+        *
+        *      <cc># The maximum log file size.
+        *      # Suffixes available for numbers.
+        *      # See ConfigFile.getInt(String,int) for details.
+        *      # Default is 1M.</cc>
+        *      <ck>limit</ck> = 10M
+        *
+        *      <cc># Max number of log files.
+        *      # Default is 1.</cc>
+        *      <ck>count</ck> = 5
+        *
+        *      <cc># Default log levels.
+        *      # Keys are logger names.
+        *      # Values are serialized Level POJOs.</cc>
+        *      <ck>levels</ck> = { org.apache.juneau:'INFO' }
+        *
+        *      <cc># 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.</cc>
+        *      <ck>useStackTraceHashes</ck> = true
+        *
+        *      <cc># The default level for the console logger.
+        *      # Default is WARNING.</cc>
+        *      <ck>consoleLevel</ck> = WARNING
+        * </p>
+        *
+        * @throws Exception
+        */
+       protected void initLogging() throws Exception {
+               ConfigFile cf = getConfig();
+               logger = Logger.getLogger("");
+               String logFile = cf.getString("Logging/logFile");
+               if (! isEmpty(logFile)) {
+                       LogManager.getLogManager().reset();
+                       String logDir = cf.getString("Logging/logDir", ".");
+                       mkdirs(new File(logDir), false);
+                       boolean append = cf.getBoolean("Logging/append");
+                       int limit = cf.getInt("Logging/limit", 1024*1024);
+                       int count = cf.getInt("Logging/count", 1);
+                       FileHandler fh = new FileHandler(logDir + '/' + 
logFile, limit, count, append);
+
+                       boolean useStackTraceHashes = 
cf.getBoolean("Logging/useStackTraceHashes");
+                       String format = cf.getString("Logging/format", "[{date} 
{level}] {msg}%n");
+                       String dateFormat = cf.getString("Logging/dateFormat", 
"yyyy.MM.dd hh:mm:ss");
+                       fh.setFormatter(new LogEntryFormatter(format, 
dateFormat, useStackTraceHashes));
+                       
fh.setLevel(cf.getObjectWithDefault("Logging/fileLevel", Level.INFO, 
Level.class));
+                       logger.addHandler(fh);
+
+                       ConsoleHandler ch = new ConsoleHandler();
+                       
ch.setLevel(cf.getObjectWithDefault("Logging/consoleLevel", Level.WARNING, 
Level.class));
+                       ch.setFormatter(new LogEntryFormatter(format, 
dateFormat, false));
+                       logger.addHandler(ch);
+               }
+               ObjectMap loggerLevels = cf.getObject("Logging/levels", 
ObjectMap.class);
+               if (loggerLevels != null)
+                       for (String l : loggerLevels.keySet())
+                               
Logger.getLogger(l).setLevel(loggerLevels.get(l, Level.class));
+       }
+
+       /**
         * Joins the application with the current thread.
         * 
         * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
index 0579d4b..2bb1632 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
@@ -13,7 +13,6 @@
 package org.apache.juneau.microservice;
 
 import static org.apache.juneau.rest.annotation.HookEvent.*;
-import static javax.servlet.http.HttpServletResponse.*;
 
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
@@ -64,11 +63,12 @@ public abstract class Resource extends RestServletDefault {
         */
        @RestHook(INIT) 
        public void addConfigVars(RestConfig config) throws Exception {
-               if (Microservice.getArgs() == null || Microservice.getConfig() 
== null)
-                       throw new RestException(SC_INTERNAL_SERVER_ERROR, 
"Attempting to use Resource class outside of RestMicroservice.");
-               config
+               Microservice m = Microservice.getInstance();
+               if (m != null) {
+                       config
                        .addVars(ArgsVar.class, ManifestFileVar.class)
-                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
-                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+                       .addVarContextObject(ArgsVar.SESSION_args, m.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
m.getManifest());
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
index f3293cc..eab4048 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice;
 
-import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
 
 import org.apache.juneau.rest.*;
@@ -65,11 +64,12 @@ public abstract class ResourceGroup extends 
RestServletGroupDefault {
         */
        @RestHook(INIT) 
        public void addConfigVars(RestConfig config) throws Exception {
-               if (Microservice.getArgs() == null || Microservice.getConfig() 
== null)
-                       throw new RestException(SC_INTERNAL_SERVER_ERROR, 
"Attempting to use ResourceGroup class outside of RestMicroservice.");
-               config
+               Microservice m = Microservice.getInstance();
+               if (m != null) {
+                       config
                        .addVars(ArgsVar.class, ManifestFileVar.class)
-                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
-                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+                       .addVarContextObject(ArgsVar.SESSION_args, m.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
m.getManifest());
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
index 50f2075..80e31f8 100644
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice;
 
-import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
 
 import org.apache.juneau.jena.*;
@@ -79,11 +78,12 @@ public abstract class ResourceJenaGroup extends 
RestServletGroupDefault {
         */
        @RestHook(INIT) 
        public void addConfigVars(RestConfig config) throws Exception {
-               if (Microservice.getArgs() == null || Microservice.getConfig() 
== null)
-                       throw new RestException(SC_INTERNAL_SERVER_ERROR, 
"Attempting to use ResourceJenaGroup class outside of RestMicroservice.");
-               config
+               Microservice m = Microservice.getInstance();
+               if (m != null) {
+                       config
                        .addVars(ArgsVar.class, ManifestFileVar.class)
-                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
-                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+                       .addVarContextObject(ArgsVar.SESSION_args, m.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
m.getManifest());
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
index 8dfda47..39ce7c7 100755
--- 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
@@ -12,10 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.microservice;
 
-import static org.apache.juneau.internal.StringUtils.*;
-import static org.apache.juneau.internal.FileUtils.*;
-import static org.apache.juneau.internal.ClassUtils.*;
-
 import java.io.*;
 import java.net.*;
 import java.util.*;
@@ -25,11 +21,11 @@ import javax.servlet.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.ini.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.microservice.resources.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.svl.*;
 import org.eclipse.jetty.server.*;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.handler.*;
 import org.eclipse.jetty.servlet.*;
 import org.eclipse.jetty.xml.*;
 
@@ -43,8 +39,7 @@ import org.eclipse.jetty.xml.*;
  *
  * <h6 class='topic'>Defining REST Resources</h6>
  * 
- * Top-level REST resources are defined by the {@link #getResourceMap()} 
method.
- * This method can be overridden to provide a customized list of REST 
resources.
+ * Top-level REST resources are defined in the <code>jetty.xml</code> file as 
normal servlets.
  *
  * <h6 class='topic'>Logging</h6>
  * 
@@ -72,12 +67,24 @@ import org.eclipse.jetty.xml.*;
  */
 public class RestMicroservice extends Microservice {
        
-       ServletContextHandler servletContextHandler; 
-       Server server;
-       int port;
-       String contextPath;
-       Logger logger;
-       Object jettyXml;
+       private Server server;
+       private Object jettyXml;
+       
+       private static volatile RestMicroservice INSTANCE;
+       
+       /**
+        * Returns the Microservice instance.  
+        * <p>
+        * This method only works if there's only one Microservice instance in 
a JVM.  
+        * Otherwise, it's just overwritten by the last call to {@link 
#RestMicroservice(String...)}.
+        * 
+        * @return The Microservice instance, or <jk>null</jk> if there isn't 
one.
+        */
+       public static RestMicroservice getInstance() {
+               synchronized(RestMicroservice.class) {
+                       return INSTANCE;
+               }
+       }
        
        /**
         * Main method.
@@ -100,8 +107,14 @@ public class RestMicroservice extends Microservice {
         */
        public RestMicroservice(String...args) throws Exception {
                super(args);
+               setInstance(this);
+       }
+       
+       private static void setInstance(RestMicroservice rm) {
+               synchronized(RestMicroservice.class) {
+                       INSTANCE = rm;
+               }
        }
-
 
        
//--------------------------------------------------------------------------------
        // Methods implemented on Microservice API
@@ -110,12 +123,6 @@ public class RestMicroservice extends Microservice {
        @Override /* Microservice */
        public RestMicroservice start() throws Exception {
                super.start();
-               try {
-                       initLogging();
-               } catch (Exception e) {
-                       // If logging can be initialized, just print a stack 
trace and continue.
-                       e.printStackTrace();
-               }
                createServer();
                startServer();
                return this;
@@ -132,6 +139,7 @@ public class RestMicroservice extends Microservice {
                Thread t = new Thread() {
                        @Override /* Thread */
                        public void run() {
+                               Logger logger = getLogger();
                                try {
                                        if (server.isStopping() || 
server.isStopped())
                                                return;
@@ -162,131 +170,85 @@ public class RestMicroservice extends Microservice {
 
        /**
         * Returns the port that this microservice started up on.
+        * <p>
+        * The value is determined by looking at the 
<code>Server/Connectors[ServerConnector]/port</code> value in the 
+        * Jetty configuration.
+        * 
         * @return The port that this microservice started up on.
         */
        public int getPort() {
-               return port;
+               for (Connector c : getServer().getConnectors()) 
+                       if (c instanceof ServerConnector)
+                               return ((ServerConnector)c).getPort();
+               throw new RuntimeException("Could not locate ServerConnector in 
Jetty server.");
+       }
+       
+       /**
+        * Returns the context path that this microservice is using.
+        * <p>
+        * The value is determined by looking at the 
<code>Server/Handlers[ServletContextHandler]/contextPath</code> value 
+        * in the Jetty configuration.
+        * 
+        * @return The context path that this microservice is using.
+        */
+       public String getContextPath() {
+               for (Handler h : getServer().getHandlers()) {
+                       if (h instanceof HandlerCollection) {
+                               for (Handler h2 : 
((HandlerCollection)h).getChildHandlers())
+                                       if (h2 instanceof 
ServletContextHandler) 
+                                               return 
((ServletContextHandler)h2).getContextPath();
+                       }
+                       if (h instanceof ServletContextHandler) 
+                               return 
((ServletContextHandler)h).getContextPath();
+               }
+               throw new RuntimeException("Could not locate 
ServletContextHandler in Jetty server.");
+       }
+       
+       /**
+        * Returns whether this microservice is using <js>"http"</js> or 
<js>"https"</js>.
+        * <p>
+        * The value is determined by looking for the existence of an SSL 
Connection Factorie by looking for the
+        * 
<code>Server/Connectors[ServerConnector]/ConnectionFactories[SslConnectionFactory]</code>
 value in the Jetty
+        * configuration.
+        * 
+        * @return Whether this microservice is using <js>"http"</js> or 
<js>"https"</js>.
+        */
+       public String getProtocol() {
+               for (Connector c : getServer().getConnectors())
+                       if (c instanceof ServerConnector) 
+                               for (ConnectionFactory cf : 
((ServerConnector)c).getConnectionFactories())
+                                       if (cf instanceof SslConnectionFactory)
+                                               return "https";
+               return "http";
        }
 
        /**
-        * Returns the URI where this microservice is listening on.
-        * @return The URI where this microservice is listening on.
+        * Returns the hostname of this microservice.
+        * <p>
+        * Simply uses <code>InetAddress.getLocalHost().getHostName()</code>.
+        * 
+        * @return The hostname of this microservice.
         */
-       public URI getURI() {
-               String scheme = getConfig().getBoolean("REST/useSsl") ? "https" 
: "http";
+       public String getHostName() {
                String hostname = "localhost";
-               String ctx = "/".equals(contextPath) ? null : contextPath;
                try {
                        hostname = InetAddress.getLocalHost().getHostName();
                } catch (UnknownHostException e) {}
-               try {
-                       return new URI(scheme, null, hostname, port, ctx, null, 
null);
-               } catch (URISyntaxException e) {
-                       throw new RuntimeException(e);
-               }
+               return hostname;
        }
-
+       
        /**
-        * Initialize the logging for this microservice.
-        * 
-        * <p>
-        * Subclasses can override this method to provide customized logging.
+        * Returns the URI where this microservice is listening on.
         * 
-        * <p>
-        * The default implementation uses the <cs>Logging</cs> section in the 
config file to set up logging:
-        * <p class='bcode'>
-        *      
<cc>#================================================================================
-        *      # Logger settings
-        *      # See FileHandler Java class for details.
-        *      
#================================================================================</cc>
-        *      <cs>[Logging]</cs>
-        *
-        *      <cc># The directory where to create the log file.
-        *      # Default is ".".</cc>
-        *      <ck>logDir</ck> = logs
-        *
-        *      <cc># The name of the log file to create for the main logger.
-        *      # The logDir and logFile make up the pattern that's passed to 
the FileHandler
-        *      # constructor.
-        *      # If value is not specified, then logging to a file will not be 
set up.</cc>
-        *      <ck>logFile</ck> = microservice.%g.log
-        *
-        *      <cc># Whether to append to the existing log file or create a 
new one.
-        *      # Default is false.</cc>
-        *      <ck>append</ck> =
-        *
-        *      <cc># The SimpleDateFormat format to use for dates.
-        *      # Default is "yyyy.MM.dd hh:mm:ss".</cc>
-        *      <ck>dateFormat</ck> =
-        *
-        *      <cc># The log message format.
-        *      # The value can contain any of the following variables:
-        *      #       {date} - The date, formatted per dateFormat.
-        *      #       {class} - The class name.
-        *      #       {method} - The method name.
-        *      #       {logger} - The logger name.
-        *      #       {level} - The log level name.
-        *      #       {msg} - The log message.
-        *      #       {threadid} - The thread ID.
-        *      #       {exception} - The localized exception message.
-        *      # Default is "[{date} {level}] {msg}%n".</cc>
-        *      <ck>format</ck> =
-        *
-        *      <cc># The maximum log file size.
-        *      # Suffixes available for numbers.
-        *      # See ConfigFile.getInt(String,int) for details.
-        *      # Default is 1M.</cc>
-        *      <ck>limit</ck> = 10M
-        *
-        *      <cc># Max number of log files.
-        *      # Default is 1.</cc>
-        *      <ck>count</ck> = 5
-        *
-        *      <cc># Default log levels.
-        *      # Keys are logger names.
-        *      # Values are serialized Level POJOs.</cc>
-        *      <ck>levels</ck> = { org.apache.juneau:'INFO' }
-        *
-        *      <cc># 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.</cc>
-        *      <ck>useStackTraceHashes</ck> = true
-        *
-        *      <cc># The default level for the console logger.
-        *      # Default is WARNING.</cc>
-        *      <ck>consoleLevel</ck> = WARNING
-        * </p>
-        *
-        * @throws Exception
+        * @return The URI where this microservice is listening on.
         */
-       protected void initLogging() throws Exception {
-               ConfigFile cf = getConfig();
-               logger = Logger.getLogger("");
-               String logFile = cf.getString("Logging/logFile");
-               if (! isEmpty(logFile)) {
-                       LogManager.getLogManager().reset();
-                       String logDir = cf.getString("Logging/logDir", ".");
-                       mkdirs(new File(logDir), false);
-                       boolean append = cf.getBoolean("Logging/append");
-                       int limit = cf.getInt("Logging/limit", 1024*1024);
-                       int count = cf.getInt("Logging/count", 1);
-                       FileHandler fh = new FileHandler(logDir + '/' + 
logFile, limit, count, append);
-
-                       boolean useStackTraceHashes = 
cf.getBoolean("Logging/useStackTraceHashes");
-                       String format = cf.getString("Logging/format", "[{date} 
{level}] {msg}%n");
-                       String dateFormat = cf.getString("Logging/dateFormat", 
"yyyy.MM.dd hh:mm:ss");
-                       fh.setFormatter(new LogEntryFormatter(format, 
dateFormat, useStackTraceHashes));
-                       logger.addHandler(fh);
-
-                       ConsoleHandler ch = new ConsoleHandler();
-                       
ch.setLevel(Level.parse(cf.getString("Logging/consoleLevel", "WARNING")));
-                       ch.setFormatter(new LogEntryFormatter(format, 
dateFormat, false));
-                       logger.addHandler(ch);
+       public URI getURI() {
+               String cp = getContextPath(); 
+               try {
+                       return new URI(getProtocol(), null, getHostName(), 
getPort(), "/".equals(cp) ? null : cp, null, null);
+               } catch (URISyntaxException e) {
+                       throw new RuntimeException(e);
                }
-               ObjectMap loggerLevels = cf.getObject("Logging/levels", 
ObjectMap.class);
-               if (loggerLevels != null)
-               for (String l : loggerLevels.keySet())
-                       Logger.getLogger(l).setLevel(loggerLevels.get(l, 
Level.class));
        }
 
        /**
@@ -300,22 +262,22 @@ public class RestMicroservice extends Microservice {
         * if a jetty.xml is not specified via a <code>REST/jettyXml</code> 
setting:
         * <p class='bcode'>
         *      
<cc>#================================================================================
-        *      # REST settings
+        *      # Jetty settings
         *      
#================================================================================</cc>
-        *      <cs>[REST]</cs>
-        *
-        *      <cc># The HTTP port number to use.
-        *      # Default is Rest-Port setting in manifest file, or 8000.
-        *      # Can also specify a comma-delimited lists of ports to try, 
including 0 meaning
-        *      # try a random port.</cc>
-        *      <ck>port</ck> = 10000
-        *
-        *      <cc># The context root of the Jetty server.
-        *      # Default is Rest-ContextPath in manifest file, or "/".</cc>
-        *      <ck>contextPath</ck> =
-        *
-        *      <cc># Enable SSL support.</cc>
-        *      <ck>useSsl</ck> = false
+        *      <cs>[Jetty]</cs>
+        *      
+        *      <cc># Path of the jetty.xml file used to configure the Jetty 
server.</cc>
+        *      <ck>config</ck> = jetty.xml
+        *      
+        *      <cc># Resolve Juneau variables in the jetty.xml file.</cc>
+        *      <ck>resolveVars</ck> = true
+        *      
+        *      <cc># Port to use for the jetty server.
+        *      # You can specify multiple ports.  The first available will be 
used.  '0' indicates to try a random port.
+        *      # The resulting available port gets set as the system property 
"availablePort" which can be referenced in the 
+        *      # jetty.xml file as "$S{availablePort}" (assuming resolveVars 
is enabled).</cc>
+        *      <ck>port</ck> = 10000,0,0,0
+        * </p>
         *
         * @return The newly-created server.
         * @throws Exception
@@ -325,47 +287,41 @@ public class RestMicroservice extends Microservice {
 
                ConfigFile cf = getConfig();
                ObjectMap mf = getManifest();
+               VarResolver vr = getVarResolver();
+               
+               int[] ports = cf.getObjectWithDefault("Jetty/port", 
mf.getWithDefault("Jetty-Port", new int[]{8000}, int[].class), int[].class);
+               int availablePort = findOpenPort(ports);
+               System.setProperty("availablePort", 
String.valueOf(availablePort));
+               
                if (jettyXml == null)
-                       jettyXml = cf.getString("REST/jettyXml", 
mf.getString("Rest-JettyXml", null));
-               if (jettyXml != null) {
-                       InputStream is = null;
-                       if (jettyXml instanceof String) {
-                               jettyXml = new File(jettyXml.toString());
-                       }
-                       if (jettyXml instanceof File) {
-                               File f = (File)jettyXml;
-                               if (f.exists())
-                                       is = new 
FileInputStream((File)jettyXml);
-                               else 
-                                       throw new 
FormattedRuntimeException("Jetty.xml file ''{0}'' was specified but not found 
on the file system.", jettyXml);
-                       } else if (jettyXml instanceof InputStream) {
-                               is = (InputStream)jettyXml;
-                       }
-                       
-                       XmlConfiguration config = new XmlConfiguration(is);
-                       server = (Server)config.configure();
+                       jettyXml = cf.getString("Jetty/config", 
mf.getString("Jetty-Config", null));
                
+               if (jettyXml == null)
+                       throw new FormattedRuntimeException("Jetty.xml file 
location was not specified in the configuration file (Jetty/config) or manifest 
file (Jetty-Config).");
+               
+               String xmlConfig = null;
+               
+               if (jettyXml instanceof String) 
+                       jettyXml = new File(jettyXml.toString());
+               
+               if (jettyXml instanceof File) {
+                       File f = (File)jettyXml;
+                       if (f.exists())
+                               xmlConfig = IOUtils.read((File)jettyXml);
+                       else 
+                               throw new FormattedRuntimeException("Jetty.xml 
file ''{0}'' was specified but not found on the file system.", jettyXml);
                } else {
-                       int[] ports = cf.getObjectWithDefault("REST/port", 
mf.getWithDefault("Rest-Port", new int[]{8000}, int[].class), int[].class);
-
-                       port = findOpenPort(ports);
-                       if (port == 0) {
-                               System.err.println("Open port not found.  Tried 
" + JsonSerializer.DEFAULT_LAX.toString(ports));
-                               System.exit(1);
-                       }
-
-                       contextPath = cf.getString("REST/contextPath", 
mf.getString("Rest-ContextPath", "/"));
-                       server = new Server(port);
-                       
-                       servletContextHandler = new 
ServletContextHandler(ServletContextHandler.SESSIONS);
-
-                       servletContextHandler.setContextPath(contextPath);
-                       server.setHandler(servletContextHandler);
-
-                       for (Map.Entry<String,Class<? extends Servlet>> e : 
getResourceMap().entrySet())
-                               servletContextHandler.addServlet(e.getValue(), 
e.getKey()).setInitOrder(0);
+                       xmlConfig = IOUtils.read(jettyXml);
                }
                
+               if (cf.getBoolean("Jetty/resolveVars", false))
+                       xmlConfig = vr.resolve(xmlConfig);
+               
+               getLogger().info(xmlConfig);
+               
+               XmlConfiguration config = new XmlConfiguration(new 
ByteArrayInputStream(xmlConfig.getBytes()));
+               server = (Server)config.configure();
+               
                return server;
        }
        
@@ -378,11 +334,14 @@ public class RestMicroservice extends Microservice {
         * @throws RuntimeException if {@link #createServer()} has not 
previously been called.
         */
        public RestMicroservice addServlet(Servlet servlet, String pathSpec) {
-               if (servletContextHandler == null)
-                       throw new RuntimeException("Servlet context handler not 
found.  createServer() must be called first.");
-               ServletHolder sh = new ServletHolder(servlet);
-               servletContextHandler.addServlet(sh, pathSpec);
-               return this;
+               for (Handler h : getServer().getHandlers()) {
+                       if (h instanceof ServletContextHandler) {
+                               ServletHolder sh = new ServletHolder(servlet);
+                               ((ServletContextHandler)h).addServlet(sh, 
pathSpec);
+                               return this;
+                       }
+               }
+               throw new RuntimeException("Servlet context handler not found 
in jetty server.");
        }
        
        /**
@@ -394,9 +353,7 @@ public class RestMicroservice extends Microservice {
         * @throws RuntimeException if {@link #createServer()} has not 
previously been called.
         */
        public RestMicroservice addServletAttribute(String name, Object value) {
-               if (server == null)
-                       throw new RuntimeException("Server not found.  
createServer() must be called first.");
-               server.setAttribute(name, value);
+               getServer().setAttribute(name, value);
                return this;
        }
        
@@ -406,6 +363,8 @@ public class RestMicroservice extends Microservice {
         * @return The underlying Jetty server, or <jk>null</jk> if {@link 
#createServer()} has not yet been called.
         */
        public Server getServer() {
+               if (server == null)
+                       throw new RuntimeException("Server not found.  
createServer() must be called first.");
                return server;
        }
        
@@ -435,85 +394,9 @@ public class RestMicroservice extends Microservice {
        protected int startServer() throws Exception {
                onStartServer();
                server.start();
-               this.port = 
((ServerConnector)server.getConnectors()[0]).getLocalPort();
-               logger.warning("Server started on port " + port);
+               getLogger().warning("Server started on port " + getPort());
                onPostStartServer();
-               return port;
-       }
-
-       /**
-        * Returns the resource map to use for this microservice.
-        * 
-        * <p>
-        * Subclasses can override this method to programmatically specify 
their resources.
-        * 
-        * <p>
-        * The default implementation is configured by the following values in 
the config file:
-        * <p class='bcode'>
-        *
-        *      
<cc>#================================================================================
-        *      # REST settings
-        *      
#================================================================================</cc>
-        *      <cs>[REST]</cs>
-        *
-        *      <cc># A JSON map of servlet paths to servlet classes.
-        *      # Example:
-        *      #       resourceMap = {'/*':'com.foo.MyServlet'}
-        *      # Either resourceMap or resources must be specified if it's not 
defined in
-        *      # the manifest file.</cc>
-        *      <ck>resourceMap</ck> =
-        *
-        *      <cc># A comma-delimited list of names of classes that extend 
from Servlet.
-        *      # Resource paths are pulled from @RestResource.path() 
annotation, or
-        *      #       "/*" if annotation not specified.
-        *      # Example:
-        *      #       resources = com.foo.MyServlet
-        *       *      # Default is Rest-Resources in manifest file.
-        *      # Either resourceMap or resources must be specified if it's not 
defined in
-        *      # the manifest file.</cc>
-        *      <ck>resources</ck> =
-        * </p>
-        * 
-        * <p>
-        * In most cases, the rest resources will be specified in the manifest 
file since it's not likely to be a 
-        * configurable property:
-        * <p class='bcode'>
-        *      <mk>Rest-Resources:</mk> 
org.apache.juneau.microservice.sample.RootResources
-        * </p>
-        *
-        * @return The map of REST resources.
-        * @throws ClassNotFoundException
-        * @throws ParseException
-        */
-       @SuppressWarnings("unchecked")
-       protected Map<String,Class<? extends Servlet>> getResourceMap() throws 
ClassNotFoundException, ParseException {
-               ConfigFile cf = getConfig();
-               ObjectMap mf = getManifest();
-               Map<String,Class<? extends Servlet>> rm = new 
LinkedHashMap<String,Class<? extends Servlet>>();
-
-               ObjectMap resourceMap = cf.getObject("REST/resourceMap", 
ObjectMap.class);
-               String[] resources = cf.getStringArray("REST/resources", 
mf.getStringArray("Rest-Resources"));
-
-               if (resourceMap != null && ! resourceMap.isEmpty()) {
-                       for (Map.Entry<String,Object> e : 
resourceMap.entrySet()) {
-                               Class<?> c = 
Class.forName(e.getValue().toString());
-                               if (! isParentClass(Servlet.class, c))
-                                       throw new 
ClassNotFoundException("Invalid class specified as resource.  Must be a 
Servlet.  Class='"+c.getName()+"'");
-                               rm.put(e.getKey(), (Class<? extends Servlet>)c);
-                       }
-               } else if (resources.length > 0) {
-                       for (String resource : resources) {
-                               Class<?> c = Class.forName(resource);
-                               if (! isParentClass(Servlet.class, c))
-                                       throw new 
ClassNotFoundException("Invalid class specified as resource.  Must be a 
Servlet.  Class='"+c.getName()+"'");
-                               RestResource rr = 
c.getAnnotation(RestResource.class);
-                               String path = rr == null ? "/*" : rr.path();
-                               if (! path.endsWith("*"))
-                                       path += (path.endsWith("/") ? "*" : 
"/*");
-                               rm.put(path, (Class<? extends Servlet>)c);
-                       }
-               }
-               return rm;
+               return getPort();
        }
 
        /**
@@ -522,8 +405,6 @@ public class RestMicroservice extends Microservice {
         * <p>
         * The default behavior is configured by the following value in the 
config file:
         * <p class='bcode'>
-        *      <cs>[REST]</cs>
-        *
         *      <cc># What to do when the config file is saved.
         *      # Possible values:
         *      #       NOTHING - Don't do anything. (default)
@@ -535,7 +416,7 @@ public class RestMicroservice extends Microservice {
        @Override /* Microservice */
        protected void onConfigSave(ConfigFile cf) {
                try {
-                       String saveConfigAction = 
cf.getString("REST/saveConfigAction", "NOTHING");
+                       String saveConfigAction = 
cf.getString("saveConfigAction", "NOTHING");
                        if (saveConfigAction.equals("RESTART_SERVER")) {
                                new Thread() {
                                        @Override /* Thread */
@@ -544,7 +425,7 @@ public class RestMicroservice extends Microservice {
                                                        
RestMicroservice.this.stop();
                                                        
RestMicroservice.this.start();
                                                } catch (Exception e) {
-                                                       
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+                                                       
getLogger().log(Level.SEVERE, e.getLocalizedMessage(), e);
                                                }
                                        }
                                }.start();
@@ -573,7 +454,7 @@ public class RestMicroservice extends Microservice {
         * @return This object (for method chaining).
         */
        public RestMicroservice setJettyXml(Object jettyXml) {
-               if (jettyXml instanceof String || jettyXml instanceof File || 
jettyXml instanceof InputStream)
+               if (jettyXml instanceof String || jettyXml instanceof File || 
jettyXml instanceof InputStream || jettyXml instanceof Reader)
                        this.jettyXml = jettyXml;
                else
                        throw new FormattedRuntimeException("Invalid object 
type passed to setJettyXml()", jettyXml == null ? null : 
jettyXml.getClass().getName());

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DebugResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DebugResource.java
 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DebugResource.java
new file mode 100644
index 0000000..8051a72
--- /dev/null
+++ 
b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DebugResource.java
@@ -0,0 +1,74 @@
+// 
***************************************************************************************************************************
+// * 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.microservice.resources;
+
+import java.io.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.microservice.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.labels.*;
+
+/**
+ * Microservice debug utilities.
+ */
+@RestResource(
+       path="/debug",
+       title="Debug",
+       description="Debug Utilities.",
+       htmldoc=@HtmlDoc(
+               links={
+                       "up: request:/..",
+                       "jetty-thread-dump: servlet:/jetty/dump?method=POST",
+                       "options: servlet:/?method=OPTIONS"
+               }
+       ),
+       allowMethodParam="OPTIONS,POST"
+)
+@SuppressWarnings("javadoc")
+public class DebugResource extends Resource {
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * [GET /] - Shows child utilities.
+        * 
+        * @return Child utility links.
+        * @throws Exception 
+        */
+       @RestMethod(name="GET", path="/", description="Show contents of config 
file.")
+       public ResourceDescription[] getChildren() throws Exception {
+               return new ResourceDescription[] {
+                       new ResourceDescription("jetty/dump", "Jetty thread 
dump")
+               };
+       }
+
+       /**
+        * [GET /jetty/dump] - Generates and retrieves the jetty thread dump.
+        */
+       @RestMethod(name="GET", path="/jetty/dump", description="Generates and 
retrieves the jetty thread dump.")
+       public Reader getJettyDump(RestRequest req, RestResponse res) {
+               res.setContentType("text/plain");
+               return new 
StringReader(RestMicroservice.getInstance().getServer().dump());
+       }
+
+       /**
+        * [POST /jetty/dump] - Generates and saves the jetty thread dump file 
to jetty-thread-dump.log.
+        */
+       @RestMethod(name="POST", path="/jetty/dump", description="Generates and 
saves the jetty thread dump file to jetty-thread-dump.log.")
+       public String createJettyDump(RestRequest req, RestResponse res) throws 
Exception {
+               String dump = RestMicroservice.getInstance().getServer().dump();
+               IOUtils.pipe(dump, new 
FileWriter(req.getConfigFile().getString("Logging/logDir") + 
"/jetty-thread-dump.log"));
+               return "OK";
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
----------------------------------------------------------------------
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 0c0edc4..744b3a1 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
@@ -15,7 +15,6 @@ package org.apache.juneau.microservice.resources;
 import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 import java.net.*;
@@ -69,9 +68,9 @@ import org.apache.juneau.utils.*;
                        "options: servlet:/?method=OPTIONS"
                }
        ),
+       allowMethodParam="*",
        properties={
                @Property(name=HTML_uriAnchorText, value=PROPERTY_NAME),
-               @Property(name=REST_allowMethodParam, value="*"),
                @Property(name="DirectoryResource.rootDir", value="")
        }
 )

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
----------------------------------------------------------------------
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 d98b77d..4e9938f 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
@@ -14,7 +14,6 @@ package org.apache.juneau.microservice.resources;
 
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestContext.*;
 import static org.apache.juneau.rest.annotation.HookEvent.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
@@ -44,7 +43,7 @@ import org.apache.juneau.transforms.*;
        properties={
                @Property(name=HTML_uriAnchorText, value=PROPERTY_NAME),
        },
-       flags={REST_allowMethodParam},
+       allowMethodParam="*",
        pojoSwaps={
                IteratorSwap.class,       // Allows Iterators and Iterables to 
be serialized.
                DateSwap.ISO8601DT.class  // Serialize Date objects as ISO8601 
strings.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-template/jetty.xml
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-template/jetty.xml 
b/juneau-microservice/juneau-microservice-template/jetty.xml
index 77c5cf0..3fc3408 100644
--- a/juneau-microservice/juneau-microservice-template/jetty.xml
+++ b/juneau-microservice/juneau-microservice-template/jetty.xml
@@ -24,7 +24,7 @@
                                        <Arg>
                                                <Ref refid="ExampleServer" />
                                        </Arg>
-                                       <Set name="port">10000</Set>
+                                       <Set 
name="port">$S{availablePort,8080}</Set>
                                </New>
                        </Item>
                </Array>
@@ -55,11 +55,22 @@
                        </Set>
                </New>
        </Set>
-       
+
+       <Set name="requestLog">
+               <New id="RequestLogImpl" 
class="org.eclipse.jetty.server.NCSARequestLog">
+                       <Set name="filename"><Property name="jetty.logs" 
default="$C{Logging/logDir,logs}"/>/jetty-requests.log</Set>
+                       <Set name="filenameDateFormat">yyyy_MM_dd</Set>
+                       <Set name="LogTimeZone">GMT</Set>
+                       <Set name="retainDays">90</Set>
+                       <Set name="append">false</Set>
+                       <Set name="LogLatency">true</Set>
+               </New>
+       </Set>
+
     <Get name="ThreadPool">
         <Set name="minThreads" type="int">10</Set>
         <Set name="maxThreads" type="int">100</Set>
         <Set name="idleTimeout" type="int">60000</Set>
         <Set name="detailedDump">true</Set>
-    </Get>     
+    </Get>
 </Configure>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/720e7ff6/juneau-microservice/juneau-microservice-template/my-microservice.cfg
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/juneau-microservice-template/my-microservice.cfg 
b/juneau-microservice/juneau-microservice-template/my-microservice.cfg
index 2f461a3..41c7ed9 100755
--- a/juneau-microservice/juneau-microservice-template/my-microservice.cfg
+++ b/juneau-microservice/juneau-microservice-template/my-microservice.cfg
@@ -11,18 +11,40 @@
 # * specific language governing permissions and limitations under the License. 
                                             *
 # 
***************************************************************************************************************************
 
-#================================================================================
+#=======================================================================================================================
 # Basic configuration file for SaaS microservices
 # Subprojects can use this as a starting point.
-#================================================================================
+#=======================================================================================================================
 
-#================================================================================
+# What to do when the config file is saved.
+# Possible values:
+#      NOTHING - Don't do anything. (default)
+#      RESTART_SERVER - Restart the Jetty server.
+#      RESTART_SERVICE - Shutdown and exit with code '3'.
+saveConfigAction = RESTART_SERVER
+
+#=======================================================================================================================
+# Jetty settings
+#=======================================================================================================================
+[Jetty]
+
+# Path of the jetty.xml file used to configure the Jetty server.
+config = jetty.xml
+
+# Resolve Juneau variables in the jetty.xml file.
+resolveVars = true
+
+# Port to use for the jetty server.
+# You can specify multiple ports.  The first available will be used.  '0' 
indicates to try a random port.
+# The resulting available port gets set as the system property "availablePort" 
which can be referenced in the 
+# jetty.xml file as "$S{availablePort}" (assuming resolveVars is enabled).
+port = 10000,0,0,0
+
+#=======================================================================================================================
 # REST settings
-#================================================================================
+#=======================================================================================================================
 [REST]
 
-jettyXml = jetty.xml
-
 # Stylesheet to use for HTML views.
 # The default options are:
 #  - servlet:/styles/juneau.css
@@ -30,17 +52,11 @@ jettyXml = jetty.xml
 # Other stylesheets can be referenced relative to the servlet package or 
working directory.
 stylesheet = servlet:/styles/devops.css
 
-# What to do when the config file is saved.
-# Possible values:
-#      NOTHING - Don't do anything. (default)
-#      RESTART_SERVER - Restart the Jetty server.
-#      RESTART_SERVICE - Shutdown and exit with code '3'.
-saveConfigAction = RESTART_SERVER
-
-#================================================================================
+#=======================================================================================================================
 # Logger settings
+#-----------------------------------------------------------------------------------------------------------------------
 # See FileHandler Java class for details.
-#================================================================================
+#=======================================================================================================================
 [Logging]
 
 # The directory where to create the log file.
@@ -85,9 +101,15 @@ limit = 10M
 count = 5
 
 # Default log levels.
+# Format is lax-JSON.
 # Keys are logger names.
-# Values are serialized Level POJOs.
-levels = { org.apache.juneau:'INFO' }
+# Values are serialized Level POJOs (SEVERE, WARNING, INFO, CONFIG, FINE, 
FINER, FINEST)
+levels = 
+       { 
+               '': 'WARNING', 
+               org.apache.juneau: 'WARNING', 
+               org.eclipse.jetty: 'WARNING' 
+       }
 
 # 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.
@@ -95,18 +117,30 @@ levels = { org.apache.juneau:'INFO' }
 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 = 
+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
 
-#================================================================================
+#=======================================================================================================================
 # System properties
-#--------------------------------------------------------------------------------
+#-----------------------------------------------------------------------------------------------------------------------
 # These are arbitrary system properties that are set during startup.
-#================================================================================
+#=======================================================================================================================
 [SystemProperties]
 
 # Configure Jetty for StdErrLog Logging
-org.eclipse.jetty.util.log.class = org.eclipse.jetty.util.log.StrErrLog
+# org.eclipse.jetty.util.log.class = org.eclipse.jetty.util.log.StrErrLog
+
+# Configure Jetty to log using java-util logging
+org.eclipse.jetty.util.log.class = org.apache.juneau.microservice.JettyLogger
 
 # Jetty logging level
+# Possible values:  ALL, DEBUG, INFO, WARN, OFF
 org.eclipse.jetty.LEVEL = WARN
+
+derby.stream.error.file = $C{Logging/logDir}/derby-errors.log

Reply via email to