From 0b80343610a215f26a7d764cc849f8e9ca44fea0 Mon Sep 17 00:00:00 2001 From: emaclee Date: Fri, 6 May 2022 10:36:29 +0100 Subject: Add graceful shutdown for Session Manager Introduce singleton session manager Add unit test for session manager Issue-Id: CPS-898 Signed-off-by: emaclee Change-Id: Iaf91f1aa6c1ebfe0ab907e7f7d80a01e940a0fdd --- .../org/onap/cps/spi/config/CpsSessionFactory.java | 70 ++++++++++++++++++++++ .../spi/impl/CpsDataPersistenceServiceImpl.java | 2 +- .../org/onap/cps/spi/utils/SessionManager.java | 54 +++++++++++------ 3 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java (limited to 'cps-ri/src/main') diff --git a/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java b/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java new file mode 100644 index 0000000000..5241ea0096 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java @@ -0,0 +1,70 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.config; + +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.onap.cps.spi.entities.AnchorEntity; +import org.onap.cps.spi.entities.DataspaceEntity; +import org.onap.cps.spi.entities.SchemaSetEntity; +import org.onap.cps.spi.entities.YangResourceEntity; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) +public class CpsSessionFactory { + + private SessionFactory sessionFactory = null; + + /** + * Open a session from session factory. + * + * @return session + * @throws HibernateException hibernate exception + */ + public Session openSession() throws HibernateException { + return getSessionFactory().openSession(); + } + + /** + * Close session factory. + * + * @throws HibernateException hibernate exception + */ + public void closeSessionFactory() throws HibernateException { + getSessionFactory().close(); + } + + private SessionFactory getSessionFactory() { + if (sessionFactory == null) { + sessionFactory = new org.hibernate.cfg.Configuration().configure("hibernate.cfg.xml") + .addAnnotatedClass(AnchorEntity.class) + .addAnnotatedClass(DataspaceEntity.class) + .addAnnotatedClass(SchemaSetEntity.class) + .addAnnotatedClass(YangResourceEntity.class) + .buildSessionFactory(); + } + return sessionFactory; + } +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index daf4dd757b..ded234bb48 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -217,7 +217,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void closeSession(final String sessionId) { - sessionManager.closeSession(sessionId); + sessionManager.closeSession(sessionId, SessionManager.WITH_COMMIT); } @Override diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java b/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java index e2786887ac..6f96cffdc1 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java @@ -29,44 +29,54 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; +import org.onap.cps.spi.config.CpsSessionFactory; import org.onap.cps.spi.entities.AnchorEntity; import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; -import org.onap.cps.spi.entities.YangResourceEntity; import org.onap.cps.spi.exceptions.SessionManagerException; import org.onap.cps.spi.exceptions.SessionTimeoutException; import org.onap.cps.spi.repository.AnchorRepository; import org.onap.cps.spi.repository.DataspaceRepository; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @RequiredArgsConstructor @Slf4j @Component +@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class SessionManager { + private final CpsSessionFactory cpsSessionFactory; private final TimeLimiterProvider timeLimiterProvider; private final DataspaceRepository dataspaceRepository; private final AnchorRepository anchorRepository; - private static SessionFactory sessionFactory; - private static ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); + public static final boolean WITH_COMMIT = true; + public static final boolean WITH_ROLLBACK = false; - private synchronized void buildSessionFactory() { - if (sessionFactory == null) { - sessionFactory = new Configuration().configure("hibernate.cfg.xml") - .addAnnotatedClass(AnchorEntity.class) - .addAnnotatedClass(DataspaceEntity.class) - .addAnnotatedClass(SchemaSetEntity.class) - .addAnnotatedClass(YangResourceEntity.class) - .buildSessionFactory(); + @PostConstruct + private void postConstruct() { + final Thread shutdownHook = new Thread(this::closeAllSessionsInShutdown); + Runtime.getRuntime().addShutdownHook(shutdownHook); + } + + private void closeAllSessionsInShutdown() { + for (final String sessionId : sessionMap.keySet()) { + try { + closeSession(sessionId, WITH_ROLLBACK); + log.info("Session with session ID {} rolled back and closed", sessionId); + } catch (final Exception e) { + log.warn("Session with session ID {} failed to close", sessionId); + } } + cpsSessionFactory.closeSessionFactory(); } /** @@ -75,8 +85,7 @@ public class SessionManager { * @return Session ID string */ public String startSession() { - buildSessionFactory(); - final Session session = sessionFactory.openSession(); + final Session session = cpsSessionFactory.openSession(); final String sessionId = UUID.randomUUID().toString(); sessionMap.put(sessionId, session); session.beginTransaction(); @@ -85,14 +94,20 @@ public class SessionManager { /** * Close session. - * Locks will be released and changes will be committed. + * Changes are committed when commit boolean is set to true. + * Rollback will execute when commit boolean is set to false. * * @param sessionId session ID + * @param commit indicator whether session will commit or rollback */ - public void closeSession(final String sessionId) { + public void closeSession(final String sessionId, final boolean commit) { try { final Session session = getSession(sessionId); - session.getTransaction().commit(); + if (commit) { + session.getTransaction().commit(); + } else { + session.getTransaction().rollback(); + } session.close(); } catch (final HibernateException e) { throw new SessionManagerException("Cannot close session", @@ -162,4 +177,5 @@ public class SessionManager { } return session; } + } -- cgit 1.2.3-korg