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

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


The following commit(s) were added to refs/heads/master by this push:
     new b9a37fa  Improvements to org.apache.juneau.cp package.
b9a37fa is described below

commit b9a37fa2261e197ce9b6e233d84b6b2f240301f6
Author: JamesBognar <[email protected]>
AuthorDate: Sat Jul 25 15:02:12 2020 -0400

    Improvements to org.apache.juneau.cp package.
---
 .../juneau-core-utest/files/Test3.properties       |  15 ++
 .../juneau-core-utest/files/Test3_ja.properties    |  15 ++
 .../juneau-core-utest/files/Test3_ja_JP.properties |  15 ++
 juneau-core/juneau-core-utest/files/test2.txt      |   1 +
 .../apache/juneau/cp/BasicResourceFinder_Test.java | 191 +++++++++++++++++++++
 .../org/apache/juneau/cp/MessageBundle_Test.java   |  52 +++---
 .../juneau/cp/RecursiveResourceFinder_Test.java    |  69 ++++++++
 .../org/apache/juneau/cp/ResourceFinder_Test.java  |  45 +----
 .../org/apache/juneau/cp/ResourceManager_Test.java | 123 +++++++++++++
 .../juneau/cp/SimpleResourceFinder_Test.java       |  56 ++++++
 .../apache/juneau/cp/test1/MessageBundleTest1.java |  40 +----
 .../java/org/apache/juneau/cp/test1/Test1.java     |  40 +----
 .../java/org/apache/juneau/cp/test2/Test2.java     |  40 +----
 .../juneau/cp/test1/MessageBundleTest1.properties  |  15 ++
 .../cp/test1/MessageBundleTest1_ja.properties      |  15 ++
 .../cp/test1/MessageBundleTest1_ja_JP.properties   |  15 ++
 .../apache/juneau/cp/test1/files/Test1.properties  |  15 ++
 .../juneau/cp/test1/files/Test1_ja.properties      |  15 ++
 .../juneau/cp/test1/files/Test1_ja_JP.properties   |  15 ++
 .../org/apache/juneau/cp/test2/Test2.properties    |  15 ++
 .../org/apache/juneau/cp/test2/Test2_ja.properties |  15 ++
 .../apache/juneau/cp/test2/Test2_ja_JP.properties  |  15 ++
 .../resources/org/apache/juneau/cp/test2/Test4     |  15 ++
 .../resources/org/apache/juneau/cp/test2/Test4_ja  |  15 ++
 .../org/apache/juneau/cp/test2/Test4_ja_JP         |  15 ++
 .../juneau/assertions/FluentStringAssertion.java   |  30 ++++
 .../org/apache/juneau/cp/BasicResourceFinder.java  |  17 +-
 .../java/org/apache/juneau/cp/MessageBundle.java   | 100 +++++------
 .../java/org/apache/juneau/cp/ResourceFinder.java  |   2 +-
 .../java/org/apache/juneau/cp/ResourceManager.java |  76 ++------
 .../apache/juneau/microservice/Microservice.java   |   2 +-
 .../juneau/microservice/console/ConfigCommand.java |   2 +-
 .../juneau/microservice/console/ExitCommand.java   |   2 +-
 .../juneau/microservice/console/HelpCommand.java   |   2 +-
 .../microservice/console/RestartCommand.java       |   2 +-
 .../microservice/jetty/JettyMicroservice.java      |   2 +-
 .../java/org/apache/juneau/rest/RestContext.java   | 107 +-----------
 .../java/org/apache/juneau/rest/RestServlet.java   |   2 +-
 .../java/org/apache/juneau/rest/StaticFiles.java   |   4 +-
 .../juneau/rest/reshandlers/DefaultHandler.java    |   2 +-
 40 files changed, 821 insertions(+), 413 deletions(-)

diff --git a/juneau-core/juneau-core-utest/files/Test3.properties 
b/juneau-core/juneau-core-utest/files/Test3.properties
new file mode 100644
index 0000000..90a3dcd
--- /dev/null
+++ b/juneau-core/juneau-core-utest/files/Test3.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test3.properties
diff --git a/juneau-core/juneau-core-utest/files/Test3_ja.properties 
b/juneau-core/juneau-core-utest/files/Test3_ja.properties
new file mode 100644
index 0000000..4ffd31e
--- /dev/null
+++ b/juneau-core/juneau-core-utest/files/Test3_ja.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test3_ja.properties
diff --git a/juneau-core/juneau-core-utest/files/Test3_ja_JP.properties 
b/juneau-core/juneau-core-utest/files/Test3_ja_JP.properties
new file mode 100644
index 0000000..ed1139f
--- /dev/null
+++ b/juneau-core/juneau-core-utest/files/Test3_ja_JP.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test3_ja_JP.properties
diff --git a/juneau-core/juneau-core-utest/files/test2.txt 
b/juneau-core/juneau-core-utest/files/test2.txt
index 917e02b..d12cd3c 100644
--- a/juneau-core/juneau-core-utest/files/test2.txt
+++ b/juneau-core/juneau-core-utest/files/test2.txt
@@ -10,3 +10,4 @@
 * "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.   
                                           *
 
***************************************************************************************************************************
+test2.txt
\ No newline at end of file
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/BasicResourceFinder_Test.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/BasicResourceFinder_Test.java
new file mode 100644
index 0000000..6edc881
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/BasicResourceFinder_Test.java
@@ -0,0 +1,191 @@
+// 
***************************************************************************************************************************
+// * 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.cp;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+import static java.util.Locale.*;
+
+import org.apache.juneau.cp.test1.*;
+import org.apache.juneau.cp.test2.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BasicResourceFinder_Test {
+
+       @Test
+       public void a01_basic() throws Exception {
+               ResourceFinder x = BasicResourceFinder.INSTANCE;
+
+               
assertStream(x.findResource(null,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",CHINA)).doesNotExist();
+
+               
assertStream(x.findResource(null,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"Test2.properties",null)).string().contains("Test2.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPANESE)).string().contains("Test2_ja.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPAN)).string().contains("Test2_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",CHINA)).string().contains("Test2.properties");
+
+               
assertStream(x.findResource(null,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+
+               assertStream(x.findResource(null,"Test4",null)).doesNotExist();
+               
assertStream(x.findResource(null,"Test4",JAPANESE)).doesNotExist();
+               assertStream(x.findResource(null,"Test4",JAPAN)).doesNotExist();
+               assertStream(x.findResource(null,"Test4",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test4",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test4",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test4",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test4",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"Test4",null)).string().contains("Test4");
+               
assertStream(x.findResource(Test2.class,"Test4",JAPANESE)).string().contains("Test4_ja");
+               
assertStream(x.findResource(Test2.class,"Test4",JAPAN)).string().contains("Test4_ja_JP");
+               
assertStream(x.findResource(Test2.class,"Test4",CHINA)).string().contains("Test4");
+       }
+
+       @Test
+       public void a02_noFileSystem() throws Exception {
+               ResourceFinder x = new BasicResourceFinder(false, false);
+
+               
assertStream(x.findResource(null,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",CHINA)).doesNotExist();
+
+               
assertStream(x.findResource(null,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",CHINA)).doesNotExist();
+       }
+
+       @Test
+       public void a03_recursive() throws Exception {
+               ResourceFinder x = new BasicResourceFinder(true, true);
+
+               
assertStream(x.findResource(null,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+
+               
assertStream(x.findResource(null,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"Test2.properties",null)).string().contains("Test2.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPANESE)).string().contains("Test2_ja.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPAN)).string().contains("Test2_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",CHINA)).string().contains("Test2.properties");
+
+               
assertStream(x.findResource(null,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+       }
+
+       @Test
+       public void a04_invalidNames() throws Exception {
+               ResourceFinder x = BasicResourceFinder.INSTANCE;
+
+               for (String s : new 
String[]{"bad.properties","",null,"files/../files/Test1.properties"}) {
+                       
assertStream(x.findResource(null,s,null)).doesNotExist();
+                       
assertStream(x.findResource(null,s,JAPANESE)).doesNotExist();
+                       
assertStream(x.findResource(null,s,JAPAN)).doesNotExist();
+                       
assertStream(x.findResource(null,s,CHINA)).doesNotExist();
+                       
assertStream(x.findResource(Test1.class,s,null)).doesNotExist();
+                       
assertStream(x.findResource(Test1.class,s,JAPANESE)).doesNotExist();
+                       
assertStream(x.findResource(Test1.class,s,JAPAN)).doesNotExist();
+                       
assertStream(x.findResource(Test1.class,s,CHINA)).doesNotExist();
+                       
assertStream(x.findResource(Test2.class,s,null)).doesNotExist();
+                       
assertStream(x.findResource(Test2.class,s,JAPANESE)).doesNotExist();
+                       
assertStream(x.findResource(Test2.class,s,JAPAN)).doesNotExist();
+                       
assertStream(x.findResource(Test2.class,s,CHINA)).doesNotExist();
+               }
+
+               String s = ".";
+               assertStream(x.findResource(null,s,null)).doesNotExist();
+               assertStream(x.findResource(null,s,JAPANESE)).doesNotExist();
+               assertStream(x.findResource(null,s,JAPAN)).doesNotExist();
+               assertStream(x.findResource(null,s,CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,s,null)).string().contains("Test1.class");
+               
assertStream(x.findResource(Test1.class,s,JAPANESE)).string().contains("Test1.class");
+               
assertStream(x.findResource(Test1.class,s,JAPAN)).string().contains("Test1.class");
+               
assertStream(x.findResource(Test1.class,s,CHINA)).string().contains("Test1.class");
+               
assertStream(x.findResource(Test2.class,s,null)).string().contains("Test2.class");
+               
assertStream(x.findResource(Test2.class,s,JAPANESE)).string().contains("Test2.class");
+               
assertStream(x.findResource(Test2.class,s,JAPAN)).string().contains("Test2.class");
+               
assertStream(x.findResource(Test2.class,s,CHINA)).string().contains("Test2.class");
+       }
+}
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/MessageBundle_Test.java
similarity index 57%
copy from 
juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
copy to 
juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/MessageBundle_Test.java
index 5cb2216..e5c74f8 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/MessageBundle_Test.java
@@ -10,44 +10,34 @@
 // * "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.console;
+package org.apache.juneau.cp;
 
-import java.io.*;
-import java.util.*;
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
 
-import org.apache.juneau.collections.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.cp.*;
+import java.util.*;
 
-/**
- * Implements the 'exit' console command to gracefully shut down the 
microservice and JVM.
- */
-public class ExitCommand extends ConsoleCommand {
+import static java.util.Locale.*;
 
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
+import org.apache.juneau.cp.test1.*;
+import org.junit.*;
 
-       @Override /* ConsoleCommand */
-       public String getName() {
-               return "exit";
-       }
-
-       @Override /* ConsoleCommand */
-       public String getInfo() {
-               return mb.getString("info");
-       }
+@FixMethodOrder(NAME_ASCENDING)
+public class MessageBundle_Test {
 
-       @Override /* ConsoleCommand */
-       public String getDescription() {
-               return mb.getString("description");
+       @Test
+       public void a01_nonExistent() throws Exception {
+               assertThrown(()->MessageBundle.of(Test1.class)).contains("Could 
not find bundle path for class");
+               
assertThrown(()->MessageBundle.of(Test1.class,"bad.properties")).contains("Bundle
 path should not end with '.properties'");
        }
 
-       @Override /* ConsoleCommand */
-       public boolean execute(Scanner in, PrintWriter out, Args args) {
-               try {
-                       Microservice.getInstance().stop().exit();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return true;
+       @Test
+       public void a02_sameDirectory() throws Exception {
+               MessageBundle x = MessageBundle.of(MessageBundleTest1.class);
+               
assertString(x.getString("file")).is("MessageBundleTest1.properties");
+               
assertString(x.getBundle(JAPANESE).getString("file")).is("MessageBundleTest1_ja.properties");
+               
assertString(x.getBundle(JAPAN).getString("file")).is("MessageBundleTest1_ja_JP.properties");
+               
assertString(x.getBundle(CHINA).getString("file")).is("MessageBundleTest1.properties");
+               
assertString(x.getBundle((Locale)null).getString("file")).is("MessageBundleTest1.properties");
        }
 }
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/RecursiveResourceFinder_Test.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/RecursiveResourceFinder_Test.java
new file mode 100644
index 0000000..6907e66
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/RecursiveResourceFinder_Test.java
@@ -0,0 +1,69 @@
+// 
***************************************************************************************************************************
+// * 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.cp;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+import static java.util.Locale.*;
+
+import org.apache.juneau.cp.test1.*;
+import org.apache.juneau.cp.test2.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class RecursiveResourceFinder_Test {
+
+       @Test
+       public void a01_basic() throws Exception {
+               ResourceFinder x = RecursiveResourceFinder.INSTANCE;
+
+               
assertStream(x.findResource(null,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+
+               
assertStream(x.findResource(null,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"Test2.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"Test2.properties",null)).string().contains("Test2.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPANESE)).string().contains("Test2_ja.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",JAPAN)).string().contains("Test2_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"Test2.properties",CHINA)).string().contains("Test2.properties");
+
+               
assertStream(x.findResource(null,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(null,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",null)).string().contains("Test3.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPANESE)).string().contains("Test3_ja.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPAN)).string().contains("Test3_ja_JP.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",CHINA)).string().contains("Test3.properties");
+       }
+}
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceFinder_Test.java
similarity index 62%
copy from 
juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
copy to 
juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceFinder_Test.java
index 5cb2216..b6c656a 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceFinder_Test.java
@@ -10,44 +10,17 @@
 // * "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.console;
+package org.apache.juneau.cp;
 
-import java.io.*;
-import java.util.*;
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+import org.junit.*;
 
-import org.apache.juneau.collections.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.cp.*;
+@FixMethodOrder(NAME_ASCENDING)
+public class ResourceFinder_Test {
 
-/**
- * Implements the 'exit' console command to gracefully shut down the 
microservice and JVM.
- */
-public class ExitCommand extends ConsoleCommand {
-
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
-
-       @Override /* ConsoleCommand */
-       public String getName() {
-               return "exit";
-       }
-
-       @Override /* ConsoleCommand */
-       public String getInfo() {
-               return mb.getString("info");
-       }
-
-       @Override /* ConsoleCommand */
-       public String getDescription() {
-               return mb.getString("description");
-       }
-
-       @Override /* ConsoleCommand */
-       public boolean execute(Scanner in, PrintWriter out, Args args) {
-               try {
-                       Microservice.getInstance().stop().exit();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return true;
+       @Test
+       public void a01_basic_Null() throws Exception {
+               assertObject(new ResourceFinder.Null().findResource(null, null, 
null)).isNull();
        }
 }
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceManager_Test.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceManager_Test.java
new file mode 100644
index 0000000..9f2897b
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/ResourceManager_Test.java
@@ -0,0 +1,123 @@
+// 
***************************************************************************************************************************
+// * 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.cp;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+import static java.util.Locale.*;
+
+import org.apache.juneau.cp.test2.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.plaintext.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ResourceManager_Test {
+
+       private static Parser PARSER = PlainTextParser.DEFAULT;
+
+       @Test
+       public void a01_basic_BasicResourceFinder_nocache() throws Exception {
+               ResourceManager rm = of(Test2.class);
+               String f, fja, fjp;
+
+               f = "Test2.properties"; fja = "Test2_ja.properties"; fjp = 
"Test2_ja_JP.properties";
+               assertStream(rm.getStream(f)).string().contains(f);
+               assertStream(rm.getStream(f,JAPANESE)).string().contains(fja);
+               assertStream(rm.getStream(f,JAPAN)).string().contains(fjp);
+               assertStream(rm.getStream(f,CHINA)).string().contains(f);
+               assertString(rm.getString(f)).contains(f);
+               assertString(rm.getString(f,JAPANESE)).contains(fja);
+               assertString(rm.getString(f,JAPAN)).contains(fjp);
+               assertString(rm.getString(f,CHINA)).contains(f);
+               
assertObject(rm.getResource(String.class,PARSER,f)).string().contains(f);
+               
assertObject(rm.getResource(String.class,PARSER,f,JAPANESE)).string().contains(fja);
+               
assertObject(rm.getResource(String.class,PARSER,f,JAPAN)).string().contains(fjp);
+               
assertObject(rm.getResource(String.class,PARSER,f,CHINA)).string().contains(f);
+
+               f = "bad.properties"; fja = "bad.properties"; fjp = 
"bad.properties";
+               assertStream(rm.getStream(f)).doesNotExist();
+               assertStream(rm.getStream(f,JAPANESE)).doesNotExist();
+               assertStream(rm.getStream(f,JAPAN)).doesNotExist();
+               assertStream(rm.getStream(f,CHINA)).doesNotExist();
+               assertString(rm.getString(f)).doesNotExist();
+               assertString(rm.getString(f,JAPANESE)).doesNotExist();
+               assertString(rm.getString(f,JAPAN)).doesNotExist();
+               assertString(rm.getString(f,CHINA)).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,f)).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,f,JAPANESE)).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,f,JAPAN)).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,f,CHINA)).doesNotExist();
+       }
+
+       @Test
+       public void a02_basic_BasicResourceFinder_cache() throws Exception {
+               ResourceManager rm = of(Test2.class, 
BasicResourceFinder.INSTANCE, true);
+               String f, fja, fjp;
+
+               for (int i = 0; i <= 1; i++) {
+                       f = "Test2.properties"; fja = "Test2_ja.properties"; 
fjp = "Test2_ja_JP.properties";
+                       assertStream(rm.getStream(f)).string().contains(f);
+                       
assertStream(rm.getStream(f,JAPANESE)).string().contains(fja);
+                       
assertStream(rm.getStream(f,JAPAN)).string().contains(fjp);
+                       
assertStream(rm.getStream(f,CHINA)).string().contains(f);
+                       assertString(rm.getString(f)).contains(f);
+                       assertString(rm.getString(f,JAPANESE)).contains(fja);
+                       assertString(rm.getString(f,JAPAN)).contains(fjp);
+                       assertString(rm.getString(f,CHINA)).contains(f);
+                       
assertObject(rm.getResource(String.class,PARSER,f)).string().contains(f);
+                       
assertObject(rm.getResource(String.class,PARSER,f,JAPANESE)).string().contains(fja);
+                       
assertObject(rm.getResource(String.class,PARSER,f,JAPAN)).string().contains(fjp);
+                       
assertObject(rm.getResource(String.class,PARSER,f,CHINA)).string().contains(f);
+
+                       f = "bad.properties"; fja = "bad.properties"; fjp = 
"bad.properties";
+                       assertStream(rm.getStream(f)).doesNotExist();
+                       assertStream(rm.getStream(f,JAPANESE)).doesNotExist();
+                       assertStream(rm.getStream(f,JAPAN)).doesNotExist();
+                       assertStream(rm.getStream(f,CHINA)).doesNotExist();
+                       assertString(rm.getString(f)).doesNotExist();
+                       assertString(rm.getString(f,JAPANESE)).doesNotExist();
+                       assertString(rm.getString(f,JAPAN)).doesNotExist();
+                       assertString(rm.getString(f,CHINA)).doesNotExist();
+                       
assertObject(rm.getResource(String.class,PARSER,f)).doesNotExist();
+                       
assertObject(rm.getResource(String.class,PARSER,f,JAPANESE)).doesNotExist();
+                       
assertObject(rm.getResource(String.class,PARSER,f,JAPAN)).doesNotExist();
+                       
assertObject(rm.getResource(String.class,PARSER,f,CHINA)).doesNotExist();
+               }
+       }
+
+       @Test
+       public void a03_basic_other() throws Exception {
+               ResourceManager rm = of(Test2.class);
+
+               assertStream(rm.getStream(null)).doesNotExist();
+               assertString(rm.getString(null)).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,null)).doesNotExist();
+               assertStream(rm.getStream("")).doesNotExist();
+               assertString(rm.getString("")).doesNotExist();
+               
assertObject(rm.getResource(String.class,PARSER,"")).doesNotExist();
+       }
+
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Utility methods
+       
//------------------------------------------------------------------------------------------------------------------
+
+       private ResourceManager of(Class<?> baseClass, ResourceFinder 
resourceFinder, boolean useCache) {
+               return new ResourceManager(baseClass, resourceFinder, useCache);
+       }
+
+       private ResourceManager of(Class<?> baseClass) {
+               return new ResourceManager(baseClass);
+       }
+}
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/SimpleResourceFinder_Test.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/SimpleResourceFinder_Test.java
new file mode 100644
index 0000000..fb1cb48
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/SimpleResourceFinder_Test.java
@@ -0,0 +1,56 @@
+// 
***************************************************************************************************************************
+// * 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.cp;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+import static java.util.Locale.*;
+
+import org.apache.juneau.cp.test1.*;
+import org.apache.juneau.cp.test2.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class SimpleResourceFinder_Test {
+
+       @Test
+       public void a01_basic() throws Exception {
+               ResourceFinder x = SimpleResourceFinder.INSTANCE;
+
+               
assertStream(x.findResource(null,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test1.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",null)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPANESE)).string().contains("Test1_ja.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",JAPAN)).string().contains("Test1_ja_JP.properties");
+               
assertStream(x.findResource(Test1.class,"files/Test1.properties",CHINA)).string().contains("Test1.properties");
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test1.properties",CHINA)).doesNotExist();
+
+               
assertStream(x.findResource(null,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(null,"files/Test3.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test1.class,"files/Test3.properties",CHINA)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",null)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPANESE)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",JAPAN)).doesNotExist();
+               
assertStream(x.findResource(Test2.class,"files/Test3.properties",CHINA)).doesNotExist();
+       }
+}
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/MessageBundleTest1.java
similarity index 61%
copy from 
juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
copy to 
juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/MessageBundleTest1.java
index 5cb2216..cd3b40c 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/MessageBundleTest1.java
@@ -10,44 +10,8 @@
 // * "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.console;
+package org.apache.juneau.cp.test1;
 
-import java.io.*;
-import java.util.*;
+public class MessageBundleTest1 {
 
-import org.apache.juneau.collections.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.cp.*;
-
-/**
- * Implements the 'exit' console command to gracefully shut down the 
microservice and JVM.
- */
-public class ExitCommand extends ConsoleCommand {
-
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
-
-       @Override /* ConsoleCommand */
-       public String getName() {
-               return "exit";
-       }
-
-       @Override /* ConsoleCommand */
-       public String getInfo() {
-               return mb.getString("info");
-       }
-
-       @Override /* ConsoleCommand */
-       public String getDescription() {
-               return mb.getString("description");
-       }
-
-       @Override /* ConsoleCommand */
-       public boolean execute(Scanner in, PrintWriter out, Args args) {
-               try {
-                       Microservice.getInstance().stop().exit();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return true;
-       }
 }
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/Test1.java
similarity index 61%
copy from 
juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
copy to 
juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/Test1.java
index 5cb2216..36757f6 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test1/Test1.java
@@ -10,44 +10,8 @@
 // * "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.console;
+package org.apache.juneau.cp.test1;
 
-import java.io.*;
-import java.util.*;
+public class Test1 {
 
-import org.apache.juneau.collections.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.cp.*;
-
-/**
- * Implements the 'exit' console command to gracefully shut down the 
microservice and JVM.
- */
-public class ExitCommand extends ConsoleCommand {
-
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
-
-       @Override /* ConsoleCommand */
-       public String getName() {
-               return "exit";
-       }
-
-       @Override /* ConsoleCommand */
-       public String getInfo() {
-               return mb.getString("info");
-       }
-
-       @Override /* ConsoleCommand */
-       public String getDescription() {
-               return mb.getString("description");
-       }
-
-       @Override /* ConsoleCommand */
-       public boolean execute(Scanner in, PrintWriter out, Args args) {
-               try {
-                       Microservice.getInstance().stop().exit();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return true;
-       }
 }
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test2/Test2.java
similarity index 61%
copy from 
juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
copy to 
juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test2/Test2.java
index 5cb2216..e433353 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/cp/test2/Test2.java
@@ -10,44 +10,10 @@
 // * "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.console;
+package org.apache.juneau.cp.test2;
 
-import java.io.*;
-import java.util.*;
+import org.apache.juneau.cp.test1.*;
 
-import org.apache.juneau.collections.*;
-import org.apache.juneau.microservice.*;
-import org.apache.juneau.cp.*;
+public class Test2 extends Test1 {
 
-/**
- * Implements the 'exit' console command to gracefully shut down the 
microservice and JVM.
- */
-public class ExitCommand extends ConsoleCommand {
-
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
-
-       @Override /* ConsoleCommand */
-       public String getName() {
-               return "exit";
-       }
-
-       @Override /* ConsoleCommand */
-       public String getInfo() {
-               return mb.getString("info");
-       }
-
-       @Override /* ConsoleCommand */
-       public String getDescription() {
-               return mb.getString("description");
-       }
-
-       @Override /* ConsoleCommand */
-       public boolean execute(Scanner in, PrintWriter out, Args args) {
-               try {
-                       Microservice.getInstance().stop().exit();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-               return true;
-       }
 }
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1.properties
new file mode 100644
index 0000000..7c463ea
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=MessageBundleTest1.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja.properties
new file mode 100644
index 0000000..927ee6d
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=MessageBundleTest1_ja.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja_JP.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja_JP.properties
new file mode 100644
index 0000000..99c16a2
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/MessageBundleTest1_ja_JP.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=MessageBundleTest1_ja_JP.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1.properties
new file mode 100644
index 0000000..54fa0f5
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test1.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja.properties
new file mode 100644
index 0000000..fdaeca0
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test1_ja.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja_JP.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja_JP.properties
new file mode 100644
index 0000000..6ee8fdb
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test1/files/Test1_ja_JP.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test1_ja_JP.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2.properties
new file mode 100644
index 0000000..80affa9
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=Test2.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja.properties
new file mode 100644
index 0000000..0431d1c
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=Test2_ja.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja_JP.properties
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja_JP.properties
new file mode 100644
index 0000000..a0e86c7
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test2_ja_JP.properties
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=Test2_ja_JP.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4
new file mode 100644
index 0000000..b4721db
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test4.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja
new file mode 100644
index 0000000..24cdc09
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test4_ja.properties
diff --git 
a/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja_JP
 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja_JP
new file mode 100644
index 0000000..416ba5c
--- /dev/null
+++ 
b/juneau-core/juneau-core-utest/src/test/resources/org/apache/juneau/cp/test2/Test4_ja_JP
@@ -0,0 +1,15 @@
+# 
***************************************************************************************************************************
+# * 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. 
                                             *
+# *                                                                            
                                             *
+# 
***************************************************************************************************************************
+
+file=files/Test4_ja_JP.properties
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
index 31156e9..38efbfa 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
@@ -482,6 +482,36 @@ public class FluentStringAssertion<R> extends 
FluentObjectAssertion<R> {
                return returns();
        }
 
+       /**
+        * Asserts that the text starts with the specified string.
+        *
+        * @param string The string to test for.
+        * @return The response object (for method chaining).
+        * @throws AssertionError If assertion failed.
+        */
+       public R startsWith(String string) {
+               exists();
+               assertNotNull("string", string);
+               if (! text.startsWith(string))
+                       throw error("Text did not start with expected 
string.\n\tString=[{0}]\n\tText=[{1}]", fix(string), fix(text));
+               return returns();
+       }
+
+       /**
+        * Asserts that the text ends with the specified string.
+        *
+        * @param string The string to test for.
+        * @return The response object (for method chaining).
+        * @throws AssertionError If assertion failed.
+        */
+       public R endsWith(String string) {
+               exists();
+               assertNotNull("string", string);
+               if (! text.endsWith(string))
+                       throw error("Text did not end with expected 
string.\n\tString=[{0}]\n\tText=[{1}]", fix(string), fix(text));
+               return returns();
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Utility methods
        
//------------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicResourceFinder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicResourceFinder.java
index e21be54..9834581 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicResourceFinder.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicResourceFinder.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.cp;
 
 import static org.apache.juneau.internal.FileUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
 import java.util.*;
@@ -74,6 +75,8 @@ public class BasicResourceFinder implements ResourceFinder {
        @SuppressWarnings("resource")
        @Override /* ClasspathResourceFinder */
        public InputStream findResource(Class<?> baseClass, String name, Locale 
locale) throws IOException {
+               if (isInvalidName(name))
+                       return null;
                InputStream is = null;
                if (includeFileSystem)
                        is = findFileSystemResource(name, locale);
@@ -125,12 +128,10 @@ public class BasicResourceFinder implements 
ResourceFinder {
         * @throws IOException Thrown by underlying stream.
         */
        protected InputStream findFileSystemResource(String name, Locale 
locale) throws IOException {
-               if (name.indexOf("..") == -1) {
-                       for (String n2 : getCandidateFileNames(name, locale)) {
-                               File f = new File(n2);
-                               if (f.exists() && f.canRead() && ! 
f.isAbsolute()) {
-                                       return new FileInputStream(f);
-                               }
+               for (String n2 : getCandidateFileNames(name, locale)) {
+                       File f = new File(n2);
+                       if (f.exists() && f.isFile() && f.canRead() && ! 
f.isAbsolute()) {
+                               return new FileInputStream(f);
                        }
                }
                return null;
@@ -209,4 +210,8 @@ public class BasicResourceFinder implements ResourceFinder {
                        return ROOT_LOCALE;
                return RB_CONTROL.getCandidateLocales("", locale);
        }
+
+       private static boolean isInvalidName(String name) {
+               return isEmpty(name) || name.contains("..");
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/MessageBundle.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/MessageBundle.java
index f755185..c2ae09d 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/MessageBundle.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/MessageBundle.java
@@ -88,57 +88,22 @@ public class MessageBundle extends ResourceBundle {
        /**
         * Constructor.
         *
-        * <p>
-        * When this method is used, the bundle path is determined by searching 
for the resource bundle
-        * in the following locations:
-        * <ul>
-        *      <li><c>[package].ForClass.properties</c>
-        *      <li><c>[package].nls.ForClass.properties</c>
-        *      <li><c>[package].i18n.ForClass.properties</c>
-        * </ul>
-        *
         * @param forClass The class
         * @return A new message bundle belonging to the class.
         */
-       public static final MessageBundle create(Class<?> forClass) {
-               return create(forClass, findBundlePath(forClass));
+       public static final MessageBundle of(Class<?> forClass) {
+               return new MessageBundle(forClass, null, null);
        }
 
        /**
         * Constructor.
         *
-        * <p>
-        * A shortcut for calling <c>new MessageBundle(forClass, 
bundlePath)</c>.
-        *
         * @param forClass The class
         * @param bundlePath The location of the resource bundle.
         * @return A new message bundle belonging to the class.
         */
-       public static final MessageBundle create(Class<?> forClass, String 
bundlePath) {
-               return new MessageBundle(forClass, bundlePath);
-       }
-
-       private static final String findBundlePath(Class<?> forClass) {
-               String path = forClass.getName();
-               if (tryBundlePath(forClass, path))
-                       return path;
-               path = forClass.getPackage().getName() + ".nls." + 
forClass.getSimpleName();
-               if (tryBundlePath(forClass, path))
-                       return path;
-               path = forClass.getPackage().getName() + ".i18n." + 
forClass.getSimpleName();
-               if (tryBundlePath(forClass, path))
-                       return path;
-               return null;
-       }
-
-       private static final boolean tryBundlePath(Class<?> c, String path) {
-               try {
-                       path = c.getName();
-                       ResourceBundle.getBundle(path, Locale.getDefault(), 
c.getClassLoader());
-                       return true;
-               } catch (MissingResourceException e) {
-                       return false;
-               }
+       public static final MessageBundle of(Class<?> forClass, String 
bundlePath) {
+               return new MessageBundle(forClass, bundlePath, null);
        }
 
        /**
@@ -147,21 +112,32 @@ public class MessageBundle extends ResourceBundle {
         * @param forClass The class using this resource bundle.
         * @param bundlePath
         *      The path of the resource bundle to wrap.
-        *      This can be an absolute path (e.g. 
<js>"com.foo.MyMessages"</js>) or a path relative to the package of the
+        *      <br>This can be an absolute path (e.g. 
<js>"com.foo.MyMessages"</js>) or a path relative to the package of the
         *      <l>forClass</l> (e.g. <js>"MyMessages"</js> if <l>forClass</l> 
is <js>"com.foo.MyClass"</js>).
+        *      <br>If <jk>null</jk>, searches for the following locations:
+        *      <ul>
+        *              <li><c>[package].ForClass.properties</c>
+        *              <li><c>[package].nls.ForClass.properties</c>
+        *              <li><c>[package].i18n.ForClass.properties</c>
+        *      </ul>
+        * @param locale
+        *      The locale.
+        *      <br>If <jk>null</jk>, uses the default locale.
+        * @throws MissingResourceException If resource bundle could not be 
found.
         */
-       public MessageBundle(Class<?> forClass, String bundlePath) {
-               this(forClass, bundlePath, Locale.getDefault());
-       }
-
-       private MessageBundle(Class<?> forClass, String bundlePath, Locale 
locale) {
+       public MessageBundle(Class<?> forClass, String bundlePath, Locale 
locale) throws MissingResourceException {
                this.forClass = forClass;
                this.className = forClass.getSimpleName();
+
                if (bundlePath == null)
-                       throw new RuntimeException("Bundle path was null.");
+                       bundlePath = findBundlePath(forClass);
                if (bundlePath.endsWith(".properties"))
                        throw new RuntimeException("Bundle path should not end 
with '.properties'");
                this.bundlePath = bundlePath;
+
+               if (locale == null)
+                       locale = Locale.getDefault();
+
                this.creationThreadId = Thread.currentThread().getId();
                ClassLoader cl = forClass.getClassLoader();
                ResourceBundle trb = null;
@@ -214,7 +190,7 @@ public class MessageBundle extends ResourceBundle {
         */
        public MessageBundle addSearchPath(Class<?> forClass, String 
bundlePath) {
                assertSameThread(creationThreadId, "This method can only be 
called from the same thread that created the object.");
-               MessageBundle srb = new MessageBundle(forClass, bundlePath);
+               MessageBundle srb = new MessageBundle(forClass, bundlePath, 
null);
                if (srb.rb != null) {
                        allKeys.addAll(srb.keySet());
                        searchBundles.add(srb);
@@ -372,10 +348,15 @@ public class MessageBundle extends ResourceBundle {
        /**
         * Returns the resource bundle for the specified locale.
         *
-        * @param locale The client locale.
+        * @param locale
+        *      The client locale.
+        *      <br>If <jk>null</jk>, assumes the default locale.
         * @return The resource bundle for the specified locale.  Never 
<jk>null</jk>.
         */
        public MessageBundle getBundle(Locale locale) {
+               if (locale == null)
+                       locale = Locale.getDefault();
+
                MessageBundle mb = localizedBundles.get(locale);
                if (mb != null)
                        return mb;
@@ -391,4 +372,27 @@ public class MessageBundle extends ResourceBundle {
                localizedBundles.putIfAbsent(locale, mb);
                return localizedBundles.get(locale);
        }
+
+       private static final String findBundlePath(Class<?> forClass) {
+               String path = forClass.getName();
+               if (tryBundlePath(forClass, path))
+                       return path;
+               path = forClass.getPackage().getName() + ".nls." + 
forClass.getSimpleName();
+               if (tryBundlePath(forClass, path))
+                       return path;
+               path = forClass.getPackage().getName() + ".i18n." + 
forClass.getSimpleName();
+               if (tryBundlePath(forClass, path))
+                       return path;
+               throw new MissingResourceException("Could not find bundle path 
for class ", forClass.getName(), null);
+       }
+
+       private static final boolean tryBundlePath(Class<?> c, String path) {
+               try {
+                       path = c.getName();
+                       ResourceBundle.getBundle(path, Locale.getDefault(), 
c.getClassLoader());
+                       return true;
+               } catch (MissingResourceException e) {
+                       return false;
+               }
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceFinder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceFinder.java
index 2749250..a41f3a7 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceFinder.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceFinder.java
@@ -40,7 +40,7 @@ public interface ResourceFinder {
        public static final class Null implements ResourceFinder {
                @Override
                public InputStream findResource(Class<?> baseClass, String 
name, Locale locale) throws IOException {
-                       throw new NoSuchMethodError();
+                       return null;
                }
        }
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceManager.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceManager.java
index 6506cd3..e3527bc 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceManager.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/ResourceManager.java
@@ -64,7 +64,7 @@ public final class ResourceManager {
         * @param baseClass The default class to use for retrieving resources 
from the classpath.
         */
        public ResourceManager(Class<?> baseClass) {
-               this(baseClass, new BasicResourceFinder(), false);
+               this(baseClass, BasicResourceFinder.INSTANCE, false);
        }
 
        /**
@@ -87,24 +87,9 @@ public final class ResourceManager {
         * @throws IOException Thrown by underlying stream.
         */
        public InputStream getStream(String name, Locale locale) throws 
IOException {
-               return getStream(baseClass, name, locale);
-       }
-
-       /**
-        * Finds the resource with the given name for the specified locale and 
returns it as an input stream.
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the base class passed in through the 
constructor of this class.
-        * @param name Name of the desired resource.
-        * @param locale The locale.  Can be <jk>null</jk>.
-        * @return An input stream to the object, or <jk>null</jk> if the 
resource could not be found.
-        * @throws IOException Thrown by underlying stream.
-        */
-       public InputStream getStream(Class<?> baseClass, String name, Locale 
locale) throws IOException {
 
-               if (baseClass == null)
-                       baseClass = this.baseClass;
+               if (isEmpty(name))
+                       return null;
 
                if (! useCache)
                        return resourceFinder.findResource(baseClass, name, 
locale);
@@ -131,21 +116,7 @@ public final class ResourceManager {
         * @throws IOException Thrown by underlying stream.
         */
        public String getString(String name) throws IOException {
-               return getString(baseClass, name, null);
-       }
-
-       /**
-        * Finds the resource with the given name and converts it to a simple 
string.
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the base class passed in through the 
constructor of this class.
-        * @param name Name of the desired resource.
-        * @return The resource converted to a string, or <jk>null</jk> if the 
resource could not be found.
-        * @throws IOException Thrown by underlying stream.
-        */
-       public String getString(Class<?> baseClass, String name) throws 
IOException {
-               return getString(baseClass, name, null);
+               return getString(name, null);
        }
 
        /**
@@ -157,24 +128,9 @@ public final class ResourceManager {
         * @throws IOException Thrown by underlying stream.
         */
        public String getString(String name, Locale locale) throws IOException {
-               return getString(baseClass, name, locale);
-       }
 
-       /**
-        * Finds the resource with the given name and converts it to a simple 
string.
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the base class passed in through the 
constructor of this class.
-        * @param name Name of the desired resource.
-        * @param locale The locale.  Can be <jk>null</jk>.
-        * @return The resource converted to a string, or <jk>null</jk> if the 
resource could not be found.
-        * @throws IOException Thrown by underlying stream.
-        */
-       public String getString(Class<?> baseClass, String name, Locale locale) 
throws IOException {
-
-               if (baseClass == null)
-                       baseClass = this.baseClass;
+               if (isEmpty(name))
+                       return null;
 
                if (! useCache) {
                        try (InputStream is = 
resourceFinder.findResource(baseClass, name, locale)) {
@@ -201,24 +157,17 @@ public final class ResourceManager {
         * @param c The class type of the POJO to create.
         * @param parser The parser to use to parse the stream.
         * @param name The resource name (e.g. "htdocs/styles.css").
-        * @param locale
-        *      Optional locale.
-        *      <br>If <jk>null</jk>, won't look for localized file names.
         * @return The parsed resource, or <jk>null</jk> if the resource could 
not be found.
         * @throws IOException Thrown by underlying stream.
         * @throws ParseException If stream could not be parsed using the 
specified parser.
         */
-       public <T> T getResource(Class<T> c, Parser parser, String name, Locale 
locale) throws IOException, ParseException {
-               return getResource(null, c, parser, name, locale);
+       public <T> T getResource(Class<T> c, Parser parser, String name) throws 
IOException, ParseException {
+               return getResource(c, parser, name, null);
        }
 
        /**
-        * Same as {@link #getResource(Class, Parser, String, Locale)}, except 
overrides the class used
-        * for retrieving the classpath resource.
+        * Reads the input stream and parses it into a POJO using the specified 
parser.
         *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the REST resource class.
         * @param c The class type of the POJO to create.
         * @param parser The parser to use to parse the stream.
         * @param name The resource name (e.g. "htdocs/styles.css").
@@ -229,8 +178,9 @@ public final class ResourceManager {
         * @throws IOException Thrown by underlying stream.
         * @throws ParseException If stream could not be parsed using the 
specified parser.
         */
-       public <T> T getResource(Class<?> baseClass, Class<T> c, Parser parser, 
String name, Locale locale) throws IOException, ParseException {
-               InputStream is = getStream(baseClass, name, locale);
+       public <T> T getResource(Class<T> c, Parser parser, String name, Locale 
locale) throws IOException, ParseException {
+
+               InputStream is = getStream(name, locale);
                if (is == null)
                        return null;
                try (Closeable in = parser.isReaderParser() ? new 
InputStreamReader(is, UTF8) : is) {
@@ -254,7 +204,7 @@ public final class ResourceManager {
 
                @Override
                public boolean equals(Object o) {
-                       return (o instanceof ResourceKey) && eq(this, 
(ResourceKey)o, (x,y)->eq(x.name, y.name) && eq(x.locale, y.locale));
+                       return eq(this, (ResourceKey)o, (x,y)->eq(x.name, 
y.name) && eq(x.locale, y.locale));
                }
        }
 }
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
index 4724eeb..b54aea9 100755
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
@@ -111,7 +111,7 @@ public class Microservice implements ConfigEventListener {
        }
 
 
-       final MessageBundle messages = MessageBundle.create(Microservice.class);
+       final MessageBundle messages = MessageBundle.of(Microservice.class);
 
        
//-----------------------------------------------------------------------------------------------------------------
        // Properties set in constructor
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ConfigCommand.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ConfigCommand.java
index 33232e6..c5d2a47 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ConfigCommand.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ConfigCommand.java
@@ -25,7 +25,7 @@ import org.apache.juneau.cp.*;
  */
 public class ConfigCommand extends ConsoleCommand {
 
-       private final MessageBundle mb = 
MessageBundle.create(ConfigCommand.class, "Messages");
+       private final MessageBundle mb = MessageBundle.of(ConfigCommand.class, 
"Messages");
 
        @Override /* ConsoleCommand */
        public String getName() {
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
index 5cb2216..03914c9 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/ExitCommand.java
@@ -24,7 +24,7 @@ import org.apache.juneau.cp.*;
  */
 public class ExitCommand extends ConsoleCommand {
 
-       private final MessageBundle mb = 
MessageBundle.create(ExitCommand.class, "Messages");
+       private final MessageBundle mb = MessageBundle.of(ExitCommand.class, 
"Messages");
 
        @Override /* ConsoleCommand */
        public String getName() {
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/HelpCommand.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/HelpCommand.java
index 34610b6..f4f7318 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/HelpCommand.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/HelpCommand.java
@@ -24,7 +24,7 @@ import org.apache.juneau.cp.*;
  */
 public class HelpCommand extends ConsoleCommand {
 
-       private final MessageBundle mb = 
MessageBundle.create(HelpCommand.class, "Messages");
+       private final MessageBundle mb = MessageBundle.of(HelpCommand.class, 
"Messages");
 
        @Override /* ConsoleCommand */
        public String getName() {
diff --git 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/RestartCommand.java
 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/RestartCommand.java
index 64108f0..4a4bc4e 100644
--- 
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/RestartCommand.java
+++ 
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/console/RestartCommand.java
@@ -24,7 +24,7 @@ import org.apache.juneau.cp.*;
  */
 public class RestartCommand extends ConsoleCommand {
 
-       private final MessageBundle mb = 
MessageBundle.create(RestartCommand.class, "Messages");
+       private final MessageBundle mb = MessageBundle.of(RestartCommand.class, 
"Messages");
 
        @Override /* ConsoleCommand */
        public String getName() {
diff --git 
a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java
 
b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java
index 14f16a5..538f020 100644
--- 
a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java
+++ 
b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java
@@ -88,7 +88,7 @@ public class JettyMicroservice extends Microservice {
                        .join();
        }
 
-       final MessageBundle messages = 
MessageBundle.create(JettyMicroservice.class);
+       final MessageBundle messages = 
MessageBundle.of(JettyMicroservice.class);
 
        
//-----------------------------------------------------------------------------------------------------------------
        // Properties set in constructor
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 62f727f..386e4f3 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -801,11 +801,8 @@ public final class RestContext extends BeanContext {
         *      <li class='jc'>{@link RestContext}
         *      <ul>
         *              <li class='jm'>{@link 
#getClasspathResource(String,Locale) getClasspathResource(String,Locale)}
-        *              <li class='jm'>{@link 
#getClasspathResource(Class,String,Locale) 
getClasspathResource(Class,String,Locale)}
         *              <li class='jm'>{@link 
#getClasspathResource(Class,MediaType,String,Locale) 
getClasspathResource(Class,MediaType,String,Locale)}
-        *              <li class='jm'>{@link 
#getClasspathResource(Class,Class,MediaType,String,Locale) 
getClasspathResource(Class,Class,MediaType,String,Locale)}
         *              <li class='jm'>{@link 
#getClasspathResourceAsString(String,Locale) 
getClasspathResourceAsString(String,Locale)}
-        *              <li class='jm'>{@link 
#getClasspathResourceAsString(Class,String,Locale) 
getClasspathResourceAsString(Class,String,Locale)}
         *              <li class='jm'>{@link #resolveStaticFile(String) 
resolveStaticFile(String)}
         *      </ul>
         *      <li class='jc'>{@link RestRequest}
@@ -3855,9 +3852,9 @@ public final class RestContext extends BeanContext {
 
                        MessageBundleLocation[] mbl = 
getInstanceArrayProperty(REST_messages, MessageBundleLocation.class, new 
MessageBundleLocation[0]);
                        if (mbl.length == 0)
-                               msgs = new MessageBundle(rci.inner(), "");
+                               msgs = new MessageBundle(rci.inner(), "", null);
                        else {
-                               msgs = new MessageBundle(mbl[0] != null ? 
mbl[0].baseClass : rci.inner(), mbl[0].bundlePath);
+                               msgs = new MessageBundle(mbl[0] != null ? 
mbl[0].baseClass : rci.inner(), mbl[0].bundlePath, null);
                                for (int i = 1; i < mbl.length; i++)
                                        msgs.addSearchPath(mbl[i] != null ? 
mbl[i].baseClass : rci.inner(), mbl[i].bundlePath);
                        }
@@ -4335,38 +4332,6 @@ public final class RestContext extends BeanContext {
        }
 
        /**
-        * Same as {@link #getClasspathResource(String, Locale)}, but allows 
you to override the class used for looking
-        * up the classpath resource.
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bcode w800'>
-        *      <jc>// A rest method that (unsafely!) returns the contents of a 
localized file </jc>
-        *      <jc>// from the classpath.</jc>
-        *      <ja>@RestMethod</ja>(path=<js>"/foo"</js>)
-        *      <jk>public</jk> Object myMethod(RestRequest req, 
<ja>@Query</ja>(<js>"file"</js>) String file) {
-        *              <jk>return</jk> 
getContext().getClasspathResource(SomeOtherClass.<jk>class</jk>, file, 
req.getLocale());
-        *      }
-        * </p>
-        *
-        * <ul class='seealso'>
-        *      <li class='jf'>{@link #REST_classpathResourceFinder}
-        * </ul>
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the REST resource class.
-        * @param name The resource name.
-        * @param locale
-        *      Optional locale.
-        *      <br>If <jk>null</jk>, won't look for localized file names.
-        * @return An input stream of the resource, or <jk>null</jk> if the 
resource could not be found.
-        * @throws IOException Thrown by underlying stream.
-        */
-       public InputStream getClasspathResource(Class<?> baseClass, String 
name, Locale locale) throws IOException {
-               return staticResourceManager.getStream(baseClass, name, locale);
-       }
-
-       /**
         * Reads the input stream from {@link #getClasspathResource(String, 
Locale)} into a String.
         *
         * <h5 class='section'>Example:</h5>
@@ -4394,37 +4359,6 @@ public final class RestContext extends BeanContext {
                return staticResourceManager.getString(name, locale);
        }
 
-       /**
-        * Same as {@link #getClasspathResourceAsString(String, Locale)}, but 
allows you to override the class used for looking
-        * up the classpath resource.
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bcode w800'>
-        *      <jc>// A rest method that (unsafely!) returns the contents of a 
localized file </jc>
-        *      <jc>// from the classpath.</jc>
-        *      <ja>@RestMethod</ja>(path=<js>"/foo"</js>)
-        *      <jk>public</jk> String myMethod(RestRequest req, 
<ja>@Query</ja>(<js>"file"</js>) String file) {
-        *              <jk>return</jk> 
getContext().getClasspathResourceAsString(SomeOtherClass.<jk>class</jk>, file, 
req.getLocale());
-        *      }
-        * </p>
-        *
-        * <ul class='seealso'>
-        *      <li class='jf'>{@link #REST_classpathResourceFinder}
-        * </ul>
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the REST resource class.
-        * @param name The resource name.
-        * @param locale
-        *      Optional locale.
-        *      <br>If <jk>null</jk>, won't look for localized file names.
-        * @return The contents of the stream as a string, or <jk>null</jk> if 
the resource could not be found.
-        * @throws IOException If resource could not be found.
-        */
-       public String getClasspathResourceAsString(Class<?> baseClass, String 
name, Locale locale) throws IOException {
-               return staticResourceManager.getString(baseClass, name, locale);
-       }
 
        /**
         * Reads the input stream from {@link #getClasspathResource(String, 
Locale)} and parses it into a POJO using the parser
@@ -4458,42 +4392,7 @@ public final class RestContext extends BeanContext {
         * @throws ServletException If the media type was unknown or the input 
could not be parsed into a POJO.
         */
        public <T> T getClasspathResource(Class<T> c, MediaType mediaType, 
String name, Locale locale) throws IOException, ServletException {
-               return getClasspathResource(null, c, mediaType, name, locale);
-       }
-
-       /**
-        * Same as {@link #getClasspathResource(Class, MediaType, String, 
Locale)}, except overrides the class used
-        * for retrieving the classpath resource.
-        *
-        * <ul class='seealso'>
-        *      <li class='jf'>{@link #REST_classpathResourceFinder}
-        * </ul>
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bcode w800'>
-        *      <jc>// A rest method that (unsafely!) returns the contents of a 
localized file </jc>
-        *      <jc>// from the classpath parsed as an array of beans.</jc>
-        *      <ja>@RestMethod</ja>(path=<js>"/foo"</js>)
-        *      <jk>public</jk> MyBean[] myMethod(RestRequest req, 
<ja>@Query</ja>(<js>"file"</js>) String file) {
-        *              <jk>return</jk> 
getContext().getClasspathResource(SomeOtherClass.<jk>class</jk>, 
MyBean[].<jk>class</jk>, <jsf>JSON</jsf>, file, req.getLocale());
-        *      }
-        * </p>
-        *
-        * @param baseClass
-        *      Overrides the default class to use for retrieving the classpath 
resource.
-        *      <br>If <jk>null</jk>, uses the REST resource class.
-        * @param c The class type of the POJO to create.
-        * @param mediaType The media type of the data in the stream (e.g. 
<js>"text/json"</js>)
-        * @param name The resource name (e.g. "htdocs/styles.css").
-        * @param locale
-        *      Optional locale.
-        *      <br>If <jk>null</jk>, won't look for localized file names.
-        * @return The parsed resource, or <jk>null</jk> if the resource could 
not be found.
-        * @throws IOException Thrown by underlying stream.
-        * @throws ServletException If the media type was unknown or the input 
could not be parsed into a POJO.
-        */
-       public <T> T getClasspathResource(Class<?> baseClass, Class<T> c, 
MediaType mediaType, String name, Locale locale) throws IOException, 
ServletException {
-               InputStream is = getClasspathResource(baseClass, name, locale);
+               InputStream is = getClasspathResource(name, locale);
                if (is == null)
                        return null;
                try {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index 1270102..0e5074a 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -101,7 +101,7 @@ public abstract class RestServlet extends HttpServlet 
implements RestCallHandler
                callHandler = new BasicRestCallHandler(context);
                infoProvider = new BasicRestInfoProvider(context);
                callLogger = new BasicRestCallLogger(context);
-               resourceFinder = new BasicResourceFinder();
+               resourceFinder = new RecursiveResourceFinder();
                context.postInit();
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
index 82bfa40..3987b83 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
@@ -24,7 +24,6 @@ import org.apache.juneau.internal.*;
  * The static file resource resolver for a single {@link StaticFileMapping}.
  */
 class StaticFiles {
-       private final Class<?> resourceClass;
        private final String path, location;
        private final Map<String,Object> responseHeaders;
 
@@ -32,7 +31,6 @@ class StaticFiles {
        private final MimetypesFileTypeMap mimetypesFileTypeMap;
 
        StaticFiles(StaticFileMapping sfm, ResourceManager 
staticResourceManager, MimetypesFileTypeMap mimetypesFileTypeMap, 
Map<String,Object> staticFileResponseHeaders) {
-               this.resourceClass = sfm.resourceClass;
                this.path = sfm.path;
                this.location = sfm.location;
                this.responseHeaders = sfm.responseHeaders != null ? 
sfm.responseHeaders : staticFileResponseHeaders;
@@ -49,7 +47,7 @@ class StaticFiles {
                        String remainder = (p.equals(path) ? "" : 
p.substring(path.length()));
                        if (remainder.isEmpty() || remainder.startsWith("/")) {
                                String p2 = location + remainder;
-                               try (InputStream is = 
staticResourceManager.getStream(resourceClass, p2, null)) {
+                               try (InputStream is = 
staticResourceManager.getStream(p2, null)) {
                                        if (is != null) {
                                                int i = p2.lastIndexOf('/');
                                                String name = (i == -1 ? p2 : 
p2.substring(i+1));
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index cf55519..130ace6 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -216,7 +216,7 @@ public class DefaultHandler implements ResponseHandler {
                }
 
                // Non-existent Accept or plain/text can just be serialized 
as-is.
-               if (o != null && (isEmpty(accept) || 
accept.startsWith("text/plain") || accept.equals("*/*"))) {
+               if (o != null && (isEmpty(accept) || 
accept.startsWith("text/plain") || accept.contains("*/*"))) {
                        String out = null;
                        if (isEmpty(res.getContentType()))
                                res.setContentType("text/plain");

Reply via email to