Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
 Fri Sep  8 23:21:12 2017
@@ -0,0 +1,631 @@
+// 
***************************************************************************************************************************
+// * 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 org.apache.juneau.internal.IOUtils.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.svl.vars.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Parent class for all microservices.
+ * 
+ * <p>
+ * A microservice defines a simple API for starting and stopping simple Java 
services contained in executable jars.
+ * 
+ * <p>
+ * The general command for invoking these services is...
+ * <p class='bcode'>
+ *     java -jar mymicroservice.jar [mymicroservice.cfg]
+ * </p>
+ * 
+ * <p>
+ * Your microservice class must be specified as the <jk>Main-Class</jk> entry 
in the manifest file of your microservice 
+ * jar file.
+ *
+ * <h6 class='topic'>Microservice Configuration</h6>
+ *
+ * This class defines the following method for accessing configuration for 
your microservice:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link #getArgs()} - The command-line arguments passed to the 
jar file.
+ *     <li>
+ *             {@link #getConfig()} - An external INI-style configuration file.
+ *     <li>
+ *             {@link #getManifest()} - The manifest file for the main jar 
file.
+ * </ul>
+ *
+ * <h6 class='topic'>Entry point Method</h6>
+ *
+ * Subclasses must implement a static void main method as the entry point for 
the microservice.
+ * Typically, this method will simply consist of the following...
+ * <p class='bcode'>
+ *     <jk>public static void</jk> main(String[] args) <jk>throws</jk> 
Exception {
+ *             <jk>new</jk> MyMicroservice(args).start();
+ *     }
+ * </p>
+ *
+ * <h6 class='topic'>Lifecycle Methods</h6>
+ *
+ * Subclasses must implement the following lifecycle methods:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link #start()} - Gets executed during startup.
+ *     <li>
+ *             {@link #stop()} - Gets executed when 'exit' is typed in the 
console or an external shutdown signal is received.
+ *     <li>
+ *             {@link #kill()} - Can be used to forcibly shut down the 
service.  Doesn't get called during normal operation.
+ * </ul>
+ *
+ * <h6 class='topic'>Lifecycle Listener Methods</h6>
+ *
+ * Subclasses can optionally implement the following event listener methods:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link #onStart()} - Gets executed before {@link #start()}.
+ *     <li>
+ *             {@link #onStop()} - Gets executed before {@link #stop()}.
+ *     <li>
+ *             {@link #onConfigSave(ConfigFile)} - Gets executed after a 
config file has been saved.
+ *     <li>
+ *             {@link #onConfigChange(ConfigFile, Set)} - Gets executed after 
a config file has been modified.
+ * </ul>
+ *
+ * <h6 class='topic'>Other Methods</h6>
+ *
+ * Subclasses can optionally override the following methods to provide 
customized behavior:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link #createVarResolver()} - Creates the {@link VarResolver} 
used to resolve variables in the config file 
+ *             returned by {@link #getConfig()}.
+ * </ul>
+ */
+public abstract class Microservice {
+
+       private static Args args;
+       private static ConfigFile cf;
+       private static ManifestFile mf;
+
+       private String cfPath;
+
+       /**
+        * Constructor.
+        *
+        * @param args Command line arguments.
+        * @throws Exception
+        */
+       protected Microservice(String...args) throws Exception {
+               Microservice.args = new Args(args);
+       }
+
+       /**
+        * Specifies the path of the config file for this microservice.
+        * 
+        * <p>
+        * If you do not specify the config file location, we attempt to 
resolve it through the following methods:
+        * <ol>
+        *      <li>The first argument in the command line arguments passed in 
through the constructor.
+        *      <li>The value of the <code>Main-ConfigFile</code> entry in the 
manifest file.
+        *      <li>A config file in the same location and with the same name 
as the executable jar file.
+        *              (e.g. <js>"java -jar myjar.jar"</js> will look for 
<js>"myjar.cfg"</js>).
+        * </ol>
+        * 
+        * <p>
+        * If this path does not exist, a {@link FileNotFoundException} will be 
thrown from the {@link #start()} command.
+        *
+        * @param cfPath The absolute or relative path of the config file.
+        * @param create Create the file if it doesn't exist.
+        * @return This object (for method chaining).
+        * @throws IOException If config file does not exist at the specified 
location or could not be read or created.
+        */
+       public Microservice setConfig(String cfPath, boolean create) throws 
IOException {
+               File f = new File(cfPath);
+               if (! f.exists()) {
+                       if (! create)
+                               throw new FileNotFoundException("Could not 
locate config file at '"+f.getAbsolutePath()+"'");
+                       if (! f.createNewFile())
+                               throw new FileNotFoundException("Could not 
create config file at '"+f.getAbsolutePath()+"'");
+               }
+               this.cfPath = cfPath;
+               return this;
+       }
+
+       /**
+        * Specifies the config file for this microservice.
+        * 
+        * <p>
+        * Note that if you use this method instead of {@link 
#setConfig(String,boolean)}, the config file will not use
+        * the variable resolver constructed from {@link #createVarResolver()}.
+        *
+        * @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;
+       }
+
+       /**
+        * Specifies the manifest file of the jar file this microservice is 
contained within.
+        * 
+        * <p>
+        * If you do not specify the manifest file, we attempt to resolve it 
through the following methods:
+        * <ol>
+        *      <li>Looking on the file system for a file at 
<js>"META-INF/MANIFEST.MF"</js>.
+        *              This is primarily to allow for running microservices 
from within eclipse workspaces where the manifest file
+        *              is located in the project root.
+        *      <li>Using the class loader for this class to find the file at 
the URL <js>"META-INF/MANIFEST.MF"</js>.
+        * </ol>
+        *
+        * @param mf The manifest file of this microservice.
+        */
+       public static void setManifest(Manifest mf) {
+               Microservice.mf = new ManifestFile(mf);
+       }
+
+       /**
+        * Convenience method for specifying the manifest contents directly.
+        *
+        * @param contents The lines in the manifest file.
+        * @return This object (for method chaining).
+        * @throws IOException
+        */
+       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"))));
+               return this;
+       }
+
+       /**
+        * Same as {@link #setManifest(Manifest)} except specified through a 
{@link File} object.
+        *
+        * @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);
+       }
+
+       /**
+        * Same as {@link #setManifest(Manifest)} except finds and loads the 
manifest file of the jar file that the  
+        * specified class is contained within.
+        *
+        * @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);
+       }
+
+       /**
+        * Creates the {@link VarResolver} used to resolve variables in the 
config file returned by {@link #getConfig()}.
+        * 
+        * <p>
+        * The default implementation resolves the following variables:
+        * <ul>
+        *      <li><code>$S{key}</code>, <code>$S{key,default}</code> - System 
properties.
+        *      <li><code>$E{key}</code>, <code>$E{key,default}</code> - 
Environment variables.
+        *      <li><code>$C{key}</code>, <code>$C{key,default}</code> - Config 
file entries.
+        *      <li><code>$MF{key}</code>, <code>$MF{key,default}</code> - 
Manifest file entries.
+        *      <li><code>$ARG{key}</code>, <code>$ARG{key,default}</code> - 
Command-line arguments.
+        *      <li><code>$IF{boolArg,thenValue}</code>, 
<code>$IF{boolArg,thenValue,elseValue}</code> - If-block logic.
+        *      <li><code>$SW{stringArg,pattern,thenVal...}</code>, 
+        *              <code>$SW{stringArg,pattern,thenVal,elseVal...}</code>  
- Switch-block logic.
+        * </ul>
+        * 
+        * <p>
+        * Subclasses can override this method to provide their own variables.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode'>
+        *      <jd>/**
+        *       * Augment default var resolver with a custom $B{...} variable 
that simply wraps strings inside square brackets.
+        *       * /</jd>
+        *      <ja>@Override</ja> <jc>// Microservice</jc>
+        *      <jk>protected</jk> StringVarResolver createVarResolver() {
+        *              <jk>return super</jk>.createVarResolver()
+        *                      .addVar(<js>"B"</js>,
+        *                              <jk>new</jk> StringVarWithDefault() {
+        *                                      <ja>@Override</ja> <jc>// 
StringVar</jc>
+        *                                      <jk>public</jk> String 
resolve(String varVal) {
+        *                                              <jk>return</jk> 
<js>'['</js> + varVal + <js>']'</js>;
+        *                                      }
+        *                              }
+        *                      );
+        *      }
+        * </p>
+        * <p class='bcode'>
+        *      <cc># Example config file</cc>
+        *      <cs>[MySection]</cs>
+        *      <ck>myEntry</ck> = $B{foo}
+        * </p>
+        * <p class='bcode'>
+        *      <jc>// Example java code</jc>
+        *      String myentry = 
getConfig().getString(<js>"MySection/myEntry"</js>); <jc>// == "[foo]"</js>
+        * </p>
+        *
+        * @return A new {@link VarResolver}.
+        */
+       protected VarResolverBuilder createVarResolver() {
+               VarResolverBuilder b = new VarResolverBuilder()
+                       .defaultVars()
+                       .vars(ConfigFileVar.class, ManifestFileVar.class, 
ArgsVar.class, SwitchVar.class, IfVar.class)
+                       .contextObject(ManifestFileVar.SESSION_manifest, mf)
+                       .contextObject(ArgsVar.SESSION_args, args);
+               if (cf != null)
+                       b.contextObject(ConfigFileVar.SESSION_config, cf);
+               return b;
+       }
+
+       /**
+        * Returns the command-line arguments passed into the application.
+        * 
+        * <p>
+        * This method can be called from the class constructor.
+        * 
+        * <p>
+        * See {@link Args} for details on using this method.
+        *
+        * @return The command-line arguments passed into the application.
+        */
+       protected static Args getArgs() {
+               return args;
+       }
+
+       /**
+        * Returns the external INI-style configuration file that can be used 
to configure your microservice.
+        * 
+        * <p>
+        * The config file location is determined in the following order:
+        * <ol class='spaced-list'>
+        *      <li>
+        *              The first argument passed to the microservice jar.
+        *      <li>
+        *              The <code>Main-ConfigFile</code> entry in the 
microservice jar manifest file.
+        *      <li>
+        *              The name of the microservice jar with a <js>".cfg"</js> 
suffix (e.g. 
+        *              
<js>"mymicroservice.jar"</js>-&gt;<js>"mymicroservice.cfg"</js>).
+        * </ol>
+        * 
+        * <p>
+        * If all methods for locating the config file fail, then this method 
returns <jk>null</jk>.
+        * 
+        * <p>
+        * Subclasses can set their own config file by calling the {@link 
#setConfig(ConfigFile)} method.
+        * 
+        * <p>
+        * String variables defined by {@link #createVarResolver()} are 
automatically resolved when using this method.
+        * 
+        * <p>
+        * This method can be called from the class constructor.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode'>
+        *      <cc>#--------------------------</cc>
+        *      <cc># My section</cc>
+        *      <cc>#--------------------------</cc>
+        *      <cs>[MySection]</cs>
+        *
+        *      <cc># An integer</cc>
+        *      <ck>anInt</ck> = 1
+        *
+        *      <cc># A boolean</cc>
+        *      <ck>aBoolean</ck> = true
+        *
+        *      <cc># An int array</cc>
+        *      <ck>anIntArray</ck> = 1,2,3
+        *
+        *      <cc># A POJO that can be converted from a String</cc>
+        *      <ck>aURL</ck> = http://foo
+        *
+        *      <cc># A POJO that can be converted from JSON</cc>
+        *      <ck>aBean</ck> = {foo:'bar',baz:123}
+        *
+        *      <cc># A system property</cc>
+        *      <ck>locale</ck> = $S{java.locale, en_US}
+        *
+        *      <cc># An environment variable</cc>
+        *      <ck>path</ck> = $E{PATH, unknown}
+        *
+        *      <cc># A manifest file entry</cc>
+        *      <ck>mainClass</ck> = $MF{Main-Class}
+        *
+        *      <cc># Another value in this config file</cc>
+        *      <ck>sameAsAnInt</ck> = $C{MySection/anInt}
+        *
+        *      <cc># A command-line argument in the form "myarg=foo"</cc>
+        *      <ck>myArg</ck> = $ARG{myarg}
+        *
+        *      <cc># The first command-line argument</cc>
+        *      <ck>firstArg</ck> = $ARG{0}
+        *
+        *      <cc># Look for system property, or env var if that doesn't 
exist, or command-line arg if that doesn't exist.</cc>
+        *      <ck>nested</ck> = $S{mySystemProperty,$E{MY_ENV_VAR,$ARG{0}}}
+        *
+        *      <cc># A POJO with embedded variables</cc>
+        *      <ck>aBean2</ck> = {foo:'$ARG{0}',baz:$C{MySection/anInt}}
+        * </p>
+        * 
+        * <p class='bcode'>
+        *      <jc>// Java code for accessing config entries above.</jc>
+        *      ConfigFile cf = getConfig();
+        *
+        *      <jk>int</jk> anInt = cf.getInt(<js>"MySection/anInt"</js>);
+        *      <jk>boolean</jk> aBoolean = 
cf.getBoolean(<js>"MySection/aBoolean"</js>);
+        *      <jk>int</jk>[] anIntArray = 
cf.getObject(<jk>int</jk>[].<jk>class</jk>, <js>"MySection/anIntArray"</js>);
+        *      URL aURL = cf.getObject(URL.<jk>class</jk>, 
<js>"MySection/aURL"</js>);
+        *      MyBean aBean = cf.getObject(MyBean.<jk>class</jk>, 
<js>"MySection/aBean"</js>);
+        *      Locale locale = cf.getObject(Locale.<jk>class</jk>, 
<js>"MySection/locale"</js>);
+        *      String path = cf.getString(<js>"MySection/path"</js>);
+        *      String mainClass = cf.getString(<js>"MySection/mainClass"</js>);
+        *      <jk>int</jk> sameAsAnInt = 
cf.getInt(<js>"MySection/sameAsAnInt"</js>);
+        *      String myArg = cf.getString(<js>"MySection/myArg"</js>);
+        *      String firstArg = cf.getString(<js>"MySection/firstArg"</js>);
+        * </p>
+        *
+        * @return The config file for this application, or <jk>null</jk> if no 
config file is configured.
+        */
+       protected static ConfigFile getConfig() {
+               return cf;
+       }
+
+       /**
+        * Returns the main jar manifest file contents as a simple {@link 
ObjectMap}.
+        * 
+        * <p>
+        * This map consists of the contents of {@link 
Manifest#getMainAttributes()} with the keys and entries converted to 
+        * simple strings.
+        * <p>
+        * This method can be called from the class constructor.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode'>
+        *      <jc>// Get Main-Class from manifest file.</jc>
+        *      String mainClass = 
Microservice.<jsm>getManifest</jsm>().getString(<js>"Main-Class"</js>, 
<js>"unknown"</js>);
+        *
+        *      <jc>// Get Rest-Resources from manifest file.</jc>
+        *      String[] restResources = 
Microservice.<jsm>getManifest</jsm>().getStringArray(<js>"Rest-Resources"</js>);
+        * </p>
+        *
+        * @return The manifest file from the main jar, or <jk>null</jk> if the 
manifest file could not be retrieved.
+        */
+       protected static ManifestFile getManifest() {
+               return mf;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Abstract lifecycle methods.
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Start this application.
+        * 
+        * <p>
+        * Default implementation simply calls {@link #onStart()}.
+        * 
+        * <p>
+        * Overridden methods MUST call this method FIRST so that the {@link 
#onStart()} method is called.
+        *
+        * @return This object (for method chaining).
+        * @throws Exception
+        */
+       public Microservice start() throws Exception {
+
+               // 
--------------------------------------------------------------------------------
+               // Try to get the manifest file if it wasn't already set.
+               // 
--------------------------------------------------------------------------------
+               if (mf == null) {
+                       Manifest m = new Manifest();
+
+                       // If running within an eclipse workspace, need to get 
it from the file system.
+                       File f = new File("META-INF/MANIFEST.MF");
+                       if (f.exists()) {
+                               try {
+                                       m.read(new FileInputStream(f));
+                               } catch (IOException e) {
+                                       System.err.println("Problem detected in 
MANIFEST.MF.  Contents below:\n" + read(f));
+                                       throw e;
+                               }
+                       } else {
+                               // Otherwise, read from manifest file in the 
jar file containing the main class.
+                               URLClassLoader cl = 
(URLClassLoader)getClass().getClassLoader();
+                               URL url = 
cl.findResource("META-INF/MANIFEST.MF");
+                               if (url != null) {
+                                       try {
+                                               m.read(url.openStream());
+                                       } catch (IOException e) {
+                                               System.err.println("Problem 
detected in MANIFEST.MF.  Contents below:\n" + read(url.openStream()));
+                                               throw e;
+                                       }
+                               }
+                       }
+                       mf = new ManifestFile(m);
+               }
+
+               // 
--------------------------------------------------------------------------------
+               // Resolve the config file if the path was specified.
+               // 
--------------------------------------------------------------------------------
+               ConfigFileBuilder cfb = new ConfigFileBuilder();
+               if (cfPath != null)
+                       cf = 
cfb.build(cfPath).getResolving(createVarResolver().build());
+
+               // 
--------------------------------------------------------------------------------
+               // Find config file.
+               // Can either be passed in as first parameter, or we discover 
it using
+               // the 'sun.java.command' system property.
+               // 
--------------------------------------------------------------------------------
+               if (cf == null) {
+                       if (args.hasArg(0))
+                               cfPath = args.getArg(0);
+                       else if (mf.containsKey("Main-ConfigFile"))
+                               cfPath = mf.getString("Main-ConfigFile");
+                       else {
+                               String cmd = 
System.getProperty("sun.java.command", "not_found").split("\\s+")[0];
+                               if (cmd.endsWith(".jar"))
+                                       cfPath = cmd.replace(".jar", ".cfg");
+                       }
+
+                       if (cfPath == null) {
+                               System.err.println("Running class 
["+getClass().getSimpleName()+"] without a config file.");
+                               cf = cfb.build();
+                       } else {
+                               System.out.println("Running class 
["+getClass().getSimpleName()+"] using config file ["+cfPath+"]");
+                               cf = 
cfb.build(cfPath).getResolving(createVarResolver().build());
+                       }
+               }
+
+               if (cfPath != null)
+                       System.setProperty("juneau.configFile", cfPath);
+
+               // 
--------------------------------------------------------------------------------
+               // Set system properties.
+               // 
--------------------------------------------------------------------------------
+               Set<String> spKeys = cf.getSectionKeys("SystemProperties");
+               if (spKeys != null)
+                       for (String key : spKeys)
+                               System.setProperty(key, 
cf.get("SystemProperties", key));
+
+               // 
--------------------------------------------------------------------------------
+               // Add a config file change listener.
+               // 
--------------------------------------------------------------------------------
+               cf.addListener(new ConfigFileListener() {
+                       @Override /* ConfigFileListener */
+                       public void onSave(ConfigFile cf) {
+                               onConfigSave(cf);
+                       }
+                       @Override /* ConfigFileListener */
+                       public void onChange(ConfigFile cf, Set<String> 
changes) {
+                               onConfigChange(cf, changes);
+                       }
+               });
+
+               // 
--------------------------------------------------------------------------------
+               // Add exit listeners.
+               // 
--------------------------------------------------------------------------------
+               new Thread() {
+                       @Override /* Thread */
+                       public void run() {
+                               Console c = System.console();
+                               if (c == null)
+                                       System.out.println("No available 
console.");
+                               else {
+                                       while (true) {
+                                               String l = c.readLine("\nEnter 
'exit' to exit.\n");
+                                               if (l == null || 
l.equals("exit")) {
+                                                       
Microservice.this.stop();
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }.start();
+               Runtime.getRuntime().addShutdownHook(
+                       new Thread() {
+                               @Override /* Thread */
+                               public void run() {
+                                       Microservice.this.stop();
+                               }
+                       }
+               );
+               onStart();
+               return this;
+       }
+
+       /**
+        * Joins the application with the current thread.
+        * 
+        * <p>
+        * Default implementation is a no-op.
+        *
+        * @return This object (for method chaining).
+        * @throws Exception
+        */
+       public Microservice join() throws Exception {
+               return this;
+       }
+
+       /**
+        * Stop this application.
+        * 
+        * <p>
+        * Default implementation simply calls {@link #onStop()}.
+        * 
+        * <p>
+        * Overridden methods MUST call this method LAST so that the {@link 
#onStop()} method is called.
+        *
+        * @return This object (for method chaining).
+        */
+       public Microservice stop() {
+               onStop();
+               return this;
+       }
+
+       /**
+        * Kill the JVM by calling <code>System.exit(2);</code>.
+        */
+       public void kill() {
+               // This triggers the shutdown hook.
+               System.exit(2);
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Lifecycle listener methods.
+       // Subclasses can override these methods to run code on certain events.
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Called at the beginning of the {@link #start()} call.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onStart() {}
+
+       /**
+        * Called at the end of the {@link #stop()} call.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onStop() {}
+
+       /**
+        * Called if the {@link ConfigFile#save()} is called on the config file.
+        * 
+        * <p>
+        * Subclasses can override this method to listen for config file 
changes.
+        *
+        * @param cf The config file.
+        */
+       protected void onConfigSave(ConfigFile cf) {}
+
+       /**
+        * Called if one or more changes occur in the config file.
+        * 
+        * <p>
+        * Subclasses can override this method to listen for config file 
changes.
+        *
+        * @param cf The config file.
+        * @param changes The list of keys in the config file being changed.
+        */
+       protected void onConfigChange(ConfigFile cf, Set<String> changes) {}
+}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Microservice.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
 Fri Sep  8 23:21:12 2017
@@ -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;
+
+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.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Superclass for all REST resources.
+ * 
+ * <p>
+ * In additional to the functionality of the {@link RestServletDefault} group,
+ * augments the {@link RestContext#getVarResolver()} method with the following 
additional variable types:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             <code class='snippet'>$ARG{...}</code> - Command line arguments 
pulled from {@link Microservice#getArgs()}.
+ *             
+ *                     <h6 class='figure'>Example:</h6>
+ *             <p class='bcode'>
+ *     String firstArg = request.getVarResolver().resolve(<js>"$ARG{0}"</js>); 
 <jc>// First argument.</jc>
+ *     String namedArg = 
request.getVarResolver().resolve(<js>"$ARG{myarg}"</js>);  <jc>// Named 
argument (e.g. "myarg=foo"). </jc>
+ *             </p>
+ *     <li>
+ *             <code class='snippet'>$MF{...}</code> - Manifest file entries 
pulled from {@link Microservice#getManifest()}.
+ *             
+ *             <h6 class='figure'>Example:</h6>
+ *             <p class='bcode'>
+ *     String mainClass = 
request.getVarResolver().resolve(<js>"$MF{Main-Class}"</js>);  <jc>// Main 
class. </jc>
+ *             </p>
+ * </ul>
+ */
+@SuppressWarnings("serial")
+@RestResource(
+       htmldoc=@HtmlDoc(
+               links={
+                       "up: request:/..",
+                       "options: servlet:/?method=OPTIONS"
+               },
+               stylesheet="$C{REST/stylesheet,servlet:/styles/devops.css}"
+       ),
+       config="$S{juneau.configFile}"
+)
+public abstract class Resource extends RestServletDefault {
+
+       /**
+        * Add <code>$ARGS</code> and <code>$MF</code> variable resolvers.
+        * 
+        * @param config The resource config.
+        * @throws Exception
+        */
+       @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
+                       .addVars(ArgsVar.class, ManifestFileVar.class)
+                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+       }
+}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/Resource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
 Fri Sep  8 23:21:12 2017
@@ -0,0 +1,75 @@
+// 
***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.rest.annotation.HookEvent.*;
+
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Superclass for all REST resource groups.
+ * 
+ * <p>
+ * In additional to the functionality of the {@link RestServletGroupDefault} 
group,
+ * augments the {@link RestContext#getVarResolver()} method with the following 
additional variable types:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             <jk>$ARG{...}</jk> - Command line arguments.
+ *             <br>Resolves values from {@link Microservice#getArgs()}.
+ *             
+ *             <h6>Example:</h6>
+ *             <p class='bcode'>
+ *     String firstArg = request.getVarResolver().resolve(<js>"$ARG{0}"</js>); 
 <jc>// First argument.</jc>
+ *     String namedArg = 
request.getVarResolver().resolve(<js>"$ARG{myarg}"</js>);  <jc>// Named 
argument (e.g. "myarg=foo"). </jc>
+ *             </p>
+ *     <li>
+ *             <jk>$MF{...}</jk> - Manifest file entries.
+ *             
+ *             <h6>Example:</h6>
+ *             <p class='bcode'>
+ *     String mainClass = 
request.getVarResolver().resolve(<js>"$MF{Main-Class}"</js>);  <jc>// Main 
class. </jc>
+ *             </p>
+ * </ul>
+ */
+@SuppressWarnings("serial")
+@RestResource(
+       htmldoc=@HtmlDoc(
+               links={
+                       "up: request:/..",
+                       "options: servlet:/?method=OPTIONS"
+               },
+               stylesheet="$C{REST/stylesheet,servlet:/styles/devops.css}"
+       ),
+       config="$S{juneau.configFile}"
+)
+public abstract class ResourceGroup extends RestServletGroupDefault {
+
+       /**
+        * Initializes the registry URL and rest clent.
+        * 
+        * @param config The resource config.
+        * @throws Exception
+        */
+       @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
+                       .addVars(ArgsVar.class, ManifestFileVar.class)
+                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+       }
+}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJena.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJena.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJena.java
 Fri Sep  8 23:21:12 2017
@@ -0,0 +1,46 @@
+// 
***************************************************************************************************************************
+// * 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 org.apache.juneau.jena.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * Superclass for all REST resources with RDF support.
+ */
+@SuppressWarnings("serial")
+@RestResource(
+       htmldoc=@HtmlDoc(
+               links={
+                       "up: request:/..",
+                       "options: servlet:/?method=OPTIONS"
+               },
+               stylesheet="$C{REST/stylesheet,servlet:/styles/devops.css}"
+       ),
+       config="$S{juneau.configFile}",
+       serializers={
+               RdfSerializer.Xml.class,
+               RdfSerializer.XmlAbbrev.class,
+               RdfSerializer.Turtle.class,
+               RdfSerializer.NTriple.class,
+               RdfSerializer.N3.class
+       },
+       parsers={
+               RdfParser.Xml.class,
+               RdfParser.Turtle.class,
+               RdfParser.NTriple.class,
+               RdfParser.N3.class
+       }
+)
+public abstract class ResourceJena extends RestServletDefault {}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJena.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJena.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
 Fri Sep  8 23:21:12 2017
@@ -0,0 +1,89 @@
+// 
***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.rest.annotation.HookEvent.*;
+
+import org.apache.juneau.jena.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Superclass for all REST resource groups.
+ * 
+ * <p>
+ * In additional to the functionality of the {@link RestServletGroupDefault} 
group,
+ * augments the {@link RestContext#getVarResolver()} method with the following 
additional variable types:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             <jk>$ARG{...}</jk> - Command line arguments.
+ *             <br>Resolves values from {@link Microservice#getArgs()}.
+ *             
+ *             <h6>Example:</h6>
+ *             <p class='bcode'>
+ *     String firstArg = request.getVarResolver().resolve(<js>"$ARG{0}"</js>); 
 <jc>// First argument.</jc>
+ *     String namedArg = 
request.getVarResolver().resolve(<js>"$ARG{myarg}"</js>);  <jc>// Named 
argument (e.g. "myarg=foo"). </jc>
+ *             </p>
+ *     <li>
+ *             <jk>$MF{...}</jk> - Manifest file entries.
+ *             
+ *             <h6>Example:</h6>
+ *             <p class='bcode'>
+ *     String mainClass = 
request.getVarResolver().resolve(<js>"$MF{Main-Class}"</js>);  <jc>// Main 
class. </jc>
+ *             </p>
+ * </ul>
+ */
+@SuppressWarnings("serial")
+@RestResource(
+       htmldoc=@HtmlDoc(
+               links={
+                       "up: request:/..",
+                       "options: servlet:/?method=OPTIONS"
+               },
+               stylesheet="$C{REST/stylesheet,servlet:/styles/devops.css}"
+       ),
+       config="$S{juneau.configFile}",
+       serializers={
+               RdfSerializer.Xml.class,
+               RdfSerializer.XmlAbbrev.class,
+               RdfSerializer.Turtle.class,
+               RdfSerializer.NTriple.class,
+               RdfSerializer.N3.class
+       },
+       parsers={
+               RdfParser.Xml.class,
+               RdfParser.Turtle.class,
+               RdfParser.NTriple.class,
+               RdfParser.N3.class
+       }
+)
+public abstract class ResourceJenaGroup extends RestServletGroupDefault {
+
+       /**
+        * Add <code>$ARGS</code> and <code>$MF</code> variable resolvers.
+        * 
+        * @param config The resource config.
+        * @throws Exception
+        */
+       @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
+                       .addVars(ArgsVar.class, ManifestFileVar.class)
+                       .addVarContextObject(ArgsVar.SESSION_args, 
Microservice.getArgs())
+                       .addVarContextObject(ManifestFileVar.SESSION_manifest, 
Microservice.getManifest());
+       }
+}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/ResourceJenaGroup.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
==============================================================================
--- 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
 (added)
+++ 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
 Fri Sep  8 23:21:12 2017
@@ -0,0 +1,644 @@
+// 
***************************************************************************************************************************
+// * 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 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.*;
+import java.util.logging.*;
+
+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.eclipse.jetty.server.*;
+import org.eclipse.jetty.servlet.*;
+import org.eclipse.jetty.xml.*;
+
+/**
+ * Entry point for Juneau microservice that implements a REST interface using 
Jetty on a single port.
+ *
+ * <h6 class='topic'>Jetty Server Details</h6>
+ * 
+ * The Jetty server is created by the {@link #createServer()} method and 
started with the {@link #startServer()} method.
+ * These methods can be overridden to provided customized behavior.
+ *
+ * <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.
+ *
+ * <h6 class='topic'>Logging</h6>
+ * 
+ * Logging is initialized by the {@link #initLogging()} method.
+ * This method can be overridden to provide customized logging behavior.
+ *
+ * <h6 class='topic'>Lifecycle Listener Methods</h6>
+ * Subclasses can optionally implement the following event listener methods:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link #onStart()} - Gets executed before {@link #start()}.
+ *     <li>
+ *             {@link #onStop()} - Gets executed before {@link #stop()}.
+ *     <li>
+ *             {@link #onCreateServer()} - Gets executed before {@link 
#createServer()}.
+ *     <li>
+ *             {@link #onStartServer()} - Gets executed before {@link 
#startServer()}.
+ *     <li>
+ *             {@link #onPostStartServer()} - Gets executed after {@link 
#startServer()}.
+ *     <li>
+ *             {@link #onStopServer()} - Gets executed before {@link #stop()}.
+ *     <li>
+ *             {@link #onPostStopServer()} - Gets executed after {@link 
#stop()}.
+ * </ul>
+ */
+public class RestMicroservice extends Microservice {
+       
+       ServletContextHandler servletContextHandler; 
+       Server server;
+       int port;
+       String contextPath;
+       Logger logger;
+       Object jettyXml;
+       
+       /**
+        * Main method.
+        * 
+        * <p>
+        * Subclasses must also implement this method!
+        *
+        * @param args Command line arguments.
+        * @throws Exception
+        */
+       public static void main(String[] args) throws Exception {
+               new RestMicroservice(args).start().join();
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param args Command line arguments.
+        * @throws Exception
+        */
+       public RestMicroservice(String...args) throws Exception {
+               super(args);
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Methods implemented on Microservice API
+       
//--------------------------------------------------------------------------------
+
+       @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;
+       }
+
+       @Override /* Microservice */
+       public RestMicroservice join() throws Exception {
+               server.join();
+               return this;
+       }
+
+       @Override /* Microservice */
+       public RestMicroservice stop() {
+               Thread t = new Thread() {
+                       @Override /* Thread */
+                       public void run() {
+                               try {
+                                       if (server.isStopping() || 
server.isStopped())
+                                               return;
+                                       onStopServer();
+                                       logger.warning("Stopping server.");
+                                       server.stop();
+                                       logger.warning("Server stopped.");
+                                       onPostStopServer();
+                               } catch (Exception e) {
+                                       logger.log(Level.SEVERE, 
e.getLocalizedMessage(), e);
+                               }
+                       }
+               };
+               t.start();
+               try {
+                       t.join();
+               } catch (InterruptedException e) {
+                       e.printStackTrace();
+               }
+               super.stop();
+               return this;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // RestMicroservice API methods.
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Returns the port that this microservice started up on.
+        * @return The port that this microservice started up on.
+        */
+       public int getPort() {
+               return port;
+       }
+
+       /**
+        * Returns the URI where this microservice is listening on.
+        * @return The URI where this microservice is listening on.
+        */
+       public URI getURI() {
+               String scheme = getConfig().getBoolean("REST/useSsl") ? "https" 
: "http";
+               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);
+               }
+       }
+
+       /**
+        * 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));
+                       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);
+               }
+               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));
+       }
+
+       /**
+        * Method used to create (but not start) an instance of a Jetty server.
+        * 
+        * <p>
+        * Subclasses can override this method to customize the Jetty server 
before it is started.
+        * 
+        * <p>
+        * The default implementation is configured by the following values in 
the config file 
+        * if a jetty.xml is not specified via a <code>REST/jettyXml</code> 
setting:
+        * <p class='bcode'>
+        *      
<cc>#================================================================================
+        *      # REST 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
+        *
+        * @return The newly-created server.
+        * @throws Exception
+        */
+       protected Server createServer() throws Exception {
+               onCreateServer();
+
+               ConfigFile cf = getConfig();
+               ObjectMap mf = getManifest();
+               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();
+               
+               } 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);
+               }
+               
+               return server;
+       }
+       
+       /**
+        * Adds an arbitrary servlet to this microservice.
+        * 
+        * @param servlet The servlet instance.
+        * @param pathSpec The context path of the servlet.
+        * @return This object (for method chaining).
+        * @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;
+       }
+       
+       /**
+        * Adds a servlet attribute to the Jetty server.
+        * 
+        * @param name The server attribute name.
+        * @param value The context path of the servlet.
+        * @return This object (for method chaining).
+        * @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);
+               return this;
+       }
+       
+       /**
+        * Returns the underlying Jetty server.
+        * 
+        * @return The underlying Jetty server, or <jk>null</jk> if {@link 
#createServer()} has not yet been called.
+        */
+       public Server getServer() {
+               return server;
+       }
+       
+       private static int findOpenPort(int[] ports) {
+               for (int port : ports) {
+                       try {
+                               // If port is 0, try a random port between 
ports[0] and 32767.
+                               if (port == 0)
+                                       port = new Random().nextInt(32767 - 
ports[0] + 1) + ports[0];
+                               ServerSocket ss = new ServerSocket(port);
+                               ss.close();
+                               return port;
+                       } catch (IOException e) {}
+               }
+               return 0;
+       }
+
+       /**
+        * Method used to start the Jetty server created by {@link 
#createServer()}.
+        * 
+        * <p>
+        * Subclasses can override this method to customize server startup.
+        *
+        * @return The port that this server started on.
+        * @throws Exception
+        */
+       protected int startServer() throws Exception {
+               onStartServer();
+               server.start();
+               this.port = 
((ServerConnector)server.getConnectors()[0]).getLocalPort();
+               logger.warning("Server started on port " + port);
+               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;
+       }
+
+       /**
+        * Called when {@link ConfigFile#save()} is called on the config file.
+        * 
+        * <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)
+        *      #       RESTART_SERVER - Restart the Jetty server.
+        *      #       RESTART_SERVICE - Shutdown and exit with code '3'.</cc>
+        *      <ck>saveConfigAction</ck> = RESTART_SERVER
+        * </p>
+        */
+       @Override /* Microservice */
+       protected void onConfigSave(ConfigFile cf) {
+               try {
+                       String saveConfigAction = 
cf.getString("REST/saveConfigAction", "NOTHING");
+                       if (saveConfigAction.equals("RESTART_SERVER")) {
+                               new Thread() {
+                                       @Override /* Thread */
+                                       public void run() {
+                                               try {
+                                                       
RestMicroservice.this.stop();
+                                                       
RestMicroservice.this.start();
+                                               } catch (Exception e) {
+                                                       
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+                                               }
+                                       }
+                               }.start();
+                       } else if (saveConfigAction.equals("RESTART_SERVICE")) {
+                               stop();
+                               System.exit(3);
+                       }
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Sets the <code>jetty.xml</code> used to configure the Jetty server.
+        * 
+        * <p>
+        * 
+        * @param jettyXml 
+        *      The <code>jetty.xml</code>.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li>A {@link File} representing the location on the 
file system.
+        *              <li>An {@link InputStream} containing the contents of 
the file.
+        *              <li>A {@link String} representing the file system path.
+        *      </ul>
+        * @return This object (for method chaining).
+        */
+       public RestMicroservice setJettyXml(Object jettyXml) {
+               if (jettyXml instanceof String || jettyXml instanceof File || 
jettyXml instanceof InputStream)
+                       this.jettyXml = jettyXml;
+               else
+                       throw new FormattedRuntimeException("Invalid object 
type passed to setJettyXml()", jettyXml == null ? null : 
jettyXml.getClass().getName());
+               return this;
+       }
+
+       
+       
//--------------------------------------------------------------------------------
+       // Lifecycle listener methods.
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Called before {@link #createServer()} is called.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onCreateServer() {}
+
+       /**
+        * Called before {@link #startServer()} is called.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onStartServer() {}
+
+       /**
+        * Called after the Jetty server is started.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onPostStartServer() {}
+
+       /**
+        * Called before the Jetty server is stopped.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onStopServer() {}
+
+       /**
+        * Called after the Jetty server is stopped.
+        * 
+        * <p>
+        * Subclasses can override this method to hook into the lifecycle of 
this application.
+        */
+       protected void onPostStopServer() {}
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods.
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Microservice */
+       public RestMicroservice setConfig(String cfPath, boolean create) throws 
IOException {
+               super.setConfig(cfPath, create);
+               return this;
+       }
+
+       @Override /* Microservice */
+       public RestMicroservice setManifestContents(String...contents) throws 
IOException {
+               super.setManifestContents(contents);
+               return this;
+       }
+}

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build1.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build1.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build1.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build2.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build2.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/build2.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/helloworld1.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/helloworld1.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/helloworld1.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions1.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions1.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions1.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions2.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions2.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions2.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions3.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions3.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions3.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions4.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions4.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions4.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions5.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions5.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions5.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions6.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions6.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/instructions6.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/manifest1.png
==============================================================================
Binary file - no diff available.

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/manifest1.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: 
release/incubator/juneau/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/doc-files/manifest1.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream


Reply via email to