/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed 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 io.restassured.module.mockmvc.internal;

import io.restassured.module.mockmvc.config.MockMvcConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.AbstractMockMvcBuilder;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.context.WebApplicationContext;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class MockMvcFactory {

    private Object mockMvc;

    MockMvcFactory() {
        mockMvc = null;
    }

    public MockMvcFactory(MockMvc mockMvc) {
        this.mockMvc = mockMvc;
    }

    public MockMvcFactory(MockMvcBuilder mockMvc) {
        this.mockMvc = mockMvc;
    }

    public synchronized MockMvc build(final MockMvcConfig config) {
        final MockMvc mockMvcToReturn;
        if (!isAssigned()) {
            throw new IllegalStateException("You haven't configured a MockMVC instance. You can do this statically\n\nRestAssured.mockMvc(..)\nRestAssured.standaloneSetup(..);\nRestAssured.webAppContextSetup(..);\n\nor using the DSL:\n\ngiven().\n\t\tmockMvc(..). ..\n");
        } else if (mockMvc instanceof MockMvc) {
            mockMvcToReturn = (MockMvc) this.mockMvc;
        } else if (mockMvc instanceof AbstractMockMvcBuilder) {
            AbstractMockMvcBuilder builder = (AbstractMockMvcBuilder) this.mockMvc;
            if (config.shouldAutomaticallyApplySpringSecurityMockMvcConfigurer() && SpringSecurityTestClassPathChecker.isSpringSecurityTestInClasspath()) {
                // Avoid duplicates if possible
                List<MockMvcConfigurer> configurers = getAlreadyConfiguredMockMacConfigurers(builder);
                boolean isOkToAdd = true;

                Iterator<MockMvcConfigurer> iterator = configurers.iterator();
                while (iterator.hasNext()) {
                    MockMvcConfigurer configurer = iterator.next();
                    if (configurer.getClass().getName().equals("org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurer")) {
                        isOkToAdd = false;
                        break;
                    } else if (configurer instanceof ConditionalSpringMockMvcConfigurer) {
                        // Remove to avoid duplicates
                        iterator.remove();
                    }
                }
                // End avoid duplicates
                if (isOkToAdd) {
                    final MockMvcConfigurer configurer = org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity();
                    builder.apply(new ConditionalSpringMockMvcConfigurer(configurer));
                }
            }
            mockMvcToReturn = builder.build();
        } else if (mockMvc instanceof MockMvcBuilder) {
            mockMvcToReturn = ((MockMvcBuilder) mockMvc).build();
        } else {
            throw new IllegalStateException("Cannot construct MockMvc instance because mock mvc instance is of type " + mockMvc.getClass().getName());
        }

        // We cache the created MockMvc instance
        this.mockMvc = mockMvcToReturn;
        return mockMvcToReturn;
    }

    @SuppressWarnings("unchecked")
    private List<MockMvcConfigurer> getAlreadyConfiguredMockMacConfigurers(AbstractMockMvcBuilder builder) {
        try {
            Field configurers = AbstractMockMvcBuilder.class.getDeclaredField("configurers");
            configurers.setAccessible(true);
            Object o = configurers.get(builder);
            configurers.setAccessible(false);
            return (List<MockMvcConfigurer>) o;
        } catch (Exception e) {
            return Collections.emptyList();
        }
    }

    public boolean isAssigned() {
        return mockMvc != null;
    }

    /**
     * Delegates to the {@link MockMvcConfigurer} only if the <code>springSecurityFilterChain</code> bean is available in the context.
     * The reason for checking this is that SpringSecurity may not be available if starting from a standalone setup.
     */
    private static class ConditionalSpringMockMvcConfigurer implements MockMvcConfigurer {
        private static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain";
        private final MockMvcConfigurer configurer;

        public ConditionalSpringMockMvcConfigurer(MockMvcConfigurer configurer) {
            this.configurer = configurer;
        }

        public void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> builder) {
            configurer.afterConfigurerAdded(builder);
        }

        public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
            if (!context.containsBean(SPRING_SECURITY_FILTER_CHAIN)) {
                return null;
            }
            return configurer.beforeMockMvcCreated(builder, context);
        }
    }
}

@@ -1858,16 +1858,23 @@
 tAssured
+MockMvc
 .mockMvc
@@ -1890,16 +1890,23 @@
 tAssured
+MockMvc
 .standal
@@ -1931,16 +1931,23 @@
 tAssured
+MockMvc
 .webAppC

Fix wrong class name in the exception message