You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@turbine.apache.org by gk...@apache.org on 2021/12/06 15:42:30 UTC

[turbine-core] 02/06: Use streams, collectors and lamda features, cleanup url mapper from pieplien-classic.xml, add datetimeformatter and test

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

gk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/turbine-core.git

commit dd28d3167d8514a6222269b3ace2e8bac599ce98
Author: Georg Kallidis <gk...@apache.org>
AuthorDate: Wed Dec 1 15:30:24 2021 +0000

    Use streams, collectors and lamda features, cleanup url mapper from pieplien-classic.xml, add datetimeformatter and test
---
 conf/test/TurbineURLMapperServiceTest.properties   |   2 +-
 ...e.xml => turbine-classic-pipeline-extended.xml} |   0
 conf/turbine-classic-pipeline.xml                  |   1 -
 src/java/org/apache/turbine/Turbine.java           |   2 +-
 .../org/apache/turbine/modules/ActionEvent.java    |  14 +-
 .../apache/turbine/services/BaseServiceBroker.java |  13 +-
 .../turbine/services/jsp/TurbineJspService.java    |  10 +-
 .../services/pull/util/DateTimeFormatterTool.java  | 200 +++++++++++++++++++
 .../apache/turbine/services/schedule/JobQueue.java |   2 +-
 .../services/security/DefaultUserManager.java      |  12 +-
 .../services/template/TurbineTemplateService.java  |  12 +-
 .../urlmapper/TurbineURLMapperService.java         |  12 +-
 src/java/org/apache/turbine/util/ServletUtils.java |   4 +-
 .../org/apache/turbine/util/uri/TemplateURI.java   |   6 +-
 .../services/pull/util/DateTimeFormatterTest.java  | 215 +++++++++++++++++++++
 .../services/velocity/PathConverterTest.java       |  17 +-
 16 files changed, 460 insertions(+), 62 deletions(-)

diff --git a/conf/test/TurbineURLMapperServiceTest.properties b/conf/test/TurbineURLMapperServiceTest.properties
index 0a7c791..ae3e62d 100644
--- a/conf/test/TurbineURLMapperServiceTest.properties
+++ b/conf/test/TurbineURLMapperServiceTest.properties
@@ -24,7 +24,7 @@
 log4j2.file = log4j2.xml
 
 # resource relative to context
-pipeline.default.descriptor = /conf/turbine-classic-pipeline.xml
+pipeline.default.descriptor = /conf/turbine-classic-pipeline-extended.xml
 
 
 # If module.cache=true, then how large should we make the hashtables
diff --git a/conf/turbine-classic-pipeline.xml b/conf/turbine-classic-pipeline-extended.xml
similarity index 100%
copy from conf/turbine-classic-pipeline.xml
copy to conf/turbine-classic-pipeline-extended.xml
diff --git a/conf/turbine-classic-pipeline.xml b/conf/turbine-classic-pipeline.xml
index 2c2f281..bac4aa7 100644
--- a/conf/turbine-classic-pipeline.xml
+++ b/conf/turbine-classic-pipeline.xml
@@ -26,7 +26,6 @@
     <valve>org.apache.turbine.pipeline.DefaultLoginValve</valve>
     <valve>org.apache.turbine.pipeline.DefaultSessionValidationValve</valve>
     <valve>org.apache.turbine.pipeline.DefaultACLCreationValve</valve>
-    <valve>org.apache.turbine.services.urlmapper.URLMapperValve</valve>
     <valve>org.apache.turbine.pipeline.ExecutePageValve</valve>
     <valve>org.apache.turbine.pipeline.CleanUpValve</valve>
     <valve>org.apache.turbine.pipeline.DetermineRedirectRequestedValve</valve>
diff --git a/src/java/org/apache/turbine/Turbine.java b/src/java/org/apache/turbine/Turbine.java
index 06a5bec..b1f4662 100644
--- a/src/java/org/apache/turbine/Turbine.java
+++ b/src/java/org/apache/turbine/Turbine.java
@@ -465,7 +465,7 @@ public class Turbine extends HttpServlet
                                         .setFileName(confFile)
                                         .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
                                         .setLocationStrategy(new HomeDirectoryLocationStrategy(confPath.getCanonicalPath(), false)));
-                // meta configuration : this may contain any commons configuration: system<>, jndi, env
+                // meta configuration: this may contain any commons configuration: system<>, jndi, env
                 configuration = propertiesBuilder.getConfiguration();
                 break;
             case JSON:
diff --git a/src/java/org/apache/turbine/modules/ActionEvent.java b/src/java/org/apache/turbine/modules/ActionEvent.java
index 5bbc7ff..0b0ad64 100644
--- a/src/java/org/apache/turbine/modules/ActionEvent.java
+++ b/src/java/org/apache/turbine/modules/ActionEvent.java
@@ -25,6 +25,7 @@ import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fulcrum.parser.ParameterParser;
@@ -133,13 +134,12 @@ public abstract class ActionEvent implements Action
 	 */
 	protected Method getMethod(String name, Class<?>[] signature, ParameterParser pp) throws NoSuchMethodException
 	{
-	    StringBuilder cacheKey = new StringBuilder(name);
-	    for (Class<?> clazz : signature)
-	    {
-	        cacheKey.append(':').append(clazz.getCanonicalName());
-	    }
+	    String cacheKey =
+				Arrays.stream(signature)
+						.map(clazz -> ':' + clazz.getCanonicalName())
+						.collect(Collectors.joining("", name, ""));
 
-	    Method method = this.methodCache.get(cacheKey.toString());
+		Method method = this.methodCache.get(cacheKey);
 
 	    if (method == null)
 	    {
@@ -172,7 +172,7 @@ public abstract class ActionEvent implements Action
 	            method = getClass().getMethod(METHOD_NAME_PREFIX + StringUtils.capitalize(tmp), signature);
 	        }
 
-	        Method oldMethod = this.methodCache.putIfAbsent(cacheKey.toString(), method);
+	        Method oldMethod = this.methodCache.putIfAbsent(cacheKey, method);
 	        if (oldMethod != null)
 	        {
 	            method = oldMethod;
diff --git a/src/java/org/apache/turbine/services/BaseServiceBroker.java b/src/java/org/apache/turbine/services/BaseServiceBroker.java
index e1ad728..0d08424 100644
--- a/src/java/org/apache/turbine/services/BaseServiceBroker.java
+++ b/src/java/org/apache/turbine/services/BaseServiceBroker.java
@@ -337,13 +337,8 @@ public abstract class BaseServiceBroker implements ServiceBroker
     public Iterator<String> getServiceNames(String prefix)
     {
         Set<String> keys = new LinkedHashSet<>(mapping.keySet());
-        for(Iterator<String> key = keys.iterator(); key.hasNext();)
-        {
-            if (!key.next().startsWith(prefix))
-            {
-                key.remove();
-            }
-        }
+
+        keys.removeIf(key -> !key.startsWith(prefix));
 
         return keys.iterator();
     }
@@ -508,9 +503,9 @@ public abstract class BaseServiceBroker implements ServiceBroker
             reverseServicesList.add(0, serviceName);
         }
 
-        for (Iterator<String> serviceNames = reverseServicesList.iterator(); serviceNames.hasNext();)
+        for (String s : reverseServicesList)
         {
-            serviceName = serviceNames.next();
+            serviceName = s;
             log.info("Shutting down service: {}", serviceName);
             shutdownService(serviceName);
         }
diff --git a/src/java/org/apache/turbine/services/jsp/TurbineJspService.java b/src/java/org/apache/turbine/services/jsp/TurbineJspService.java
index 114e79a..3e7a636 100644
--- a/src/java/org/apache/turbine/services/jsp/TurbineJspService.java
+++ b/src/java/org/apache/turbine/services/jsp/TurbineJspService.java
@@ -23,6 +23,7 @@ package org.apache.turbine.services.jsp;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.http.HttpServletRequest;
@@ -242,14 +243,7 @@ public class TurbineJspService
     @Override
     public boolean templateExists(String template)
     {
-        for (String templatePath : templatePaths)
-        {
-            if (templateExists(templatePath, template))
-            {
-                return true;
-            }
-        }
-        return false;
+        return Arrays.stream(templatePaths).anyMatch(templatePath -> templateExists(templatePath, template));
     }
 
     /**
diff --git a/src/java/org/apache/turbine/services/pull/util/DateTimeFormatterTool.java b/src/java/org/apache/turbine/services/pull/util/DateTimeFormatterTool.java
new file mode 100644
index 0000000..118f662
--- /dev/null
+++ b/src/java/org/apache/turbine/services/pull/util/DateTimeFormatterTool.java
@@ -0,0 +1,200 @@
+package org.apache.turbine.services.pull.util;
+
+
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.services.pull.ApplicationTool;
+
+/**
+ * This pull tool is used to format {@link TemporalAccessor} and
+ * {@link #map(String, DateTimeFormatter, Locale)} (different falvors)
+ * objects into strings.
+ *
+ * The methods may throw {@link java.time.temporal.UnsupportedTemporalTypeException} or
+ * {@link DateTimeParseException}.
+ * if the source and the target format do not match appropriately.
+ *
+ */
+public class DateTimeFormatterTool
+        implements ApplicationTool
+{
+    /** Default date format. find supporrted formats in {@link DateTimeFormatterTool} */
+    private static final String DATE_TIME_FORMAT_DEFAULT = "MM/dd/yyyy";
+
+    /**
+     * Property tag for the date format that is to be used for the web
+     * application.
+     */
+    private static final String DATE_TIME_FORMAT_KEY = "tool.datetimeTool.format";
+
+    private String dateFormat = null;
+    
+    private java.time.format.DateTimeFormatter defaultFormat = null;
+
+    public java.time.format.DateTimeFormatter getDefaultFormat()
+    {
+        return defaultFormat;
+    }
+
+    private static final Logger log = LogManager.getLogger(DateTimeFormatterTool.class);
+
+    /**
+     * Initialize the application tool. The data parameter holds a different
+     * type depending on how the tool is being instantiated:
+     * <ul>
+     * <li>For global tools data will be null</li>
+     * <li>For request tools data will be of type RunData</li>
+     * <li>For session and persistent tools data will be of type User</li>
+     * </ul>
+     *
+     * the {@link #defaultFormat} from {@link #dateFormat} with default Locale {@link Locale#getDefault()} and
+     * Default zone: {@link ZoneId#systemDefault()}
+     *
+     * @param data initialization data
+     */
+    @Override
+    public void init(Object data)
+    {
+        dateFormat = Turbine.getConfiguration()
+                .getString(DATE_TIME_FORMAT_KEY, DATE_TIME_FORMAT_DEFAULT);
+        defaultFormat = DateTimeFormatter.ofPattern(dateFormat)
+                .withLocale(Locale.getDefault()).withZone(ZoneId.systemDefault());
+
+        log.info("Initialized DateTimeFormatterTool with pattern {}, locale {} and zone {}",
+                dateFormat, defaultFormat.getLocale(), defaultFormat.getZone());
+    }
+
+    /**
+     * Refresh the application tool. This is
+     * necessary for development work where you
+     * probably want the tool to refresh itself
+     * if it is using configuration information
+     * that is typically cached after initialization
+     */
+    @Override
+    public void refresh()
+    {
+        // empty
+    }
+    
+    /**
+     * Formats the given datetime as a String with the #{@link DateTimeFormatterTool#defaultFormat}.
+     * using the default date format.
+     *
+     * @param the {@link TemporalAccessor to format
+     * @return String value of the date
+     */
+    public <T extends TemporalAccessor> String format(T temporalAccessor)
+    {
+        return defaultFormat.format(temporalAccessor);
+    }
+
+    public <T extends TemporalAccessor> String format(T temporalAccessor, String dateFormatString)
+    {
+        return format(temporalAccessor, dateFormatString, null);
+    }
+    /**
+     * Formats the given date as a String.
+     *
+     * @param the TimeDate date to format
+     * @param dateFormatString format string to use.  See java.text.SimpleDateFormat
+     * for details.
+     * @return String value of the date
+     */
+    public <T extends TemporalAccessor> String format(T temporalAccessor, String dateFormatString, Locale locale)
+    {
+        String result = null;
+
+        if (StringUtils.isEmpty(dateFormatString) || temporalAccessor == null)
+        {
+            result = "";
+        }
+        else
+        {
+            DateTimeFormatter dtf = DateTimeFormatter.ofPattern(dateFormatString);
+            if (locale != null)
+            {
+                dtf.withLocale(locale);
+            }
+            result = dtf.format(temporalAccessor);
+        }
+        return result;
+    }
+    
+    /**
+     * Maps from an incoming format to an outgoing format {@link java.time.format.DateTimeFormatter}.
+     * @param src the formatted datetime
+     * @param outgoingFormat {@link java.time.format.DateTimeFormatter}
+     * @param locale  Locale, if needed for outgoing formatting, no default.
+     * @param incomingFormat {@link java.time.format.DateTimeFormatter}, optional, default is {@link #defaultFormat}.
+     * @return the newly mapped
+     */
+    public String map( String src, java.time.format.DateTimeFormatter outgoingFormat, Locale locale, 
+            java.time.format.DateTimeFormatter incomingFormat)
+    {
+        if (StringUtils.isEmpty(src) || outgoingFormat == null)
+        {
+            return "";
+        }
+        if (incomingFormat == null)
+        {
+            incomingFormat = defaultFormat;
+        }
+        if (incomingFormat.equals( outgoingFormat )) {
+            return "";
+        }
+        if (locale != null)
+        {
+            outgoingFormat = outgoingFormat.withLocale( locale );
+            //incomingFormat = incomingFormat.withLocale( locale );
+        }
+        return  outgoingFormat.format(
+                incomingFormat.parse( src ));
+    }
+
+    /**
+     * Uses as incoming format {@link #defaultFormat} and no locale.
+     * @param src
+     * @param outgoingFormat
+     * @return the formatted string
+     *
+     * @throws java.time.temporal.UnsupportedTemporalTypeException
+     */
+    public String mapTo( String src, DateTimeFormatter outgoingFormat )
+    {
+        return map( src, outgoingFormat, null, defaultFormat );
+    }
+
+    /**
+     * Uses as outgoing {@link DateTimeFormatter} {@link #defaultFormat} and no locale.
+     * @param src the datetime formatted string
+     * @param incomingFormat the format of this string
+     * @return the date time formatted using the {@link #defaultFormat}.
+     */
+    public String mapFrom( String src, DateTimeFormatter incomingFormat )
+    {
+        return map( src, defaultFormat, null, incomingFormat );
+    }
+
+    /**
+     * Uses as incoming {@link DateTimeFormatter}  {@link #defaultFormat}.
+     * @param src the datetime formatted string
+     * @param outgoingFormat the format to which this string should be formatted.
+     * @param locale
+     * @return the newly formatted date time string
+     *
+     * @throws java.time.temporal.UnsupportedTemporalTypeException
+     */
+    public String map( String src,  DateTimeFormatter outgoingFormat, Locale locale )
+    {
+        return map( src, outgoingFormat, locale, defaultFormat );
+    }
+}
diff --git a/src/java/org/apache/turbine/services/schedule/JobQueue.java b/src/java/org/apache/turbine/services/schedule/JobQueue.java
index f9634ca..1ef9142 100644
--- a/src/java/org/apache/turbine/services/schedule/JobQueue.java
+++ b/src/java/org/apache/turbine/services/schedule/JobQueue.java
@@ -187,6 +187,6 @@ public class JobQueue<J extends JobEntry>
             return time1.compareTo(time2);
         };
 
-        Collections.sort(queue, aComparator);
+        queue.sort(aComparator);
     }
 }
diff --git a/src/java/org/apache/turbine/services/security/DefaultUserManager.java b/src/java/org/apache/turbine/services/security/DefaultUserManager.java
index 9779a58..b93b06f 100644
--- a/src/java/org/apache/turbine/services/security/DefaultUserManager.java
+++ b/src/java/org/apache/turbine/services/security/DefaultUserManager.java
@@ -23,6 +23,7 @@ package org.apache.turbine.services.security;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import org.apache.commons.configuration2.Configuration;
 import org.apache.fulcrum.factory.FactoryService;
@@ -256,13 +257,12 @@ public class DefaultUserManager implements UserManager
             throws DataBackendException
     {
         UserSet<org.apache.fulcrum.security.entity.User> uset = umDelegate.getAllUsers();
-        List<User> userList = new ArrayList<>();
 
-        for (org.apache.fulcrum.security.entity.User u : uset)
-        {
-            TurbineUser tu = (TurbineUser)u;
-            userList.add(wrap(tu));
-        }
+        List<User> userList = uset.stream()
+                .map(u -> (TurbineUser) u)
+                .map(this::wrap)
+                .map(u -> (User)u)
+                .collect(Collectors.toList());
 
         return userList;
     }
diff --git a/src/java/org/apache/turbine/services/template/TurbineTemplateService.java b/src/java/org/apache/turbine/services/template/TurbineTemplateService.java
index d12d112..9ef3f91 100644
--- a/src/java/org/apache/turbine/services/template/TurbineTemplateService.java
+++ b/src/java/org/apache/turbine/services/template/TurbineTemplateService.java
@@ -22,6 +22,7 @@ package org.apache.turbine.services.template;
 
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -650,14 +651,9 @@ public class TurbineTemplateService
     @Deprecated
     public boolean templateExists(String template, String[] templatePaths)
     {
-        for (String templatePath : templatePaths)
-        {
-            if (new File(templatePath, template).exists())
-            {
-                return true;
-            }
-        }
-        return false;
+        return Arrays.stream(templatePaths)
+                .anyMatch(templatePath -> new File(templatePath, template)
+                        .exists());
     }
 
     /**
diff --git a/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java b/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java
index 7ca791a..2377787 100644
--- a/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java
+++ b/src/java/org/apache/turbine/services/urlmapper/TurbineURLMapperService.java
@@ -231,16 +231,16 @@ public class TurbineURLMapperService
                                 pp.setString(group.getKey(), matcher.group(group.getValue().intValue())));
 
                 // add implicit parameters
-                urlMap.getImplicitParameters().entrySet().forEach(e ->
-                        pp.add(e.getKey(), e.getValue()));
+                urlMap.getImplicitParameters()
+                        .forEach((key1, value1) -> pp.add(key1, value1));
 
                 // add override parameters
-                urlMap.getOverrideParameters().entrySet().forEach(e ->
-                        pp.setString(e.getKey(), e.getValue()));
+                urlMap.getOverrideParameters()
+                        .forEach((key, value) -> pp.setString(key, value));
 
                 // remove ignore parameters
-                urlMap.getIgnoreParameters().keySet().forEach(k ->
-                        pp.remove(k));
+                urlMap.getIgnoreParameters().keySet()
+                        .forEach(pp::remove);
                 
                 log.debug("mapped {} params from url {} ", pp.getKeys().length, url);
 
diff --git a/src/java/org/apache/turbine/util/ServletUtils.java b/src/java/org/apache/turbine/util/ServletUtils.java
index 10e0200..c93fee1 100644
--- a/src/java/org/apache/turbine/util/ServletUtils.java
+++ b/src/java/org/apache/turbine/util/ServletUtils.java
@@ -24,6 +24,7 @@ import java.io.File;
 
 
 import java.util.StringTokenizer;
+import java.util.stream.Stream;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
@@ -66,8 +67,7 @@ public class ServletUtils
         final String expandedText;
 
         // attempt to make it relative
-        if (!text.startsWith("/") && !text.startsWith("./")
-                && !text.startsWith("\\") && !text.startsWith(".\\"))
+        if (Stream.of("/", "./", "\\", ".\\").noneMatch(text::startsWith))
         {
             StringBuilder sb = new StringBuilder();
             sb.append("./");
diff --git a/src/java/org/apache/turbine/util/uri/TemplateURI.java b/src/java/org/apache/turbine/util/uri/TemplateURI.java
index 58758fa..3fe47b1 100644
--- a/src/java/org/apache/turbine/util/uri/TemplateURI.java
+++ b/src/java/org/apache/turbine/util/uri/TemplateURI.java
@@ -25,6 +25,8 @@ import org.apache.fulcrum.parser.ParameterParser;
 import org.apache.turbine.util.RunData;
 import org.apache.turbine.util.ServerData;
 
+import java.util.stream.Stream;
+
 /**
  * This class allows you to keep all the information needed for a single
  * link at one place. It keeps your query data, path info, the server
@@ -268,9 +270,7 @@ public class TemplateURI
         {
             String key = (String) name;
 
-            if (!key.equalsIgnoreCase(CGI_ACTION_PARAM) &&
-                    !key.equalsIgnoreCase(CGI_SCREEN_PARAM) &&
-                    !key.equalsIgnoreCase(CGI_TEMPLATE_PARAM))
+            if (Stream.of(CGI_ACTION_PARAM, CGI_SCREEN_PARAM, CGI_TEMPLATE_PARAM).noneMatch(key::equalsIgnoreCase))
             {
                 String[] values = pp.getStrings(key);
                 if(values != null)
diff --git a/src/test/org/apache/turbine/services/pull/util/DateTimeFormatterTest.java b/src/test/org/apache/turbine/services/pull/util/DateTimeFormatterTest.java
new file mode 100644
index 0000000..49e9429
--- /dev/null
+++ b/src/test/org/apache/turbine/services/pull/util/DateTimeFormatterTest.java
@@ -0,0 +1,215 @@
+package org.apache.turbine.services.pull.util;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+
+import org.apache.turbine.test.BaseTestCase;
+import org.apache.turbine.util.TurbineConfig;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class for DateTimeFormatter.
+ *
+ */
+public class DateTimeFormatterTest
+{
+
+    private static DateTimeFormatterTool df;
+
+    private static TurbineConfig tc = null;
+
+    @BeforeAll
+    public static void setup() 
+    {
+        // required to initialize defaults
+        tc = new TurbineConfig(
+                        ".",
+                        "/conf/test/TestFulcrumComponents.properties");
+        tc.initialize();
+        df = new DateTimeFormatterTool();
+        df.init(null);
+    }
+
+    @AfterAll
+    public static void tearDown()
+    {
+        tc.dispose();
+    }
+    /*
+     * Class under test for String format(Date, String)
+     */
+    @Test public void testFormatDateString()
+    {
+        LocalDateTime ldt = LocalDateTime.now();
+        int day = ldt.get(ChronoField.DAY_OF_MONTH);
+        int month = ldt.get(ChronoField.MONTH_OF_YEAR) ; // one based
+        int year = ldt.get(ChronoField.YEAR);
+        
+        String dayString = (day < 10 ? "0" : "") + day;
+        String monthString = (month < 10 ? "0" : "") + month;
+        String ddmmyyyy = dayString + "/" + monthString + "/" + year;
+        assertEquals(ddmmyyyy, df.format(ldt, "dd/MM/yyyy"));
+
+        String mmddyyyy = "" + monthString + "/" + dayString + "/" + year;
+        assertEquals(mmddyyyy, df.format(ldt, "MM/dd/yyyy"));
+    }
+    
+    @Test public void testFormatZonedDateString()
+    {  
+        ZonedDateTime zdt = ZonedDateTime.now();
+        int day = zdt.get(ChronoField.DAY_OF_MONTH);
+        int month = zdt.get(ChronoField.MONTH_OF_YEAR) ; // one based
+        int year = zdt.get(ChronoField.YEAR);
+        zdt = zdt.truncatedTo( ChronoUnit.MINUTES );
+
+        String dayString = (day < 10 ? "0" : "") + day;
+        String monthString = (month < 10 ? "0" : "") + month;
+        String ddmmyyyy = dayString + "/" + monthString + "/" + year;
+        assertEquals(ddmmyyyy, df.format(zdt, "dd/MM/yyyy"));
+
+        int hours = zdt.get(ChronoField.HOUR_OF_DAY);
+        int mins =zdt.get(ChronoField.MINUTE_OF_HOUR);
+        int secs = zdt.get(ChronoField.SECOND_OF_MINUTE);
+        String hourString = (hours < 10 ? "0" : "") + hours;
+        String minsString = (mins < 10 ? "0" : "") + mins;
+        String secsString = (secs < 10 ? "0" : "") + secs;
+
+        String zone = zdt.getZone().getId();
+        String offset = zdt.getOffset().getId();
+        // offset formatting not easy matchable, removed
+        String mmddyyyy = "" + monthString + "/" + dayString + "/" + year + " " + hourString + ":" + minsString + ":"+ secsString + " " + zone;
+        // zone + offset format, removed offset ZZZ
+        assertEquals(mmddyyyy, df.format(zdt, "MM/dd/yyyy HH:mm:ss VV"));
+    }
+
+    /*
+     * Class under test for String mapFrom(String, DateTimeFormatter)
+     */
+    @Test public void testDefaultMapFromInstant()
+    {
+        DateTimeFormatter incomingFormat = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault());
+        // may throws an DateTimeParseException
+        Instant now = Instant.now().truncatedTo( ChronoUnit.MINUTES );
+        String source = incomingFormat.format(now);
+
+        TemporalAccessor dateTimeFromInstant = incomingFormat.parse(source);
+        int day = dateTimeFromInstant.get(ChronoField.DAY_OF_MONTH);
+        int month = dateTimeFromInstant.get(ChronoField.MONTH_OF_YEAR) ; // one based
+        int year = dateTimeFromInstant.get(ChronoField.YEAR);
+
+        String dayString = (day < 10 ? "0" : "") + day;
+        String monthString = (month < 10 ? "0" : "") + month;
+        String mmddyyyy = "" + monthString + "/" + dayString + "/" + year;
+        assertEquals(mmddyyyy, df.mapFrom(source,incomingFormat));
+    }
+
+    /*
+     * Class under test for String format(Date, "")
+     */
+    @Test public void testDefaultMapInstant()
+    {
+        String source = df.format(Instant.now());
+
+        TemporalAccessor dateTimeFromInstant = df.getDefaultFormat().parse(source);
+
+        int day = dateTimeFromInstant.get(ChronoField.DAY_OF_MONTH);
+        int month = dateTimeFromInstant.get(ChronoField.MONTH_OF_YEAR) ; // one based
+        int year = dateTimeFromInstant.get(ChronoField.YEAR);
+
+        String dayString = (day < 10 ? "0" : "") + day;
+        String monthString = (month < 10 ? "0" : "") + month;
+        String yyyymmdd = year + "-" + monthString + "-" + dayString;
+
+        // caution we are mapping from the DateTimeFormatterTool defaultFormat7-pattern without time!
+        // ISO_DATE_TIME will throw an error:
+        // java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
+        DateTimeFormatter outgoingFormat = DateTimeFormatter.ISO_DATE.withZone(ZoneId.systemDefault());
+        assertEquals(yyyymmdd, df.mapTo(source,outgoingFormat));
+
+        outgoingFormat = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.systemDefault());
+        assertEquals(yyyymmdd, df.mapTo(source,outgoingFormat));
+
+        // ISO_OFFSET_DATE :  Unsupported field: OffsetSeconds
+        // ISO_INSTANT; Unsupported field: InstantSeconds
+        yyyymmdd = year +  monthString + dayString;
+        outgoingFormat = DateTimeFormatter.BASIC_ISO_DATE.withZone(ZoneId.systemDefault());
+        assertEquals(yyyymmdd, df.mapTo(source,outgoingFormat));
+    }
+    
+//    /*
+//     * Class under test for String format(Date, "")
+//     */
+//    @Test public void testDefaultMapInstantString()
+//    {
+//        Instant today =  Instant.now();
+//        String todayFormatted = df.format(today);
+//        assertEquals("",
+//                df.map(todayFormatted), "Empty pattern should produce empty String");
+//    }
+
+    /*
+     * Class under test for String format(null, String)
+     */
+    @Test public void testMapDateStringNullString()
+    {
+        DateTimeFormatter outgoingFormat = DateTimeFormatter.ISO_INSTANT;
+        assertEquals("",
+                df.mapFrom(null, outgoingFormat), "null argument should produce an empty String");
+    }
+
+    /*
+     * Class under test for String format(Date, "")
+     */
+    @Test public void testMapDateStringEmptyString()
+    {
+        Instant today =  Instant.now();
+        String todayFormatted = df.format(today);
+        assertEquals("",
+                df.mapFrom(todayFormatted, null), "Empty pattern should map to empty String");
+    }
+
+
+    /*
+     * Class under test for String format(null, String)
+     */
+    @Test public void testFormatDateStringNullString()
+    {
+        assertEquals("",
+               df.format(null, "MM/dd/yyyy"), "null argument should produce an empty String");
+    }
+
+    /*
+     * Class under test for String format(Date, "")
+     */
+    @Test public void testFormatDateStringEmptyString()
+    {
+        Instant today =  Instant.now();
+        assertEquals("",
+                df.format(today, ""), "Empty pattern should produce empty String");
+    }
+
+    /*
+     * Class under test for String format(Date, "")
+     */
+    @Test public void testFormatDateStringNullFormat()
+    {
+        Instant today =  Instant.now();
+        assertEquals("",
+               df.format(today, null),"null pattern should produce empty String");
+    }
+
+}
diff --git a/src/test/org/apache/turbine/services/velocity/PathConverterTest.java b/src/test/org/apache/turbine/services/velocity/PathConverterTest.java
index 5bde115..f608131 100644
--- a/src/test/org/apache/turbine/services/velocity/PathConverterTest.java
+++ b/src/test/org/apache/turbine/services/velocity/PathConverterTest.java
@@ -84,18 +84,18 @@ public class PathConverterTest
 
         String test1 = (String) ve.getProperty("test1.resource.loader.path");
         assertNotNull("No Test1 Property found", test1);
-        assertEquals("Test1 Path translation failed", rootPath
-                +File.separator+"relative"+File.separator+"path" , test1);
+        assertEquals("Test1 Path translation failed",
+                String.join(File.separator, rootPath, "relative", "path"), test1);
 
         String test2 = (String) ve.getProperty("test2.resource.loader.path");
         assertNotNull("No Test2 Property found", test2);
-        assertEquals("Test2 Path translation failed", rootPath
-                +File.separator+"absolute"+File.separator+"path" , test2);
+        assertEquals("Test2 Path translation failed",
+                String.join(File.separator, rootPath, "absolute", "path"), test2);
 
         String test3 = (String) ve.getProperty("test3.resource.loader.path");
         assertNotNull("No Test3 Property found", test3);
-        assertEquals("Test3 Path translation failed", rootPath
-                +File.separator+"jar-file.jar!/", test3);
+        assertEquals("Test3 Path translation failed",
+                rootPath +File.separator+"jar-file.jar!/", test3);
 
         String test4 = (String) ve.getProperty("test4.resource.loader.path");
         assertNotNull("No Test4 Property found", test4);
@@ -113,9 +113,8 @@ public class PathConverterTest
 
         String test7 = (String) ve.getProperty("test7.resource.loader.path");
         assertNotNull("No Test7 Property found", test7);
-        assertEquals("Test7 Path translation failed", rootPath
-                +File.separator+"file"+File.separator
-                +"system"+File.separator+"reference" , test7);
+        assertEquals("Test7 Path translation failed",
+                String.join(File.separator, rootPath, "file", "system", "reference"), test7);
 
         String test8 = (String) ve.getProperty("test8.resource.loader.path");
         assertNotNull("No Test8 Property found", test8);