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

lukaszlenart pushed a commit to branch WW-3737-custom-separator
in repository https://gitbox.apache.org/repos/asf/struts.git

commit c41f05fe68c4f89ba5042747a43bb74e108ce550
Author: Lukasz Lenart <lukaszlen...@apache.org>
AuthorDate: Wed Oct 19 13:57:59 2022 +0200

    WW-3737 Allows to define a custom separator used to split patterns
---
 .../com/opensymphony/xwork2/XWorkTestCase.java     |  31 ++--
 .../opensymphony/xwork2/inject/ContainerImpl.java  | 175 +++++++++------------
 .../java/org/apache/struts2/StrutsConstants.java   |   3 +
 .../apache/struts2/dispatcher/InitOperations.java  |  37 +++--
 .../struts2/dispatcher/InitOperationsTest.java     |  86 ++++++++++
 5 files changed, 202 insertions(+), 130 deletions(-)

diff --git a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java 
b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
index 330e96412..ec7a8d755 100644
--- a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
+++ b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
@@ -34,22 +34,22 @@ import java.util.Locale;
 import java.util.Map;
 
 /**
- * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses 
+ * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
  * the generic test setup for logic.
  *
  * @author plightbo
  */
 public abstract class XWorkTestCase extends TestCase {
-    
+
     protected ConfigurationManager configurationManager;
     protected Configuration configuration;
     protected Container container;
     protected ActionProxyFactory actionProxyFactory;
-    
+
     public XWorkTestCase() {
         super();
     }
-    
+
     @Override
     protected void setUp() throws Exception {
         configurationManager = XWorkTestCaseHelper.setUp();
@@ -57,7 +57,7 @@ public abstract class XWorkTestCase extends TestCase {
         container = configuration.getContainer();
         actionProxyFactory = container.getInstance(ActionProxyFactory.class);
     }
-    
+
     @Override
     protected void tearDown() throws Exception {
         XWorkTestCaseHelper.tearDown(configurationManager);
@@ -66,34 +66,33 @@ public abstract class XWorkTestCase extends TestCase {
         container = null;
         actionProxyFactory = null;
     }
-    
+
     protected void loadConfigurationProviders(ConfigurationProvider... 
providers) {
         configurationManager = 
XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
         configuration = configurationManager.getConfiguration();
         container = configuration.getContainer();
         actionProxyFactory = container.getInstance(ActionProxyFactory.class);
     }
-    
-    protected void loadButAdd(final Class<?> type, final Object impl) {
+
+    protected <T> void loadButAdd(final Class<T> type, final T impl) {
         loadButAdd(type, Container.DEFAULT_NAME, impl);
     }
-    
-    protected void loadButAdd(final Class<?> type, final String name, final 
Object impl) {
+
+    protected <T> void loadButAdd(final Class<T> type, final String name, 
final T impl) {
         loadConfigurationProviders(new StubConfigurationProvider() {
             @Override
-            public void register(ContainerBuilder builder,
-                    LocatableProperties props) throws ConfigurationException {
+            public void register(ContainerBuilder builder, LocatableProperties 
props) throws ConfigurationException {
                 if (impl instanceof String || 
ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
                     props.setProperty(name, "" + impl);
                 } else {
-                    builder.factory(type, name, new Factory() {
-                        public Object create(Context context) throws Exception 
{
+                    builder.factory(type, name, new Factory<T>() {
+                        public T create(Context context) throws Exception {
                             return impl;
                         }
 
                         @Override
-                        public Class type() {
-                            return impl.getClass();
+                        public Class<T> type() {
+                            return (Class<T>) impl.getClass();
                         }
                     }, Scope.SINGLETON);
                 }
diff --git 
a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java 
b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
index ad3d43c2f..455f8a449 100644
--- a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
@@ -19,10 +19,26 @@ import com.opensymphony.xwork2.inject.util.ReferenceCache;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.*;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ReflectPermission;
 import java.security.AccessControlException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Default {@link Container} implementation.
@@ -39,11 +55,7 @@ class ContainerImpl implements Container {
         this.factories = factories;
         final Map<Class<?>, Set<String>> map = new HashMap<>();
         for (Key<?> key : factories.keySet()) {
-            Set<String> names = map.get(key.getType());
-            if (names == null) {
-                names = new HashSet<>();
-                map.put(key.getType(), names);
-            }
+            Set<String> names = map.computeIfAbsent(key.getType(), k -> new 
HashSet<>());
             names.add(key.getName());
         }
 
@@ -63,20 +75,20 @@ class ContainerImpl implements Container {
      * Field and method injectors.
      */
     final Map<Class<?>, List<Injector>> injectors =
-            new ReferenceCache<Class<?>, List<Injector>>() {
-                @Override
-                protected List<Injector> create(Class<?> key) {
-                    List<Injector> injectors = new ArrayList<>();
-                    addInjectors(key, injectors);
-                    return injectors;
-                }
-            };
+        new ReferenceCache<Class<?>, List<Injector>>() {
+            @Override
+            protected List<Injector> create(Class<?> key) {
+                List<Injector> injectors = new ArrayList<>();
+                addInjectors(key, injectors);
+                return injectors;
+            }
+        };
 
     /**
      * Recursively adds injectors for fields and methods from the given class 
to the given list. Injects parent classes
      * before sub classes.
      */
-    void addInjectors(Class clazz, List<Injector> injectors) {
+    void addInjectors(Class<?> clazz, List<Injector> injectors) {
         if (clazz == Object.class) {
             return;
         }
@@ -97,38 +109,24 @@ class ContainerImpl implements Container {
             addInjectorsForMethods(clazz.getDeclaredMethods(), true, 
injectors);
         }
 
-        callInContext(new ContextualCallable<Void>() {
-            public Void call(InternalContext context) {
-                for (Injector injector : injectors) {
-                    injector.inject(context, null);
-                }
-                return null;
+        callInContext((ContextualCallable<Void>) context -> {
+            for (Injector injector : injectors) {
+                injector.inject(context, null);
             }
+            return null;
         });
     }
 
     void addInjectorsForMethods(Method[] methods, boolean statics, 
List<Injector> injectors) {
-        addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
-                new InjectorFactory<Method>() {
-                    public Injector create(ContainerImpl container, Method 
method,
-                                           String name) throws 
MissingDependencyException {
-                        return new MethodInjector(container, method, name);
-                    }
-                });
+        addInjectorsForMembers(Arrays.asList(methods), statics, injectors, 
MethodInjector::new);
     }
 
     void addInjectorsForFields(Field[] fields, boolean statics, List<Injector> 
injectors) {
-        addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
-                new InjectorFactory<Field>() {
-                    public Injector create(ContainerImpl container, Field 
field,
-                                           String name) throws 
MissingDependencyException {
-                        return new FieldInjector(container, field, name);
-                    }
-                });
+        addInjectorsForMembers(Arrays.asList(fields), statics, injectors, 
FieldInjector::new);
     }
 
     <M extends Member & AnnotatedElement> void addInjectorsForMembers(
-            List<M> members, boolean statics, List<Injector> injectors, 
InjectorFactory<M> injectorFactory) {
+        List<M> members, boolean statics, List<Injector> injectors, 
InjectorFactory<M> injectorFactory) {
         for (M member : members) {
             if (isStatic(member) == statics) {
                 Inject inject = member.getAnnotation(Inject.class);
@@ -148,12 +146,12 @@ class ContainerImpl implements Container {
     interface InjectorFactory<M extends Member & AnnotatedElement> {
 
         Injector create(ContainerImpl container, M member, String name)
-                throws MissingDependencyException;
+            throws MissingDependencyException;
     }
 
     /**
      * Determines if a given {@link Member} is static or not.
-     * 
+     *
      * @param member checked for the static modifier.
      * @return true if member is static, false otherwise.
      */
@@ -163,13 +161,13 @@ class ContainerImpl implements Container {
 
     /**
      * Determines if a given {@link Member} is considered to be public for 
reflection usage or not.
-     * 
+     *
      * @param member checked to see if it is public for reflection usage.
      * @return true if member is public for reflection usage, false otherwise.
      */
     private static boolean isPublicForReflection(Member member) {
         return Modifier.isPublic(member.getModifiers()) &&
-               Modifier.isPublic(member.getDeclaringClass().getModifiers());
+            Modifier.isPublic(member.getDeclaringClass().getModifiers());
     }
 
     static class FieldInjector implements Injector {
@@ -179,7 +177,7 @@ class ContainerImpl implements Container {
         final ExternalContext<?> externalContext;
 
         public FieldInjector(ContainerImpl container, Field field, String name)
-                throws MissingDependencyException {
+            throws MissingDependencyException {
             this.field = field;
             if (!isPublicForReflection(field) && !field.isAccessible()) {
                 SecurityManager sm = System.getSecurityManager();
@@ -190,7 +188,7 @@ class ContainerImpl implements Container {
                     field.setAccessible(true);
                 } catch (AccessControlException e) {
                     throw new DependencyException("Security manager in use, 
could not access field: "
-                            + field.getDeclaringClass().getName() + "(" + 
field.getName() + ")", e);
+                        + field.getDeclaringClass().getName() + "(" + 
field.getName() + ")", e);
                 }
             }
 
@@ -225,8 +223,12 @@ class ContainerImpl implements Container {
      * @param parameterTypes parameter types
      * @return injections
      */
-    <M extends AccessibleObject & Member> ParameterInjector<?>[]
-    getParametersInjectors(M member, Annotation[][] annotations, Class[] 
parameterTypes, String defaultName) throws MissingDependencyException {
+    <M extends AccessibleObject & Member> ParameterInjector<?>[] 
getParametersInjectors(
+        M member,
+        Annotation[][] annotations,
+        Class<?>[] parameterTypes,
+        String defaultName
+    ) throws MissingDependencyException {
         final List<ParameterInjector<?>> parameterInjectors = new 
ArrayList<>();
 
         final Iterator<Annotation[]> annotationsIterator = 
Arrays.asList(annotations).iterator();
@@ -247,12 +249,11 @@ class ContainerImpl implements Container {
         }
 
         final ExternalContext<T> externalContext = 
ExternalContext.newInstance(member, key, this);
-        return new ParameterInjector<T>(externalContext, factory);
+        return new ParameterInjector<>(externalContext, factory);
     }
 
-    @SuppressWarnings("unchecked")
     private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> 
parameterInjections) {
-        return parameterInjections.toArray(new 
ParameterInjector[parameterInjections.size()]);
+        return parameterInjections.toArray(new ParameterInjector[0]);
     }
 
     /**
@@ -261,7 +262,7 @@ class ContainerImpl implements Container {
     Inject findInject(Annotation[] annotations) {
         for (Annotation annotation : annotations) {
             if (annotation.annotationType() == Inject.class) {
-                return Inject.class.cast(annotation);
+                return (Inject) annotation;
             }
         }
         return null;
@@ -283,7 +284,7 @@ class ContainerImpl implements Container {
                     method.setAccessible(true);
                 } catch (AccessControlException e) {
                     throw new DependencyException("Security manager in use, 
could not access method: "
-                            + name + "(" + method.getName() + ")", e);
+                        + name + "(" + method.getName() + ")", e);
                 }
             }
 
@@ -292,7 +293,7 @@ class ContainerImpl implements Container {
                 throw new DependencyException(method + " has no parameters to 
inject.");
             }
             parameterInjectors = container.getParametersInjectors(
-                    method, method.getParameterAnnotations(), parameterTypes, 
name);
+                method, method.getParameterAnnotations(), parameterTypes, 
name);
         }
 
         @Override
@@ -305,14 +306,12 @@ class ContainerImpl implements Container {
         }
     }
 
-    Map<Class<?>, ConstructorInjector> constructors =
-            new ReferenceCache<Class<?>, ConstructorInjector>() {
-                @Override
-                @SuppressWarnings("unchecked")
-                protected ConstructorInjector<?> create(Class<?> 
implementation) {
-                    return new ConstructorInjector(ContainerImpl.this, 
implementation);
-                }
-            };
+    Map<Class<?>, ConstructorInjector<?>> constructors = new 
ReferenceCache<Class<?>, ConstructorInjector<?>>() {
+        @Override
+        protected ConstructorInjector<?> create(Class<?> implementation) {
+            return new ConstructorInjector<>(ContainerImpl.this, 
implementation);
+        }
+    };
 
     static class ConstructorInjector<T> {
 
@@ -334,7 +333,7 @@ class ContainerImpl implements Container {
                     constructor.setAccessible(true);
                 } catch (AccessControlException e) {
                     throw new DependencyException("Security manager in use, 
could not access constructor: "
-                            + implementation.getName() + "(" + 
constructor.getName() + ")", e);
+                        + implementation.getName() + "(" + 
constructor.getName() + ")", e);
                 }
             }
 
@@ -359,14 +358,14 @@ class ContainerImpl implements Container {
         }
 
         ParameterInjector<?>[] constructParameterInjector(
-                Inject inject, ContainerImpl container, Constructor<T> 
constructor) throws MissingDependencyException {
+            Inject inject, ContainerImpl container, Constructor<T> 
constructor) throws MissingDependencyException {
             return constructor.getParameterTypes().length == 0
-                    ? null // default constructor.
-                    : container.getParametersInjectors(
-                    constructor,
-                    constructor.getParameterAnnotations(),
-                    constructor.getParameterTypes(),
-                    inject.value()
+                ? null // default constructor.
+                : container.getParametersInjectors(
+                constructor,
+                constructor.getParameterAnnotations(),
+                constructor.getParameterTypes(),
+                inject.value()
             );
         }
 
@@ -378,7 +377,7 @@ class ContainerImpl implements Container {
                 if (constructor.getAnnotation(Inject.class) != null) {
                     if (found != null) {
                         throw new DependencyException("More than one 
constructor annotated"
-                                + " with @Inject found in " + implementation + 
".");
+                            + " with @Inject found in " + implementation + 
".");
                     }
                     found = constructor;
                 }
@@ -466,7 +465,7 @@ class ContainerImpl implements Container {
         }
     }
 
-    private static Object[] getParameters(Member member, InternalContext 
context, ParameterInjector[] parameterInjectors) {
+    private static Object[] getParameters(Member member, InternalContext 
context, ParameterInjector<?>[] parameterInjectors) {
         if (parameterInjectors == null) {
             return null;
         }
@@ -494,13 +493,12 @@ class ContainerImpl implements Container {
         }
     }
 
-    @SuppressWarnings("unchecked")
     <T> T getInstance(Class<T> type, String name, InternalContext context) {
         final ExternalContext<?> previous = context.getExternalContext();
         final Key<T> key = Key.newInstance(type, name);
         context.setExternalContext(ExternalContext.newInstance(null, key, 
this));
         try {
-            final InternalFactory o = getFactory(key);
+            final InternalFactory<? extends T> o = getFactory(key);
             if (o != null) {
                 return getFactory(key).create(context);
             } else {
@@ -517,39 +515,25 @@ class ContainerImpl implements Container {
 
     @Override
     public void inject(final Object o) {
-        callInContext(new ContextualCallable<Void>() {
-            public Void call(InternalContext context) {
-                inject(o, context);
-                return null;
-            }
+        callInContext((ContextualCallable<Void>) context -> {
+            inject(o, context);
+            return null;
         });
     }
 
     @Override
     public <T> T inject(final Class<T> implementation) {
-        return callInContext(new ContextualCallable<T>() {
-            public T call(InternalContext context) {
-                return inject(implementation, context);
-            }
-        });
+        return callInContext(context -> inject(implementation, context));
     }
 
     @Override
     public <T> T getInstance(final Class<T> type, final String name) {
-        return callInContext(new ContextualCallable<T>() {
-            public T call(InternalContext context) {
-                return getInstance(type, name, context);
-            }
-        });
+        return callInContext(context -> getInstance(type, name, context));
     }
 
     @Override
     public <T> T getInstance(final Class<T> type) {
-        return callInContext(new ContextualCallable<T>() {
-            public T call(InternalContext context) {
-                return getInstance(type, context);
-            }
-        });
+        return callInContext(context -> getInstance(type, context));
     }
 
     @Override
@@ -561,12 +545,7 @@ class ContainerImpl implements Container {
         return names;
     }
 
-    ThreadLocal<Object[]> localContext = new ThreadLocal<Object[]>() {
-        @Override
-        protected Object[] initialValue() {
-            return new Object[1];
-        }
-    };
+    ThreadLocal<Object[]> localContext = ThreadLocal.withInitial(() -> new 
Object[1]);
 
     /**
      * Looks up thread local context. Creates (and removes) a new context if 
necessary.
@@ -598,7 +577,7 @@ class ContainerImpl implements Container {
      */
     @SuppressWarnings("unchecked")
     <T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
-        return constructors.get(implementation);
+        return (ConstructorInjector<T>) constructors.get(implementation);
     }
 
     final ThreadLocal<Object> localScopeStrategy = new ThreadLocal<>();
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java 
b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index 18ccfbc93..f37a85078 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -56,6 +56,9 @@ public final class StrutsConstants {
     /** Comma separated list of patterns (java.util.regex.Pattern) to be 
excluded from Struts2-processing */
     public static final String STRUTS_ACTION_EXCLUDE_PATTERN = 
"struts.action.excludePattern";
 
+    /** A custom separator used to split list of patterns 
(java.util.regex.Pattern) to be excluded from Struts2-processing */
+    public static final String STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR = 
"struts.action.excludePattern.separator";
+
     /** Whether to use the response encoding (JSP page encoding) for s:include 
tag processing (false - use STRUTS_I18N_ENCODING - by default) */
     public static final String STRUTS_TAG_INCLUDETAG_USERESPONSEENCODING = 
"struts.tag.includetag.useResponseEncoding";
 
diff --git 
a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java 
b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
index 7c6b7626f..819e7cdb9 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java
@@ -21,7 +21,12 @@ package org.apache.struts2.dispatcher;
 import com.opensymphony.xwork2.ActionContext;
 import org.apache.struts2.StrutsConstants;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.regex.Pattern;
 
 /**
@@ -36,10 +41,9 @@ public class InitOperations {
      * Creates and initializes the dispatcher
      *
      * @param filterConfig host configuration
-     *
      * @return the dispatcher
      */
-    public Dispatcher initDispatcher( HostConfig filterConfig ) {
+    public Dispatcher initDispatcher(HostConfig filterConfig) {
         Dispatcher dispatcher = createDispatcher(filterConfig);
         dispatcher.init();
         return dispatcher;
@@ -49,10 +53,10 @@ public class InitOperations {
      * Initializes the static content loader with the filter configuration
      *
      * @param filterConfig host configuration
-     * @param dispatcher the dispatcher
+     * @param dispatcher   the dispatcher
      * @return the static content loader
      */
-    public StaticContentLoader initStaticContentLoader( HostConfig 
filterConfig, Dispatcher dispatcher ) {
+    public StaticContentLoader initStaticContentLoader(HostConfig 
filterConfig, Dispatcher dispatcher) {
         StaticContentLoader loader = 
dispatcher.getContainer().getInstance(StaticContentLoader.class);
         loader.setHostConfig(filterConfig);
         return loader;
@@ -60,7 +64,6 @@ public class InitOperations {
 
     /**
      * @return The dispatcher on the thread.
-     *
      * @throws IllegalStateException If there is no dispatcher available
      */
     public Dispatcher findDispatcherOnThread() {
@@ -75,12 +78,11 @@ public class InitOperations {
      * Create a {@link Dispatcher}
      *
      * @param filterConfig host configuration
-     *
      * @return The dispatcher on the thread.
      */
     protected Dispatcher createDispatcher(HostConfig filterConfig) {
         Map<String, String> params = new HashMap<>();
-        for ( Iterator<String> parameterNames = 
filterConfig.getInitParameterNames(); parameterNames.hasNext(); ) {
+        for (Iterator<String> parameterNames = 
filterConfig.getInitParameterNames(); parameterNames.hasNext(); ) {
             String name = parameterNames.next();
             String value = filterConfig.getInitParameter(name);
             params.put(name, value);
@@ -96,20 +98,23 @@ public class InitOperations {
      * Extract a list of patterns to exclude from request filtering
      *
      * @param dispatcher The dispatcher to check for exclude pattern 
configuration
-     *
      * @return a List of Patterns for request to exclude if apply, or 
<tt>null</tt>
-     *
      * @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN
      */
-    public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
-        return 
buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, 
StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
+    public List<Pattern> buildExcludedPatternsList(Dispatcher dispatcher) {
+        String excludePatterns = 
dispatcher.getContainer().getInstance(String.class, 
StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN);
+        String separator = dispatcher.getContainer().getInstance(String.class, 
StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR);
+        if (separator == null) {
+            separator = ",";
+        }
+        return buildExcludedPatternsList(excludePatterns, separator);
     }
-            
-    private List<Pattern> buildExcludedPatternsList( String patterns ) {
+
+    private List<Pattern> buildExcludedPatternsList(String patterns, String 
separator) {
         if (null != patterns && patterns.trim().length() != 0) {
             List<Pattern> list = new ArrayList<>();
-            String[] tokens = patterns.split(",");
-            for ( String token : tokens ) {
+            String[] tokens = patterns.split(separator);
+            for (String token : tokens) {
                 list.add(Pattern.compile(token.trim()));
             }
             return Collections.unmodifiableList(list);
diff --git 
a/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java 
b/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java
new file mode 100644
index 000000000..aa2aaeaa3
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.struts2.dispatcher;
+
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.config.PropertiesConfigurationProvider;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class InitOperationsTest extends StrutsInternalTestCase {
+
+    public void testExcludePatterns() {
+        // given
+        loadConfigurationProviders(new PropertiesConfigurationProvider() {
+            @Override
+            public void register(ContainerBuilder builder, LocatableProperties 
props) throws ConfigurationException {
+                
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, 
"/ns1/.*\\.json,/ns2/.*\\.json");
+            }
+        });
+
+        Dispatcher mockDispatcher = mock(Dispatcher.class);
+        when(mockDispatcher.getContainer()).thenReturn(container);
+
+        // when
+        InitOperations init = new InitOperations();
+        List<Pattern> patterns = 
init.buildExcludedPatternsList(mockDispatcher);
+
+        // then
+        assertThat(patterns).extracting(Pattern::toString).containsOnly(
+            "/ns1/.*\\.json",
+            "/ns2/.*\\.json"
+        );
+    }
+
+    public void testExcludePatternsUsingCustomSeparator() {
+        // given
+        loadConfigurationProviders(new PropertiesConfigurationProvider() {
+            @Override
+            public void register(ContainerBuilder builder, LocatableProperties 
props) throws ConfigurationException {
+                
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, 
"/ns1/[a-z]{1,10}.json///ns2/[a-z]{1,10}.json");
+                
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, 
"//");
+            }
+        });
+
+        Dispatcher mockDispatcher = mock(Dispatcher.class);
+        when(mockDispatcher.getContainer()).thenReturn(container);
+
+        // when
+        InitOperations init = new InitOperations();
+
+        String separator = container.getInstance(String.class, 
StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR);
+        List<Pattern> patterns = 
init.buildExcludedPatternsList(mockDispatcher);
+
+        // then
+        assertThat(separator).isNotBlank().isEqualTo("//");
+        assertThat(patterns).extracting(Pattern::toString).containsOnly(
+            "/ns1/[a-z]{1,10}.json",
+            "/ns2/[a-z]{1,10}.json"
+        );
+    }
+}

Reply via email to