Hibernate support (#2147)
* Hibernate support --------- Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.PostPersist;
|
||||
import jakarta.persistence.PostRemove;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreRemove;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
|
||||
/**
|
||||
* Interface for the entity interceptor lifecycle.
|
||||
*/
|
||||
// Exception squid:EmptyStatementUsageCheck - don't want to force users to
|
||||
// implement all methods
|
||||
@SuppressWarnings("squid:EmptyStatementUsageCheck")
|
||||
public interface EntityInterceptor {
|
||||
|
||||
/**
|
||||
* Callback for the {@link PrePersist} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void prePersist(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostPersist} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postPersist(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostRemove} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postRemove(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PreRemove} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void preRemove(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostLoad} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postLoad(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PreUpdate} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void preUpdate(final Object entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the {@link PostUpdate} lifecycle event.
|
||||
*
|
||||
* @param entity the model entity
|
||||
*/
|
||||
default void postUpdate(final Object entity) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||
|
||||
/**
|
||||
* {@link EclipseLinkJpaDialect} with additional exception translation
|
||||
* mechanisms based on {@link SQLStateSQLExceptionTranslator}.
|
||||
*
|
||||
* There are multiple variations of exceptions coming out of persistence
|
||||
* provider:
|
||||
*
|
||||
* <p>
|
||||
* 1) {@link PersistenceException}s that can be mapped by
|
||||
* {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}.
|
||||
* <p>
|
||||
* 2) {@link PersistenceException}s that could not be mapped by
|
||||
* {@link EclipseLinkJpaDialect} directly but instead are wrapped into
|
||||
* {@link JpaSystemException}.
|
||||
* <p>
|
||||
* 2.a) here the wrapped exception's causes might be an {@link SQLException}
|
||||
* which might be mappable by {@link SQLStateSQLExceptionTranslator} or
|
||||
* <p>
|
||||
* 2.b.) the wrapped exception's causes due not contain an {@link SQLException}
|
||||
* and as a result cannot be mapped.
|
||||
* <p>
|
||||
* 3) A {@link RuntimeException} that is no {@link PersistenceException}.
|
||||
* <p>
|
||||
* 3.a) here a cause might be an {@link SQLException} which might be mappable by
|
||||
* {@link SQLStateSQLExceptionTranslator} or
|
||||
* <p>
|
||||
* 3.b.) the cause is not an {@link SQLException} and as a result cannot be
|
||||
* mapped.
|
||||
*/
|
||||
public class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final SQLStateSQLExceptionTranslator SQLSTATE_EXCEPTION_TRANSLATOR = new SQLStateSQLExceptionTranslator();
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(@NonNull final RuntimeException ex) {
|
||||
final DataAccessException dataAccessException = super.translateExceptionIfPossible(ex);
|
||||
|
||||
if (dataAccessException == null) {
|
||||
return searchAndTranslateSqlException(ex);
|
||||
}
|
||||
return translateJpaSystemExceptionIfPossible(dataAccessException);
|
||||
}
|
||||
|
||||
private static DataAccessException translateJpaSystemExceptionIfPossible(
|
||||
final DataAccessException accessException) {
|
||||
if (!(accessException instanceof JpaSystemException)) {
|
||||
return accessException;
|
||||
}
|
||||
|
||||
final DataAccessException sqlException = searchAndTranslateSqlException(accessException);
|
||||
if (sqlException == null) {
|
||||
return accessException;
|
||||
}
|
||||
return sqlException;
|
||||
}
|
||||
|
||||
private static DataAccessException searchAndTranslateSqlException(final RuntimeException ex) {
|
||||
final SQLException sqlException = findSqlException(ex);
|
||||
if (sqlException == null) {
|
||||
return null;
|
||||
}
|
||||
return SQLSTATE_EXCEPTION_TRANSLATOR.translate("", null, sqlException);
|
||||
}
|
||||
|
||||
private static SQLException findSqlException(final RuntimeException jpaSystemException) {
|
||||
Throwable exception = jpaSystemException;
|
||||
do {
|
||||
final Throwable cause = exception.getCause();
|
||||
if (cause instanceof SQLException) {
|
||||
return (SQLException) cause;
|
||||
}
|
||||
exception = cause;
|
||||
} while (exception != null);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jakarta.persistence.Query;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
|
||||
public class Jpa {
|
||||
|
||||
public enum JpaVendor {
|
||||
ECLIPSELINK,
|
||||
HIBERNATE // NOT SUPPORTED!
|
||||
}
|
||||
|
||||
public static final JpaVendor JPA_VENDOR = JpaVendor.ECLIPSELINK;
|
||||
|
||||
public static char NATIVE_QUERY_PARAMETER_PREFIX = switch (JPA_VENDOR) {
|
||||
case ECLIPSELINK -> '?';
|
||||
case HIBERNATE -> ':';
|
||||
};
|
||||
|
||||
public static <T> String formatNativeQueryInClause(final String name, final List<T> list) {
|
||||
return switch (Jpa.JPA_VENDOR) {
|
||||
case ECLIPSELINK -> formatEclipseLinkNativeQueryInClause(IntStream.range(0, list.size()).mapToObj(i -> name + "_" + i).toList());
|
||||
case HIBERNATE -> ":" + name;
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> void setNativeQueryInParameter(final Query deleteQuery, final String name, final List<T> list) {
|
||||
if (Jpa.JPA_VENDOR == Jpa.JpaVendor.ECLIPSELINK) {
|
||||
for (int i = 0, len = list.size(); i < len; i++) {
|
||||
deleteQuery.setParameter(name + "_" + i, list.get(i));
|
||||
}
|
||||
} else if (Jpa.JPA_VENDOR == Jpa.JpaVendor.HIBERNATE) {
|
||||
deleteQuery.setParameter(name, list);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatEclipseLinkNativeQueryInClause(final Collection<String> elements) {
|
||||
return "?" + String.join(",?", elements);
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.validation.Validation;
|
||||
|
||||
@@ -81,7 +77,6 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleMetadataBuild
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
@@ -166,10 +161,7 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.hibernate.validator.BaseHibernateValidatorConfiguration;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -177,9 +169,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
@@ -191,14 +181,10 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
|
||||
/**
|
||||
@@ -213,15 +199,9 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
||||
@EnableRetry
|
||||
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
|
||||
@PropertySource("classpath:/hawkbit-jpa-defaults.properties")
|
||||
@Import({ RepositoryDefaultConfiguration.class, DataSourceAutoConfiguration.class,
|
||||
SystemManagementCacheKeyGenerator.class })
|
||||
@Import({ JpaConfiguration.class, RepositoryDefaultConfiguration.class, DataSourceAutoConfiguration.class, SystemManagementCacheKeyGenerator.class })
|
||||
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
|
||||
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
protected RepositoryApplicationConfiguration(final DataSource dataSource, final JpaProperties properties,
|
||||
final ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider) {
|
||||
super(dataSource, properties, jtaTransactionManagerProvider);
|
||||
}
|
||||
public class RepositoryApplicationConfiguration {
|
||||
|
||||
/**
|
||||
* Defines the validation processor bean.
|
||||
@@ -233,58 +213,14 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
final MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
|
||||
// ValidatorFactory shall NOT be closed because after closing the generated Validator
|
||||
// methods shall not be called - we need the validator in future
|
||||
processor.setValidator(Validation.byDefaultProvider().configure()
|
||||
.addProperty(BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS,"true")
|
||||
.buildValidatorFactory().getValidator());
|
||||
processor.setValidator(Validation.byDefaultProvider()
|
||||
.configure()
|
||||
.addProperty(org.hibernate.validator.BaseHibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, "true")
|
||||
.buildValidatorFactory()
|
||||
.getValidator());
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MultiTenantJpaTransactionManager} bean.
|
||||
*
|
||||
* @return a new {@link PlatformTransactionManager}
|
||||
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#transactionManager(ObjectProvider)
|
||||
*/
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(
|
||||
final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new MultiTenantJpaTransactionManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new EclipseLinkJpaVendorAdapter() {
|
||||
|
||||
private final HawkbitEclipseLinkJpaDialect jpaDialect = new HawkbitEclipseLinkJpaDialect();
|
||||
|
||||
@Override
|
||||
public EclipseLinkJpaDialect getJpaDialect() {
|
||||
return jpaDialect;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
final Map<String, Object> properties = new HashMap<>(7);
|
||||
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
|
||||
properties.put(PersistenceUnitProperties.WEAVING, "false");
|
||||
// needed for reports
|
||||
properties.put(PersistenceUnitProperties.ALLOW_NATIVE_SQL_QUERIES, "true");
|
||||
// flyway
|
||||
properties.put(PersistenceUnitProperties.DDL_GENERATION, "none");
|
||||
// Embed into hawkBit logging
|
||||
properties.put(PersistenceUnitProperties.LOGGING_LOGGER, "JavaLogger");
|
||||
// Ensure that we flush only at the end of the transaction
|
||||
properties.put(PersistenceUnitProperties.PERSISTENCE_CONTEXT_FLUSH_MODE, "COMMIT");
|
||||
// Enable batch writing
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING, "JDBC");
|
||||
// Batch size
|
||||
properties.put(PersistenceUnitProperties.BATCH_WRITING_SIZE, "500");
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor entityManagerBeanPostProcessor(
|
||||
@Autowired(required = false) final AccessController<JpaArtifact> artifactAccessController,
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.configuration;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.transaction.Transaction;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* {@link JpaTransactionManager} that sets the {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
|
||||
* to be done in eclipselink after a {@link Transaction} has been started.
|
||||
*/
|
||||
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
private transient TenantAware tenantAware;
|
||||
|
||||
@Override
|
||||
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
|
||||
super.doBegin(transaction, definition);
|
||||
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
if (currentTenant != null) {
|
||||
final EntityManagerFactory emFactory = Objects.requireNonNull(getEntityManagerFactory());
|
||||
final EntityManagerHolder emHolder = Objects.requireNonNull(
|
||||
(EntityManagerHolder) TransactionSynchronizationManager.getResource(emFactory),
|
||||
"No EntityManagerHolder provided by TransactionSynchronizationManager");
|
||||
final EntityManager em = emHolder.getEntityManager();
|
||||
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.executor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* A Service which calls register runnable. This runnables will be executed after a successful spring transaction commit.
|
||||
* The class is thread safe.
|
||||
*/
|
||||
@Slf4j
|
||||
public class AfterTransactionCommitDefaultServiceExecutor implements AfterTransactionCommitExecutor {
|
||||
|
||||
private static class TransactionSynchronizationImpl implements TransactionSynchronization {
|
||||
|
||||
private final List<Runnable> afterCommitRunnables = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
// Exception squid:S1217 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCommit() {
|
||||
log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
|
||||
for (final Runnable afterCommitRunnable : afterCommitRunnables) {
|
||||
log.debug("Executing runnable {}", afterCommitRunnable);
|
||||
try {
|
||||
afterCommitRunnable.run();
|
||||
} catch (final RuntimeException e) {
|
||||
log.error("Failed to execute runnable {}", afterCommitRunnable, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCompletion(final int status) {
|
||||
log.debug("Transaction completed after commit with status {}", status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
|
||||
}
|
||||
|
||||
private void afterCommit(final Runnable runnable) {
|
||||
afterCommitRunnables.add(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S1217 - we want to run this synchronous
|
||||
@SuppressWarnings("squid:S1217")
|
||||
public void afterCommit(final Runnable runnable) {
|
||||
log.debug("Submitting new runnable {} to run after transaction commit", runnable);
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.getSynchronizations().stream().filter(TransactionSynchronizationImpl.class::isInstance)
|
||||
.map(TransactionSynchronizationImpl.class::cast).findAny().orElseGet(() -> {
|
||||
final TransactionSynchronizationImpl newTS = new TransactionSynchronizationImpl();
|
||||
TransactionSynchronizationManager.registerSynchronization(newTS);
|
||||
return newTS;
|
||||
}).afterCommit(runnable);
|
||||
} else {
|
||||
log.info("Transaction synchronization is NOT ACTIVE/ INACTIVE. Executing right now runnable {}", runnable);
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.executor;
|
||||
|
||||
/**
|
||||
* A interface to register a runnable, which will be executed after a successful
|
||||
* spring transaction.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AfterTransactionCommitExecutor {
|
||||
|
||||
/**
|
||||
* Register a runnable which will be executed after a successful spring
|
||||
* transaction.
|
||||
*
|
||||
* @param runnable the after commit runnable
|
||||
*/
|
||||
void afterCommit(Runnable runnable);
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -36,7 +35,6 @@ import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException;
|
||||
import org.eclipse.hawkbit.repository.jpa.EncryptionAwareDbArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
@@ -53,17 +51,13 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,6 @@ import jakarta.validation.constraints.NotEmpty;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.FilterParams;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
@@ -90,7 +89,6 @@ import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PostPersist;
|
||||
import jakarta.persistence.PostRemove;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
import jakarta.persistence.Version;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Base hawkBit entity class containing the common attributes.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
|
||||
@MappedSuperclass
|
||||
@EntityListeners({ AuditingEntityListener.class, EntityInterceptorListener.class })
|
||||
public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
|
||||
protected static final int USERNAME_FIELD_LENGTH = 64;
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Setter // should be used just for test purposes
|
||||
@Getter
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Setter // should be used just for test purposes
|
||||
@Getter
|
||||
@Version
|
||||
@Column(name = "optlock_revision")
|
||||
private int optLockRevision;
|
||||
|
||||
// Audit fields. use property access to ensure that setters will be called and checked for modification
|
||||
// (touch implementation depends on setLastModifiedAt(1).
|
||||
@Column(name = "created_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH)
|
||||
private String createdBy;
|
||||
@Column(name = "created_at", updatable = false, nullable = false)
|
||||
private long createdAt;
|
||||
@Column(name = "last_modified_by", nullable = false, length = USERNAME_FIELD_LENGTH)
|
||||
private String lastModifiedBy;
|
||||
@Column(name = "last_modified_at", nullable = false)
|
||||
private long lastModifiedAt;
|
||||
|
||||
@CreatedBy
|
||||
public void setCreatedBy(final String createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
// maybe needed to have correct createdBy value in the database
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
@CreatedDate
|
||||
public void setCreatedAt(final long createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
// property access to make entity manager to detect touch
|
||||
@Access(AccessType.PROPERTY)
|
||||
public long getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
@LastModifiedBy
|
||||
public void setLastModifiedBy(final String lastModifiedBy) {
|
||||
if (this.lastModifiedBy != null && isController()) {
|
||||
// initialized and controller = doesn't update
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedBy = lastModifiedBy;
|
||||
}
|
||||
|
||||
// property access to make entity manager to detect touch
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getLastModifiedBy() {
|
||||
return lastModifiedBy == null ? createdBy : lastModifiedBy;
|
||||
}
|
||||
|
||||
@LastModifiedDate
|
||||
public void setLastModifiedAt(final long lastModifiedAt) {
|
||||
if (this.lastModifiedAt != 0 && isController()) {
|
||||
// initialized and controller = doesn't update
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastModifiedAt = lastModifiedAt;
|
||||
}
|
||||
|
||||
// property access to make entity manager to detect touch
|
||||
@Access(AccessType.PROPERTY)
|
||||
public long getLastModifiedAt() {
|
||||
return lastModifiedAt == 0 ? createdAt : lastModifiedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined equals/hashcode strategy for the repository in general is that an entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*/
|
||||
@Override
|
||||
// Exception squid:S864 - generated code
|
||||
@SuppressWarnings({ "squid:S864" })
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
result = prime * result + optLockRevision;
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined equals/hashcode strategy for the repository in general is that an entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(this.getClass().isInstance(obj))) {
|
||||
return false;
|
||||
}
|
||||
final AbstractJpaBaseEntity other = (AbstractJpaBaseEntity) obj;
|
||||
final Long id = getId();
|
||||
final Long otherId = other.getId();
|
||||
if (id == null) {
|
||||
if (otherId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(otherId)) {
|
||||
return false;
|
||||
}
|
||||
return getOptLockRevision() == other.getOptLockRevision();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + " [id=" + id + "]";
|
||||
}
|
||||
|
||||
@PostPersist
|
||||
public void postInsert() {
|
||||
if (this instanceof EventAwareEntity eventAwareEntity) {
|
||||
doNotify(eventAwareEntity::fireCreateEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@PostUpdate
|
||||
public void postUpdate() {
|
||||
if (this instanceof EventAwareEntity eventAwareEntity) {
|
||||
doNotify(eventAwareEntity::fireUpdateEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@PostRemove
|
||||
public void postDelete() {
|
||||
if (this instanceof EventAwareEntity eventAwareEntity) {
|
||||
doNotify(eventAwareEntity::fireDeleteEvent);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void doNotify(final Runnable runnable) {
|
||||
// fire events onl AFTER transaction commit
|
||||
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
|
||||
}
|
||||
|
||||
private boolean isController() {
|
||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
||||
&& SecurityContextHolder.getContext().getAuthentication()
|
||||
.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails
|
||||
&& tenantAwareDetails.isController();
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.persistence.annotations.Multitenant;
|
||||
import org.eclipse.persistence.annotations.MultitenantType;
|
||||
import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
||||
|
||||
/**
|
||||
* Holder of the base attributes common to all tenant aware entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
|
||||
@Setter
|
||||
@Getter
|
||||
@MappedSuperclass
|
||||
// Eclipse link MultiTenant support
|
||||
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
||||
@Multitenant(MultitenantType.SINGLE_TABLE)
|
||||
public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEntity implements TenantAwareBaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
||||
@Size(min = 1, max = 40)
|
||||
@NotNull
|
||||
private String tenant;
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of different tenants.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (tenant == null ? 0 : tenant.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*/
|
||||
@Override
|
||||
// exception squid:S2259 - obj is checked for null in super
|
||||
@SuppressWarnings("squid:S2259")
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
final AbstractJpaTenantAwareBaseEntity other = (AbstractJpaTenantAwareBaseEntity) obj;
|
||||
return Objects.equals(getTenant(), other.getTenant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseEntity [id=" + super.getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* PrePersist listener method for all {@link TenantAwareBaseEntity} entities.
|
||||
*
|
||||
* // TODO - check if the tenant support should set tenant from context
|
||||
* // TODO - should we check if tenant exists in the system? Note: seems it's not good to work with db in the listener
|
||||
*/
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by using the TenantAware service
|
||||
final String currentTenant = TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException(
|
||||
String.format(
|
||||
"Tenant %s does not exists, cannot create entity %s with id %d",
|
||||
TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant(), getClass(), getId()));
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.PostPersist;
|
||||
import jakarta.persistence.PostRemove;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreRemove;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.EntityInterceptor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
|
||||
|
||||
/**
|
||||
* Entity listener which calls the callback's of all registered entity interceptors.
|
||||
*/
|
||||
public class EntityInterceptorListener {
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre persist</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.prePersist(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>post persist</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostPersist
|
||||
public void postPersist(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.postPersist(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>post remove</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostRemove
|
||||
public void postRemove(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.postRemove(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre remove</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PreRemove
|
||||
public void preRemove(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.preRemove(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>post load</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostLoad
|
||||
public void postLoad(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.postLoad(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>pre update</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PreUpdate
|
||||
public void preUpdate(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.preUpdate(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for lifecyle event <i>post update</i>.
|
||||
*
|
||||
* @param entity the JPA entity which this listener is associated with
|
||||
*/
|
||||
@PostUpdate
|
||||
public void postUpdate(final Object entity) {
|
||||
notifyAll(interceptor -> interceptor.postUpdate(entity));
|
||||
}
|
||||
|
||||
private static void notifyAll(final Consumer<? super EntityInterceptor> action) {
|
||||
EntityInterceptorHolder.getInstance().getEntityInterceptors().forEach(action);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
/**
|
||||
* Interfaces which can be implemented by entities to be called when the entity should fire an event because the entity has been created,
|
||||
* updated or deleted.
|
||||
*/
|
||||
public interface EventAwareEntity {
|
||||
|
||||
/**
|
||||
* Fired for the Entity creation.
|
||||
*/
|
||||
void fireCreateEvent();
|
||||
|
||||
/**
|
||||
* Fired for the Entity update.
|
||||
*/
|
||||
void fireUpdateEvent();
|
||||
|
||||
/**
|
||||
* Fired for the Entity deletion.
|
||||
*/
|
||||
void fireDeleteEvent();
|
||||
}
|
||||
@@ -19,8 +19,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
@@ -37,7 +35,6 @@ import jakarta.persistence.NamedEntityGraph;
|
||||
import jakarta.persistence.NamedEntityGraphs;
|
||||
import jakarta.persistence.NamedSubgraph;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
@@ -56,7 +53,6 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Action}.
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
@@ -27,7 +26,6 @@ import jakarta.persistence.Convert;
|
||||
import jakarta.persistence.Converter;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Index;
|
||||
@@ -67,11 +65,6 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
|
||||
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||
import org.hibernate.event.spi.PreUpdateEvent;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Target}.
|
||||
@@ -88,7 +81,6 @@ import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
uniqueConstraints = @UniqueConstraint(columnNames = { "controller_id", "tenant" }, name = "uk_tenant_controller_id"))
|
||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||
@SuppressWarnings("squid:S2160")
|
||||
@EntityListeners({ JpaTarget.EntityPropertyChangeListener.class }) // add listener to the listeners declared into suppers
|
||||
@Slf4j
|
||||
public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity {
|
||||
|
||||
@@ -330,40 +322,4 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
), null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to updates on {@link JpaTarget} entities, Filtering out updates that only change the "lastTargetQuery" or "address" fields.
|
||||
*/
|
||||
public static class EntityPropertyChangeListener extends DescriptorEventAdapter implements PreUpdateEventListener {
|
||||
|
||||
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = List.of(
|
||||
"lastTargetQuery", "address", // actual to be skipped
|
||||
"optLockRevision", "lastModifiedAt", "lastModifiedBy" // system to be skipped
|
||||
);
|
||||
|
||||
@Override
|
||||
public void postUpdate(final DescriptorEvent event) {
|
||||
final Object object = event.getObject();
|
||||
if (((UpdateObjectQuery) event.getQuery()).getObjectChangeSet().getChangedAttributeNames().stream()
|
||||
.anyMatch(field -> !TARGET_UPDATE_EVENT_IGNORE_FIELDS.contains(field))) {
|
||||
doNotify(() -> ((EventAwareEntity) object).fireUpdateEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreUpdate(final PreUpdateEvent event) {
|
||||
final Object[] oldState = event.getOldState();
|
||||
final Object[] newState = event.getState();
|
||||
for (int i = 0; i < newState.length; i++) {
|
||||
if (!Objects.equals(oldState[i], newState[i])) {
|
||||
final String attribute = event.getPersister().getAttributeMapping(i).getAttributeName();
|
||||
if (!TARGET_UPDATE_EVENT_IGNORE_FIELDS.contains(attribute)) {
|
||||
doNotify(() -> ((EventAwareEntity) event.getEntity()).fireUpdateEvent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to provide it to in beans not instantiated by spring e.g. JPA
|
||||
* entities which cannot be autowired.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class AfterTransactionCommitExecutorHolder {
|
||||
|
||||
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static AfterTransactionCommitExecutorHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the afterCommit
|
||||
*/
|
||||
public AfterTransactionCommitExecutor getAfterCommit() {
|
||||
return afterCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param afterCommit the afterCommit to set
|
||||
*/
|
||||
public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) {
|
||||
this.afterCommit = afterCommit;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.EntityInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link EntityInterceptor} to have all
|
||||
* interceptors in spring beans.
|
||||
*/
|
||||
public final class EntityInterceptorHolder {
|
||||
|
||||
private static final EntityInterceptorHolder SINGLETON = new EntityInterceptorHolder();
|
||||
|
||||
@Autowired(required = false)
|
||||
private final List<EntityInterceptor> entityInterceptors = new ArrayList<>();
|
||||
|
||||
private EntityInterceptorHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the entity intreceptor holder singleton instance
|
||||
*/
|
||||
public static EntityInterceptorHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
public List<EntityInterceptor> getEntityInterceptors() {
|
||||
return entityInterceptors;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link SecurityTokenGenerator} and make it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
public final class SecurityTokenGeneratorHolder {
|
||||
|
||||
private static final SecurityTokenGeneratorHolder INSTANCE = new SecurityTokenGeneratorHolder();
|
||||
|
||||
@Autowired
|
||||
private SecurityTokenGenerator securityTokenGenerator;
|
||||
|
||||
/**
|
||||
* private constructor.
|
||||
*/
|
||||
private SecurityTokenGeneratorHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a singleton instance of the security token generator holder.
|
||||
*/
|
||||
public static SecurityTokenGeneratorHolder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to {@link SecurityTokenGenerator#generateToken()}.
|
||||
*
|
||||
* @return the result {@link SecurityTokenGenerator#generateToken()}
|
||||
*/
|
||||
public String generateToken() {
|
||||
return securityTokenGenerator.generateToken();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link TenantAware} service and makes it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
public final class TenantAwareHolder {
|
||||
|
||||
private static final TenantAwareHolder INSTANCE = new TenantAwareHolder();
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
private TenantAwareHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton {@link TenantAwareHolder} instance
|
||||
*/
|
||||
public static TenantAwareHolder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantAware} service
|
||||
*/
|
||||
public TenantAware getTenantAware() {
|
||||
return tenantAware;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import org.eclipse.hawkbit.repository.rsql.SuggestToken;
|
||||
import org.eclipse.hawkbit.repository.rsql.SuggestionContext;
|
||||
import org.eclipse.hawkbit.repository.rsql.SyntaxErrorContext;
|
||||
import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext;
|
||||
import org.eclipse.persistence.exceptions.ConversionException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -75,8 +74,12 @@ public class RsqlParserValidationOracle implements RsqlValidationOracle {
|
||||
} catch (final RSQLParameterUnsupportedFieldException | IllegalArgumentException ex) {
|
||||
errorContext.setErrorMessage(getCustomMessage(ex.getMessage(), null));
|
||||
log.trace("Illegal argument on parsing :", ex);
|
||||
} catch (@SuppressWarnings("squid:S1166") final ConversionException | JpaSystemException e) {
|
||||
} catch (final JpaSystemException e) {
|
||||
// noop
|
||||
} catch (final RuntimeException e) {
|
||||
if (!"org.eclipse.persistence.exceptions.ConversionException".equals(e.getClass().getName())) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.utils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
public class MapAttributeConverter<JAVA_TYPE extends Enum<JAVA_TYPE>, DB_TYPE> implements AttributeConverter<JAVA_TYPE, DB_TYPE> {
|
||||
|
||||
private final Map<JAVA_TYPE, DB_TYPE> javaToDbMap;
|
||||
private final Map<DB_TYPE, JAVA_TYPE> dbToJavaMap;
|
||||
private final DB_TYPE nullMapping;
|
||||
|
||||
protected MapAttributeConverter(final Map<JAVA_TYPE, DB_TYPE> javaToDbMap, final DB_TYPE nullMapping) {
|
||||
this.javaToDbMap = javaToDbMap;
|
||||
this.nullMapping = nullMapping;
|
||||
this.dbToJavaMap = javaToDbMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
|
||||
if (javaToDbMap.size() != dbToJavaMap.size()) {
|
||||
throw new IllegalArgumentException("Duplicate values in javaToDbMap");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DB_TYPE convertToDatabaseColumn(final JAVA_TYPE attribute) {
|
||||
if (attribute == null) {
|
||||
return nullMapping;
|
||||
}
|
||||
return javaToDbMap.get(attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JAVA_TYPE convertToEntityAttribute(final DB_TYPE dbData) {
|
||||
if (Objects.equals(dbData, nullMapping)) {
|
||||
return null;
|
||||
}
|
||||
return dbToJavaMap.get(dbData);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user