java.lang.IllegalArgumentException: Multiple entries with same key: ActionKey{namespace=/console/secure/plugins/crowd-sso, name=null}

Hi,

I am developing a plugin for crowd server, which is working fine on 3.0.2 and earlier version but when I try to install it on 3.1.1 and 3.1.2, I get the following errors.

2018-02-15 09:54:09,887 http-nio-8095-exec-2 ERROR [[Catalina].[localhost].[/crowd].[jsp]] Servlet.service() for servlet [jsp] in context with path [/crowd] threw exception
java.lang.IllegalArgumentException: Multiple entries with same key: ActionKey{namespace=/console/secure/plugins/crowd-sso, name=null}=com.miniorange.sso.saml.crowd-sso and ActionKey{namespace=/console/secure/plugins/crowd-sso, name=null}=com.miniorange.sso.saml.crowd-sso
at com.google.common.collect.ImmutableMap.checkNoConflict(ImmutableMap.java:150)
at com.google.common.collect.RegularImmutableMap.checkNoConflictInBucket(RegularImmutableMap.java:104)
at com.google.common.collect.RegularImmutableMap.(RegularImmutableMap.java:70)
at com.google.common.collect.ImmutableMap$Builder.build(ImmutableMap.java:254)
at com.atlassian.crowd.plugin.descriptors.webwork.PluginAwareConfigurationDecorator.rebuildRuntimeConfiguration(PluginAwareConfigurationDecorator.java:36)
at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:202)
at com.opensymphony.xwork2.config.ConfigurationManager.reloadProviders(ConfigurationManager.java:224)
at com.opensymphony.xwork2.config.ConfigurationManager.conditionalReload(ConfigurationManager.java:179)
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:69)
at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:960)
at org.apache.struts2.dispatcher.PrepareOperations.createActionContext(PrepareOperations.java:84)
at org.apache.struts2.dispatcher.filter.StrutsPrepareFilter.doFilter(StrutsPrepareFilter.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.plugin.servlet.filter.IteratingFilterChain.doFilter(IteratingFilterChain.java:39)
at com.atlassian.plugin.servlet.filter.DelegatingPluginFilter$1.doFilter(DelegatingPluginFilter.java:58)
at com.atlassian.oauth.serviceprovider.internal.servlet.OAuthFilter.doFilter(OAuthFilter.java:67)
at com.atlassian.plugin.servlet.filter.DelegatingPluginFilter.doFilter(DelegatingPluginFilter.java:64)
at com.atlassian.plugin.servlet.filter.IteratingFilterChain.doFilter(IteratingFilterChain.java:37)
at com.atlassian.plugin.servlet.filter.ServletFilterModuleContainerFilter.doFilter(ServletFilterModuleContainerFilter.java:70)
at com.atlassian.plugin.servlet.filter.ServletFilterModuleContainerFilter.doFilter(ServletFilterModuleContainerFilter.java:58)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.orm.hibernate5.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:151)
at com.atlassian.crowd.console.filter.CrowdOpenSessionInViewFilter.doFilterInternal(CrowdOpenSessionInViewFilter.java:23)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.crowd.plugin.web.filter.RequestCacheThreadLocalFilter.doFilter(RequestCacheThreadLocalFilter.java:27)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.plugin.servlet.filter.IteratingFilterChain.doFilter(IteratingFilterChain.java:39)
at com.atlassian.plugin.servlet.filter.DelegatingPluginFilter$1.doFilter(DelegatingPluginFilter.java:58)
at com.atlassian.analytics.client.filter.DefaultAnalyticsFilter.doFilter(DefaultAnalyticsFilter.java:38)
at com.atlassian.analytics.client.filter.AbstractHttpFilter.doFilter(AbstractHttpFilter.java:39)
at com.atlassian.plugin.servlet.filter.DelegatingPluginFilter.doFilter(DelegatingPluginFilter.java:64)
at com.atlassian.plugin.servlet.filter.IteratingFilterChain.doFilter(IteratingFilterChain.java:37)
at com.atlassian.plugin.servlet.filter.ServletFilterModuleContainerFilter.doFilter(ServletFilterModuleContainerFilter.java:70)
at com.atlassian.plugin.servlet.filter.ServletFilterModuleContainerFilter.doFilter(ServletFilterModuleContainerFilter.java:58)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.crowd.security.BlockOpenSocialRequestFilter.doFilter(BlockOpenSocialRequestFilter.java:39)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.crowd.plugin.web.filter.NodeInformationFilter.doFilterInternal(NodeInformationFilter.java:37)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at com.atlassian.crowd.console.filter.CrowdDelegatingFilterProxy.doFilter(CrowdDelegatingFilterProxy.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.crowd.plugin.web.filter.HttpRequestLoggingFilter.doFilter(HttpRequestLoggingFilter.java:30)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.atlassian.crowd.plugin.web.filter.HttpContextFilter.doFilter(HttpContextFilter.java:30)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

hi,

i came across the same issue. did you find a solution?

as far as i have tested it, this issue is triggered by using the xwork-element in the atlassian-plugin.xml and then defining one package with 2 or more actions. it seems to work, though, if you put every action in its own package with its own distinctive namespace.

    <xwork name="TestActions" key="testactions">
    
        <!-- this config doesnt work -->
        <!-- 
        <package name="crowd-xwork-bug" extends="console" namespace="/console/cxb">
            <action name="action1" class="just.a.test.Action1" method="execute" />
            <action name="action2" class="just.a.test.Action2" method="execute" />
        </package>
        -->
        <!-- end of not working config -->
    

        <!-- this config works -->
        <package name="crowd-xwork-bug" extends="console" namespace="/console/cxb">
            <action name="action1" class="just.a.test.Action1" method="execute" />
            <!-- <action name="action2" class="just.a.test.Action2" method="execute" /> -->
        </package>
        
        <package name="crowd-xwork-bug2" extends="console" namespace="/console/cxb2">
            <!-- <action name="action1" class="just.a.test.Action1" method="execute" /> -->
            <action name="action2" class="just.a.test.Action2" method="execute" />
        </package>
        <!-- end of working config -->

    </xwork>

the problem seems to be in com.atlassian.crowd.plugin.descriptors.webwork.PluginAwareConfigurationDecorator or maybe even further down the stack in some struts class. as far as i can tell right now, actionConfig.getName() seems to return null which causes the duplicate key.

maybe some of the atlassian folks could review the issue? thx!

drilling a bit further down the code i come to believe, that there is a bug in com.atlassian.crowd.plugin.descriptors.webwork.PluginAwareActionConfig.build:

public static PluginAwareActionConfig build(String methodName, String className, Map<String, String> parameters,
                                                Map<String, ResultConfig> results, List<InterceptorMapping> interceptors,
                                                List<? extends ExceptionMappingConfig> exceptionMappings, String packageName, List<String> allowedMethods) {
        ActionConfig base = new ActionConfig.Builder(packageName, null, className)
                .methodName(methodName)
                .addParams(parameters)
                .addResultConfigs(results)
                .addInterceptors(interceptors)
                .addExceptionMappings(exceptionMappings)
                .addAllowedMethod(allowedMethods)
                .build();
        return new PluginAwareActionConfig(base);
    }

the line

ActionConfig base = new ActionConfig.Builder(packageName, null, className)

sets the (action-)name to null and ignores the name configured in the atlassian-plugin.xml. thatswhy every action in the same package gets the same key in com.atlassian.crowd.plugin.descriptors.webwork.PluginAwareConfigurationDecorator.

I get the same exception in my Marketplace plugin which I tried to port to 3.1. I’m pretty sure this is a crowd bug and your explanation is right, so I filed a bug CWD-5088

Hi @all

Indeed this looks like a bug in the Crowd and the aforementioned code might be a culprit here.
We are tracking this issue here https://jira.atlassian.com/browse/CWD-5088.
There is also a potential workaround for this problem mentioned in the description of the CWD-5088 issue.

Best Regards,

Marcin Kempa
Crowd Team