Skip to content
Snippets Groups Projects
Commit c213ec93 authored by Martin Weise's avatar Martin Weise
Browse files

Fixed the session bug in the database service

parent f9ec4a8f
No related branches found
No related tags found
2 merge requests!81New stable release,!80Multiple features connected with user management and ownership of databases
This commit is part of merge request !81. Comments created here will be created in the context of that merge request.
......@@ -178,15 +178,6 @@
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application*.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
......
......@@ -122,7 +122,8 @@ public class ContainerDatabaseEndpoint {
public ResponseEntity<?> delete(@NotBlank @PathVariable("id") Long containerId,
@NotBlank @PathVariable Long databaseId,
Principal principal) throws DatabaseNotFoundException,
ImageNotSupportedException, DatabaseMalformedException, AmqpException, ContainerConnectionException {
ImageNotSupportedException, DatabaseMalformedException, AmqpException, ContainerConnectionException,
ContainerNotFoundException {
final Database database = databaseService.findById(containerId, databaseId);
messageQueueService.deleteExchange(database);
databaseService.delete(containerId, databaseId, principal);
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="current_session_context_class">thread</property>
<property name="transaction.coordinator_class">jdbc</property>
<property name="c3p0.min_size">5</property>
<property name="c3p0.max_size">30</property>
<property name="c3p0.acquire_increment">5</property>
<property name="c3p0.timeout">1800</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
package at.tuwien.service;
import at.tuwien.entities.container.Container;
import at.tuwien.exception.ContainerNotFoundException;
public interface ContainerService {
Container find(Long id) throws ContainerNotFoundException;
}
......@@ -63,7 +63,7 @@ public interface DatabaseService {
*/
void delete(Long id, Long databaseId, Principal principal)
throws DatabaseNotFoundException, ImageNotSupportedException,
DatabaseMalformedException, AmqpException, ContainerConnectionException;
DatabaseMalformedException, AmqpException, ContainerConnectionException, ContainerNotFoundException;
/**
* Creates a new database with minimal metadata in the metadata database and creates a new database on the container.
......@@ -95,23 +95,4 @@ public interface DatabaseService {
*/
Database modify(Long id, Long databaseId, DatabaseModifyDto modifyDto)
throws UserNotFoundException, DatabaseNotFoundException, LicenseNotFoundException;
/**
* Returns a new session for a given {@link Database} entity.
*
* @param database The database entity.
* @return A new session if successful.
* @throws ContainerConnectionException The container is not reachable from the database service.
* @throws DatabaseMalformedException The database is malformed e.g. a session can be created.
*/
Session getSession(Database database) throws ContainerConnectionException, DatabaseMalformedException;
/**
* Returns a new transaction for a given session.
*
* @param session The session.
* @return The transaction if successful.
* @throws ContainerConnectionException When no connection to the remote database fails (e.g. the container is not running).
*/
Transaction getTransaction(Session session) throws ContainerConnectionException;
}
package at.tuwien.service.impl;
import at.tuwien.entities.container.Container;
import at.tuwien.exception.ContainerNotFoundException;
import at.tuwien.repository.jpa.ContainerRepository;
import at.tuwien.service.ContainerService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Log4j2
@Service
public class ContainerServiceImpl implements ContainerService {
private final ContainerRepository containerRepository;
@Autowired
public ContainerServiceImpl(ContainerRepository containerRepository) {
this.containerRepository = containerRepository;
}
@Override
public Container find(Long id) throws ContainerNotFoundException {
final Optional<Container> optional = containerRepository.findById(id);
if (optional.isEmpty()) {
log.error("Failed to find container with id {}", id);
throw new ContainerNotFoundException("Failed to find container");
}
return optional.get();
}
}
package at.tuwien.service.impl;
import at.tuwien.entities.container.Container;
import at.tuwien.entities.container.image.ContainerImage;
import at.tuwien.entities.container.image.ContainerImageEnvironmentItem;
import at.tuwien.entities.container.image.ContainerImageEnvironmentItemType;
import at.tuwien.entities.database.Database;
import lombok.extern.log4j.Log4j2;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.stereotype.Service;
......@@ -15,37 +18,35 @@ import java.util.stream.Collectors;
@Service
public abstract class HibernateConnector {
private static final Integer MIN_SIZE = 5;
private static final Integer MAX_SIZE = 30;
private static final Integer INCREMENT_SIZE = 5;
private static final Integer TIMEOUT = 1800;
private static final String SESSION_CONTEXT = "thread";
private static final String COORDINATOR_CLASS = "jdbc";
@Transactional
protected SessionFactory getSessionFactory(Database database) {
final String url = "jdbc:" + database.getContainer().getImage().getJdbcMethod() + "://" + database.getContainer().getInternalName();
log.trace("hibernate jdbc url '{}'", url);
final String password = database.getContainer().getImage().getEnvironment()
protected static Session getCurrentSession(ContainerImage image, Container container, Database database) {
final String url = "jdbc:" + image.getJdbcMethod() + "://" + container.getInternalName() + "/" + database.getInternalName();
final String username = image.getEnvironment()
.stream()
.filter(e -> e.getType().equals(ContainerImageEnvironmentItemType.PASSWORD))
.filter(e -> e.getType().equals(ContainerImageEnvironmentItemType.PRIVILEGED_USERNAME))
.map(ContainerImageEnvironmentItem::getValue)
.collect(Collectors.toList())
.get(0);
final Configuration configuration = new Configuration()
.setProperty("hibernate.connection.url", url)
.setProperty("hibernate.connection.username", "root")
.setProperty("hibernate.connection.password", password)
.setProperty("hibernate.connection.driver_class", database.getContainer().getImage().getDriverClass())
.setProperty("hibernate.dialect", database.getContainer().getImage().getDialect())
.setProperty("hibernate.current_session_context_class", SESSION_CONTEXT)
.setProperty("hibernate.transaction.coordinator_class", COORDINATOR_CLASS)
.setProperty("hibernate.hbm2ddl.auto", "update")
.setProperty("hibernate.c3p0.min_size", String.valueOf(MIN_SIZE))
.setProperty("hibernate.c3p0.max_size", String.valueOf(MAX_SIZE))
.setProperty("hibernate.c3p0.acquire_increment", String.valueOf(INCREMENT_SIZE))
.setProperty("hibernate.c3p0.timeout", String.valueOf(TIMEOUT));
return configuration.buildSessionFactory();
final String password = image.getEnvironment()
.stream()
.filter(e -> e.getType().equals(ContainerImageEnvironmentItemType.PRIVILEGED_PASSWORD))
.map(ContainerImageEnvironmentItem::getValue)
.collect(Collectors.toList())
.get(0);
final Configuration config = new Configuration();
config.configure("mariadb_hibernate.cfg.xml");
config.setProperty("hibernate.connection.url", url);
config.setProperty("hibernate.connection.username", username);
config.setProperty("hibernate.connection.password", password);
config.setProperty("hibernate.connection.driver_class", image.getDriverClass());
config.setProperty("hibernate.dialect", image.getDialect());
final SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
if (!session.isOpen()) {
log.warn("Session is closed, opening...");
session = sessionFactory.openSession();
}
return session;
}
......
......@@ -4,7 +4,6 @@ import at.tuwien.api.database.DatabaseCreateDto;
import at.tuwien.api.database.DatabaseModifyDto;
import at.tuwien.entities.container.Container;
import at.tuwien.entities.database.Database;
import at.tuwien.entities.database.LanguageType;
import at.tuwien.entities.database.License;
import at.tuwien.entities.user.User;
import at.tuwien.exception.*;
......@@ -13,15 +12,13 @@ import at.tuwien.mapper.DatabaseMapper;
import at.tuwien.repository.jpa.ContainerRepository;
import at.tuwien.repository.jpa.DatabaseRepository;
import at.tuwien.repository.elastic.DatabaseidxRepository;
import at.tuwien.service.ContainerService;
import at.tuwien.service.DatabaseService;
import at.tuwien.service.LicenseService;
import at.tuwien.service.UserService;
import lombok.extern.log4j.Log4j2;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.query.NativeQuery;
import org.hibernate.service.spi.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -44,6 +41,7 @@ public class MariaDbServiceImpl extends HibernateConnector implements DatabaseSe
private final LicenseService licenseService;
private final DatabaseMapper databaseMapper;
private final RabbitMqServiceImpl amqpService;
private final ContainerService containerService;
private final DatabaseRepository databaseRepository;
private final ContainerRepository containerRepository;
private final DatabaseidxRepository databaseidxRepository;
......@@ -51,13 +49,14 @@ public class MariaDbServiceImpl extends HibernateConnector implements DatabaseSe
@Autowired
public MariaDbServiceImpl(AmqpMapper amqpMapper, UserService userService, LicenseService licenseService,
DatabaseMapper databaseMapper, RabbitMqServiceImpl amqpService,
DatabaseRepository databaseRepository, ContainerRepository containerRepository,
DatabaseidxRepository databaseidxRepository) {
ContainerService containerService, DatabaseRepository databaseRepository,
ContainerRepository containerRepository, DatabaseidxRepository databaseidxRepository) {
this.amqpMapper = amqpMapper;
this.userService = userService;
this.licenseService = licenseService;
this.databaseMapper = databaseMapper;
this.amqpService = amqpService;
this.containerService = containerService;
this.databaseRepository = databaseRepository;
this.containerRepository = containerRepository;
this.databaseidxRepository = databaseidxRepository;
......@@ -107,23 +106,24 @@ public class MariaDbServiceImpl extends HibernateConnector implements DatabaseSe
@Override
@Transactional
public void delete(Long containerId, Long databaseId, Principal principal) throws DatabaseNotFoundException,
ImageNotSupportedException, DatabaseMalformedException, ContainerConnectionException, AmqpException {
ImageNotSupportedException, DatabaseMalformedException, ContainerConnectionException, AmqpException,
ContainerNotFoundException {
final Container container = containerService.find(containerId);
final Database database = findPublicOrMineById(containerId, databaseId, principal);
if (!database.getContainer().getImage().getRepository().equals("mariadb")) {
throw new ImageNotSupportedException("Currently only MariaDB is supported");
}
/* run query */
final Session session = getSession(database);
final Transaction transaction = getTransaction(session);
final Session session = getCurrentSession(container.getImage(), container, database);
final Transaction transaction = session.beginTransaction();
final NativeQuery<?> query = session.createSQLQuery(databaseMapper.databaseToRawDeleteDatabaseQuery(database));
try {
try (session) {
log.debug("query affected {} rows", query.executeUpdate());
transaction.commit();
} catch (ServiceException e) {
log.error("Failed to delete database.");
throw new DatabaseMalformedException("Failed to delete database", e);
}
transaction.commit();
session.close();
database.setDeleted(Instant.now()) /* method has void, only for debug logs */;
/* save in metadata database */
databaseRepository.deleteById(databaseId);
......@@ -135,38 +135,34 @@ public class MariaDbServiceImpl extends HibernateConnector implements DatabaseSe
@Override
@Transactional
public Database create(Long id, DatabaseCreateDto createDto, Principal principal)
public Database create(Long containerId, DatabaseCreateDto createDto, Principal principal)
throws ImageNotSupportedException, ContainerNotFoundException,
DatabaseMalformedException, AmqpException, ContainerConnectionException, UserNotFoundException {
final Optional<Container> container = containerRepository.findById(id);
if (container.isEmpty()) {
log.warn("Container with id {} does not exist", id);
throw new ContainerNotFoundException("Container does not exist.");
}
final Container container = containerService.find(containerId);
/* start the object */
final Database database = new Database();
database.setName(createDto.getName());
database.setInternalName(databaseMapper.nameToInternalName(database.getName()));
database.setContainer(container.get());
database.setContainer(container);
/* run query */
final Session session = getSession(database);
final Transaction transaction = getTransaction(session);
final Session session = getCurrentSession(container.getImage(), container, database);
final Transaction transaction = session.beginTransaction();
final NativeQuery<?> query = session.createSQLQuery(databaseMapper.databaseToRawCreateDatabaseQuery(database));
try {
log.debug("query affected {} rows", query.executeUpdate());
} catch (PersistenceException e) {
log.error("Failed to delete database.");
session.close();
throw new DatabaseMalformedException("Failed to delete database", e);
}
final NativeQuery<?> grant = session.createSQLQuery(databaseMapper.imageToRawGrantReadonlyAccessQuery());
try {
try (session) {
log.debug("grant affected {} rows", grant.executeUpdate());
transaction.commit();
} catch (PersistenceException e) {
log.error("Failed to grant privileges.");
throw new DatabaseMalformedException("Failed to grant privileges", e);
}
transaction.commit();
session.close();
/* save in metadata database */
database.setExchange(amqpMapper.exchangeName(database));
database.setDescription(createDto.getDescription());
......@@ -207,36 +203,4 @@ public class MariaDbServiceImpl extends HibernateConnector implements DatabaseSe
return out;
}
@Override
public Session getSession(Database database) throws ContainerConnectionException, DatabaseMalformedException {
final SessionFactory factory;
try {
factory = getSessionFactory(database);
} catch (HibernateException e) {
log.error("Connection failed: {}", e.getMessage());
log.throwing(e);
throw new ContainerConnectionException("Connection failed", e);
}
final Session session;
try {
session = factory.openSession();
} catch (HibernateException e) {
log.error("Session failed");
throw new DatabaseMalformedException("Session failed", e);
}
return session;
}
@Override
public Transaction getTransaction(Session session) throws ContainerConnectionException {
final Transaction transaction;
try {
transaction = session.beginTransaction();
} catch (GenericJDBCException e) {
log.error("Failed to begin transaction");
throw new ContainerConnectionException("Failed to begin transaction", e);
}
return transaction;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment