You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/09/24 19:54:17 UTC

[2/2] incubator-juneau git commit: Improve debugging support in microservice.

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 <ja...@apache.org>
Authored: Sun Sep 24 15:54:11 2017 -0400
Committer: JamesBognar <ja...@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