Remove swagger and minor feature extensions and bug fixes

- Various Bug fixes and improvements
- Management API extended
- Swagger removed
- Guava Upgraded to 19
This commit is contained in:
Kai Zimmermann
2016-01-21 13:42:38 +01:00
parent fb9dfd204c
commit 64deaeea3c
813 changed files with 9787 additions and 4929 deletions

0
hawkbit-repository/src/fbExcludeFilter.xml Executable file → Normal file
View File

View File

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.aspects.SpExceptionMappingAspect;
import org.eclipse.hawkbit.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
import org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper;
@@ -123,11 +123,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
}
/**
* @return {@link SpExceptionMappingAspect} aspect bean
* @return {@link ExceptionMappingAspectHandler} aspect bean
*/
@Bean
public SpExceptionMappingAspect createRepositoryExceptionHandlerAdvice() {
return new SpExceptionMappingAspect();
public ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
return new ExceptionMappingAspectHandler();
}
/*

View File

@@ -0,0 +1,179 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.aspects;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.transaction.TransactionManager;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.hawkbit.exception.GenericSpServerException;
import org.eclipse.hawkbit.repository.exception.ConcurrentModificationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.transaction.TransactionSystemException;
/**
* {@link Aspect} catches persistence exceptions and wraps them to custom
* specific exceptions Additionally it checks and prevents access to certain
* packages. Logging aspect which logs the call stack
*
*
*
*/
@Aspect
public class ExceptionMappingAspectHandler implements Ordered {
private static final Logger LOG = LoggerFactory.getLogger(ExceptionMappingAspectHandler.class);
private static final Map<String, String> EXCEPTION_MAPPING = new HashMap<>();
/**
* this is required to enable a certain order of exception and to select the
* most specific mappable exception according to the type hierarchy of the
* exception.
*/
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = new ArrayList<>();
@Autowired
private JpaVendorAdapter jpaVendorAdapter;
private final SQLStateSQLExceptionTranslator sqlStateExceptionTranslator = new SQLStateSQLExceptionTranslator();
static {
MAPPED_EXCEPTION_ORDER.add(DuplicateKeyException.class);
MAPPED_EXCEPTION_ORDER.add(DataIntegrityViolationException.class);
MAPPED_EXCEPTION_ORDER.add(ConcurrencyFailureException.class);
MAPPED_EXCEPTION_ORDER.add(AccessDeniedException.class);
EXCEPTION_MAPPING.put(DuplicateKeyException.class.getName(), EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(DataIntegrityViolationException.class.getName(),
EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(ConcurrencyFailureException.class.getName(),
ConcurrentModificationException.class.getName());
EXCEPTION_MAPPING.put(AccessDeniedException.class.getName(), InsufficientPermissionException.class.getName());
}
/**
* catch exceptions of the {@link TransactionManager} and wrap them to
* custom exceptions.
*
* @param ex
* the thrown and catched exception
* @throws Throwable
*/
@AfterThrowing(pointcut = "( execution( * org.springframework.transaction..*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
LOG.trace("exception occured", ex);
Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex);
if (translatedAccessException == null && ex instanceof TransactionSystemException) {
final TransactionSystemException systemException = (TransactionSystemException) ex;
translatedAccessException = translateEclipseLinkExceptionIfPossible((Exception) systemException
.getOriginalException());
}
if (translatedAccessException == null) {
translatedAccessException = ex;
}
Exception mappingException = translatedAccessException;
LOG.trace("translated excpetion is", translatedAccessException);
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
if (mappedEx.isAssignableFrom(translatedAccessException.getClass())) {
if (!EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
LOG.error("there is no mapping configured for exception class {}", mappedEx.getName());
mappingException = new GenericSpServerException(ex);
} else {
mappingException = (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName()))
.getConstructor(Throwable.class).newInstance(ex);
}
break;
}
}
LOG.trace("mapped exception {} to {}", translatedAccessException.getClass(), mappingException.getClass());
throw mappingException;
}
private DataAccessException translateEclipseLinkExceptionIfPossible(final Exception exception) {
final DataAccessException translatedAccessException = jpaVendorAdapter.getJpaDialect()
.translateExceptionIfPossible((RuntimeException) exception);
return translateSQLStateExceptionIfPossible(translatedAccessException);
}
/**
* There is no EclipseLinkExceptionTranslator. So we have to check and
* translate the exception by the sql error code. Luckily, there we can use
* {@link SQLStateSQLExceptionTranslator} if we can get a
* {@link SQLException}.
*
* @param accessException
* the base access exception from jpa
* @return the translated accessException
*/
private DataAccessException translateSQLStateExceptionIfPossible(final DataAccessException accessException) {
if (!(accessException instanceof JpaSystemException)) {
return accessException;
}
final SQLException ex = findSqlException((JpaSystemException) accessException);
if (ex == null) {
return accessException;
}
return sqlStateExceptionTranslator.translate(null, null, ex);
}
private static SQLException findSqlException(final JpaSystemException jpaSystemException) {
Throwable exception = jpaSystemException.getCause();
while (exception != null) {
final Throwable cause = exception.getCause();
if (cause instanceof SQLException) {
return (SQLException) cause;
}
exception = cause;
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.springframework.core.Ordered#getOrder()
*/
@Override
public int getOrder() {
return 1;
}
}

View File

@@ -1,108 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.aspects;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.transaction.TransactionManager;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.hawkbit.exception.GenericSpServerException;
import org.eclipse.hawkbit.repository.exception.ConcurrentModificationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.access.AccessDeniedException;
/**
* {@link Aspect} catches persistence exceptions and wraps them to custom
* specific exceptions Additionally it checks and prevents access to certain
* packages. Logging aspect which logs the call stack
*
*
*
*/
@Aspect
public class SpExceptionMappingAspect implements Ordered {
private static final Logger LOG = LoggerFactory.getLogger(SpExceptionMappingAspect.class);
private static final Map<String, String> EXCEPTION_MAPPING = new HashMap<String, String>();
/**
* this is required to enable a certain order of exception and to select the
* most specific mappable exception according to the type hierarchy of the
* exception.
*/
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = new ArrayList<Class<?>>();
static {
MAPPED_EXCEPTION_ORDER.add(DuplicateKeyException.class);
MAPPED_EXCEPTION_ORDER.add(DataIntegrityViolationException.class);
MAPPED_EXCEPTION_ORDER.add(ConcurrencyFailureException.class);
MAPPED_EXCEPTION_ORDER.add(AccessDeniedException.class);
EXCEPTION_MAPPING.put(DuplicateKeyException.class.getName(), EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(ConcurrencyFailureException.class.getName(),
ConcurrentModificationException.class.getName());
EXCEPTION_MAPPING.put(AccessDeniedException.class.getName(), InsufficientPermissionException.class.getName());
}
/**
* catch exceptions of the {@link TransactionManager} and wrap them to
* custom exceptions.
*
* @param ex
* the thrown and catched exception
* @throws Throwable
*/
@AfterThrowing(pointcut = "( execution( * org.springframework.transaction..*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
final Class<? extends Exception> exClass = ex.getClass();
Exception newEx = ex;
LOG.trace("exception occured", ex);
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
if (mappedEx.isAssignableFrom(exClass)) {
if (!EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
LOG.error("there is no mapping configured for exception class {}", mappedEx.getName());
newEx = new GenericSpServerException(ex);
} else {
newEx = (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName()))
.getConstructor(Throwable.class).newInstance(ex);
}
break;
}
}
LOG.trace("mapped exception {} to {}", ex.getClass(), newEx.getClass());
throw newEx;
}
/*
* (non-Javadoc)
*
* @see org.springframework.core.Ordered#getOrder()
*/
@Override
public int getOrder() {
return 1;
}
}

View File

View File

View File

View File

@@ -18,6 +18,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.eclipse.hawkbit.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.TargetRepository;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.Target;
@@ -49,6 +50,9 @@ public class EntityChangeEventListener {
@Autowired
private EntityManager entityManager;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* In case the a {@link Target} is created a corresponding
* {@link TargetInfo} is created as well. We need the {@link TargetInfo}
@@ -68,10 +72,6 @@ public class EntityChangeEventListener {
final Object result = joinpoint.proceed();
if (result instanceof TargetInfo) {
if (isNew) {
// we need to flush here because seems like eclipselink
// implementation setting the ID of the target no immediately
// otherwise.
entityManager.flush();
notifyTargetCreated(entityManager.merge(entityManager.merge(((TargetInfo) result).getTarget())));
} else {
notifyTargetInfoChanged((TargetInfo) result);
@@ -129,15 +129,16 @@ public class EntityChangeEventListener {
}
private void notifyTargetCreated(final Target t) {
eventBus.post(new TargetCreatedEvent(t));
afterCommit.afterCommit(() -> eventBus.post(new TargetCreatedEvent(t)));
}
private void notifyTargetInfoChanged(final TargetInfo targetInfo) {
eventBus.post(new TargetInfoUpdateEvent(targetInfo));
afterCommit.afterCommit(() -> eventBus.post(new TargetInfoUpdateEvent(targetInfo)));
}
private void notifyTargetDeleted(final String tenant, final Long targetId) {
eventBus.post(new TargetDeletedEvent(tenant, targetId));
afterCommit.afterCommit(() -> eventBus.post(new TargetDeletedEvent(tenant, targetId)));
}
private boolean isTargetInfoNew(final Object targetInfo) {

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.util.Arrays;
import java.util.List;
import org.eclipse.hawkbit.repository.model.BaseEntity;
/**
*
* A abstract typesafe bulkevent which contains all changed base entities.
*
* @param <E>
*/
public abstract class AbstractEntityBulkEvent<E extends BaseEntity> implements EntityBulkEvent<E> {
private static final long serialVersionUID = 1L;
private List<E> entities;
private String tenant;
/**
* Constructor.
*
* @param tenant
* the tenant
* @param entities
* the changed entities
*/
public AbstractEntityBulkEvent(final String tenant, final List<E> entities) {
this.entities = entities;
this.tenant = tenant;
}
/**
* Constructor.
*
* @param tenant
* the tenant
* @param entitiy
* the changed entity
*/
public AbstractEntityBulkEvent(final String tenant, final E entitiy) {
this(tenant, Arrays.asList(entitiy));
}
@Override
public List<E> getEntities() {
return entities;
}
@Override
public long getRevision() {
return -1;
}
@Override
public String getTenant() {
return tenant;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssigmentResult;
/**
* A event for assignment target tag.
*/
public class DistributionSetTagAssigmentResultEvent {
private final DistributionSetTagAssigmentResult assigmentResult;
/**
* Constructor.
*
* @param assigmentResult
* the assignment result-
*/
public DistributionSetTagAssigmentResultEvent(final DistributionSetTagAssigmentResult assigmentResult) {
this.assigmentResult = assigmentResult;
}
public DistributionSetTagAssigmentResult getAssigmentResult() {
return assigmentResult;
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.util.List;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
/**
* * A bulk event which contains one or many new ds tag after creating.
*/
public class DistributionSetTagCreatedBulkEvent extends AbstractEntityBulkEvent<DistributionSetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tenant
* the tenant
* @param entities
* the new ds tags
*/
public DistributionSetTagCreatedBulkEvent(final String tenant, final List<DistributionSetTag> entities) {
super(tenant, entities);
}
/**
* Constructor.
*
* @param tenant
* the tenant.
* @param entity
* the new ds tag
*/
public DistributionSetTagCreatedBulkEvent(final String tenant, final DistributionSetTag entity) {
super(tenant, entity);
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
/**
* Defines the {@link AbstractBaseEntityEvent} of update a
* {@link DistributionSetTag}.
*
*/
public class DistributionSetTagDeletedEvent extends AbstractBaseEntityEvent<DistributionSetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tag
* the tag which is deleted
*/
public DistributionSetTagDeletedEvent(final DistributionSetTag tag) {
super(tag);
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
/**
* Defines the {@link AbstractBaseEntityEvent} for update a
* {@link DistributionSetTag}.
*
*/
public class DistributionSetTagUpdateEvent extends AbstractBaseEntityEvent<DistributionSetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tag
* the tag which is updated
*/
public DistributionSetTagUpdateEvent(final DistributionSetTag tag) {
super(tag);
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.io.Serializable;
import java.util.List;
import org.eclipse.hawkbit.repository.model.BaseEntity;
/**
* An event interface which declares event types that an entities has been
* changed.
*
* @param <E>
* the entity type
*/
public interface EntityBulkEvent<E extends BaseEntity> extends Serializable, Event {
/**
* A typesafe way to retrieve the the entities from the event, which might
* be loaded lazy in case the event has been distributed from another node.
*
* @return the entities might be lazy loaded. Might be {@code null} in case
* the entity e.g. is queried lazy on a different node and has been
* already deleted from the database
*/
List<E> getEntities();
}

View File

@@ -11,9 +11,7 @@ package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.TargetInfo;
/**
*
*
*
* Event for update the targets info.
*/
public class TargetInfoUpdateEvent implements EntityEvent {
@@ -24,6 +22,8 @@ public class TargetInfoUpdateEvent implements EntityEvent {
private String nodeId;
/**
* Constructor.
*
* @param targetInfo
* the target info entity
*/

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.TargetTagAssigmentResult;
/**
* A event for assignment target tag.
*/
public class TargetTagAssigmentResultEvent {
private final TargetTagAssigmentResult assigmentResult;
/**
* Constructor.
*
* @param assigmentResult
* the assignment result-
*/
public TargetTagAssigmentResultEvent(final TargetTagAssigmentResult assigmentResult) {
this.assigmentResult = assigmentResult;
}
public TargetTagAssigmentResult getAssigmentResult() {
return assigmentResult;
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import java.util.List;
import org.eclipse.hawkbit.repository.model.TargetTag;
/**
* A bulk event which contains one or many new target tags after creating.
*/
public class TargetTagCreatedBulkEvent extends AbstractEntityBulkEvent<TargetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tenant
* the tenant
* @param entities
* the new targets
*/
public TargetTagCreatedBulkEvent(final String tenant, final List<TargetTag> entities) {
super(tenant, entities);
}
/**
* Constructor.
*
* @param tenant
* the tenant
* @param entity
* one new target
*/
public TargetTagCreatedBulkEvent(final String tenant, final TargetTag entity) {
super(tenant, entity);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.TargetTag;
/**
* Defines the {@link AbstractBaseEntityEvent} of update a {@link TargetTag}.
*
*/
public class TargetTagDeletedEvent extends AbstractBaseEntityEvent<TargetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tag
* the tag which is deleted
*/
public TargetTagDeletedEvent(final TargetTag tag) {
super(tag);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
import org.eclipse.hawkbit.repository.model.TargetTag;
/**
* Defines the {@link AbstractBaseEntityEvent} for update a {@link TargetTag}.
*
*/
public class TargetTagUpdateEvent extends AbstractBaseEntityEvent<TargetTag> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param tag
* the tag which is updated
*/
public TargetTagUpdateEvent(final TargetTag tag) {
super(tag);
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.executor;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
*
* A Service which calls register runnable. This runnables will executed after a
* successful spring transaction commit.The class is thread safe.
*/
@Service
public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSynchronizationAdapter implements
AfterTransactionCommitExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(AfterTransactionCommitDefaultServiceExecutor.class);
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
@Override
public void afterCommit() {
final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get();
LOGGER.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
for (final Runnable afterCommitRunnable : afterCommitRunnables) {
LOGGER.debug("Executing runnable {}", afterCommitRunnable);
try {
afterCommitRunnable.run();
} catch (final RuntimeException e) {
LOGGER.error("Failed to execute runnable " + afterCommitRunnable, e);
}
}
}
@Override
public void afterCommit(final Runnable runnable) {
LOGGER.debug("Submitting new runnable {} to run after transaction commit", runnable);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<Runnable> localRunnables = THREAD_LOCAL_RUNNABLES.get();
if (localRunnables == null) {
localRunnables = new ArrayList<>();
THREAD_LOCAL_RUNNABLES.set(localRunnables);
TransactionSynchronizationManager.registerSynchronization(this);
}
localRunnables.add(runnable);
return;
}
LOGGER.info("Transaction synchronization is NOT ACTIVE/ INACTIVE. Executing right now runnable {}", runnable);
runnable.run();
}
@Override
public void afterCompletion(final int status) {
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
LOGGER.debug("Transaction completed after commit with status {}", transactionStatus);
THREAD_LOCAL_RUNNABLES.remove();
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.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);
}

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.report.model;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ImmutableList;
/**
* Bean for holding the system usage stats.
*
*/
public class SystemUsageReport {
private final long overallTargets;
private final long overallArtifacts;
private final long overallArtifactVolumeInBytes;
private final long overallActions;
private final List<TenantUsage> tenants = new ArrayList<>();
/**
* Constructor.
*
* @param overallTargets
* of the system
* @param overallArtifacts
* of the system
* @param overallActions
* of the system
* @param overallArtifactVolumeInBytes
* of the system
*/
public SystemUsageReport(final long overallTargets, final long overallArtifacts, final long overallActions,
final long overallArtifactVolumeInBytes) {
super();
this.overallTargets = overallTargets;
this.overallArtifacts = overallArtifacts;
this.overallActions = overallActions;
this.overallArtifactVolumeInBytes = overallArtifactVolumeInBytes;
}
/**
* @return overallTargets in the system
*/
public long getOverallTargets() {
return overallTargets;
}
/**
* @return overallArtifacts in the system
*/
public long getOverallArtifacts() {
return overallArtifacts;
}
/**
* @return overallArtifactVolumeInBytes of the system
*/
public long getOverallArtifactVolumeInBytes() {
return overallArtifactVolumeInBytes;
}
/**
* @param tenantUsage
* of one tenant
* @return updated bean
*/
public SystemUsageReport addTenantData(final TenantUsage tenantUsage) {
tenants.add(tenantUsage);
return this;
}
/**
* @return actions of system
*/
public long getOverallActions() {
return overallActions;
}
/**
* @return tenant data
*/
public List<TenantUsage> getTenants() {
return ImmutableList.copyOf(tenants);
}
}

View File

@@ -0,0 +1,161 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.report.model;
/**
* System usage stats element for a tenant.
*
*/
public class TenantUsage {
private final String tenantName;
private long targets;
private long artifacts;
private long actions;
private long overallArtifactVolumeInBytes;
/**
* Constructor.
*
* @param tenantName
*/
public TenantUsage(final String tenantName) {
super();
this.tenantName = tenantName;
}
/**
* @return name of the tenant
*/
public String getTenantName() {
return tenantName;
}
/**
* @return number of targets of the tenant
*/
public long getTargets() {
return targets;
}
/**
* @param targets
* of the tenant
* @return updated tenant stats element
*/
public TenantUsage setTargets(final long targets) {
this.targets = targets;
return this;
}
/**
* @return number of undeleted artifacts of the tenant
*/
public long getArtifacts() {
return artifacts;
}
/**
* @param artifacts
* of tenant
* @return updated tenant stats element
*/
public TenantUsage setArtifacts(final long artifacts) {
this.artifacts = artifacts;
return this;
}
/**
* @return current overallArtifactVolumeInBytes
*/
public long getOverallArtifactVolumeInBytes() {
return overallArtifactVolumeInBytes;
}
/**
* @param overallArtifactVolumeInBytes
* of the tenant in bytes
* @return updated tenant stats element
*/
public TenantUsage setOverallArtifactVolumeInBytes(final long overallArtifactVolumeInBytes) {
this.overallArtifactVolumeInBytes = overallArtifactVolumeInBytes;
return this;
}
/**
* @return number of actions of tenant
*/
public long getActions() {
return actions;
}
/**
* @param actions
* of the tenant
* @return updated tenant stats element
*/
public TenantUsage setActions(final long actions) {
this.actions = actions;
return this;
}
@Override
public int hashCode() { // NOSONAR - as this is generated code
final int prime = 31;
int result = 1;
result = prime * result + (int) (actions ^ (actions >>> 32));
result = prime * result + (int) (artifacts ^ (artifacts >>> 32));
result = prime * result + (int) (overallArtifactVolumeInBytes ^ (overallArtifactVolumeInBytes >>> 32));
result = prime * result + (int) (targets ^ (targets >>> 32));
result = prime * result + ((tenantName == null) ? 0 : tenantName.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is generated
// code
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TenantUsage other = (TenantUsage) obj;
if (actions != other.actions) {
return false;
}
if (artifacts != other.artifacts) {
return false;
}
if (overallArtifactVolumeInBytes != other.overallArtifactVolumeInBytes) {
return false;
}
if (targets != other.targets) {
return false;
}
if (tenantName == null) {
if (other.tenantName != null) {
return false;
}
} else if (!tenantName.equals(other.tenantName)) {
return false;
}
return true;
}
@Override
public String toString() {
return "SystemUsage [tenantName=" + tenantName + ", targets=" + targets + ", artifacts=" + artifacts
+ ", actions=" + actions + ", overallArtifactVolumeInBytes=" + overallArtifactVolumeInBytes + "]";
}
}

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
@@ -135,8 +134,8 @@ public class ControllerManagement implements EnvironmentAware {
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(targetId, module);
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
throw new EntityNotFoundException("No assigment found for module " + module.getId() + " to target "
+ targetId);
throw new EntityNotFoundException(
"No assigment found for module " + module.getId() + " to target " + targetId);
}
return action.get(0);
@@ -270,22 +269,20 @@ public class ControllerManagement implements EnvironmentAware {
}
/**
* Reports an {@link ActionStatus} for a {@link CancelAction}.
*
* @param actionStatusMessages
* Adds an {@link ActionStatus} for a {@link UpdateAction} and cancels the
* {@link UpdateAction} if necessary.
*
* @param actionStatus
* to be updated
* @param target2
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
* @throws ToManyStatusEntriesException
* if more than the allowed number of status entries are
* inserted
* @param action
* the status is for
* @return the persisted {@link Action}
*
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public void addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
checkForToManyStatusEntries(action);
action.setStatus(actionStatus.getStatus());
@@ -298,12 +295,9 @@ public class ControllerManagement implements EnvironmentAware {
case CANCELED:
case FINISHED:
// in case of successful cancelation we also report the success at
// the canceled action
// itself.
// the canceled action itself.
actionStatus.addMessage("Cancelation completion is finished sucessfully.");
// set action inactive
action.setActive(false);
successCancellation(action);
deploymentManagement.successCancellation(action);
break;
case RETRIEVED:
actionStatus.addMessage("Cancelation request retrieved");
@@ -312,30 +306,18 @@ public class ControllerManagement implements EnvironmentAware {
}
actionRepository.save(action);
actionStatusRepository.save(actionStatus);
}
private void successCancellation(final Action action) {
final Target target = action.getTarget();
final List<Action> nextActiveActions = actionRepository.findByTargetAndActiveOrderByIdAsc(target, true)
.stream().filter(a -> !a.getId().equals(action.getId())).collect(Collectors.toList());
if (nextActiveActions.isEmpty()) {
target.setAssignedDistributionSet(target.getTargetInfo().getInstalledDistributionSet());
deploymentManagement.updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false);
} else {
target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet());
}
targetManagement.updateTarget(target);
return action;
}
/**
* Reports an {@link ActionStatus} for a {@link UpdateAction}.
* Updates an {@link ActionStatus} for a {@link UpdateAction}.
*
* @param actionStatus
* to be updated
* @param action
* the update is for
* @return the persisted {@link ActionStatus}
* @return the persisted {@link Action}
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
@@ -420,8 +402,8 @@ public class ControllerManagement implements EnvironmentAware {
final TargetInfo targetInfo = target.getTargetInfo();
final DistributionSet ds = entityManager.merge(action.getDistributionSet());
targetInfo.setInstalledDistributionSet(ds);
if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null
&& target.getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) {
if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null && target
.getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) {
targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
targetInfo.setInstallationDate(System.currentTimeMillis());
} else {
@@ -461,8 +443,7 @@ public class ControllerManagement implements EnvironmentAware {
target.getTargetInfo().getControllerAttributes().putAll(data);
if (target.getTargetInfo().getControllerAttributes().size() > maxAttributes) {
LOG_DOS.info(
"Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
maxAttributes);
throw new ToManyAttributeEntriesException(String.valueOf(maxAttributes));
}
@@ -474,7 +455,7 @@ public class ControllerManagement implements EnvironmentAware {
/*
* (non-Javadoc)
*
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/

View File

@@ -28,10 +28,14 @@ import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.Constants;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.event.DeploymentManagementEvents;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -65,6 +69,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
/**
* Business service facade for managing all deployment related data and actions.
@@ -105,11 +110,14 @@ public class DeploymentManagement {
private AuditorAware<String> auditorProvider;
@Autowired
private DeploymentManagementEvents deploymentManagementEvents;
private EventBus eventBus;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* method assigns the {@link DistributionSet} to all {@link Target}s.
*
*
* @param pset
* {@link DistributionSet} which is assigned to the
* {@link Target}s
@@ -139,7 +147,7 @@ public class DeploymentManagement {
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs.
*
*
* @param dsID
* {@link DistributionSet} which is assigned to the
* {@link Target}s
@@ -168,7 +176,7 @@ public class DeploymentManagement {
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
*
* @param dsID
* the ID of the distribution set to assign
* @param actionType
@@ -190,14 +198,16 @@ public class DeploymentManagement {
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, final ActionType actionType,
final long forcedTimestamp, @NotEmpty final String... targetIDs) {
return assignDistributionSet(dsID, Arrays.stream(targetIDs)
.map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList()));
return assignDistributionSet(
dsID,
Arrays.stream(targetIDs).map(t -> new TargetWithActionType(t, actionType, forcedTimestamp))
.collect(Collectors.toList()));
}
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
*
* @param dsID
* the ID of the distribution set to assign
* @param targets
@@ -216,8 +226,8 @@ public class DeploymentManagement {
final List<TargetWithActionType> targets) {
final DistributionSet set = distributoinSetRepository.findOne(dsID);
if (set == null) {
throw new EntityNotFoundException(
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
throw new EntityNotFoundException(String.format("no %s with id %d found",
DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets);
@@ -226,7 +236,7 @@ public class DeploymentManagement {
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
*
* @param dsID
* the ID of the distribution set to assign
* @param targetsWithActionType
@@ -241,8 +251,8 @@ public class DeploymentManagement {
final List<TargetWithActionType> targetsWithActionType) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
"Distribution set of type " + set.getType().getKey() + " is incomplete: " + set.getId());
throw new IncompleteDistributionSetException("Distribution set of type " + set.getType().getKey()
+ " is incomplete: " + set.getId());
}
final List<String> controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getTargetId)
@@ -250,8 +260,8 @@ public class DeploymentManagement {
LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size());
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
.collect(Collectors.toMap(TargetWithActionType::getTargetId, Function.identity()));
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream().collect(
Collectors.toMap(TargetWithActionType::getTargetId, Function.identity()));
// split tIDs length into max entries in-statement because many database
// have constraint of
@@ -261,10 +271,12 @@ public class DeploymentManagement {
// we take the target only into account if the requested operation is no
// duplicate of a
// previous one
final List<Target> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
.flatMap(t -> t.stream()).collect(Collectors.toList());
final List<Target> targets = Lists
.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT)
.stream()
.map(ids -> targetRepository.findAll(TargetSpecifications
.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId()))).flatMap(t -> t.stream())
.collect(Collectors.toList());
if (targets.isEmpty()) {
// detaching as it is not necessary to persist the set itself
@@ -327,8 +339,8 @@ public class DeploymentManagement {
});
// select updated targets in order to return them
final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(),
final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult(targets.stream()
.map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(),
controllerIDs.size() - targets.size(), Lists.newArrayList(targetIdsToActions.values()),
targetManagement);
@@ -340,16 +352,40 @@ public class DeploymentManagement {
entityManager.detach(set);
// send distribution set assignment event
targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId()))
.forEach(t -> deploymentManagementEvents.assignDistributionSet(t,
targetIdsToActions.get(t.getControllerId()).getId(), softwareModules));
targets.stream()
.filter(t -> !!!targetIdsCancellList.contains(t.getId()))
.forEach(
t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(),
softwareModules));
return result;
}
/**
* Sends the {@link TargetAssignDistributionSetEvent} for a specific target
* to the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* the software modules which have been assigned
*/
private void assignDistributionSetEvent(final Target target, final Long actionId,
final List<SoftwareModule> softwareModules) {
target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING);
afterCommit.afterCommit(() -> {
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), actionId, softwareModules,
target.getTargetInfo().getAddress()));
});
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
* cancelations to the controller.
* cancellations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
@@ -360,18 +396,20 @@ public class DeploymentManagement {
// Figure out if there are potential target/action combinations that
// need to be considered
// for cancelation
// for cancellation
final List<Action> activeActions = actionRepository
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
Action.Status.CANCELING);
activeActions.forEach(action -> {
action.setStatus(Status.CANCELING);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
deploymentManagementEvents.cancalAssignDistributionSet(action.getTarget(), action.getId());
cancelledTargetIds.add(action.getTarget().getId());
});
actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
cancelledTargetIds.add(action.getTarget().getId());
});
actionRepository.save(activeActions);
return cancelledTargetIds;
@@ -379,8 +417,9 @@ public class DeploymentManagement {
private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set,
@NotEmpty final List<String> tIDs, final ActionType actionType, final long forcedTime) {
return assignDistributionSetToTargets(set, tIDs.stream()
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()));
return assignDistributionSetToTargets(set,
tIDs.stream().map(t -> new TargetWithActionType(t, actionType, forcedTime))
.collect(Collectors.toList()));
}
/**
@@ -442,19 +481,77 @@ public class DeploymentManagement {
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
final Action saveAction = actionRepository.save(myAction);
deploymentManagementEvents.cancalAssignDistributionSet(target, myAction.getId());
cancelAssignDistributionSetEvent(target, myAction.getId());
return actionRepository.save(myAction);
return saveAction;
} else {
throw new CancelActionNotAllowedException(
"Action [id: " + action.getId() + "] is not active and cannot be canceled");
throw new CancelActionNotAllowedException("Action [id: " + action.getId()
+ "] is not active and cannot be canceled");
}
}
/**
* Get the {@link Action} entity for given actionId.
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
* the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
*/
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getControllerId(), actionId,
target.getTargetInfo().getAddress())));
}
/**
* Force cancels given {@link Action} for given {@link Target}. Force
* canceling means that the action is marked as canceled on the SP server
* and a cancel request is sent to the target. But however it's not tracked,
* if the targets handles the cancel request or not.
*
* @param action
* to be canceled
* @param target
* for which the action needs cancellation
*
* @return generated {@link CancelAction} or <code>null</code> if not in
* {@link Target#getActiveActions()}.
* @throws CancelActionNotAllowedException
* in case the given action is not active
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action forceQuitAction(@NotNull final Action action, @NotNull final Target target) {
final Action mergedAction = entityManager.merge(action);
if (!mergedAction.isCancelingOrCanceled()) {
throw new ForceQuitActionNotAllowedException("Action [id: " + action.getId()
+ "] is not canceled yet and cannot be force quit");
}
if (!mergedAction.isActive()) {
throw new ForceQuitActionNotAllowedException("Action [id: " + action.getId()
+ "] is not active and cannot be force quit");
}
LOG.warn("action ({}) was still activ and has been force quite.", action);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(mergedAction, Status.CANCELED, System.currentTimeMillis(),
"A force quit has been performed."));
successCancellation(mergedAction);
return actionRepository.save(mergedAction);
}
/**
* Get the {@link Action} entity for given actionId.
*
* @param actionId
* to be id of the action
* @return the corresponding {@link Action}
@@ -467,7 +564,7 @@ public class DeploymentManagement {
/**
* Get the {@link Action} entity for given actionId with all lazy
* attributes.
*
*
* @param actionId
* to be id of the action
* @return the corresponding {@link Action}
@@ -493,7 +590,7 @@ public class DeploymentManagement {
/**
* Retrieves all {@link Action}s of a specific target ordered by action ID.
*
*
* @param target
* the target associated with the actions
* @return a list of actions associated with the given target ordered by
@@ -506,7 +603,7 @@ public class DeploymentManagement {
/**
* Retrieves all {@link Action}s of a specific target ordered by action ID.
*
*
* @param target
* the target associated with the actions
* @return a list of actions associated with the given target ordered by
@@ -536,7 +633,7 @@ public class DeploymentManagement {
/**
* Retrieves all {@link Action}s assigned to a specific {@link Target} and a
* given specification.
*
*
* @param specifiction
* the specification to narrow down the search
* @param target
@@ -553,17 +650,21 @@ public class DeploymentManagement {
return actionRepository.findAll(new Specification<Action>() {
@Override
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return cb.and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
}
}, pageable);
}
/**
* @param foundTarget
* Retrieves all {@link Action}s which are referring the given
* {@link Target}.
*
* @param pageable
* @return
* page parameters
* @param foundTarget
* the target to find assigned actions
* @return the found {@link Action}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Action> findActionsByTarget(final Target foundTarget, final Pageable pageable) {
@@ -573,7 +674,7 @@ public class DeploymentManagement {
/**
* Retrieves all active {@link Action}s of a specific target ordered by
* action ID.
*
*
* @param pageable
* the pagination parameter
* @param target
@@ -588,7 +689,7 @@ public class DeploymentManagement {
/**
* Retrieves all active {@link Action}s of a specific target ordered by
* action ID.
*
*
* @param target
* the target associated with the actions
* @return a list of actions associated with the given target
@@ -601,7 +702,7 @@ public class DeploymentManagement {
/**
* Retrieves all inactive {@link Action}s of a specific target ordered by
* action ID.
*
*
* @param target
* the target associated with the actions
* @return a list of actions associated with the given target
@@ -614,7 +715,7 @@ public class DeploymentManagement {
/**
* Retrieves all inactive {@link Action}s of a specific target ordered by
* action ID.
*
*
* @param pageable
* the pagination parameter
* @param target
@@ -628,7 +729,7 @@ public class DeploymentManagement {
/**
* counts all actions associated to a specific target.
*
*
* @param target
* the target associated to the actions to count
* @return the count value of found actions associated to the target
@@ -640,7 +741,7 @@ public class DeploymentManagement {
/**
* counts all actions associated to a specific target.
*
*
* @param spec
* the specification to filter the count result
* @param target
@@ -651,8 +752,7 @@ public class DeploymentManagement {
public Long countActionsByTarget(@NotNull final Specification<Action> spec, @NotNull final Target target) {
return actionRepository.count(new Specification<Action>() {
@Override
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return cb.and(spec.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
}
});
@@ -683,7 +783,7 @@ public class DeploymentManagement {
/**
* retrieves all the {@link ActionStatus} entries of the given
* {@link Action} and {@link Target} in the order latest first.
*
*
* @param pageReq
* pagination parameter
* @param action
@@ -703,4 +803,30 @@ public class DeploymentManagement {
}
}
/**
* This method is called, when cancellation has been successful. It sets the
* action to canceled, resets the meta data of the target and in case there
* is a new action this action is triggered.
*
* @param action
* the action which is set to canceled
*/
void successCancellation(final Action action) {
// set action inactive
action.setActive(false);
action.setStatus(Status.CANCELED);
final Target target = action.getTarget();
final List<Action> nextActiveActions = actionRepository.findByTargetAndActiveOrderByIdAsc(target, true)
.stream().filter(a -> !a.getId().equals(action.getId())).collect(Collectors.toList());
if (nextActiveActions.isEmpty()) {
target.setAssignedDistributionSet(target.getTargetInfo().getInstalledDistributionSet());
updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false);
} else {
target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet());
}
targetManagement.updateTarget(target);
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.AssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
/**

View File

@@ -11,7 +11,9 @@ package org.eclipse.hawkbit.repository;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -26,6 +28,8 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.exception.DistributionSetCreationFailedMissingMandatoryModuleException;
@@ -38,6 +42,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata_;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssigmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.DistributionSetTypeElement;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
@@ -62,6 +67,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import com.google.common.eventbus.EventBus;
/**
* Business facade for managing the {@link DistributionSet}s.
@@ -96,6 +102,12 @@ public class DistributionSetManagement {
@Autowired
private ActionRepository actionRepository;
@Autowired
private EventBus eventBus;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* Find {@link DistributionSet} based on given ID including (lazy loaded)
* details, e.g. {@link DistributionSet#getAgentHub()}.
@@ -183,13 +195,16 @@ public class DistributionSetManagement {
allDSs.add(set);
}
}
result = new DistributionSetTagAssigmentResult(dsIds.size() - allDSs.size(), 0, allDSs.size(), null,
distributionSetRepository.save(allDSs));
result = new DistributionSetTagAssigmentResult(dsIds.size() - allDSs.size(), 0, allDSs.size(),
Collections.emptyList(), distributionSetRepository.save(allDSs), myTag);
} else {
result = new DistributionSetTagAssigmentResult(dsIds.size() - allDSs.size(), allDSs.size(), 0,
distributionSetRepository.save(allDSs), null);
distributionSetRepository.save(allDSs), Collections.emptyList(), myTag);
}
final DistributionSetTagAssigmentResult resultAssignment = result;
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment)));
// no reason to persist the tag
entityManager.detach(myTag);
return result;
@@ -204,8 +219,7 @@ public class DistributionSetManagement {
* @return the found {@link DistributionSet}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<DistributionSet> findDistributionSetListWithDetails(
@NotEmpty final Collection<Long> distributionIdSet) {
public List<DistributionSet> findDistributionSetListWithDetails(@NotEmpty final Collection<Long> distributionIdSet) {
return distributionSetRepository.findAll(DistributionSetSpecification.byIds(distributionIdSet));
}
@@ -383,8 +397,7 @@ public class DistributionSetManagement {
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds,
final SoftwareModule softwareModule) {
public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds, final SoftwareModule softwareModule) {
final Set<SoftwareModule> softwareModules = new HashSet<SoftwareModule>();
softwareModules.add(softwareModule);
ds.removeModule(softwareModule);
@@ -416,9 +429,9 @@ public class DistributionSetManagement {
// throw exception if user tries to update a DS type that is already in
// use
if (!persisted.areModuleEntriesIdentical(dsType) && distributionSetRepository.countByType(persisted) > 0) {
throw new EntityReadOnlyException(
String.format("distribution set type %s set is already assigned to targets and cannot be changed",
dsType.getName()));
throw new EntityReadOnlyException(String.format(
"distribution set type %s set is already assigned to targets and cannot be changed",
dsType.getName()));
}
return distributionSetTypeRepository.save(dsType);
@@ -584,16 +597,14 @@ public class DistributionSetManagement {
final DistributionSetFilter filterWithInstalledTargets = distributionSetFilterBuilder
.setInstalledTargetId(assignedOrInstalled).setAssignedTargetId(null).build();
final DistributionSet installedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
filterWithInstalledTargets);
final DistributionSet installedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(filterWithInstalledTargets);
final DistributionSetFilter filterWithAssignedTargets = distributionSetFilterBuilder.setInstalledTargetId(null)
.setAssignedTargetId(assignedOrInstalled).build();
final DistributionSet assignedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
filterWithAssignedTargets);
final DistributionSet assignedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(filterWithAssignedTargets);
final DistributionSetFilter dsFilterWithNoTargetLinked = distributionSetFilterBuilder.setInstalledTargetId(null)
.setAssignedTargetId(null).build();
final DistributionSetFilter dsFilterWithNoTargetLinked = distributionSetFilterBuilder
.setInstalledTargetId(null).setAssignedTargetId(null).build();
// first fine the distribution sets filtered by the given filter
// parameters
final Page<DistributionSet> findDistributionSetsByFilters = findDistributionSetsByFilters(pageable,
@@ -643,8 +654,8 @@ public class DistributionSetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public DistributionSet findDistributionSetByNameAndVersion(@NotEmpty final String distributionName,
@NotEmpty final String version) {
final Specification<DistributionSet> spec = DistributionSetSpecification
.equalsNameAndVersionIgnoreCase(distributionName, version);
final Specification<DistributionSet> spec = DistributionSetSpecification.equalsNameAndVersionIgnoreCase(
distributionName, version);
return distributionSetRepository.findOne(spec);
}
@@ -998,17 +1009,17 @@ public class DistributionSetManagement {
final Set<SoftwareModule> softwareModules) {
if (!new HashSet<SoftwareModule>(distributionSet.getModules()).equals(softwareModules)
&& actionRepository.countByDistributionSet(distributionSet) > 0) {
throw new EntityLockedException(
String.format("distribution set %s:%s is already assigned to targets and cannot be changed",
distributionSet.getName(), distributionSet.getVersion()));
throw new EntityLockedException(String.format(
"distribution set %s:%s is already assigned to targets and cannot be changed",
distributionSet.getName(), distributionSet.getVersion()));
}
}
private void checkDistributionSetSoftwareModulesIsAllowedToModify(final DistributionSet distributionSet) {
if (actionRepository.countByDistributionSet(distributionSet) > 0) {
throw new EntityLockedException(
String.format("distribution set %s:%s is already assigned to targets and cannot be changed",
distributionSet.getName(), distributionSet.getVersion()));
throw new EntityLockedException(String.format(
"distribution set %s:%s is already assigned to targets and cannot be changed",
distributionSet.getName(), distributionSet.getVersion()));
}
}
@@ -1057,8 +1068,8 @@ public class DistributionSetManagement {
private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) {
if (distributionSetMetadataRepository.exists(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataId.getKey()
+ "' already exists");
}
}
@@ -1066,4 +1077,74 @@ public class DistributionSetManagement {
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
}
/**
* Assign a {@link DistributionSetTag} assignment to given
* {@link DistributionSet}s.
*
* @param dsIds
* to assign for
* @param tag
* to assign
* @return list of assigned ds
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<DistributionSet> assignTag(@NotEmpty final Collection<Long> dsIds, @NotNull final DistributionSetTag tag) {
final List<DistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
allDs.forEach(ds -> ds.getTags().add(tag));
final List<DistributionSet> save = distributionSetRepository.save(allDs);
afterCommit.afterCommit(() -> {
final DistributionSetTagAssigmentResult result = new DistributionSetTagAssigmentResult(0, save.size(), 0,
save, Collections.emptyList(), tag);
eventBus.post(new DistributionSetTagAssigmentResultEvent(result));
});
return save;
}
/**
* Unassign all {@link DistributionSet} from a given
* {@link DistributionSetTag} .
*
* @param tag
* to unassign all ds
* @return list of unassigned ds
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<DistributionSet> unAssignAllDistributionSetsByTag(@NotNull final DistributionSetTag tag) {
return unAssignTag(tag.getAssignedToDistributionSet(), tag);
}
/**
* Unassign a {@link DistributionSetTag} assignment to given
* {@link DistributionSet}.
*
* @param dsId
* to unassign for
* @param distributionSetTag
* to unassign
* @return the unassigned ds or <null> if no ds is unassigned
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public DistributionSet unAssignTag(@NotNull final Long dsId, @NotNull final DistributionSetTag distributionSetTag) {
final List<DistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));
final List<DistributionSet> unAssignTag = unAssignTag(allDs, distributionSetTag);
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
}
private List<DistributionSet> unAssignTag(final Collection<DistributionSet> distributionSets,
final DistributionSetTag tag) {
distributionSets.forEach(ds -> ds.getTags().remove(tag));
return distributionSetRepository.save(distributionSets);
}
}

View File

@@ -13,6 +13,7 @@ import java.util.List;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
@@ -23,7 +24,8 @@ import org.springframework.transaction.annotation.Transactional;
*
*/
@Transactional(readOnly = true)
public interface DistributionSetTagRepository extends BaseEntityRepository<DistributionSetTag, Long> {
public interface DistributionSetTagRepository extends BaseEntityRepository<DistributionSetTag, Long>,
JpaSpecificationExecutor<DistributionSetTag> {
/**
* deletes the {@link DistributionSet} with the given name.
*
@@ -51,4 +53,7 @@ public interface DistributionSetTagRepository extends BaseEntityRepository<Distr
*/
@Override
List<DistributionSetTag> findAll();
@Override
<S extends DistributionSetTag> List<S> save(Iterable<S> entities);
}

View File

@@ -9,20 +9,40 @@
package org.eclipse.hawkbit.repository;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link LocalArtifact} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface LocalArtifactRepository extends BaseEntityRepository<LocalArtifact, Long> {
/**
* Counts artifacts size where the related software module is not
* deleted/archived.
*
* @return sum of artifacts size in bytes
*/
@Query("SELECT SUM(la.size) FROM LocalArtifact la WHERE la.softwareModule.deleted = 0")
Optional<Long> getSumOfUndeletedArtifactSize();
/**
* Counts artifacts where the related software module is deleted/archived.
*
* @param deleted
* to true for counting the deleted artifacts
*
* @return number of artifacts
*/
Long countBySoftwareModuleDeleted(boolean deleted);
/**
* Searches for a {@link LocalArtifact} based on given gridFsFileName.
*

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.repository;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
@@ -17,13 +18,13 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.report.model.SystemUsageReport;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.TenantAware.TenantRunner;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,9 +49,6 @@ import org.springframework.validation.annotation.Validated;
/**
* Central system management operations of the SP server.
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@@ -101,6 +99,9 @@ public class SystemManagement implements EnvironmentAware {
@Autowired
private TenantAware tenantAware;
@Autowired
private TenantStatsManagement systemStatsManagement;
@Autowired
private TenancyCacheManager cacheManager;
@@ -110,12 +111,61 @@ public class SystemManagement implements EnvironmentAware {
private Environment environment;
/**
* Calculated system usage statistics, both overall for the entire system
* and per tenant;
*
* @return SystemUsageReport of the current system
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
public SystemUsageReport getSystemUsageStatistics() {
BigDecimal sumOfArtifacts = (BigDecimal) entityManager
.createNativeQuery(
"select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
.getSingleResult();
if (sumOfArtifacts == null) {
sumOfArtifacts = new BigDecimal(0);
}
// we use native queries to punch through the tenant boundaries. This
// has to be used with care!
final Long targets = (Long) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target")
.getSingleResult();
final Long artifacts = (Long) entityManager
.createNativeQuery(
"SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
.getSingleResult();
final Long actions = (Long) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
.getSingleResult();
final SystemUsageReport result = new SystemUsageReport(targets, artifacts, actions,
sumOfArtifacts.setScale(0, BigDecimal.ROUND_HALF_UP).longValue());
usageStatsPerTenant(result);
return result;
}
private void usageStatsPerTenant(final SystemUsageReport report) {
final List<String> tenants = findTenants();
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
report.addTenantData(systemStatsManagement.getStatsOfTenant(tenant));
return null;
}));
}
/**
* Registers the key generator for the {@link #currentTenant()} method
* because this key generator is aware of the {@link #createInitialTenant}
* thread local in case we are currently creating a tenant and insert the
* default distribution set types.
*
*
* @return the {@link CurrentTenantKeyGenerator}
*/
@Bean
@@ -127,8 +177,8 @@ public class SystemManagement implements EnvironmentAware {
* Returns {@link TenantMetaData} of given and current tenant.
*
* DISCLAIMER: this variant is used during initial login (where the tenant
* is not yet in teh session). Please user {@link #getTenantMetadata()} for
* reluar requests.
* is not yet in the session). Please user {@link #getTenantMetadata()} for
* regular requests.
*
* @param tenant
* @return
@@ -179,25 +229,22 @@ public class SystemManagement implements EnvironmentAware {
public void deleteTenant(@NotNull final String tenant) {
cacheManager.evictCaches(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
tenantAware.runAsTenant(tenant, new TenantRunner<Void>() {
@Override
public Void run() {
entityManager.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenant.toUpperCase());
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant);
artifactRepository.deleteByTenantIgnoreCase(tenant);
externalArtifactRepository.deleteByTenantIgnoreCase(tenant);
externalArtifactProviderRepository.deleteByTenantIgnoreCase(tenant);
targetTagRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
distributionSetTagRepository.deleteByTenantIgnoreCase(tenant);
distributionSetRepository.deleteByTenantIgnoreCase(tenant);
distributionSetTypeRepository.deleteByTenantIgnoreCase(tenant);
softwareModuleRepository.deleteByTenantIgnoreCase(tenant);
softwareModuleTypeRepository.deleteByTenantIgnoreCase(tenant);
return null;
}
tenantAware.runAsTenant(tenant, () -> {
entityManager.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenant.toUpperCase());
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant);
artifactRepository.deleteByTenantIgnoreCase(tenant);
externalArtifactRepository.deleteByTenantIgnoreCase(tenant);
externalArtifactProviderRepository.deleteByTenantIgnoreCase(tenant);
targetTagRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
distributionSetTagRepository.deleteByTenantIgnoreCase(tenant);
distributionSetRepository.deleteByTenantIgnoreCase(tenant);
distributionSetTypeRepository.deleteByTenantIgnoreCase(tenant);
softwareModuleRepository.deleteByTenantIgnoreCase(tenant);
softwareModuleTypeRepository.deleteByTenantIgnoreCase(tenant);
return null;
});
}
@@ -218,7 +265,7 @@ public class SystemManagement implements EnvironmentAware {
/**
* Checks if a specific tenant exists. The tenant will not be created lazy.
*
*
* @param tenant
* the tenant to check
* @return {@code true} in case the tenant exits or {@code false} if not
@@ -268,7 +315,7 @@ public class SystemManagement implements EnvironmentAware {
* Retrieves a configuration value from the e.g. tenant overwritten
* configuration values or in case the tenant does not a have a specific
* configuration the global default value hold in the {@link Environment}.
*
*
* @param configurationKey
* the key of the configuration
* @param propertyType
@@ -298,7 +345,7 @@ public class SystemManagement implements EnvironmentAware {
/**
* Adds or updates a specific configuration for a specific tenant.
*
*
* @param tenantConf
* the tenant configuration object which contains the key and
* value of the specific configuration to update
@@ -319,7 +366,7 @@ public class SystemManagement implements EnvironmentAware {
/**
* Deletes a specific configuration for the current tenant.
*
*
* @param configurationKey
* the configuration key to be deleted
*/
@@ -337,7 +384,7 @@ public class SystemManagement implements EnvironmentAware {
/*
* (non-Javadoc)
*
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@@ -379,7 +426,7 @@ public class SystemManagement implements EnvironmentAware {
* {@link TenantAware}, but in case we are in a tenant creation with its
* default types we need to use the tenant the current tenant which is
* currently created and not the one currently in the {@link TenantAware}.
*
*
*
*
*/
@@ -387,7 +434,7 @@ public class SystemManagement implements EnvironmentAware {
/*
* (non-Javadoc)
*
*
* @see
* org.springframework.cache.interceptor.KeyGenerator#generate(java.lang
* .Object, java.lang.reflect.Method, java.lang.Object[])

View File

@@ -15,6 +15,13 @@ import java.util.List;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagCreatedBulkEvent;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagUpdateEvent;
import org.eclipse.hawkbit.eventbus.event.TargetTagCreatedBulkEvent;
import org.eclipse.hawkbit.eventbus.event.TargetTagDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetTagUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -22,16 +29,20 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.common.eventbus.EventBus;
/**
*
* Mangement service class for {@link Tag}s.
@@ -58,6 +69,15 @@ public class TagManagement {
@Autowired
private DistributionSetRepository distributionSetRepository;
@Autowired
private EventBus eventBus;
@Autowired
private TenantAware tenantAware;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* Find {@link TargetTag} based on given Name.
*
@@ -94,7 +114,12 @@ public class TagManagement {
throw new EntityAlreadyExistsException();
}
return targetTagRepository.save(targetTag);
final TargetTag save = targetTagRepository.save(targetTag);
afterCommit
.afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
return save;
}
/**
@@ -117,8 +142,10 @@ public class TagManagement {
throw new EntityAlreadyExistsException();
}
});
return targetTagRepository.save(targetTags);
final List<TargetTag> save = targetTagRepository.save(targetTags);
afterCommit
.afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
return save;
}
/**
@@ -144,6 +171,9 @@ public class TagManagement {
// finally delete the tag itself
targetTagRepository.deleteByName(targetTagName);
afterCommit.afterCommit(() -> eventBus.post(new TargetTagDeletedEvent(tag)));
}
/**
@@ -157,6 +187,31 @@ public class TagManagement {
return targetTagRepository.findAll();
}
/**
* Retrieves all target tags based on the given specification.
*
* @param spec
* the specification for the query
* @param pageable
* pagination parameter
* @return the found {@link Target}s, never {@code null}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<TargetTag> findAllTargetTags(@NotNull final Specification<TargetTag> spec,
@NotNull final Pageable pageable) {
return targetTagRepository.findAll(spec, pageable);
}
/**
* count {@link TargetTag}s.
*
* @return size of {@link TargetTag}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public long countTargetTags() {
return targetTagRepository.count();
}
/**
* updates the {@link TargetTag}.
*
@@ -171,7 +226,9 @@ public class TagManagement {
public TargetTag updateTargetTag(@NotNull final TargetTag targetTag) {
checkNotNull(targetTag.getName());
checkNotNull(targetTag.getId());
return targetTagRepository.save(targetTag);
final TargetTag save = targetTagRepository.save(targetTag);
afterCommit.afterCommit(() -> eventBus.post(new TargetTagUpdateEvent(save)));
return save;
}
/**
@@ -208,7 +265,11 @@ public class TagManagement {
throw new EntityAlreadyExistsException();
}
return distributionSetTagRepository.save(distributionSetTag);
final DistributionSetTag save = distributionSetTagRepository.save(distributionSetTag);
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware
.getCurrentTenant(), save)));
return save;
}
/**
@@ -223,15 +284,18 @@ public class TagManagement {
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public Iterable<DistributionSetTag> createDistributionSetTags(
public List<DistributionSetTag> createDistributionSetTags(
@NotNull final Iterable<DistributionSetTag> distributionSetTags) {
for (final DistributionSetTag dsTag : distributionSetTags) {
if (dsTag.getId() != null) {
throw new EntityAlreadyExistsException();
}
}
final List<DistributionSetTag> save = distributionSetTagRepository.save(distributionSetTags);
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware
.getCurrentTenant(), save)));
return distributionSetTagRepository.save(distributionSetTags);
return save;
}
/**
@@ -257,6 +321,8 @@ public class TagManagement {
distributionSetRepository.save(changed);
distributionSetTagRepository.deleteByName(tagName);
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagDeletedEvent(tag)));
}
/**
@@ -275,7 +341,10 @@ public class TagManagement {
public DistributionSetTag updateDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
checkNotNull(distributionSetTag.getName());
checkNotNull(distributionSetTag.getId());
return distributionSetTagRepository.save(distributionSetTag);
final DistributionSetTag save = distributionSetTagRepository.save(distributionSetTag);
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagUpdateEvent(save)));
return save;
}
/**
@@ -284,7 +353,7 @@ public class TagManagement {
* @return all {@link DistributionTag}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<DistributionSetTag> findDistributionSetTagsAll() {
public List<DistributionSetTag> findAllDistributionSetTags() {
return distributionSetTagRepository.findAll();
}
@@ -328,16 +397,31 @@ public class TagManagement {
}
/**
* returns all {@link TargetTag}s.
* returns all {@link DistributionSetTag}s.
*
* @param pageReq
* page parameter
* @return all {@link TargetTag}s
* @return all {@link DistributionSetTag}s
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<DistributionSetTag> findDistributionSetTagsAll(@NotNull final Pageable pageReq) {
public Page<DistributionSetTag> findAllDistributionSetTags(@NotNull final Pageable pageReq) {
return distributionSetTagRepository.findAll(pageReq);
}
/**
* Retrieves all DistributionSet tags based on the given specification.
*
* @param spec
* the specification for the query
* @param pageable
* pagination parameter
* @return the found {@link DistributionSetTag}s, never {@code null}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<DistributionSetTag> findAllDistributionSetTags(@NotNull final Specification<DistributionSetTag> spec,
@NotNull final Pageable pageable) {
return distributionSetTagRepository.findAll(spec, pageable);
}
}

View File

@@ -10,7 +10,9 @@ package org.eclipse.hawkbit.repository;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -28,6 +30,8 @@ import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.Constants;
import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -39,6 +43,7 @@ import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetInfo_;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssigmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
@@ -86,9 +91,6 @@ public class TargetManagement {
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private TargetInfoRepository targetInfoRepository;
@@ -98,6 +100,9 @@ public class TargetManagement {
@Autowired
private EventBus eventBus;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* Find {@link Target} based on given ID returns found Target without
* details, i.e. NO {@link Target#getTags()} and
@@ -179,14 +184,11 @@ public class TargetManagement {
// workarround - no join fetch allowed that is why we need specification
// instead of query for
// count() of Pageable
final Specification<Target> spec = new Specification<Target>() {
@Override
public Predicate toPredicate(final Root<Target> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
if (!query.getResultType().isAssignableFrom(Long.class)) {
root.fetch(Target_.targetInfo);
}
return cb.conjunction();
final Specification<Target> spec = (root, query, cb) -> {
if (!query.getResultType().isAssignableFrom(Long.class)) {
root.fetch(Target_.targetInfo);
}
return cb.conjunction();
};
return criteriaNoCountDao.findAll(spec, pageable, Target.class);
}
@@ -223,7 +225,7 @@ public class TargetManagement {
/**
* Retrieves all targets based on the given specification.
*
*
* @param spec
* the specification for the query
* @param pageable
@@ -256,7 +258,7 @@ public class TargetManagement {
/**
* updates the {@link Target}.
*
*
* @param target
* to be updated
* @return the updated {@link Target}
@@ -274,7 +276,7 @@ public class TargetManagement {
/**
* updates multiple {@link Target}s.
*
*
* @param targets
* to be updated
* @return the updated {@link Target}s
@@ -291,7 +293,7 @@ public class TargetManagement {
/**
* Deletes all targets with the given IDs.
*
*
* @param targetIDs
* the technical IDs of the targets to be deleted
*/
@@ -336,7 +338,7 @@ public class TargetManagement {
* details, i.e. NO {@link Target#getTags()} and
* {@link Target#getActiveActions()} possible including the filtering based
* on the given {@code spec}.
*
*
* @param distributionSetID
* the ID of the {@link DistributionSet}
* @param spec
@@ -349,14 +351,9 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
public Page<Target> findTargetByAssignedDistributionSet(@NotNull final Long distributionSetID,
final Specification<Target> spec, @NotNull final Pageable pageReq) {
return targetRepository.findAll(new Specification<Target>() {
@Override
public Predicate toPredicate(final Root<Target> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return cb
.and(TargetSpecifications.hasAssignedDistributionSet(distributionSetID).toPredicate(root,
query, cb), spec.toPredicate(root, query, cb));
}
}, pageReq);
return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and(
TargetSpecifications.hasAssignedDistributionSet(distributionSetID).toPredicate(root, query, cb),
spec.toPredicate(root, query, cb)), pageReq);
}
/**
@@ -393,14 +390,9 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET)
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetId,
final Specification<Target> spec, final Pageable pageable) {
return targetRepository.findAll(new Specification<Target>() {
@Override
public Predicate toPredicate(final Root<Target> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return cb.and(
TargetSpecifications.hasInstalledDistributionSet(distributionSetId)
.toPredicate(root, query, cb), spec.toPredicate(root, query, cb));
}
}, pageable);
return targetRepository.findAll((Specification<Target>) (root, query, cb) -> cb.and(
TargetSpecifications.hasInstalledDistributionSet(distributionSetId).toPredicate(root, query, cb),
spec.toPredicate(root, query, cb)), pageable);
}
/**
@@ -487,16 +479,16 @@ public class TargetManagement {
return countByCriteriaAPI(specList);
}
private List<Specification<Target>> buildSpecificationList(final Collection<TargetUpdateStatus> status,
private static List<Specification<Target>> buildSpecificationList(final Collection<TargetUpdateStatus> status,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final boolean fetch, final String... tagNames) {
final List<Specification<Target>> specList = new ArrayList<Specification<Target>>();
final List<Specification<Target>> specList = new ArrayList<>();
if (status != null && !status.isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch));
}
if (installedOrAssignedDistributionSetId != null) {
specList.add(TargetSpecifications
.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId));
specList.add(
TargetSpecifications.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId));
}
if (!Strings.isNullOrEmpty(searchText)) {
specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText));
@@ -509,7 +501,7 @@ public class TargetManagement {
/**
* executes findAll with the given {@link Target} {@link Specification}s.
*
*
* @param pageable
* paging parameter
* @param specList
@@ -538,7 +530,7 @@ public class TargetManagement {
}
private Specifications<Target> creatingTargetSpecifications(final List<Specification<Target>> specList) {
private static Specifications<Target> creatingTargetSpecifications(final List<Specification<Target>> specList) {
Specifications<Target> specs = null;
if (!specList.isEmpty()) {
specs = Specifications.where(specList.get(0));
@@ -568,8 +560,8 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTagAssigmentResult toggleTagAssignment(@NotEmpty final List<Target> targets,
@NotNull final TargetTag tag) {
return toggleTagAssignment(targets.stream().map(target -> target.getControllerId())
.collect(Collectors.toList()), tag.getName());
return toggleTagAssignment(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), tag.getName());
}
/**
@@ -592,25 +584,107 @@ public class TargetManagement {
@NotNull final String tagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName);
final List<Target> alreadyAssignedTargets = targetRepository.findByTagNameAndControllerIdIn(tagName, targetIds);
final List<Target> allTargets = targetRepository.findAll(TargetSpecifications
.byControllerIdWithStatusAndTagsInJoin(targetIds));
final List<Target> allTargets = targetRepository
.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds));
// all are already assigned -> unassign
if (alreadyAssignedTargets.size() == allTargets.size()) {
alreadyAssignedTargets.forEach(target -> target.getTags().remove(tag));
return new TargetTagAssigmentResult(0, 0, alreadyAssignedTargets.size(), null, alreadyAssignedTargets);
final TargetTagAssigmentResult result = new TargetTagAssigmentResult(0, 0, alreadyAssignedTargets.size(),
Collections.emptyList(), alreadyAssignedTargets, tag);
afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result)));
return result;
}
allTargets.removeAll(alreadyAssignedTargets);
// some or none are assigned -> assign
allTargets.forEach(target -> target.getTags().add(tag));
final TargetTagAssigmentResult result = new TargetTagAssigmentResult(alreadyAssignedTargets.size(),
allTargets.size(), 0, targetRepository.save(allTargets), null);
allTargets.size(), 0, targetRepository.save(allTargets), Collections.emptyList(), tag);
afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result)));
// no reason to persist the tag
entityManager.detach(tag);
return result;
}
/**
* Assign a {@link TargetTag} assignment to given {@link Target}s.
*
* @param targetIds
* to assign for
* @param tagName
* to assign
* @return list of assigned targets
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<Target> assignTag(@NotEmpty final Collection<String> targetIds, @NotNull final TargetTag tag) {
final List<Target> allTargets = targetRepository.findAll(TargetSpecifications
.byControllerIdWithStatusAndTagsInJoin(targetIds));
allTargets.forEach(target -> target.getTags().add(tag));
final List<Target> save = targetRepository.save(allTargets);
afterCommit.afterCommit(() -> {
final TargetTagAssigmentResult assigmentResult = new TargetTagAssigmentResult(0, save.size(), 0, save,
Collections.emptyList(), tag);
eventBus.post(new TargetTagAssigmentResultEvent(assigmentResult));
});
return save;
}
private List<Target> unAssignTag(@NotEmpty final Collection<Target> targets, @NotNull final TargetTag tag) {
targets.forEach(target -> target.getTags().remove(tag));
final List<Target> save = targetRepository.save(targets);
afterCommit.afterCommit(() -> {
final TargetTagAssigmentResult assigmentResult = new TargetTagAssigmentResult(0, 0, save.size(),
Collections.emptyList(), save, tag);
eventBus.post(new TargetTagAssigmentResultEvent(assigmentResult));
});
return save;
}
/**
* Unassign all {@link Target} from a given {@link TargetTag} .
*
* @param tag
* to unassign all targets
* @return list of unassigned targets
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<Target> unAssignAllTargetsByTag(@NotNull final TargetTag tag) {
return unAssignTag(tag.getAssignedToTargets(), tag);
}
/**
* Unassign a {@link TargetTag} assignment to given {@link Target}.
*
* @param controllerID
* to unassign for
* @param targetTag
* to unassign
* @return the unassigned target or <null> if no target is unassigned
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Target unAssignTag(@NotNull final String controllerID, @NotNull final TargetTag targetTag) {
final List<Target> allTargets = targetRepository.findAll(TargetSpecifications
.byControllerIdWithStatusAndTagsInJoin(Arrays.asList(controllerID)));
final List<Target> unAssignTag = unAssignTag(allTargets, targetTag);
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
}
/**
* method retrieves all {@link Target}s from the repo in the following
* order:
@@ -624,7 +698,7 @@ public class TargetManagement {
* <p>
* 3) {@link Target}s which have no connection to the given
* {@link DistributionSet}.
*
*
* @param pageable
* the page request to page the result set
* @param orderByDistributionId
@@ -667,19 +741,20 @@ public class TargetManagement {
// select case expression to retrieve the case value as a column to be
// able to order based on
// this column, installed first,...
final Expression<Object> selectCase = cb
.selectCase()
final Expression<Object> selectCase = cb.selectCase()
.when(cb.equal(targetInfo.get(TargetInfo_.installedDistributionSet).get(DistributionSet_.id),
orderByDistributionId), 1)
.when(cb.equal(targetRoot.get(Target_.assignedDistributionSet).get(DistributionSet_.id),
orderByDistributionId), 2).otherwise(100);
orderByDistributionId), 2)
.otherwise(100);
// multiselect statement order by the select case and controllerId
query.distinct(true);
// build the specifications and then to predicates necessary by the
// given filters
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
selectTargetWithNoTag, true, filterByTagNames), targetRoot, query, cb);
selectTargetWithNoTag, true, filterByTagNames),
targetRoot, query, cb);
// if we have some predicates then add it to the where clause of the
// multiselect
@@ -706,7 +781,7 @@ public class TargetManagement {
/**
* @param specifications
*/
private Predicate[] specificationsToPredicate(final List<Specification<Target>> specifications,
private static Predicate[] specificationsToPredicate(final List<Specification<Target>> specifications,
final Root<Target> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
final Predicate[] predicates = new Predicate[specifications.size()];
for (int index = 0; index < predicates.length; index++) {
@@ -745,7 +820,7 @@ public class TargetManagement {
/**
* finds all {@link Target#getControllerId()} which are currently in the
* database.
*
*
* @return all IDs of all {@link Target} in the system
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
@@ -753,9 +828,8 @@ public class TargetManagement {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<TargetIdName> query = cb.createQuery(TargetIdName.class);
final Root<Target> targetRoot = query.from(Target.class);
return entityManager.createQuery(
query.multiselect(targetRoot.get(Target_.id), targetRoot.get(Target_.controllerId),
targetRoot.get(Target_.name))).getResultList();
return entityManager.createQuery(query.multiselect(targetRoot.get(Target_.id),
targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name))).getResultList();
}
@@ -790,14 +864,15 @@ public class TargetManagement {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<Target> targetRoot = query.from(Target.class);
List<Object[]> resultList = null;
List<Object[]> resultList;
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(Target_.id),
targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name),
targetRoot.get(pageRequest.getSort().iterator().next().getProperty()));
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
selectTargetWithNoTag, false, filterByTagNames), targetRoot, multiselect, cb);
selectTargetWithNoTag, false, filterByTagNames),
targetRoot, multiselect, cb);
// if we have some predicates then add it to the where clause of the
// multiselect
@@ -805,25 +880,47 @@ public class TargetManagement {
multiselect.where(specificationsForMultiSelect);
}
if (pageRequest.getSort() != null) {
final List<Order> orders = new ArrayList<>();
final Sort sort = pageRequest.getSort();
for (final Sort.Order sortOrder : sort) {
if (sortOrder.getDirection() == Direction.ASC) {
orders.add(cb.asc(targetRoot.get(sortOrder.getProperty())));
} else {
orders.add(cb.desc(targetRoot.get(sortOrder.getProperty())));
}
}
multiselect.orderBy(orders);
resultList = entityManager.createQuery(multiselect).setFirstResult(pageRequest.getOffset())
.setMaxResults(pageRequest.getPageSize()).getResultList();
} else {
resultList = entityManager.createQuery(multiselect).getResultList();
}
resultList = getTargetIdNameResultSet(pageRequest, cb, targetRoot, multiselect);
return resultList.parallelStream().map(o -> new TargetIdName((long) o[0], o[1].toString(), o[2].toString()))
.collect(Collectors.toList());
}
/**
* Finds all {@link Target#getControllerId()} for all the given parameter
* {@link TargetFilterQuery}.
*
* @param pageRequest
* the pageRequest to enhance the query for paging and sorting
* @param targetFilterQuery
* {@link TargetFilterQuery}
* @return the found {@link Target}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<TargetIdName> findAllTargetIdsByTargetFilterQuery(final PageRequest pageRequest,
@NotNull final TargetFilterQuery targetFilterQuery) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<Target> targetRoot = query.from(Target.class);
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(Target_.id),
targetRoot.get(Target_.controllerId), targetRoot.get(Target_.name),
targetRoot.get(pageRequest.getSort().iterator().next().getProperty()));
final Specification<Target> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
entityManager);
final List<Specification<Target>> specList = new ArrayList<>();
specList.add(spec);
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(specList, targetRoot, multiselect,
cb);
// if we have some predicates then add it to the where clause of the
// multiselect
if (specificationsForMultiSelect.length > 0) {
multiselect.where(specificationsForMultiSelect);
}
final List<Object[]> resultList = getTargetIdNameResultSet(pageRequest, cb, targetRoot, multiselect);
return resultList.parallelStream().map(o -> new TargetIdName((long) o[0], o[1].toString(), o[2].toString()))
.collect(Collectors.toList());
}
@PreDestroy
@@ -880,7 +977,7 @@ public class TargetManagement {
/**
* creating a new {@link Target}.
*
*
* @param target
* to be created
* @return the created {@link Target}
@@ -901,24 +998,25 @@ public class TargetManagement {
* already exists in the DB a {@link EntityAlreadyExistsException} is
* thrown. {@link Target}s contain all objects of the parameter targets,
* including duplicates.
*
*
* @param targets
* to be created.
* @return the created {@link Target}s
*
* @throws {@link EntityAlreadyExistsException} of one of the given targets
* already exist.
* @throws {@link
* EntityAlreadyExistsException} of one of the given targets
* already exist.
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final List<Target> targets) {
if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId())
.collect(Collectors.toList())) > 0) {
if (targetRepository.countByControllerIdIn(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) {
throw new EntityAlreadyExistsException();
}
final List<Target> savedTargets = new ArrayList<Target>();
final List<Target> savedTargets = new ArrayList<>();
for (final Target t : targets) {
final Target myTarget = createTarget(t);
savedTargets.add(myTarget);
@@ -947,11 +1045,11 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final Collection<Target> targets,
@NotNull final TargetUpdateStatus status, final long lastTargetQuery, final URI address) {
if (targetRepository.countByControllerIdIn(targets.stream().map(target -> target.getControllerId())
.collect(Collectors.toList())) > 0) {
if (targetRepository.countByControllerIdIn(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) {
throw new EntityAlreadyExistsException();
}
final List<Target> savedTargets = new ArrayList<Target>();
final List<Target> savedTargets = new ArrayList<>();
for (final Target t : targets) {
final Target myTarget = createTarget(t, status, lastTargetQuery, address);
savedTargets.add(myTarget);
@@ -970,13 +1068,12 @@ public class TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<Target> findTargetsByTag(@NotNull final String tagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName);
final List<Target> assignedTargets = targetRepository.findByTag(tag);
return assignedTargets;
return targetRepository.findByTag(tag);
}
/**
* Count {@link TargetFilterQuery}s for given filter parameter.
*
*
* @param targetFilterQuery
* {link TargetFilterQuery}
* @return the found number {@link TargetFilterQuery}s
@@ -990,7 +1087,7 @@ public class TargetManagement {
/**
* Count {@link TargetFilterQuery}s for given target filter query.
*
*
* @param targetFilterQuery
* {link TargetFilterQuery}
* @return the found number {@link TargetFilterQuery}s
@@ -1000,4 +1097,26 @@ public class TargetManagement {
final Specification<Target> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class, entityManager);
return targetRepository.count(specs);
}
private List<Object[]> getTargetIdNameResultSet(final PageRequest pageRequest, final CriteriaBuilder cb,
final Root<Target> targetRoot, final CriteriaQuery<Object[]> multiselect) {
List<Object[]> resultList;
if (pageRequest.getSort() != null) {
final List<Order> orders = new ArrayList<>();
final Sort sort = pageRequest.getSort();
for (final Sort.Order sortOrder : sort) {
if (sortOrder.getDirection() == Direction.ASC) {
orders.add(cb.asc(targetRoot.get(sortOrder.getProperty())));
} else {
orders.add(cb.desc(targetRoot.get(sortOrder.getProperty())));
}
}
multiselect.orderBy(orders);
resultList = entityManager.createQuery(multiselect).setFirstResult(pageRequest.getOffset())
.setMaxResults(pageRequest.getPageSize()).getResultList();
} else {
resultList = entityManager.createQuery(multiselect).getResultList();
}
return resultList;
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.util.List;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
@@ -21,7 +22,7 @@ import org.springframework.transaction.annotation.Transactional;
*
*/
@Transactional(readOnly = true)
public interface TargetTagRepository extends BaseEntityRepository<TargetTag, Long> {
public interface TargetTagRepository extends BaseEntityRepository<TargetTag, Long>, JpaSpecificationExecutor<TargetTag> {
/**
* deletes the {@link TargetTag}s with the given tag names.

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
import java.util.Optional;
import org.eclipse.hawkbit.report.model.TenantUsage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Management service for stats of a single tenant.
*
*/
@Validated
@Service
public class TenantStatsManagement {
@Autowired
private TargetRepository targetRepository;
@Autowired
private LocalArtifactRepository artifactRepository;
@Autowired
private ActionRepository actionRepository;
/**
* Service for stats of a single tenant. Opens a new transaction and as a
* result can an be used for multiple tenants, i.e. to allow in one session
* to collect data of all tenants in the system.
*
* @param tenant
* to collect for
* @return collected statistics
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public TenantUsage getStatsOfTenant(final String tenant) {
final TenantUsage result = new TenantUsage(tenant);
result.setTargets(targetRepository.count());
final Long artifacts = artifactRepository.countBySoftwareModuleDeleted(false);
result.setArtifacts(artifacts);
final Optional<Long> artifactsSize = artifactRepository.getSumOfUndeletedArtifactSize();
if (artifactsSize.isPresent()) {
result.setOverallArtifactVolumeInBytes(artifactsSize.get());
}
result.setActions(actionRepository.count());
return result;
}
}

View File

@@ -1,70 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.event;
import java.util.List;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* Defines all events which are sent from the {@link DeploymentManagement}.
*
*
*
*
*/
@Service
public class DeploymentManagementEvents {
@Autowired
private EventBus eventBus;
/**
* Sends the {@link TargetAssignDistributionSetEvent} for a specific target
* to the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* the software modules which have been assigned
*/
public void assignDistributionSet(final Target target, final Long actionId,
final List<SoftwareModule> softwareModules) {
target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING);
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), actionId, softwareModules,
target.getTargetInfo().getAddress()));
}
/**
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
* the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
*/
public void cancalAssignDistributionSet(final Target target, final Long actionId) {
eventBus.post(new CancelTargetAssignmentEvent(target.getControllerId(), actionId,
target.getTargetInfo().getAddress()));
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.exception;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.exception.SpServerRtException;
/**
* Thrown when force quitting an actions is not allowed. e.g. the action is not
* active or it is not canceled before.
*
*/
public final class ForceQuitActionNotAllowedException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new CancelActionNotAllowed with
* {@link SpServerError#SP_ACTION_NOT_CANCELABLE} error.
*/
public ForceQuitActionNotAllowedException() {
super(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE);
}
/**
* @param cause
* for the exception
*/
public ForceQuitActionNotAllowedException(final Throwable cause) {
super(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE, cause);
}
/**
* @param message
* of the error
*/
public ForceQuitActionNotAllowedException(final String message) {
super(message, SpServerError.SP_ACTION_NOT_FORCE_QUITABLE);
}
}

View File

@@ -116,6 +116,10 @@ public class Action extends BaseEntity implements Comparable<Action> {
this.distributionSet = distributionSet;
}
/**
* @return true when action is in state {@link Status#CANCELING} or
* {@link Status#CANCELED}, false otherwise
*/
public boolean isCancelingOrCanceled() {
return status == Status.CANCELING || status == Status.CANCELED;
}
@@ -262,7 +266,7 @@ public class Action extends BaseEntity implements Comparable<Action> {
}
/**
* @return the forced
* @return true when action is forced, false otherwise
*/
public boolean isForced() {
return actionType == ActionType.FORCED;
@@ -331,14 +335,10 @@ public class Action extends BaseEntity implements Comparable<Action> {
/**
* Action status as reported by the controller.
*
* Be aware that JPA is persiting the ordnial number of the enum by means
* Be aware that JPA is persisting the ordinal number of the enum by means
* the ordered number in the enum. So don't re-order the enums within the
* Status enum declaration!
*
*
*
*
*
*/
public enum Status {
/**

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.model;
/**
* Generic assigment result bean.

View File

@@ -69,13 +69,13 @@ public class DistributionSet extends NamedVersionedEntity {
@JoinTable(name = "sp_ds_module", joinColumns = {
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds") ) }, inverseJoinColumns = {
@JoinColumn(name = "module_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module") ) })
private final Set<SoftwareModule> modules = new HashSet<SoftwareModule>();
private final Set<SoftwareModule> modules = new HashSet<>();
@ManyToMany(targetEntity = DistributionSetTag.class)
@JoinTable(name = "sp_ds_dstag", joinColumns = {
@JoinColumn(name = "ds", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds") ) }, inverseJoinColumns = {
@JoinColumn(name = "TAG", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag") ) })
private Set<DistributionSetTag> tags = new HashSet<DistributionSetTag>();
private Set<DistributionSetTag> tags = new HashSet<>();
@Column(name = "deleted")
private boolean deleted = false;
@@ -110,7 +110,7 @@ public class DistributionSet extends NamedVersionedEntity {
/**
* Parameterized constructor.
*
*
* @param name
* of the {@link DistributionSet}
* @param version
@@ -128,7 +128,7 @@ public class DistributionSet extends NamedVersionedEntity {
this.type = type;
if (moduleList != null) {
moduleList.forEach(module -> addModule(module));
moduleList.forEach(this::addModule);
}
complete = type.checkComplete(this);
}
@@ -160,7 +160,7 @@ public class DistributionSet extends NamedVersionedEntity {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#hashCode()
*/
@Override
@@ -173,7 +173,7 @@ public class DistributionSet extends NamedVersionedEntity {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@@ -234,7 +234,7 @@ public class DistributionSet extends NamedVersionedEntity {
/*
* (non-Javadoc)
*
*
* @see java.lang.Object#toString()
*/
@Override
@@ -322,8 +322,8 @@ public class DistributionSet extends NamedVersionedEntity {
* Searches through modules for the given type.
*
* @param type
* to serach for
* @return SoftwareModule of giben type or <code>null</code> if not in the
* to seach for
* @return SoftwareModule of given type or <code>null</code> if not in the
* list.
*/
public SoftwareModule findFirstModuleByType(final SoftwareModuleType type) {

Some files were not shown because too many files have changed in this diff Show More