diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java index 7b5d579599..1d21061b35 100644 --- a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java @@ -92,6 +92,7 @@ import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Actions; import org.apache.struts2.convention.dontfind.DontFindMeAction; +import org.apache.struts2.convention.flags.alwaysmapexecute.MixedAction; import org.apache.struts2.factory.DefaultInterceptorFactory; import org.apache.struts2.factory.StrutsResultFactory; import org.apache.struts2.inject.Container; @@ -114,6 +115,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import static org.apache.struts2.convention.ReflectionTools.getAnnotation; import static org.easymock.EasyMock.checkOrder; @@ -128,6 +130,11 @@ */ public class PackageBasedActionConfigBuilderTest extends TestCase { + private static final String FLAGS_PACKAGE = "org.apache.struts2.convention.flags"; + private static final String ALWAYS_MAP_EXECUTE_PACKAGE = FLAGS_PACKAGE + ".alwaysmapexecute"; + private static final String CHECK_IMPLEMENTS_PACKAGE = FLAGS_PACKAGE + ".checkimplements"; + private static final String MAP_ALL_MATCHES_PACKAGE = FLAGS_PACKAGE + ".mapallmatches"; + @Override public void setUp() throws Exception { super.setUp(); @@ -347,6 +354,89 @@ public Container getContainer() { } } + public void testAlwaysMapExecuteDisabledSkipsExecuteNextToAnnotatedMethods() { + Map actions = buildActions(ALWAYS_MAP_EXECUTE_PACKAGE, builder -> {}); + + assertEquals(Set.of("other"), actions.keySet()); + } + + public void testAlwaysMapExecuteEnabledMapsExecuteNextToAnnotatedMethods() { + Map actions = buildActions(ALWAYS_MAP_EXECUTE_PACKAGE, + builder -> builder.setAlwaysMapExecute("true")); + + assertEquals(Set.of("other", "mixed"), actions.keySet()); + assertEquals("execute", actions.get("mixed").getMethodName()); + } + + public void testCheckImplementsActionEnabledMapsActionImplementorsWithoutSuffix() { + Map actions = buildActions(CHECK_IMPLEMENTS_PACKAGE, builder -> {}); + + assertEquals(Set.of("implements-action-only"), actions.keySet()); + } + + public void testCheckImplementsActionDisabledIgnoresActionImplementorsWithoutSuffix() { + Map actions = buildActions(CHECK_IMPLEMENTS_PACKAGE, + builder -> builder.setCheckImplementsAction("false")); + + assertTrue(actions.isEmpty()); + } + + public void testMapAllMatchesDisabledSkipsActionWithoutExecute() { + Map actions = buildActions(MAP_ALL_MATCHES_PACKAGE, builder -> {}); + + assertTrue(actions.isEmpty()); + } + + public void testMapAllMatchesEnabledMapsActionWithoutExecute() { + Map actions = buildActions(MAP_ALL_MATCHES_PACKAGE, + builder -> builder.setMapAllMatches("true")); + + assertEquals(Set.of("no-execute"), actions.keySet()); + assertNull(actions.get("no-execute").getMethodName()); + } + + public void testDisableScanningSkipsConfiguredActionPackages() { + Map actions = buildActions(ALWAYS_MAP_EXECUTE_PACKAGE, + builder -> builder.setDisableActionScanning("true")); + + assertTrue(actions.isEmpty()); + } + + public void testPackageLocatorsScanFlagsPackages() { + Map actions = buildActions(null, builder -> { + builder.setPackageLocators("flags"); + builder.setPackageLocatorsBase(FLAGS_PACKAGE); + }); + + assertEquals(Set.of("other", "implements-action-only"), actions.keySet()); + } + + public void testPackageLocatorsDisableIgnoresPackageLocators() { + Map actions = buildActions(null, builder -> { + builder.setPackageLocators("flags"); + builder.setPackageLocatorsBase(FLAGS_PACKAGE); + builder.setDisablePackageLocatorsScanning("true"); + }); + + assertTrue(actions.isEmpty()); + } + + public void testEagerLoadingDisabledDoesNotLoadActionClasses() { + RecordingObjectFactory objectFactory = new RecordingObjectFactory(); + + buildActions(ALWAYS_MAP_EXECUTE_PACKAGE, objectFactory, builder -> {}); + + assertTrue(objectFactory.loadedClassNames.isEmpty()); + } + + public void testEagerLoadingEnabledLoadsActionClassesThroughObjectFactory() { + RecordingObjectFactory objectFactory = new RecordingObjectFactory(); + + buildActions(ALWAYS_MAP_EXECUTE_PACKAGE, objectFactory, builder -> builder.setEagerLoading("true")); + + assertEquals(List.of(MixedAction.class.getName()), objectFactory.loadedClassNames); + } + public void testActionPackages() throws MalformedURLException { run("org.apache.struts2.convention.actions", null, null); } @@ -444,6 +534,58 @@ public Container getContainer() { includeUnrelated); } + private Map buildActions(String actionPackages, Consumer flags) { + return buildActions(actionPackages, new ObjectFactory(), flags); + } + + private Map buildActions(String actionPackages, ObjectFactory objectFactory, + Consumer flags) { + ResultTypeConfig defaultResult = new ResultTypeConfig.Builder("dispatcher", + ServletDispatcherResult.class.getName()).defaultResultParam("location").build(); + PackageConfig strutsDefault = makePackageConfig("struts-default", null, null, "dispatcher", + new ResultTypeConfig[]{defaultResult}, null, null, null, true); + + final DummyContainer mockContainer = new DummyContainer(); + mockContainer.setActionNameBuilder(new SEOActionNameBuilder("true", "-")); + mockContainer.setConventionsService(new ConventionsServiceImpl("")); + mockContainer.setResultMapBuilder((actionClass, annotation, actionName, packageConfig) -> new HashMap<>()); + mockContainer.setInterceptorMapBuilder((actionClass, builder, actionName, annotation) -> new ArrayList<>()); + Configuration configuration = new DefaultConfiguration() { + @Override + public Container getContainer() { + return mockContainer; + } + }; + configuration.addPackageConfig("struts-default", strutsDefault); + objectFactory.setContainer(mockContainer); + + PackageBasedActionConfigBuilder builder = new PackageBasedActionConfigBuilder( + configuration, mockContainer, objectFactory, "false", "struts-default", "false"); + builder.setActionPackages(actionPackages); + builder.setActionSuffix("Action"); + builder.setFileManagerFactory(mockContainer.getInstance(FileManagerFactory.class)); + builder.setProviderAllowlist(new ProviderAllowlist()); + flags.accept(builder); + + builder.buildActionConfigs(); + + Map actions = new HashMap<>(); + configuration.getPackageConfigs().values().stream() + .filter(pkg -> pkg != strutsDefault) + .forEach(pkg -> actions.putAll(pkg.getActionConfigs())); + return actions; + } + + private static class RecordingObjectFactory extends ObjectFactory { + private final List loadedClassNames = new ArrayList<>(); + + @Override + public Class getClassInstance(String className) throws ClassNotFoundException { + loadedClassNames.add(className); + return super.getClassInstance(className); + } + } + private void run(String actionPackages, String packageLocators, String excludePackages) throws MalformedURLException { run(actionPackages, packageLocators, excludePackages, ""); } diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/flags/alwaysmapexecute/MixedAction.java b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/alwaysmapexecute/MixedAction.java new file mode 100644 index 0000000000..0c4cc3aa0e --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/alwaysmapexecute/MixedAction.java @@ -0,0 +1,33 @@ +/* + * 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.convention.flags.alwaysmapexecute; + +import org.apache.struts2.convention.annotation.Action; + +public class MixedAction { + + public String execute() { + return "success"; + } + + @Action("other") + public String other() { + return "success"; + } +} diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/flags/checkimplements/ImplementsActionOnly.java b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/checkimplements/ImplementsActionOnly.java new file mode 100644 index 0000000000..bc573d9ecf --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/checkimplements/ImplementsActionOnly.java @@ -0,0 +1,29 @@ +/* + * 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.convention.flags.checkimplements; + +import org.apache.struts2.action.Action; + +public class ImplementsActionOnly implements Action { + + @Override + public String execute() { + return SUCCESS; + } +} diff --git a/plugins/convention/src/test/java/org/apache/struts2/convention/flags/mapallmatches/NoExecuteAction.java b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/mapallmatches/NoExecuteAction.java new file mode 100644 index 0000000000..c91ba0f034 --- /dev/null +++ b/plugins/convention/src/test/java/org/apache/struts2/convention/flags/mapallmatches/NoExecuteAction.java @@ -0,0 +1,26 @@ +/* + * 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.convention.flags.mapallmatches; + +public class NoExecuteAction { + + public String browse() { + return "success"; + } +}