Restart/Reload Spring 数据 MongoDB

Restart/Reload Spring Data MongoDB

在我的项目中,我使用 属性 占位符来读取 属性 文件,其中包含我的数据库连接的配置。在我的初始设置和稍后某个时间点的重新配置期间,我希望能够更改数据库主机的详细信息。

我尝试使用在当前应用程序上下文中调用的 refresh() 方法,但不知何故 Spring 数据 Bean 没有重新加载,我收到 Spring 数据发出的 IllegalStateException:

HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: open
Exception report: Message: Request processing failed; nested exception is java.lang.IllegalStateException: open description: The server encountered an internal error that prevented it from fulfilling this request. exception: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: open
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:139)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
root cause: java.lang.IllegalStateException: open
    org.bson.util.Assertions.isTrue(Assertions.java:36)
    com.mongodb.DBTCPConnector.getPrimaryPort(DBTCPConnector.java:408)
    com.mongodb.DBCollectionImpl.update(DBCollectionImpl.java:263)
    com.mongodb.DBCollection.update(DBCollection.java:191)
    com.mongodb.DBCollection.save(DBCollection.java:975)
    com.mongodb.DBCollection.save(DBCollection.java:934)
    org.springframework.data.mongodb.core.MongoTemplate.doInCollection(MongoTemplate.java:950)
    org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:410)
    org.springframework.data.mongodb.core.MongoTemplate.saveDBObject(MongoTemplate.java:945)
    org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:885)
    org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:833)
    org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:73)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:483)
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:442)
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:427)
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:512)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    com.sun.proxy.$Proxy2987.save(Unknown Source)
    de.steilerdev.myVerein.server.controller.InitController.initSuperAdmin(InitController.java:158)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:483)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:139)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)

不幸的是,我对 bean 及其生命周期不是很有信心,所以我需要帮助重新加载适当的 bean。我也同意重新启动 Spring 应用程序本身的可能性,即使这将是一项昂贵的操作。因为很少需要它,所以我可以接受它的费用。

我不确定您需要哪部分代码,但这里是 Github 存储库的 link。 (抱歉,文档不完善,仍处于项目开始阶段)

谢谢!


编辑:一些 类 参与了这个过程:

de.steilerdev.myVerein.server.controller.InitController(接收相关请求,将设置存储到我的SettingsRepository)

@Controller
@RequestMapping("/init")
public class InitController
{
    @Autowired
    private SettingsRepository settingsRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private DivisionRepository divisionRepository;

    @Autowired
    private ReloadableResourceBundleMessageSource messageSource;

    private static Logger logger = LoggerFactory.getLogger(InitController.class);

    @RequestMapping(value = "settings")
    public ResponseEntity<String> initSettings(@RequestParam String clubName,
                                               @RequestParam String databaseHost,
                                               @RequestParam String databasePort,
                                               @RequestParam String databaseUser,
                                               @RequestParam String databasePassword,
                                               @RequestParam String databaseCollection,
                                               @RequestParam String rememberMeTokenKey,
                                               Locale locale)
    {
        logger.debug("Starting initial configuration");
        if(!settingsRepository.isInitSetup())
        {
            logger.warn("An initial setup API was used, even though the system is already configured.");
            return new ResponseEntity<>(messageSource.getMessage("init.message.settings.notAllowed", null, "You are not allowed to perform this action at the moment", locale), HttpStatus.BAD_REQUEST);
        } else if(clubName.isEmpty() || rememberMeTokenKey.isEmpty())
        {
            logger.warn("The club name or remember me key is not present");
            return new ResponseEntity<>(messageSource.getMessage("init.message.settings.noKeyOrName", null, "The club name and remember me key is required", locale), HttpStatus.BAD_REQUEST);
        } else
        {
            int databasePortInt = 27017;
            if(databaseHost.isEmpty())
            {
                logger.warn("The database host is empty, using default value.");
                databaseHost = "localhost";
            }
            if(databasePort.isEmpty())
            {
                logger.warn("The database port is empty, using default value.");
                databasePort = "27017";
            } else
            {
                try
                {
                    databasePortInt = Integer.parseInt(databasePort);
                } catch (NumberFormatException e)
                {
                    logger.warn("The database port seems not to be a number " + databasePort);
                    return new ResponseEntity<>(messageSource.getMessage("init.message.settings.dbPortNoNumber", null, "The database port needs to be a number", locale), HttpStatus.BAD_REQUEST);
                }
            }
            if(databaseCollection.isEmpty())
            {
                logger.warn("The database collection name is empty, using default value");
                databaseCollection = "myVerein";
            }

            if(!mongoIsAvailable(databaseHost, databasePortInt, databaseUser, databasePassword, databaseCollection))
            {
                logger.warn("The stated MongoDB is not available");
                return new ResponseEntity<>(messageSource.getMessage("init.message.settings.mongoNotAvailable", null, "The stated MongoDB is not available", locale), HttpStatus.BAD_REQUEST);
            }

            try
            {
                logger.debug("Temporarily storing information");
                settingsRepository.setClubName(clubName);
                settingsRepository.setDatabaseHost(databaseHost);
                settingsRepository.setDatabasePort(databasePort);
                settingsRepository.setDatabaseUser(databaseUser);
                settingsRepository.setDatabasePassword(databasePassword);
                settingsRepository.setDatabaseName(databaseCollection);
                settingsRepository.setRememberMeKey(rememberMeTokenKey);
            } catch (IOException e)
            {
                logger.warn("Unable to save settings.");
                return new ResponseEntity<>(messageSource.getMessage("init.message.settings.savingSettingsError", null, "Unable to save settings, please try again", locale), HttpStatus.INTERNAL_SERVER_ERROR);
            }
            return new ResponseEntity<>(messageSource.getMessage("init.message.settings.savingSettingsSuccess", null, "Successfully saved settings", locale), HttpStatus.OK);
        }
    }

    @RequestMapping(value = "superAdmin")
    public ResponseEntity<String> initSuperAdmin(@RequestParam String firstName,
                                                 @RequestParam String lastName,
                                                 @RequestParam String email,
                                                 @RequestParam String password,
                                                 @RequestParam String passwordRe,
                                                 Locale locale)
    {
        if(!settingsRepository.isInitSetup())
        {
            logger.warn("An initial setup API was used, even though the system is already configured.");
            return new ResponseEntity<>(messageSource.getMessage("init.message.admin.notAllowed", null, "You are not allowed to perform this action at the moment", locale), HttpStatus.BAD_REQUEST);
        } else if(firstName.isEmpty() || lastName.isEmpty() || email.isEmpty() || password.isEmpty() || passwordRe.isEmpty())
        {
            logger.warn("A required parameter of the super admin is empty or missing during initial configuration");
            return new ResponseEntity<>(messageSource.getMessage("init.message.admin.missingParameter", null, "A required parameter is empty or missing", locale), HttpStatus.BAD_REQUEST);
        } else if(!password.equals(passwordRe))
        {
            logger.warn("The password and the re-typed password do not match!");
            return new ResponseEntity<>(messageSource.getMessage("init.message.admin.passwordMatchError", null, "The password and the re-typed password do not match", locale), HttpStatus.BAD_REQUEST);
        } else
        {
            logger.debug("Creating a new initial user.");
            User superAdmin = new User();
            superAdmin.setFirstName(firstName);
            superAdmin.setLastName(lastName);
            superAdmin.setEmail(email);
            superAdmin.setPassword(password);

            logger.debug("Creating a new initial root division.");
            Division rootDivision = new Division(settingsRepository.getClubName(), null, superAdmin, null);

            //Saving changes and restarting context
            try
            {
                settingsRepository.setInitSetup(false);
                //This call is restarting the application.
                settingsRepository.saveSettings(superAdmin);
            } catch (IOException e)
            {
                logger.warn("Unable to save settings.");
                return new ResponseEntity<>(messageSource.getMessage("init.message.admin.savingSettingsError", null, "Unable to save settings, please try again", locale), HttpStatus.INTERNAL_SERVER_ERROR);
            }

            try
            {
                userRepository.save(superAdmin);
                divisionRepository.save(rootDivision);
            } catch (ConstraintViolationException e)
            {
                logger.warn("A database constraint was violated while saving the new super admin.");
                return new ResponseEntity<>(messageSource.getMessage("init.message.admin.constraintViolation", null, "A database constraint was violated while saving the new super admin", locale), HttpStatus.BAD_REQUEST);
            }
            return new ResponseEntity<>(messageSource.getMessage("init.message.admin.savingAdminSuccess", null, "Successfully saved the new super admin. The settings are now being saved and the application is restarted.", locale) , HttpStatus.OK);
        }
    }

    private boolean mongoIsAvailable(String databaseHost, int databasePort, String databaseUser, String databasePassword, String databaseCollection)
    {
        logger.debug("Creating mongo client to test connection");
        List<MongoCredential> credential = null;
        MongoClient mongoClient = null;
        if(!databaseUser.isEmpty() && !databasePassword.isEmpty())
        {
            credential = Arrays.asList(MongoCredential.createMongoCRCredential(databaseUser, databaseCollection, databasePassword.toCharArray()));
        }

        try
        {
            mongoClient = new MongoClient(new ServerAddress(databaseHost, databasePort),credential);

            //Checking if connection REALLY works
            List<String> databases = mongoClient.getDatabaseNames();
            if(databases == null)
            {
                logger.warn("The list of databases is null");
                return false;
            } else if(databases.isEmpty())
            {
                logger.info("The databases are empty");
                return true;
            } else
            {
                logger.debug("The database connection seems okay.");
                return true;
            }
        } catch (UnknownHostException e)
        {
            logger.warn("Unable to resolve mongoDB host: " + e.getMessage());
            return false;
        } catch (MongoException e)
        {
            logger.warn("Unable to receive list of present databases: " + e.getMessage());
            return false;
        } finally
        {
            if(mongoClient != null)
            {
                mongoClient.close();
            }
        }
    }
}

de.steilerdev.myVerein.server.model.SettingsRepository(在属性文件中检索和存储永久设置,以及在设置更改时重新启动应用程序)

public class SettingsRepository
{
    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private SimpleMongoDbFactory mongoDbFactory;

    @Autowired
    private ApplicationContext applicationContext;

    private Properties settings;
    private Resource settingsResource;

    private final static String settingsFileName = "myVerein.properties";
    //Property names
    private final static String databaseName = "dbName";
    private final static String databaseHost = "dbHost";
    private final static String databasePort = "dbPort";
    private final static String databaseUser = "dbUser";
    private final static String databasePassword = "dbPassword";
    private final static String rememberMeKey = "rememberMeKey";
    private final static String clubName = "clubName";
    private final static String initSetup = "initSetup";

    private boolean changed;
    private boolean databaseChanged;

    private static Logger logger = LoggerFactory.getLogger(SettingsRepository.class);

    public Properties loadSettings() throws IOException
    {
        if(settings == null)
        {
            logger.debug("Loading settings file from classpath");
            settingsResource = new ClassPathResource(settingsFileName);
            changed = false;
            databaseChanged = false;
            return (settings = PropertiesLoaderUtils.loadProperties(settingsResource));
        } else
        {
            return settings;
        }
    }

    //Getter and setter removed

    public void saveSettings(User currentUser) throws IOException
    {
        saveSettings(loadSettings(), currentUser);
    }

    public void saveSettings(Properties settings, User currentUser) throws IOException
    {
        if(changed)
        {
            logger.debug("Saving settings to " + settingsResource.getFile().getAbsolutePath());
            settings.store(new FileOutputStream(settingsResource.getFile()), "Settings last changed " + (currentUser != null ? ("by " + currentUser.getEmail() + " (" + LocalDateTime.now().toString() + ")") : LocalDateTime.now().toString()));
            if(databaseChanged)
            {
                try
                {
                    mongoDbFactory.destroy();
                } catch (Exception e)
                {
                    System.err.println("Problem destroying factory");
                }
                ((ConfigurableApplicationContext) applicationContext).refresh();
            }
            this.settings = null;
        } else
        {
            logger.debug("No need to save the settings");
        }
    }
}

在InitController的第178行抛出异常,这意味着在重新加载之后和将对象保存到新控制器期间userRepository.save(superAdmin);

很难说,为什么重新加载不起作用,基于普通的异常,但这里有一些提示:

  1. 关于重启应用上下文,有一篇很好的文章

    http://jroller.com/Solomon/entry/reloading_a_spring_web_application

    你可以从那里得到一些关于它为什么会出现故障的线索。

  2. 使用上下文重新加载时,您需要小心地在应用程序关闭时正确关闭所有打开的资源。

    检查您是否在应用程序上下文中正确关闭了 Mongo 连接。

    What is the correct way to close the mongo connection using spring-mongo?

添加代码后。很难理解,你想用这个来完成什么。这将需要您付出很多努力才能使其正常工作,而且大部分努力都是徒劳的。

您不能重新加载 Spring 上下文中的特定 bean,但您可以通过调用 ApplicationContext refresh() 重新加载整个上下文,之后将重新创建所有 bean,包括您的控制器 bean。所以这至少应该是一个单独的控制器,没有额外的功能。此外,您似乎假设您可以更改 mongo 配置,并在同一调用中使用旧引用向 mongo 写入一些内容……这不是 spring 中的工作方式。所以别这样。