Initial check in accordance with Parallel IP

This commit is contained in:
Kai Zimmermann
2016-01-21 13:18:55 +01:00
parent c5e4f1139f
commit 7497ab61ed
763 changed files with 114508 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
/**
* 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;
/**
* A constant class which holds only static constants used within the SP server.
*
*
*
*
*/
public final class Constants {
/**
* Defines the maximum entries for SQL in-statement. Many database have
* limit of elements within an SQL in-statement, e.g. Oracle with maximum
* 1000 elements. So everywhere we are using in-statements in our SQL
* queries we need to split these elements for the in-statements by this
* number.
*/
public static final int MAX_ENTRIES_IN_STATEMENT = 999;
/**
* constant class only private constructor.
*/
private Constants() {
}
}

View File

@@ -0,0 +1,59 @@
/**
* 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;
import javax.persistence.EntityManager;
import javax.transaction.Transaction;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* {@link JpaTransactionManager} that sets the
* {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
* to be done in eclipselink after a {@link Transaction} has been started.
*
*
*
*/
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
private static final long serialVersionUID = 1L;
@Autowired
private transient TenantAware tenantAware;
@Override
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
super.doBegin(transaction, definition);
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
if (!definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants")
&& !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
&& !definition.getName()
.startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator")) {
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant == null) {
throw new TenantNotExistException("Tenant Unknown. Canceling transaction.");
}
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
}
}
}

View File

@@ -0,0 +1,178 @@
/**
* 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;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.aspects.SpExceptionMappingAspect;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
import org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper;
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
/**
* General configuration for the SP Repository.
*
*
*
*
*
*/
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository" })
@EnableTransactionManagement
@EnableJpaAuditing
@EnableAspectJAutoProxy
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/**
* @return simple default {@link AsyncUncaughtExceptionHandler}
* implementation
* @Bean public SimpleAsyncUncaughtExceptionHandler
* simpleAsyncUncaughtExceptionHandler() { return new
* SimpleAsyncUncaughtExceptionHandler(); }
*
* /**
* @return the {@link PollConfigurationHelper} singleton bean which holds
* the polling and polling overdue configuration and make it
* accessible in beans which cannot not be autowired or retrieve
* environment variables due {@link EnvironmentAware} interface.
*/
@Bean
public PollConfigurationHelper pollConfigurationHelper() {
return PollConfigurationHelper.getInstance();
}
/**
* @return the {@link SystemManagementHolder} singleton bean which holds the
* current {@link SystemManagement} service and make it accessible
* in beans which cannot access the service directly, e.g. JPA
* entities.
*/
@Bean
public SystemManagementHolder systemManagementHolder() {
return SystemManagementHolder.getInstance();
}
/**
* @return the {@link TenantAwareHolder} singleton bean which holds the
* current {@link TenantAware} service and make it accessible in
* beans which cannot access the service directly, e.g. JPA
* entities.
*/
@Bean
public TenantAwareHolder tenantAwareHolder() {
return TenantAwareHolder.getInstance();
}
/**
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which
* holds the current {@link SecurityTokenGenerator} service and make
* it accessible in beans which cannot access the service via
* injection
*/
@Bean
public SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
return SecurityTokenGeneratorHolder.getInstance();
}
/**
* @return the singleton instance of the {@link CacheManagerHolder}
*/
@Bean
public CacheManagerHolder cacheManagerHolder() {
return CacheManagerHolder.getInstance();
}
/**
* Defines the validation processor bean.
*
* @return the {@link MethodValidationPostProcessor}
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
/**
* @return {@link SpExceptionMappingAspect} aspect bean
*/
@Bean
public SpExceptionMappingAspect createRepositoryExceptionHandlerAdvice() {
return new SpExceptionMappingAspect();
}
/*
* (non-Javadoc)
*
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#
* createJpaVendorAdapter()
*/
@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new EclipseLinkJpaVendorAdapter();
}
/*
* (non-Javadoc)
*
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#
* getVendorProperties()
*/
@Override
protected Map<String, Object> getVendorProperties() {
final Map<String, Object> properties = new HashMap<String, Object>();
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
properties.put("eclipselink.weaving", "false");
// needed for reports
properties.put("eclipselink.jdbc.allow-native-sql-queries", "true");
// flyway
properties.put("eclipselink.ddl-generation", "none");
properties.put("eclipselink.persistence-context.flush-mode", "auto");
return properties;
}
/**
* {@link MultiTenantJpaTransactionManager} bean.
*
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#transactionManager()
* @return a new {@link PlatformTransactionManager}
*/
@Override
@Bean
public PlatformTransactionManager transactionManager() {
return new MultiTenantJpaTransactionManager();
}
}

View File

@@ -0,0 +1,108 @@
/**
* 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

@@ -0,0 +1,41 @@
/**
* 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.cache;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
import org.springframework.cache.CacheManager;
import org.springframework.data.annotation.Transient;
/**
* Marks an field within a JPA entity as transient and this field should be
* loaded from a configured {@link CacheManager} by using the JPA entity
* listeners.
*
*
*
* @see CacheFieldEntityListener
*/
@Target({ FIELD })
@Retention(RUNTIME)
@Transient
public @interface CacheField {
/**
* @return the cache key name for this cacheable field which is used to
* store the value.
*/
String key();
}

View File

@@ -0,0 +1,54 @@
/**
* 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.cache;
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
/**
* Constants for cache keys used in multiple classes.
*
*
*
*
* @see CacheFieldEntityListener
* @see CacheWriteNotify
* @see CacheField
*
*/
public final class CacheKeys {
/**
* The cache key name for the {@link UpdateActionStatus} download progress
* event.
*/
public static final String DOWNLOAD_PROGRESS_PERCENT = "download.progress.percent";
/**
* utility class only private constructor.
*/
private CacheKeys() {
}
/**
* calculates the cache key for a specific entity. The cache key must be
* different for each different identifiable entity by its ID.
*
* @param entityId
* the ID of the entity to build the specific cache key for this
* entity
* @param cacheKey
* the cache key for the field to be cached
* @return the combined cache key based on the given {@code entityId} and
* {@code cacheKey}
*/
public static String entitySpecificCacheKey(final String entityId, final String cacheKey) {
return entityId + "." + cacheKey;
}
}

View File

@@ -0,0 +1,96 @@
/**
* 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.cache;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* An service which combines the functionality for functional use cases to write
* into the cache an notify the writing to the cache to the {@link EventBus}.
*
*
*
*/
@Service
public class CacheWriteNotify {
/**
*
*/
private static final int DOWNLOAD_PROGRESS_MAX = 100;
@Autowired
private CacheManager cacheManager;
@Autowired
private EventBus eventBus;
@Autowired
private TenantAware tenantAware;
/**
* writes the download progress in percentage into the cache
* {@link CacheKeys#DOWNLOAD_PROGRESS_PERCENT} and notifies the
* {@link EventBus} with a {@link DownloadProgressEvent}.
*
* @param statusId
* the ID of the {@link ActionStatus}
* @param progressPercent
* the progress in percentage which must be between 0-100
*/
public void downloadProgressPercent(final long statusId, final int progressPercent) {
final Cache cache = cacheManager.getCache(Action.class.getName());
final String cacheKey = CacheKeys.entitySpecificCacheKey(String.valueOf(statusId),
CacheKeys.DOWNLOAD_PROGRESS_PERCENT);
if (progressPercent < DOWNLOAD_PROGRESS_MAX) {
cache.put(cacheKey, progressPercent);
} else {
// in case we reached progress 100 delete the cache value again
// because otherwise he will
// keep there forever
cache.evict(cacheKey);
}
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, progressPercent));
}
/**
* @param cacheManager
* the cacheManager to set
*/
void setCacheManager(final CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* @param eventBus
* the eventBus to set
*/
void setEventBus(final EventBus eventBus) {
this.eventBus = eventBus;
}
/**
* @param tenantAware
* the tenantAware to set
*/
void setTenantAware(final TenantAware tenantAware) {
this.tenantAware = tenantAware;
}
}

View File

@@ -0,0 +1,131 @@
/**
* 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;
import java.io.Serializable;
import java.lang.reflect.Field;
import javax.persistence.PostLoad;
import javax.persistence.PostRemove;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.eclipse.hawkbit.cache.CacheField;
import org.eclipse.hawkbit.cache.CacheKeys;
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.hateoas.Identifiable;
/**
* An JPA entity listener which enriches the JPA entity fields which are
* annotated with {@link CacheField} with values from the {@link CacheManager}.
* Only JPA entities which are implementing {@link Identifiable} can be handled
* by this entity listener cause the cache keys are calculated with the ID of
* the entity.
*
*
*
*
*/
public class CacheFieldEntityListener {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheFieldEntityListener.class);
/**
* enriches the JPA entities after loading the entity from the SQL database.
*
* @param target
* the target which has been loaded from the database
*/
@PostLoad
public void postLoad(final Object target) {
if (target instanceof Identifiable) {
final CacheManager cacheManager = CacheManagerHolder.getInstance().getCacheManager();
@SuppressWarnings("rawtypes")
final String id = ((Identifiable) target).getId().toString();
final Class<? extends Object> type = target.getClass();
findCacheFields(type, id, new CacheFieldCallback() {
@Override
public void fromCache(final Field field, final String cacheKey, final Serializable id)
throws IllegalAccessException {
final Cache cache = cacheManager.getCache(type.getName());
final ValueWrapper valueWrapper = cache
.get(CacheKeys.entitySpecificCacheKey(id.toString(), cacheKey));
if (valueWrapper != null && valueWrapper.get() != null) {
FieldUtils.writeField(field, target, valueWrapper.get(), true);
}
}
});
}
}
/**
* deletes the associated cache fields from the cache when deleted the
* entity from the database.
*
* @param target
* the entity which has been deleted
*/
@PostRemove
public void postDelete(final Object target) {
if (target instanceof Identifiable) {
final CacheManager cacheManager = CacheManagerHolder.getInstance().getCacheManager();
@SuppressWarnings("rawtypes")
final String id = ((Identifiable) target).getId().toString();
final Class<? extends Object> type = target.getClass();
findCacheFields(type, id, new CacheFieldCallback() {
@Override
public void fromCache(final Field field, final String cacheKey, final Serializable id)
throws IllegalAccessException {
final Cache cache = cacheManager.getCache(type.getName());
cache.evict(CacheKeys.entitySpecificCacheKey(id.toString(), cacheKey));
}
});
}
}
private void findCacheFields(final Class<? extends Object> type, final Serializable id,
final CacheFieldCallback callback) {
final Field[] declaredFields = type.getDeclaredFields();
for (final Field field : declaredFields) {
if (field.getAnnotation(CacheField.class) != null) {
try {
final CacheField annotation = field.getAnnotation(CacheField.class);
callback.fromCache(field, annotation.key(), id);
} catch (final IllegalAccessException e) {
LOGGER.error("cannot access the field {} for the entity {}, ignoring the cacheable field", field,
type, e);
}
}
}
}
private interface CacheFieldCallback {
/**
* callback methods which is called by the
* {@link CacheFieldEntityListener#findCacheFields(Class, Serializable, CacheFieldCallback)}
* in case a field is annotated with {@link CacheField}.
*
* @param field
* the field which is annotaed with {@link CacheField}
* @param cacheKey
* the configured cache key in the annotation
* {@link CacheField#key()}
* @param id
* the ID of the entity
* @throws IllegalAccessException
* in case the field cannot be accessed
*/
void fromCache(final Field field, final String cacheKey, Serializable id) throws IllegalAccessException;
}
}

View File

@@ -0,0 +1,146 @@
/**
* 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;
import java.util.Collection;
import javax.persistence.EntityManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.repository.TargetRepository;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* An aspect implementation which wraps the necessary repository services for
* saving {@link BaseEntity}s to publish create or update events.
*
*
*
*
*/
@Service
@Aspect
public class EntityChangeEventListener {
@Autowired
private EventBus eventBus;
@Autowired
private TenantAware tenantAware;
@Autowired
private EntityManager entityManager;
/**
* In case the a {@link Target} is created a corresponding
* {@link TargetInfo} is created as well. We need the {@link TargetInfo}
* information in the target created event. So we are listening to the
* {@link TargetInfo} creation to indicate if an Target has been created.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.TargetInfoRepository.save(..))")
public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable {
final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]);
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);
}
}
return result;
}
/**
* Proxy method around the delete method of the {@link TargetRepository} to
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.deleteByIdIn(..))")
public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed();
final Collection<Long> targetIds = (Collection<Long>) joinpoint.getArgs()[0];
targetIds.forEach(targetId -> notifyTargetDeleted(currentTenant, targetId));
return result;
}
/**
* Proxy method around the delete method of the {@link TargetRepository} to
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@SuppressWarnings("unchecked")
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.delete(..))")
public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed();
final Object param = joinpoint.getArgs()[0];
// delete by id
if (param instanceof Long) {
notifyTargetDeleted(currentTenant, (Long) param);
} else if (param instanceof Target) {
notifyTargetDeleted(currentTenant, ((Target) param).getId());
} else if (param instanceof Iterable) {
((Iterable<Target>) param).forEach(target -> notifyTargetDeleted(currentTenant, target.getId()));
}
return result;
}
private void notifyTargetCreated(final Target t) {
eventBus.post(new TargetCreatedEvent(t));
}
private void notifyTargetInfoChanged(final TargetInfo targetInfo) {
eventBus.post(new TargetInfoUpdateEvent(targetInfo));
}
private void notifyTargetDeleted(final String tenant, final Long targetId) {
eventBus.post(new TargetDeletedEvent(tenant, targetId));
}
private boolean isTargetInfoNew(final Object targetInfo) {
return ((TargetInfo) targetInfo).isNew();
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.BaseEntity;
/**
* An abstract definition class for {@link EntityEvent} for {@link BaseEntity}s,
* which holds the {@link BaseEntity}.
*
*
*
* @param <E>
* the type of the {@link BaseEntity}
*/
public abstract class AbstractBaseEntityEvent<E extends BaseEntity> extends AbstractDistributedEvent
implements EntityEvent {
/**
*
*/
private static final long serialVersionUID = 1L;
private final E entity;
/**
* @param baseEntity
* the entity which has been created or modified
*/
public AbstractBaseEntityEvent(final E baseEntity) {
super(baseEntity.getOptLockRevision(), baseEntity.getTenant());
this.entity = baseEntity;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getEntity()
*/
@Override
public E getEntity() {
return entity;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getEntity(java.lang
* .Class)
*/
@Override
public <T> T getEntity(final Class<T> entityClass) {
return entityClass.cast(entity);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getTenant()
*/
@Override
public String getTenant() {
return entity.getTenant();
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.net.URI;
import java.util.Collection;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Event that gets sent when a distribution set gets assigned to a target.
*
*
*
*/
public class TargetAssignDistributionSetEvent {
private final Collection<SoftwareModule> softwareModules;
private final String controllerId;
private final Long actionId;
private final URI targetAdress;
/**
* Creates a new {@link TargetAssignDistributionSetEvent}.
*
* @param controllerId
* the ID of the controller
* @param actionId
* the action id of the assignment
* @param softwareModules
* the software modules which have been assigned to the target
* @param targetAdress
* the targetAdress of the target
*/
public TargetAssignDistributionSetEvent(final String controllerId, final Long actionId,
final Collection<SoftwareModule> softwareModules, final URI targetAdress) {
this.controllerId = controllerId;
this.actionId = actionId;
this.softwareModules = softwareModules;
this.targetAdress = targetAdress;
}
/**
* @return the action id of the assignment
*/
public Long getActionId() {
return actionId;
}
/**
* @return the controllerId of the Target which has been assigned to the
* distribution set
*/
public String getControllerId() {
return controllerId;
}
/**
* @return the software modules which have been assigned to the target
*/
public Collection<SoftwareModule> getSoftwareModules() {
return softwareModules;
}
public URI getTargetAdress() {
return targetAdress;
}
}

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.Target;
/**
* Defines the {@link AbstractBaseEntityEvent} of creating a new {@link Target}.
*
*
*
*
*/
public class TargetCreatedEvent extends AbstractBaseEntityEvent<Target> {
private static final long serialVersionUID = 1L;
/**
* @param target
* the target which has been created
*/
public TargetCreatedEvent(final Target target) {
super(target);
}
}

View File

@@ -0,0 +1,77 @@
/**
* 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.TargetInfo;
/**
*
*
*
*/
public class TargetInfoUpdateEvent implements EntityEvent {
private final long revision;
private final TargetInfo targetInfo;
private final String tenant;
private String originNodeId;
private String nodeId;
/**
* @param targetInfo
* the target info entity
*/
public TargetInfoUpdateEvent(final TargetInfo targetInfo) {
this.targetInfo = targetInfo;
this.tenant = targetInfo.getTarget().getTenant();
this.revision = -1;
}
@Override
public void setOriginNodeId(final String originNodeId) {
this.originNodeId = originNodeId;
}
@Override
public void setNodeId(final String nodeId) {
this.nodeId = nodeId;
}
@Override
public String getOriginNodeId() {
return this.originNodeId;
}
@Override
public String getNodeId() {
return this.nodeId;
}
@Override
public long getRevision() {
return revision;
}
@Override
public <E> E getEntity(final Class<E> entityClass) {
return entityClass.cast(targetInfo);
}
@Override
public TargetInfo getEntity() {
return targetInfo;
}
@Override
public String getTenant() {
return tenant;
}
}

View File

@@ -0,0 +1,43 @@
/**
* 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.io.Serializable;
/**
* An abstract report series.
*
*
*
*
*/
public class AbstractReportSeries implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String name;
/**
* @param name
* the name of the series
*/
public AbstractReportSeries(final String name) {
this.name = name;
}
/**
* @return the name of the series
*/
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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 java.util.stream.Stream;
/**
* A data report series which contains a list of {@link DataReportSeriesItem}.
*
*
*
* @param <T>
* the type of the report series item
*/
public class DataReportSeries<T extends Object> extends AbstractReportSeries {
private final List<DataReportSeriesItem<T>> data = new ArrayList<>();
/**
* @param name
* the name of data series
*/
public DataReportSeries(final String name) {
super(name);
}
/**
* Constructs a ListSeries with the given series name and array of values.
*
* @param name
* the name of the data series
* @param values
* data report series item for this data series.
*/
public DataReportSeries(final String name, final List<DataReportSeriesItem<T>> values) {
this(name);
setData(values);
}
private void setData(final List<DataReportSeriesItem<T>> values) {
this.data.clear();
data.addAll(values);
}
/**
* @return An array of the numeric values
*/
@SuppressWarnings("unchecked")
public DataReportSeriesItem<T>[] getData() {
return data.toArray(new DataReportSeriesItem[data.size()]);
}
public Stream<DataReportSeriesItem<T>> getDataStream() {
return data.stream();
}
}

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.report.model;
/**
* An data report series item which contains a type and a value.
*
*
*
* @param <T>
* the type of the report series item
*/
public class DataReportSeriesItem<T extends Object> {
private final T type;
private final Number data;
/**
* @param type
* the type of the data report series item
* @param data
* the data of the report series item
*/
public DataReportSeriesItem(final T type, final Number data) {
this.type = type;
this.data = data;
}
/**
* @return the type of the data report item
*/
public T getType() {
return type;
}
/**
* @return the data of the data report item
*/
public Number getData() {
return data;
}
}

View File

@@ -0,0 +1,49 @@
/**
* 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;
/**
* A double data series which contains an inner and an outer series ideal for
* showing donut charts.
*
*
*
* @param <T>
* The type parameter for the report series data
*/
public class InnerOuterDataReportSeries<T> {
private final DataReportSeries<T> innerSeries;
private final DataReportSeries<T> outerSeries;
/**
* @param innerSeries
* the innerseries of an circle donut chart
* @param outerSeries
* the outer series of an donut chart
*/
public InnerOuterDataReportSeries(final DataReportSeries<T> innerSeries, final DataReportSeries<T> outerSeries) {
this.innerSeries = innerSeries;
this.outerSeries = outerSeries;
}
/**
* @return the innerSeries
*/
public DataReportSeries<T> getInnerSeries() {
return innerSeries;
}
/**
* @return the outerSeries
*/
public DataReportSeries<T> getOuterSeries() {
return outerSeries;
}
}

View File

@@ -0,0 +1,63 @@
/**
* 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.Collections;
import java.util.List;
/**
* A simple list report series which just contains a list of values of a report.
*
*
*
*
*/
public class ListReportSeries extends AbstractReportSeries {
private final List<Number> data = new ArrayList<Number>();
/**
* @param name
* the name of the list report series
*/
public ListReportSeries(final String name) {
super(name);
}
/**
* Constructs a ListSeries with the given series name and array of values.
*
* @param name
* the name of the list report series
* @param values
* the values of the list report series
*/
public ListReportSeries(final String name, final Number... values) {
this(name);
setData(values);
}
/**
* Sets the values in the list series to the ones provided.
*
* @param values
*/
private void setData(final Number... values) {
this.data.clear();
Collections.addAll(this.data, values);
}
/**
* @return An array of the numeric values
*/
public Number[] getData() {
return data.toArray(new Number[data.size()]);
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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;
/**
* A series time enum definition for {@link DataReportSeriesItem}s.
*
*
*
*
*/
public enum SeriesTime {
/**
* hour.
*/
HOUR,
/**
* day.
*/
DAY,
/**
* week.
*/
WEEK,
/**
* month.
*/
MONTH,
/**
* year.
*/
YEAR,
/**
* more than one year.
*/
MORE_THAN_YEAR,
/**
* never.
*/
NEVER;
}

View File

@@ -0,0 +1,240 @@
/**
* 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.Collection;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link Action} repository.
*
*
*
*
*
*/
@Transactional(readOnly = true)
public interface ActionRepository extends BaseEntityRepository<Action, Long>, JpaSpecificationExecutor<Action> {
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
@Override
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Iterable<Action> findAll();
/**
* Retrieves an Action with all lazy attributes.
*
* @param actionId
* the ID of the action
* @return the found {@link Action}
*/
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Action findById(Long actionId);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link DistributionSet}.
*
* @param pageable
* page parameters
* @param ds
* the {@link DistributionSet} on which will be filtered
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Page<Action> findByDistributionSet(final Pageable pageable, final DistributionSet ds);
/**
* Retrieves all {@link Action}s which are referring the given
* {@link Target}.
*
* @param pageable
* page parameters
* @param target
* the target to find assigned actions
* @return the found {@link Action}s
*/
Slice<Action> findByTarget(Pageable pageable, Target target);
/**
* Retrieves all {@link Action}s which are active and referring the given
* {@link Target} in a specified order.
*
* @param pageable
* page parameters
* @param target
* the target to find assigned actions
* @param active
* the action active flag
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
List<Action> findByTargetAndActiveOrderByIdAsc(final Target target, boolean active);
/**
* Retrieves latest {@link UpdateAction} for given target and
* {@link SoftwareModule}.
*
* @param targetId
* to search for
* @param module
* to search for
* @return action if there is one with assigned target and module is part of
* assigned {@link DistributionSet}.
*/
@Query("Select a from Action a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul = :module order by a.id desc")
List<Action> findActionByTargetAndSoftwareModule(@Param("target") final String targetId,
@Param("module") SoftwareModule module);
/**
* Retrieves all {@link UpdateAction}s which are referring the given
* {@link DistributionSet} and {@link Target}.
*
* @param pageable
* page parameters
* @param target
* is the assigned target
* @param ds
* the {@link DistributionSet} on which will be filtered
* @return the found {@link UpdateAction}s
*/
@Query("Select a from Action a where a.target = :target and a.distributionSet = :ds order by a.id")
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
Page<Action> findByTargetAndDistributionSet(final Pageable pageable, @Param("target") final Target target,
@Param("ds") DistributionSet ds);
/**
* Retrieves all {@link Action}s of a specific target, without pagination
* ordered by action ID.
*
* @param target
* to search for
* @return a list of actions according to the searched target
*/
@Query("Select a from Action a where a.target = :target order by a.id")
List<Action> findByTarget(@Param("target") final Target target);
/**
* Retrieves all {@link Action}s of a specific target and given active flag
* ordered by action ID.
*
* @param pageable
* the pagination parameter
* @param target
* to search for
* @param active
* {@code true} for all actions which are currently active,
* {@code false} for inactive
* @return a paged list of actions ordered by action ID
*/
@Query("Select a from Action a where a.target = :target and a.active= :active order by a.id")
Page<Action> findByActiveAndTarget(Pageable pageable, @Param("target") Target target,
@Param("active") boolean active);
/**
* Retrieves all {@link Action}s of a specific target and given active flag
* ordered by action ID.
*
* @param target
* to search for
* @param active
* {@code true} for all actions which are currently active,
* {@code false} for inactive
* @return a list of actions ordered by action ID
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
@Query("Select a from Action a where a.target = :target and a.active= :active order by a.id")
List<Action> findByActiveAndTarget(@Param("target") Target target, @Param("active") boolean active);
/**
* Updates all {@link Action} to inactive for all targets with given ID.
*
* @param keySet
* the list of actions to set inactive
* @param targetsIds
* the IDs of the targets according to the action to set in
* active
*/
@Modifying
@Transactional
@Query("UPDATE Action a SET a.active = false WHERE a IN :keySet AND a.target IN :targetsIds")
void setToInactive(@Param("keySet") List<Action> keySet, @Param("targetsIds") List<Long> targetsIds);
/**
* Retrieves all {@link Action}s which are active and referring to the given
* target Ids and distribution set required migration step.
*
* @param targetIds
* the IDs of targets for the actions
* @param notStatus
* the status which the actions should not have
* @return the found list of {@link Action}s
*/
@Query("SELECT a FROM Action a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
List<Action> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(
Collection<Long> targetIds, Action.Status notStatus);
/**
* Counts all {@link Action}s referring to the given target.
*
* @param target
* the target to count the {@link Action}s
* @return the count of actions referring to the given target
*/
Long countByTarget(Target target);
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.CrudRepository#save(java.lang.
* Iterable)
*/
@Override
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
<S extends Action> List<S> save(Iterable<S> entities);
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
@Override
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
<S extends Action> S save(S entity);
/**
* Counts all {@link Action}s referring to the given DistributionSet.
*
* @param distributionSet
* DistributionSet to count the {@link Action}s from
* @return the count of actions referring to the given target
*/
Long countByDistributionSet(DistributionSet distributionSet);
}

View File

@@ -0,0 +1,76 @@
/**
* 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 org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link ActionStatus} repository.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface ActionStatusRepository
extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> {
/**
* @param target
* @param action
* @return
*/
Long countByAction(Action action);
/**
* @param action
* @param retrieved
* @return
*/
Long countByActionAndStatus(Action action, Status retrieved);
/**
* @param pageReq
* @param action
* @return
*/
Page<ActionStatus> findByAction(Pageable pageReq, Action action);
/**
* @param pageReq
* @param action
* @return
*/
Page<ActionStatus> findByActionOrderByIdDesc(Pageable pageReq, Action action);
/**
* Finds all status updates for the defined action and target order by
* {@link ActionStatus#getId()} desc including
* {@link ActionStatus#getMessages()}.
*
* @param pageReq
* for page configuration
* @param target
* to look for
* @param action
* to look for
* @return Page with found targets
*/
@EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD)
Page<ActionStatus> getByActionOrderByIdDesc(Pageable pageReq, Action action);
}

View File

@@ -0,0 +1,501 @@
/**
* 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.io.InputStream;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.GridFSDBFileNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.hateoas.Identifiable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* service for {@link Artifact} management operations.
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class ArtifactManagement {
private static final Logger LOG = LoggerFactory.getLogger(ArtifactManagement.class);
@Autowired
private LocalArtifactRepository localArtifactRepository;
@Autowired
private ExternalArtifactRepository externalArtifactRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ExternalArtifactProviderRepository externalArtifactProviderRepository;
@Autowired
private ArtifactRepository artifactRepository;
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param stream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param providedSha1Sum
* optional sha1 checksum to check the new file against
* @param providedMd5Sum
* optional md5 checksum to check the new file against
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overdiden
* if it already exists
* @param contentType
* the contentType of the file
* @return uploaded {@link LocalArtifact}
*
* @throws EntityNotFoundException
* if given software module does not exist
* @throws EntityAlreadyExistsException
* if File with that name already exists in the Software Module
* @throws ArtifactUploadFailedException
* if upload fails with internal server errors
* @throws InvalidMD5HashException
* if check against provided MD5 checksum failed
* @throws InvalidSHA1HashException
* if check against provided SHA1 checksum failed
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId,
@NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum,
final boolean overrideExisting, final String contentType) {
DbArtifact result = null;
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
try {
result = artifactRepository.store(stream, filename, contentType,
new DbArtifactHash(providedSha1Sum, providedMd5Sum));
} catch (final ArtifactStoreException e) {
throw new ArtifactUploadFailedException(e);
} catch (final HashNotMatchException e) {
if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
throw new InvalidSHA1HashException(e.getMessage(), e);
} else {
throw new InvalidMD5HashException(e.getMessage(), e);
}
}
if (result == null) {
return null;
}
return storeArtifactMetadata(softwareModule, filename, result, existing);
}
private LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
final SoftwareModule softwareModule) {
if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
if (overrideExisting) {
LOG.debug("overriding existing artifact with new filename {}", filename);
return softwareModule.getLocalArtifactByFilename(filename).get();
} else {
throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
}
}
return null;
}
private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) {
final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId);
if (softwareModule == null) {
LOG.debug("no software module with ID {} exists", moduleId);
throw new EntityNotFoundException("Software Module: " + moduleId);
}
return softwareModule;
}
/**
* Retrieves software module including details (
* {@link SoftwareModule#getArtifacts()}).
*
* @param id
* parameter
* @param isDeleted
* parameter
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
protected SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) {
final SoftwareModule result = findSoftwareModuleById(id);
if (result != null) {
result.getArtifacts().size();
}
return result;
}
/**
* Find all local artifact by sha1 and return the first artifact.
*
* @param sha1
* the sha1
* @return the first local artifact
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) {
return localArtifactRepository.findFirstByGridFsFileName(sha1);
}
/**
* Finds {@link SoftwareModule} by given id.
*
* @param id
* to search for
* @return the found {@link SoftwareModule}s or <code>null</code> if not
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
protected SoftwareModule findSoftwareModuleById(@NotNull final Long id) {
final Specification<SoftwareModule> spec = SoftwareModuleSpecification.byId(id);
return softwareModuleRepository.findOne(spec);
}
private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
final DbArtifact result, final LocalArtifact existing) {
LocalArtifact artifact = existing;
if (existing == null) {
artifact = new LocalArtifact(result.getHashes().getSha1(), providedFilename, softwareModule);
}
artifact.setMd5Hash(result.getHashes().getMd5());
artifact.setSha1Hash(result.getHashes().getSha1());
artifact.setSize(result.getSize());
LOG.debug("storing new artifact into repository {}", artifact);
final LocalArtifact artifactPersisted = localArtifactRepository.save(artifact);
return artifactPersisted;
}
/**
* Persists {@link ExternalArtifactProvider} based on given properties.
*
* @param name
* of the provided
* @param description
* which is optional
* @param basePath
* of all {@link ExternalArtifact}s of the provider
* @param defaultUrlSuffix
* that is used if {@link ExternalArtifact#getUrlSuffix()} is
* empty.
* @return created {@link ExternalArtifactProvider}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name,
final String description, @NotNull final String basePath, final String defaultUrlSuffix) {
return externalArtifactProviderRepository
.save(new ExternalArtifactProvider(name, description, basePath, defaultUrlSuffix));
}
/**
* Creates {@link ExternalArtifact} based on given provider.
*
* @param externalRepository
* the artifact is located in
* @param urlSuffix
* of the artifact
* {@link ExternalArtifactProvider#getDefaultSuffix()} is used if
* empty.
* @param moduleId
* to assign the artifact to
*
* @return created {@link ExternalArtifact}
*
* @throws EntityNotFoundException
* if {@link SoftwareModule} with given ID does not exist
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository,
final String urlSuffix, @NotNull final Long moduleId) {
final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId);
final ExternalArtifact result = externalArtifactRepository
.save(new ExternalArtifact(externalRepository, urlSuffix, module));
return result;
}
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteLocalArtifact(@NotNull final Long id) {
final LocalArtifact existing = localArtifactRepository.findOne(id);
if (null == existing) {
return;
}
deleteGridFsArtifact(existing);
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
localArtifactRepository.delete(id);
}
/**
* Delete a grid fs file.
*
* @param existing
* the related local artifact
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) {
if (existing == null) {
return;
}
boolean artifactIsOnlyUsedByOneSoftwareModule = true;
for (LocalArtifact lArtifact : localArtifactRepository.findByGridFsFileName(existing.getGridFsFileName())) {
if (!lArtifact.getSoftwareModule().isDeleted()
&& lArtifact.getSoftwareModule().getId() != existing.getSoftwareModule().getId()) {
artifactIsOnlyUsedByOneSoftwareModule = false;
break;
}
}
if (artifactIsOnlyUsedByOneSoftwareModule) {
try {
LOG.debug("deleting artifact from repository {}", existing.getGridFsFileName());
artifactRepository.deleteBySha1(existing.getGridFsFileName());
} catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e);
}
}
}
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteExternalArtifact(@NotNull final Long id) {
final ExternalArtifact existing = externalArtifactRepository.findOne(id);
if (null == existing) {
return;
}
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
externalArtifactRepository.delete(id);
}
/**
* Searches for {@link Artifact} with given file name.
*
* @param filename
* to search for
* @return found List of {@link LocalArtifact}s.
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public List<LocalArtifact> findLocalArtifactByFilename(@NotNull final String filename) {
return localArtifactRepository.findByFilename(filename);
}
/**
* Searches for {@link Artifact} with given {@link Identifiable}.
*
* @param id
* to search for
* @return found {@link Artifact} or <code>null</code> is it could not be
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Artifact findArtifact(@NotNull final Long id) {
return localArtifactRepository.findOne(id);
}
/**
* Loads {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact}
* from store for given {@link LocalArtifact}.
*
* @param artifact
* to search for
* @return loaded
* {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact}
*
* @throws GridFSDBFileNotFoundException
* if file could not be found in store
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
if (result == null) {
throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName());
}
return result;
}
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overdiden
* if it already exists
* @param contentType
* the contentType of the file
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload failes
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting, final String contentType) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType);
}
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overdiden
* if it already exists
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload failes
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);
}
/**
* Get local artifact for a base software module.
*
* @param pageReq
* Pageable
* @param swId
* software module id
* @return Page<LocalArtifact>
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<LocalArtifact> findLocalArtifactBySoftwareModule(@NotNull final Pageable pageReq,
@NotNull final Long swId) {
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
}
/**
* Find by artifact by software module id and filename.
*
* @param filename
* file name
* @param softwareModuleId
* software module id.
* @return LocalArtifact if artifact present
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public List<LocalArtifact> findByFilenameAndSoftwareModule(@NotNull final String filename,
@NotNull final Long softwareModuleId) {
return localArtifactRepository.findByFilenameAndSoftwareModuleId(filename, softwareModuleId);
}
}

View File

@@ -0,0 +1,60 @@
/**
* 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;
/**
* Generic assigment result bean.
*
*
*
*
*/
public class AssignmentResult {
private final int total;
private final int assigned;
private final int alreadyAssigned;
/**
* Constructor.
*
* @param assigned
* is the number of newly assigned elements.
* @param alreadyAssigned
* is the number of already assigned elements.
*/
public AssignmentResult(final int assigned, final int alreadyAssigned) {
super();
this.assigned = assigned;
this.alreadyAssigned = alreadyAssigned;
total = assigned + alreadyAssigned;
}
/**
* @return the assignedTargets
*/
public int getAssigned() {
return assigned;
}
/**
* @return the total
*/
public int getTotal() {
return total;
}
/**
* @return the alreadyAssigned
*/
public int getAlreadyAssigned() {
return alreadyAssigned;
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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.io.Serializable;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* Command repository operations for all {@link BaseEntity}s.
*
*
*
*
* @param <T>
* type if the entity type
* @param <I>
* of the entity type
*/
@NoRepositoryBean
@Transactional(readOnly = true)
public interface BaseEntityRepository<T extends BaseEntity, I extends Serializable>
extends PagingAndSortingRepository<T, I> {
/**
* Deletes all {@link BaseEntity} of a given tenant.
*
* @param tenant
* to delete data from
*/
@Modifying
@Transactional
void deleteByTenantIgnoreCase(String tenant);
}

View File

@@ -0,0 +1,588 @@
/**
* 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.net.URI;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionStatus_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
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;
/**
* Service layer for all operations of the controller API (with access
* permissions only for the controller).
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class ControllerManagement implements EnvironmentAware {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
@Autowired
private EntityManager entityManager;
@Autowired
private ActionRepository actionRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
private Integer maxCount = 1000;
private Integer maxAttributes = 100;
/**
* Refreshes the time of the last time the controller has been connected to
* the server.
*
* @param targetid
* of the target to to update
* @param address
* the client address of the target, might be {@code null}
* @return the updated target
*
* @throws EntityNotFoundException
* if target with given ID could not be found
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) {
final Target target = targetRepository.findByControllerId(targetid);
if (target == null) {
throw new EntityNotFoundException(targetid);
}
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
/**
* Retrieves last {@link UpdateAction} for a download of an artifact of
* given module and target.
*
* @param targetId
* to look for
* @param module
* that should be assigned to the target
* @return last {@link UpdateAction} for given combination
*
* @throws EntityNotFoundException
* if action for given combination could not be found
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action getActionForDownloadByTargetAndSoftwareModule(@NotNull final String targetId,
@NotNull final SoftwareModule module) {
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);
}
return action.get(0);
}
/**
* Refreshes the time of the last time the controller has been connected to
* the server.
*
* @param target
* to update
* @param address
* the client address of the target, might be {@code null}
* @return the updated target
*
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) {
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
}
/**
* Retrieves all {@link Action}s which are active and assigned to a
* {@link Target}.
*
* @param target
* the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public List<Action> findActionByTargetAndActive(final Target target) {
return actionRepository.findByTargetAndActiveOrderByIdAsc(target, true);
}
/**
* Retrieves all {@link SoftwareModule}s which are assigned to the given
* {@link DistributionSet}.
*
* @param distributionSet
* the distribution set which should be assigned to the returned
* {@link SoftwareModule}s
* @return a list of {@link SoftwareModule}s assigned to given
* {@code distributionSet}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
return softwareModuleRepository.findByAssignedTo(distributionSet);
}
/**
* 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}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action findActionWithDetails(@NotNull final Long actionId) {
return actionRepository.findById(actionId);
}
/**
* register new target in the repository (plug-and-play).
*
* @param targetid
* reference
* @param address
* the client IP address of the target, might be {@code null}
* @return target reference
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target findOrRegisterTargetIfItDoesNotexist(@NotNull final String targetid, final URI address) {
final Specification<Target> spec = new Specification<Target>() {
@Override
public Predicate toPredicate(final Root<Target> targetRoot, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
return cb.equal(targetRoot.get(Target_.controllerId), targetid);
}
};
Target target = targetRepository.findOne(spec);
if (target == null) {
target = new Target(targetid);
target.setDescription("Plug and Play target: " + targetid);
target.setName(targetid);
return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(),
address);
} else {
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
}
/**
* Update selective the target status of a given {@code target}.
*
* @param targetInfo
* the target to update the target status
* @param status
* the status to be set of the target. Might be {@code null} if
* the target status should not be updated
* @param lastTargetQuery
* the last target query to be set of the target. Might be
* {@code null} if the target lastTargetQuery should not be
* updated
* @param address
* the client address of the target, might be {@code null}
* @return the updated TargetInfo
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status,
final Long lastTargetQuery, final URI address) {
final TargetInfo mtargetInfo = entityManager.merge(targetInfo);
if (status != null) {
mtargetInfo.setUpdateStatus(status);
}
if (lastTargetQuery != null) {
mtargetInfo.setLastTargetQuery(lastTargetQuery);
}
if (address != null) {
mtargetInfo.setAddress(address.toString());
}
return targetInfoRepository.save(mtargetInfo);
}
/**
* Reports an {@link ActionStatus} for a {@link CancelAction}.
*
* @param actionStatusMessages
* 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
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public void addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
checkForToManyStatusEntries(action);
action.setStatus(actionStatus.getStatus());
switch (actionStatus.getStatus()) {
case WARNING:
case ERROR:
case RUNNING:
break;
case CANCELED:
case FINISHED:
// in case of successful cancelation we also report the success at
// the canceled action
// itself.
actionStatus.addMessage("Cancelation completion is finished sucessfully.");
// set action inactive
action.setActive(false);
successCancellation(action);
break;
case RETRIEVED:
actionStatus.addMessage("Cancelation request retrieved");
break;
default:
}
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);
}
/**
* Reports an {@link ActionStatus} for a {@link UpdateAction}.
*
* @param actionStatus
* to be updated
* @param action
* the update is for
* @return the persisted {@link ActionStatus}
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
* @throws ToManyStatusEntriesException
* if more than the allowed number of status entries are
* inserted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
if (!action.isActive()) {
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getId(), action.getId());
return action;
}
return handleAddUpdateActionStatus(actionStatus, action);
}
/**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
*
* @param actionStatus
* @param action
* @return
*/
public Action handleAddUpdateActionStatus(final ActionStatus actionStatus, final Action action) {
LOG.debug("addUpdateActionStatus for action {}", action.getId());
final Action mergedAction = entityManager.merge(action);
Target mergedTarget = mergedAction.getTarget();
// check for a potential DOS attack
checkForToManyStatusEntries(action);
switch (actionStatus.getStatus()) {
case ERROR:
mergedTarget = deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.ERROR, false);
// set action inactive
mergedAction.setActive(false);
mergedAction.setStatus(Status.ERROR);
mergedTarget.setAssignedDistributionSet(null);
targetManagement.updateTarget(mergedTarget);
break;
case FINISHED:
// set action inactive
mergedAction.setActive(false);
mergedAction.setStatus(Status.FINISHED);
handleFinishedAndStoreInTargetStatus(mergedTarget, mergedAction);
break;
case CANCELED:
case WARNING:
case RUNNING:
deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false);
break;
default:
break;
}
actionStatusRepository.save(actionStatus);
LOG.debug("addUpdateActionStatus {} for target {} is finished.", action.getId(), mergedTarget.getId());
return actionRepository.save(mergedAction);
}
private void checkForToManyStatusEntries(final Action action) {
if (maxCount > 0) {
final Long statusCount = actionStatusRepository.countByAction(action);
if (statusCount >= maxCount) {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
maxCount);
throw new ToManyStatusEntriesException(String.valueOf(maxCount));
}
}
}
private void handleFinishedAndStoreInTargetStatus(final Target target, final Action action) {
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())) {
targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
targetInfo.setInstallationDate(System.currentTimeMillis());
} else {
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
targetInfo.setInstallationDate(System.currentTimeMillis());
}
targetInfoRepository.save(targetInfo);
entityManager.detach(ds);
}
/**
* Updates attributes of the controller.
*
* @param targetid
* to update
* @param data
* to insert
*
* @return updated {@link Target}
*
* @throws EntityNotFoundException
* if target that has to be updated could not be found
* @throws ToManyAttributeEntriesException
* if maximum
*/
@Modifying
@NotNull
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map<String, String> data) {
final Target target = targetRepository.findByControllerId(targetid);
if (target == null) {
throw new EntityNotFoundException(targetid);
}
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!",
maxAttributes);
throw new ToManyAttributeEntriesException(String.valueOf(maxAttributes));
}
target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis());
target.getTargetInfo().setRequestControllerAttributes(false);
return targetRepository.save(target);
}
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
final RelaxedPropertyResolver env = new RelaxedPropertyResolver(environment, "hawkbit.server.");
maxCount = env.getProperty("security.dos.maxStatusEntriesPerAction", Integer.class, 1000);
maxAttributes = env.getProperty("security.dos.maxAttributeEntriesPerTarget", Integer.class, 100);
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param target
* to the handle status for
* @param message
* for the status
* @return the update action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action registerRetrieved(final Action action, final String message) {
return handleRegisterRetrieved(action, message);
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param message
* for the status
* @return the updated action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
public Action handleRegisterRetrieved(final Action action, final String message) {
// do a manual query with CriteriaBuilder to avoid unnecessary field
// queries and an extra
// count query made by spring-data when using pageable requests, we
// don't need an extra count
// query, we just want to check if the last action status is a retrieved
// or not.
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryActionStatus = cb.createQuery(Object[].class);
final Root<ActionStatus> actionStatusRoot = queryActionStatus.from(ActionStatus.class);
final CriteriaQuery<Object[]> query = queryActionStatus
.multiselect(actionStatusRoot.get(ActionStatus_.id), actionStatusRoot.get(ActionStatus_.status))
.where(cb.equal(actionStatusRoot.get(ActionStatus_.action), action))
.orderBy(cb.desc(actionStatusRoot.get(ActionStatus_.id)));
final List<Object[]> resultList = entityManager.createQuery(query).setFirstResult(0).setMaxResults(1)
.getResultList();
// if the latest status is not in retrieve state then we add a retrieved
// state again, we want
// to document a deployment retrieved status and a cancel retrieved
// status, but multiple
// retrieves after the other we don't want to store to protect to
// overflood action status in
// case controller retrieves a action multiple times.
if (resultList.isEmpty() || resultList.get(0)[1] != Status.RETRIEVED) {
// document that the status has been retrieved
actionStatusRepository
.save(new ActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
// don't change the action status itself in case the action is in
// canceling state otherwise
// we modify the action status and the controller won't get the
// cancel job anymore.
if (!action.isCancelingOrCanceled()) {
final Action actionMerge = entityManager.merge(action);
actionMerge.setStatus(Status.RETRIEVED);
return actionRepository.save(actionMerge);
}
}
return action;
}
/**
* @param statusMessage
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
@Modifying
@Transactional
public void addActionStatusMessage(final ActionStatus statusMessage) {
actionStatusRepository.save(statusMessage);
}
/**
* An direct access to the security token of an
* {@link Target#getSecurityToken()} without authorization. This is
* necessary to be able to access the security-token without any
* security-context information because the security-token is used for
* authentication.
*
* @param controllerId
* the ID of the controller to retrieve the security token for
* @return the security context of the target, in case no target exists for
* the given controllerId {@code null} is returned
*/
@Transactional
public String getSecurityTokenByControllerId(final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId);
return target != null ? target.getSecurityToken() : null;
}
}

View File

@@ -0,0 +1,706 @@
/**
* 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.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.Constants;
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.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
import org.eclipse.hawkbit.repository.model.Action_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.specifications.TargetSpecifications;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
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.collect.Lists;
/**
* Business service facade for managing all deployment related data and actions.
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class DeploymentManagement {
private static final Logger LOG = LoggerFactory.getLogger(DeploymentManagement.class);
@Autowired
private EntityManager entityManager;
@Autowired
private ActionRepository actionRepository;
@Autowired
private DistributionSetRepository distributoinSetRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private AuditorAware<String> auditorProvider;
@Autowired
private DeploymentManagementEvents deploymentManagementEvents;
/**
* method assigns the {@link DistributionSet} to all {@link Target}s.
*
* @param pset
* {@link DistributionSet} which is assigned to the
* {@link Target}s
* @param targets
* the {@link Target}s which should obtain the
* {@link DistributionSet}
*
* @return the changed targets
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}. *
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final DistributionSet pset,
@NotEmpty final List<Target> targets) {
return assignDistributionSetByTargetId(pset,
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()),
ActionType.FORCED, Action.NO_FORCE_TIME);
}
/**
* 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
* @param targetIDs
* IDs of the {@link Target}s which should obtain the
* {@link DistributionSet}
*
* @return the changed targets
*
* @throws EntityNotFoundException
* if {@link DistributionSet} does not exist.
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
@NotEmpty final String... targetIDs) {
return assignDistributionSet(dsID, ActionType.FORCED, Action.NO_FORCE_TIME, targetIDs);
}
/**
* 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
* the type of the action to apply on the assignment
* @param forcedTimestamp
* the time when the action should be forced, only necessary for
* {@link ActionType#TIMEFORCED}
* @param targetIDs
* the IDs of the target to assign the distribution set
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@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()));
}
/**
* 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
* a list of all targets and their action type
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
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));
}
return assignDistributionSetToTargets(set, targets);
}
/**
* 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
* a list of all targets and their action type
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set,
final List<TargetWithActionType> targetsWithActionType) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
"Distribution set of type " + set.getType().getKey() + " is incomplete: " + set.getId());
}
final List<String> controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getTargetId)
.collect(Collectors.toList());
LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size());
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
// max entries in in-statements e.g. Oracle with maximum 1000 elements,
// so we need to split
// the entries here and execute multiple statements
// 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());
if (targets.isEmpty()) {
// detaching as it is not necessary to persist the set itself
entityManager.detach(set);
// return with nothing as all targets had the DS already assigned
return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(),
Collections.emptyList(), targetManagement);
}
final List<List<Long>> targetIds = Lists.partition(
targets.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT);
// override all active actions and set them into canceling state, we
// need to remember which
// one we have been switched to canceling state because for targets
// which we have changed to
// canceling we don't want to publish the new action update event.
final Set<Long> targetIdsCancellList = new HashSet<Long>();
targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids)));
// check for old assigments that needs cancelation
// final List<Long> canncelledTargetIds =
// set assigned distribution set and TargetUpdateStatus
final String currentUser;
if (auditorProvider != null) {
currentUser = auditorProvider.getCurrentAuditor();
} else {
currentUser = null;
}
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(),
currentUser, tIds));
targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds));
final Map<String, Action> targetIdsToActions = actionRepository.save(targets.stream().map(t -> {
final Action tAction = new Action();
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(t.getControllerId());
tAction.setActionType(targetWithActionType.getActionType());
tAction.setForcedTime(targetWithActionType.getForceTime());
tAction.setActive(true);
tAction.setStatus(Status.RUNNING);
tAction.setTarget(t);
tAction.setDistributionSet(set);
return tAction;
}).collect(Collectors.toList())).stream()
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
// MECS-720 create initial action status when action is created so we
// remember the initial
// running status because we will change the status of the action itself
// and with this action
// status we have a nicer action history.
targetIdsToActions.values().forEach(action -> {
final ActionStatus actionStatus = new ActionStatus();
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
actionStatusRepository.save(actionStatus);
});
// select updated targets in order to return them
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);
LOG.debug("assignDistribution({}) finished {}", set, result);
final List<SoftwareModule> softwareModules = softwareModuleRepository.findByAssignedTo(set);
// detaching as it is not necessary to persist the set itself
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));
return result;
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
* cancelations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
*/
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
final Set<Long> cancelledTargetIds = new HashSet<Long>();
// Figure out if there are potential target/action combinations that
// need to be considered
// for cancelation
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());
});
actionRepository.save(activeActions);
return cancelledTargetIds;
}
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()));
}
/**
* Internal helper method used only inside service level. As a result is no
* additional security necessary.
*
* @param target
* to update
* @param status
* of the target
* @param setInstalledDate
* to set
*
* @return updated target
*/
Target updateTargetInfo(@NotNull final Target target, @NotNull final TargetUpdateStatus status,
final boolean setInstalledDate) {
final TargetInfo ts = target.getTargetInfo();
ts.setUpdateStatus(status);
if (setInstalledDate) {
ts.setInstallationDate(System.currentTimeMillis());
}
targetInfoRepository.save(ts);
return entityManager.merge(target);
}
/**
* Cancels given {@link Action} for given {@link Target}. The method will
* immediately add a {@link ActionStatus.Status#CANCELED} status to the
* action. However, it might be possible that the controller will continue
* to work on the cancellation.
*
* @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 or is already a cancel
* action
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action cancelAction(@NotNull final Action action, @NotNull final Target target) {
LOG.debug("cancelAction({}, {})", action, target);
if (action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
}
final Action myAction = entityManager.merge(action);
if (myAction.isActive()) {
LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
myAction.setStatus(Status.CANCELING);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
deploymentManagementEvents.cancalAssignDistributionSet(target, myAction.getId());
return actionRepository.save(myAction);
} else {
throw new CancelActionNotAllowedException(
"Action [id: " + action.getId() + "] is not active and cannot be canceled");
}
}
/**
* Get the {@link Action} entity for given actionId.
*
* @param actionId
* to be id of the action
* @return the corresponding {@link Action}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Action findAction(@NotNull final Long actionId) {
return actionRepository.findOne(actionId);
}
/**
* 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}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Action findActionWithDetails(@NotNull final Long actionId) {
return actionRepository.findById(actionId);
}
/**
* Retrieves all {@link Action}s of a specific target.
*
* @param pageable
* pagination parameter
* @param target
* of which the actions have to be searched
* @return a paged list of actions associated with the given target
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Action> findActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByTarget(pageable, target);
}
/**
* 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
* action ID
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<Action> findActionsByTarget(final Target target) {
return actionRepository.findByTarget(target);
}
/**
* 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
* action ID
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<ActionWithStatusCount> findActionsWithStatusCountByTargetOrderByIdDesc(final Target target) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<ActionWithStatusCount> query = cb.createQuery(ActionWithStatusCount.class);
final Root<Action> actionRoot = query.from(Action.class);
final ListJoin<Action, ActionStatus> actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT);
final Join<Action, DistributionSet> actionDsJoin = actionRoot.join(Action_.distributionSet);
final CriteriaQuery<ActionWithStatusCount> multiselect = query.distinct(true).multiselect(
actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active),
actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt),
actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id),
actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version),
cb.count(actionStatusJoin));
multiselect.where(cb.equal(actionRoot.get(Action_.target), target));
multiselect.orderBy(cb.desc(actionRoot.get(Action_.id)));
multiselect.groupBy(actionRoot.get(Action_.id));
final List<ActionWithStatusCount> resultList = entityManager.createQuery(multiselect).getResultList();
return resultList;
}
/**
* 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
* the target which must be assigned to the actions
* @param pageable
* the page request
* @return a slice of actions assigned to the specific target and the
* specification
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Action> findActionsByTarget(final Specification<Action> specifiction, final Target target,
final Pageable pageable) {
return actionRepository.findAll(new Specification<Action>() {
@Override
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
* @param pageable
* @return
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Slice<Action> findActionsByTarget(final Target foundTarget, final Pageable pageable) {
return actionRepository.findByTarget(pageable, foundTarget);
}
/**
* Retrieves all active {@link Action}s of a specific target ordered by
* action ID.
*
* @param pageable
* the pagination parameter
* @param target
* the target associated with the actions
* @return a paged list of actions associated with the given target
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByActiveAndTarget(pageable, target, true);
}
/**
* 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
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<Action> findActiveActionsByTarget(final Target target) {
return actionRepository.findByActiveAndTarget(target, true);
}
/**
* 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
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<Action> findInActiveActionsByTarget(final Target target) {
return actionRepository.findByActiveAndTarget(target, false);
}
/**
* Retrieves all inactive {@link Action}s of a specific target ordered by
* action ID.
*
* @param pageable
* the pagination parameter
* @param target
* the target associated with the actions
* @return a paged list of actions associated with the given target
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByActiveAndTarget(pageable, target, false);
}
/**
* 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
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Long countActionsByTarget(@NotNull final Target target) {
return actionRepository.countByTarget(target);
}
/**
* counts all actions associated to a specific target.
*
* @param spec
* the specification to filter the count result
* @param target
* the target associated to the actions to count
* @return the count value of found actions associated to the target
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
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) {
return cb.and(spec.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
}
});
}
/**
* Updates a {@link TargetAction} and forces the {@link TargetAction} if
* it's not already forced.
*
* @param targetId
* the ID of the target
* @param actionId
* the ID of the action
* @return the updated or the found {@link TargetAction}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action forceTargetAction(final Long actionId) {
final Action action = actionRepository.findOne(actionId);
if (action != null && !action.isForced()) {
action.setActionType(ActionType.FORCED);
return actionRepository.save(action);
}
return action;
}
/**
* 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
* to be filtered on
* @param withMessages
* to <code>true</code> if {@link ActionStatus#getMessages()}
* need to be fetched.
* @return the corresponding {@link Page} of {@link ActionStatus}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<ActionStatus> findActionStatusMessagesByActionInDescOrder(final Pageable pageReq, final Action action,
final boolean withMessages) {
if (withMessages) {
return actionStatusRepository.getByActionOrderByIdDesc(pageReq, action);
} else {
return actionStatusRepository.findByActionOrderByIdDesc(pageReq, action);
}
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Target;
/**
* A bean which holds a complex result of an service operation to combine the
* information of an assignment and how much of the assignment has been done and
* how much of the assignments had already been existed.
*
*
*
*
*/
public class DistributionSetAssignmentResult extends AssignmentResult {
private final List<String> assignedTargets;
private final List<Action> actions;
private final TargetManagement targetManagement;
/**
*
* Constructor.
*
* @param assignedTargets
* the target objects which have been assigned to the
* distribution set
* @param assigned
* count of the assigned targets
* @param alreadyAssigned
* the count of the already assigned targets
* @param targetManagement
* to retrieve the assigned targets
* @param actions
* of the assignment
*
*/
public DistributionSetAssignmentResult(final List<String> assignedTargets, final int assigned,
final int alreadyAssigned, final List<Action> actions, final TargetManagement targetManagement) {
super(assigned, alreadyAssigned);
this.assignedTargets = assignedTargets;
this.actions = actions;
this.targetManagement = targetManagement;
}
/**
* @return the assignedTargets
*/
public List<Target> getAssignedTargets() {
return targetManagement.findTargetsByControllerID(assignedTargets);
}
/**
* @return the actionId
*/
public List<Action> getActions() {
return actions;
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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.Collection;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
/**
* Holds distribution set filter parameters.
*
*
*
*/
public final class DistributionSetFilter {
private final Boolean isDeleted;
private final Boolean isComplete;
private final DistributionSetType type;
private final String searchText;
private final Boolean selectDSWithNoTag;
private final Collection<String> tagNames;
private final String assignedTargetId;
private final String installedTargetId;
/**
* Parametric constructor.
*
* @param builder
* DistributionSetFilterBuilder
*/
public DistributionSetFilter(final DistributionSetFilterBuilder builder) {
this.isDeleted = builder.isDeleted;
this.isComplete = builder.isComplete;
this.type = builder.type;
this.searchText = builder.searchText;
this.selectDSWithNoTag = builder.selectDSWithNoTag;
this.tagNames = builder.tagNames;
this.assignedTargetId = builder.assignedTargetId;
this.installedTargetId = builder.installedTargetId;
}
/**
*
* Distribution set filter builder.
*
*
*
*/
public static class DistributionSetFilterBuilder {
private Boolean isDeleted;
private Boolean isComplete;
private DistributionSetType type;
private String searchText;
private Boolean selectDSWithNoTag;
private Collection<String> tagNames;
private String assignedTargetId;
private String installedTargetId;
/**
* Build filter.
*
* @return DistributionSetFilter
*/
public DistributionSetFilter build() {
return new DistributionSetFilter(this);
}
public DistributionSetFilterBuilder setIsDeleted(final Boolean isDeleted) {
this.isDeleted = isDeleted;
return this;
}
public DistributionSetFilterBuilder setIsComplete(final Boolean isComplete) {
this.isComplete = isComplete;
return this;
}
public DistributionSetFilterBuilder setType(final DistributionSetType type) {
this.type = type;
return this;
}
public DistributionSetFilterBuilder setSearchText(final String searchText) {
this.searchText = searchText;
return this;
}
public DistributionSetFilterBuilder setSelectDSWithNoTag(final Boolean selectDSWithNoTag) {
this.selectDSWithNoTag = selectDSWithNoTag;
return this;
}
public DistributionSetFilterBuilder setTagNames(final Collection<String> tagNames) {
this.tagNames = tagNames;
return this;
}
public DistributionSetFilterBuilder setAssignedTargetId(final String assignedTargetId) {
this.assignedTargetId = assignedTargetId;
return this;
}
public DistributionSetFilterBuilder setInstalledTargetId(final String installedTargetId) {
this.installedTargetId = installedTargetId;
return this;
}
}
public Boolean getIsDeleted() {
return isDeleted;
}
public Boolean getIsComplete() {
return isComplete;
}
public DistributionSetType getType() {
return type;
}
public String getSearchText() {
return searchText;
}
public Boolean getSelectDSWithNoTag() {
return selectDSWithNoTag;
}
public Collection<String> getTagNames() {
return tagNames;
}
public String getAssignedTargetId() {
return assignedTargetId;
}
public String getInstalledTargetId() {
return installedTargetId;
}
}

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.repository;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link DistributionSetMetadata} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface DistributionSetMetadataRepository
extends PagingAndSortingRepository<DistributionSetMetadata, DsMetadataCompositeKey>,
JpaSpecificationExecutor<DistributionSetMetadata> {
}

View File

@@ -0,0 +1,135 @@
/**
* 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.Collection;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Tag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link DistributionSet} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface DistributionSetRepository
extends BaseEntityRepository<DistributionSet, Long>, JpaSpecificationExecutor<DistributionSet> {
/**
* Finds {@link DistributionSet}s by assigned {@link Tag}.
*
* @param tag
* to be found
* @return list of found {@link DistributionSet}s
*/
@Query(value = "Select Distinct ds from DistributionSet ds join ds.tags dst where dst = :tag")
List<DistributionSet> findByTag(@Param("tag") final DistributionSetTag tag);
/**
* deletes the {@link DistributionSet}s with the given IDs.
*
* @param ids
* to be deleted
*/
@Modifying
@Transactional
@Query("update DistributionSet d set d.deleted = 1 where d.id in :ids")
void deleteDistributionSet(@Param("ids") Long... ids);
/**
* deletes {@link DistributionSet}s by the given IDs.
*
* @param ids
* List of IDs of {@link DistributionSet}s to be deleted
* @return number of affected/deleted records
*/
@Modifying
@Transactional
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM DistributionSet d WHERE d.id IN ?1")
int deleteByIdIn(Collection<Long> ids);
/**
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is
* assigned.
*
* @param module
* to search for
* @return {@link List} of found {@link DistributionSet}s
*/
List<DistributionSet> findByModules(SoftwareModule module);
/**
* Finds {@link DistributionSet}s based on given ID if they are not assigned
* yet to an {@link UpdateAction}, i.e. unused.
*
* @param ids
* to searcgh for
* @return
*/
@Query("select ac.distributionSet.id from Action ac where ac.distributionSet.id in :ids")
List<Long> findAssignedDistributionSetsById(@Param("ids") Long... ids);
/**
* Saves all given {@link DistributionSet}s.
*
* @param entities
* @return the saved entities
* @throws IllegalArgumentException
* in case the given entity is (@literal null}.
*
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
@Override
<S extends DistributionSet> List<S> save(Iterable<S> entities);
/**
* Finds the distribution set for a specific action.
*
* @param action
* the action associated with the distribution set to find
* @return the distribution set associated with the given action
*/
@Query("select DISTINCT d from DistributionSet d join fetch d.modules m join d.actions a where a = :action")
DistributionSet findByAction(@Param("action") Action action);
/**
* Counts {@link DistributionSet} instances of given type in the repository.
*
* @param type
* to search for
* @return number of found {@link DistributionSet}s
*/
long countByType(DistributionSetType type);
/**
* Counts {@link DistributionSet} with given
* {@link DistributionSet#getName()} and
* {@link DistributionSet#getVersion()}.
*
* @param name
* to search for
* @param version
* to search for
* @return number of found {@link DistributionSet}s
*/
long countByNameAndVersion(String name, String version);
}

View File

@@ -0,0 +1,72 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
/**
* Result object for {@link DistributionSetTag} assigments.
*
*
*
*
*/
public class DistributionSetTagAssigmentResult extends AssignmentResult {
private final int unassigned;
private final List<DistributionSet> assignedDs;
private final List<DistributionSet> unassignedDs;
/**
* Constructor.
*
* @param alreadyAssigned
* number of already assigned/ignored elements
* @param assigned
* number of newly assigned elements
* @param unassigned
* number of newly assigned elements
* @param assignedDs
* {@link List} of assigned {@link DistributionSet}s.
* @param unassignedDs
* {@link List} of unassigned {@link DistributionSet}s.
*/
public DistributionSetTagAssigmentResult(final int alreadyAssigned, final int assigned, final int unassigned,
final List<DistributionSet> assignedDs, final List<DistributionSet> unassignedDs) {
super(assigned, alreadyAssigned);
this.unassigned = unassigned;
this.assignedDs = assignedDs;
this.unassignedDs = unassignedDs;
}
/**
* @return the unassigned
*/
public int getUnassigned() {
return unassigned;
}
/**
* @return the assignedDs
*/
public List<DistributionSet> getAssignedDs() {
return assignedDs;
}
/**
* @return the unassignedDs
*/
public List<DistributionSet> getUnassignedDs() {
return unassignedDs;
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.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.Modifying;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link TargetTag} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface DistributionSetTagRepository extends BaseEntityRepository<DistributionSetTag, Long> {
/**
* deletes the {@link DistributionSet} with the given name.
*
* @param tagName
* to be deleted
* @return 1 if tag was deleted
*/
@Modifying
@Transactional
Long deleteByName(final String tagName);
/**
* find {@link DistributionSetTag} by its name.
*
* @param tagName
* to filter on
* @return the {@link DistributionSetTag} if found, otherwise null
*/
DistributionSetTag findByNameEquals(final String tagName);
/**
* Returns all instances of the type.
*
* @return all entities
*/
@Override
List<DistributionSetTag> findAll();
}

View File

@@ -0,0 +1,60 @@
/**
* 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 org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link PagingAndSortingRepository} for {@link DistributionSetType}.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface DistributionSetTypeRepository
extends BaseEntityRepository<DistributionSetType, Long>, JpaSpecificationExecutor<DistributionSetType> {
/**
*
* @param pageable
* page parameters
* @param isDeleted
* to <code>true</code> if only soft deleted entries of
* <code>false</code> if undeleted ones
* @return list of found {@link DistributionSetType}s
*/
Page<DistributionSetType> findByDeleted(Pageable pageable, boolean isDeleted);
/**
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* count or all undeleted.
* @return number of {@link DistributionSetType}s in the repository.
*/
Long countByDeleted(boolean isDeleted);
/**
* Counts all distribution set type where a specific software module type is
* assigned to.
*
* @param softwareModuleType
* the software module type to count the distribution set type
* which has this software module type assigned
* @return the number of {@link DistributionSetType}s in the repository
* assigned to the given software module type
*/
Long countByElementsSmType(SoftwareModuleType softwareModuleType);
}

View File

@@ -0,0 +1,72 @@
/**
* 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.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
/**
* Custom repository implementation as standard spring repository fails as of
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 .
*
*
*
*/
@Service
@Transactional
public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
@Autowired
private EntityManager entityManager;
@Override
@Modifying
public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) {
final Query query = entityManager.createQuery(
"update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status");
query.setParameter("targets", targets);
query.setParameter("status", status);
}
@Override
@Modifying
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
public <S extends TargetInfo> S save(final S entity) {
if (entity.isNew()) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
@Override
@Modifying
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
public void deleteByTargetIdIn(final Collection<Long> targetIDs) {
final javax.persistence.Query query = entityManager
.createQuery("DELETE FROM TargetInfo ti where ti.targetId IN :target");
query.setParameter("target", targetIDs);
}
}

View File

@@ -0,0 +1,24 @@
/**
* 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 org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.springframework.transaction.annotation.Transactional;
/**
* Repository for {@link ExternalArtifactProvider}.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface ExternalArtifactProviderRepository extends BaseEntityRepository<ExternalArtifactProvider, Long> {
}

View File

@@ -0,0 +1,37 @@
/**
* 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 org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link ExternalArtifact} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface ExternalArtifactRepository extends BaseEntityRepository<ExternalArtifact, Long> {
/**
* Searches for external artifact for a base software module.
*
* @param pageReq
* Pageable
* @param swId
* software module id
*
* @return Page<ExternalArtifact>
*/
Page<ExternalArtifact> findBySoftwareModuleId(Pageable pageReq, final Long swId);
}

View File

@@ -0,0 +1,78 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link LocalArtifact} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface LocalArtifactRepository extends BaseEntityRepository<LocalArtifact, Long> {
/**
* Searches for a {@link LocalArtifact} based on given gridFsFileName.
*
* @param gridFsFileName
* to search
* @return list of {@link LocalArtifact}s.
*/
List<LocalArtifact> findByGridFsFileName(String gridFsFileName);
/**
* Searches for a {@link LocalArtifact} based on given gridFsFileName.
*
* @param gridFsFileName
* to search
* @return {@link LocalArtifact} the first in the result list
*/
LocalArtifact findFirstByGridFsFileName(String gridFsFileName);
/**
* Searches for a {@link LocalArtifact} based user provided filename at
* upload.
*
* @param filename
* to search
* @return list of {@link LocalArtifact}.
*/
List<LocalArtifact> findByFilename(String filename);
/**
* Searches for local artifact for a base software module.
*
* @param pageReq
* Pageable
* @param swId
* software module id
*
* @return Page<LocalArtifact>
*/
Page<LocalArtifact> findBySoftwareModuleId(Pageable pageReq, final Long swId);
/**
* Searches for a {@link LocalArtifact} based user provided filename at
* upload and selected software module id.
*
* @param filename
* to search
* @param softwareModuleId
* selected software module id
* @return list of {@link LocalArtifact}.
*/
List<LocalArtifact> findByFilenameAndSoftwareModuleId(final String filename, final Long softwareModuleId);
}

View File

@@ -0,0 +1,126 @@
/**
* 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.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;
/**
* Workaround as spring data does not provide a {@link Slice} based
* {@link JpaRepository#findAll()}.
*
*
*
*
*/
@Repository
public class NoCountPagingRepository {
@Autowired
protected EntityManager em;
/**
* Searches without the need for an extra count query.
*
* @param spec
* to search for
* @param pageable
* information
* @param domainClass
* of the {@link Entity}
*
* @return {@link Slice} of data
*
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework
* .data.jpa.domain.Specification,
* org.springframework.data.domain.Pageable)
*/
public <T, I extends Serializable> Slice<T> findAll(final Specification<T> spec, final Pageable pageable,
final Class<T> domainClass) {
final SimpleJpaNoCountRepository<T, I> noCountDao = new SimpleJpaNoCountRepository<T, I>(domainClass, em);
return noCountDao.findAll(spec, pageable);
}
/**
* Searches without the need for an extra count query.
*
* @param pageable
* information
* @param domainClass
* of the {@link Entity}
*
* @return {@link Slice} of data
*
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework
* .data.jpa.domain.Specification,
* org.springframework.data.domain.Pageable)
*/
public <T, I extends Serializable> Slice<T> findAll(final Pageable pageable, final Class<T> domainClass) {
final SimpleJpaNoCountRepository<T, I> noCountDao = new SimpleJpaNoCountRepository<T, I>(domainClass, em);
return noCountDao.findAll(pageable);
}
/**
* Repository implementation with disabled count query.
*
*
*
* @param <T>
* entity type
* @param <I>
* key or ID type
*/
public static class SimpleJpaNoCountRepository<T, I extends Serializable> extends SimpleJpaRepository<T, I> {
/**
* Constructor.
*
* @param domainClass
* of the {@link Entity}
* @param em
* {@link EntityManager} instance for the queries
*/
public SimpleJpaNoCountRepository(final Class<T> domainClass, final EntityManager em) {
super(domainClass, em);
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.jpa.repository.support.SimpleJpaRepository#
* readPage(javax.persistence .TypedQuery,
* org.springframework.data.domain.Pageable,
* org.springframework.data.jpa.domain.Specification)
*/
@Override
protected Page<T> readPage(final TypedQuery<T> query, final Pageable pageable, final Specification<T> spec) {
query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
final List<T> content = query.getResultList();
return new PageImpl<T>(content, pageable, content.size());
}
}
}

View File

@@ -0,0 +1,631 @@
/**
* 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.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.report.model.DataReportSeries;
import org.eclipse.hawkbit.report.model.DataReportSeriesItem;
import org.eclipse.hawkbit.report.model.InnerOuterDataReportSeries;
import org.eclipse.hawkbit.report.model.ListReportSeries;
import org.eclipse.hawkbit.report.model.SeriesTime;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetInfo_;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Service layer for generating SP reportings.
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
@ConfigurationProperties
public class ReportManagement {
@Value("${spring.jpa.database}")
private String databaseType;
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM");
private static final String H2_TARGET_CREATED_SQL_TEMPLATE = "SELECT TO_CHAR( DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(target0_.controller_id) AS col_1_0_ from sp_target target0_ WHERE TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'),'%s') BETWEEN TO_CHAR('%s', '%s') and TO_CHAR('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(action_.id) AS col_1_0_ FROM sp_action action_ WHERE TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') BETWEEN TO_CHAR('%s', '%s') AND TO_CHAR('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String MYSQL_TARGET_CREATED_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s') AS col_0_0_, COUNT(target0_.controller_id) AS col_1_0_ FROM sp_target target0_ WHERE DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s')";
private static final String MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s') AS col_0_0_, COUNT(action_.id) as col_1_0_ FROM sp_action action_ WHERE DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s')";
private static final String MYSQL_DB_TYPE = "MYSQL";
private static final String H2_DB_TYPE = "H2";
@Autowired
private EntityManager entityManager;
@Autowired
private TenantAware tenantAware;
/**
* Generates a report of all targets of their current update status count.
* For each {@link TargetUpdateStatus} an total count of targets which are
* in this status currently.
*
* @return a data report series which contains the target count for each
* target update status
*/
@Cacheable("targetStatus")
public DataReportSeries<TargetUpdateStatus> targetStatus() {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<Target> targetRoot = query.from(Target.class);
final Join<Target, TargetInfo> targetInfo = targetRoot.join(Target_.targetInfo);
final Expression<Long> countColumn = cb.count(targetInfo.get(TargetInfo_.targetId));
final CriteriaQuery<Object[]> multiselect = query
.multiselect(targetInfo.get(TargetInfo_.updateStatus), countColumn)
.groupBy(targetInfo.get(TargetInfo_.updateStatus))
.orderBy(cb.desc(targetInfo.get(TargetInfo_.updateStatus)));
// | col1 | col2 |
// | U_STATUS | COUNT |
final List<Object[]> resultList = entityManager.createQuery(multiselect).getResultList();
final List<DataReportSeriesItem<TargetUpdateStatus>> reportSeriesItems = resultList.stream()
.map(r -> new DataReportSeriesItem<TargetUpdateStatus>((TargetUpdateStatus) r[0], (Long) r[1]))
.collect(Collectors.toList());
return new DataReportSeries<>("Target Status Overview", reportSeriesItems);
}
/**
* Generates a report of the top x distribution set assigned usage as a list
* of {@link InnerOuterDataReportSeries} which is ideal for generate a donut
* chart out of it. The inner series contains the distribution set names and
* total count usage. The outer series contains each version usage and its
* usage count. {@code inner: ds1:5 -> outer: vers 0.0.0:3, vers 1.0.0:2}
* {@code inner: ds2:1 -> outer: vers 0.0.1:1}
*
* The top x entries are seperated within the series, the rest of the
* distribution sets usage are summarized to a "misc" series.
*
* @param topXEntries
* the top entries which should be shown, the rest distribution
* set entries are summarized as "misc"
* @return a list of inner and outer series of distribution set usage
*/
@Cacheable("distributionUsageAssigned")
public List<InnerOuterDataReportSeries<String>> distributionUsageAssigned(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, Target> joinTopX = rootTopX.join(DistributionSet_.assignedToTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
/**
* Generates a report of the top x distribution set installed usage as a
* list of {@link InnerOuterDataReportSeries} which is ideal for generate a
* donut chart out of it. The inner series contains the distribution set
* names and total count usage. The outer series contains each version usage
* and its usage count.
* {@code inner: ds1:5 -> outer: vers 0.0.0:3, vers 1.0.0:2}
* {@code inner: ds2:1 -> outer: vers 0.0.1:1}
*
* The top x entries are seperated within the series, the rest of the
* distribution sets usage are summarized to a "misc" series.
*
* @param topXEntries
* the top entries which should be shown, the rest distribution
* set entries are summarized as "misc"
* @return a list of inner and outer series of distribution set usage
*/
@Cacheable("distributionUsageInstalled")
public List<InnerOuterDataReportSeries<String>> distributionUsageInstalled(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, TargetInfo> joinTopX = rootTopX.join(DistributionSet_.installedAtTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
/**
* Generates report for target created over period.
*
* @param dateType
* {@link PerMonth}
* @param from
* start date
* @param to
* end date
* @return <T> DataReportSeries<T> ListReportSeries list of target created
* count
*/
@Cacheable("targetsCreatedOverPeriod")
public <T> DataReportSeries<T> targetsCreatedOverPeriod(final DateType<T> dateType, final LocalDateTime from,
final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
final DataReportSeries<T> report = new DataReportSeries<>("CreatedTargets", reportItems);
return report;
}
private String getTargetsCreatedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
private String getFeedbackReceivedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
/**
* Generates report for feedback over period.
*
* @param dateType
* {@link PerMonth}
* @param from
* start date
* @param to
* end date
* @return <T> DataReportSeries<T> ListReportSeries list of action status
* count
*/
@Cacheable("feedbackReceivedOverTime")
public <T> DataReportSeries<T> feedbackReceivedOverTime(final DateType<T> dateType, final LocalDateTime from,
final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getFeedbackReceivedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
final DataReportSeries<T> report = new DataReportSeries<>("FeedbackRecieved", reportItems);
return report;
}
/**
* Generates a report as a {@link ListReportSeries} targets polled based on
* the {@link TargetStatus#getLastTargetQuery()} within an hour, day, week,
* month, year, more than a year, never.
*
* The order of the numbers within the {@link DataReportSeries} is the order
* hour, day, week, month, year, more than a year, never.
*
* @return a {@link DataReportSeries} which contains the number of targets
* which have not been polled in the last hour, day, ... year,more
* than a year, never.
*
*/
@Cacheable("targetsLastPoll")
public DataReportSeries<SeriesTime> targetsLastPoll() {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime beforeHour = now.minusHours(1);
final LocalDateTime beforeDay = now.minusDays(1);
final LocalDateTime beforeWeek = now.minusWeeks(1);
final LocalDateTime beforeMonth = now.minusMonths(1);
final LocalDateTime beforeYear = now.minusYears(1);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final List<DataReportSeriesItem<SeriesTime>> resultList = new ArrayList<>();
// hours
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.HOUR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult()));
// days
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.DAY, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult()));
// weeks
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.WEEK, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult()));
// months
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MONTH, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.YEAR, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MORE_THAN_YEAR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult()));
// never
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.NEVER,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult()));
return new DataReportSeries<>("TargetLastPoll", resultList);
}
private CriteriaQuery<Long> createCountSelectTargetsLastPoll(final CriteriaBuilder cb, final LocalDateTime from,
final LocalDateTime to) {
Long start = null;
Long end = null;
if (from != null) {
start = from.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
if (to != null) {
end = to.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
// count select statement
final CriteriaQuery<Long> countSelect = cb.createQuery(Long.class);
final Root<Target> countSelectRoot = countSelect.from(Target.class);
final Join<Target, TargetInfo> targetInfoJoin = countSelectRoot.join(Target_.targetInfo);
countSelect.select(cb.count(countSelectRoot));
if (start != null && end != null) {
countSelect.where(cb.between(targetInfoJoin.get(TargetInfo_.lastTargetQuery), start, end));
} else if (from == null && to != null) {
countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(TargetInfo_.lastTargetQuery), end));
} else {
countSelect.where(cb.isNull(targetInfoJoin.get(TargetInfo_.lastTargetQuery)));
}
return countSelect;
}
private List<InnerOuterDataReportSeries<String>> mapDistirbutionUsageResultToDataReport(final int topXEntries,
final List<Object[]> resultListTop) {
final List<InnerOuterDataReportSeries<String>> innerOuterReport = new ArrayList<>();
final Map<DSName, InnerOuter> map = new LinkedHashMap<>();
int topXCounter = 0;
for (final Object[] objects : resultListTop) {
final boolean containsInnerOuter = map.containsKey(new DSName((String) objects[0]));
final String name = containsInnerOuter || topXCounter < topXEntries ? (String) objects[0] : null;
final DSName dsName = new DSName(name);
final String version = containsInnerOuter || topXCounter < topXEntries ? (String) objects[1] : null;
final Long count = (Long) objects[2];
InnerOuter innerouter = map.get(dsName);
if (innerouter == null) {
topXCounter++;
innerouter = new InnerOuter(dsName);
map.put(new DSName(name), innerouter);
}
innerouter.addOuter(new DSName(version), count);
}
for (final InnerOuter inner : map.values()) {
final List<DataReportSeriesItem<String>> outerReportItems = new ArrayList<>();
if (inner.name.getName() != null) {
for (final InnerOuter outer : inner.outer) {
outerReportItems.add(outer.toItem());
}
} else {
outerReportItems.add(new DataReportSeriesItem<String>("misc", inner.count));
}
innerOuterReport.add(new InnerOuterDataReportSeries<String>(
new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())),
new DataReportSeries<>("DS-Version", outerReportItems)));
}
return innerOuterReport;
}
private final class InnerOuter {
final DSName name;
long count;
final List<InnerOuter> outer;
private InnerOuter(final DSName idName) {
name = idName;
outer = new ArrayList<>();
}
private InnerOuter(final DSName idName, final long count) {
name = idName;
this.count = count;
outer = new ArrayList<>();
}
private void addOuter(final DSName idName, final long count) {
outer.add(new InnerOuter(idName, count));
this.count += count;
}
private DataReportSeriesItem<String> toItem() {
return new DataReportSeriesItem<String>(name.getName() != null ? name.getName() : "misc", count);
}
}
/**
* Object contains the name and the id of an entity.
*
*
*
*
*/
private static final class DSName {
private final String name;
/**
* @param id
* the ID of an entity
* @param name
* the name of an entity
*/
private DSName(final String name) {
this.name = name;
}
/**
* @return the name
*/
private String getName() {
return name;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is
// generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DSName other = (DSName) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "DSName [name=" + name + "]";
}
}
private String dateTimeFormatToSqlFormat(final DateType<?> datatype) {
switch (databaseType) {
case H2_DB_TYPE:
return datatype.h2Format();
case MYSQL_DB_TYPE:
return datatype.mySqlFormat();
default:
return null;
}
}
/**
* Return DateTypes.
*
*
*
*/
public static final class DateTypes implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final PerMonth PER_MONTH = new PerMonth();
private DateTypes() {
}
/**
* @return PerMonth
*/
public static PerMonth perMonth() {
return PER_MONTH;
}
}
/**
* Data base format.
*
*
*
* @param <T>
*/
public interface DateType<T> {
/**
* @param s
* @return T
*/
T format(String s);
/**
* h2 format.
*
* @return String
*/
String h2Format();
/**
* mysql format.
*
* @return String
*/
String mySqlFormat();
}
/**
* Gives the date format based on DB H2 or mySql.
*
*
*
*/
public static final class PerMonth implements DateType<LocalDate>, Serializable {
private static final long serialVersionUID = 1L;
private static final String DATE_PATTERN = "yyyy-MM";
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.repository.ReportManagement.DateType#
* format(java. lang.String)
*/
@Override
public LocalDate format(final String s) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_PATTERN);
final YearMonth ym = YearMonth.parse(s, formatter);
return ym.atDay(1);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.repository.ReportManagement.DateType#
* h2Format()
*/
@Override
public String h2Format() {
return DATE_PATTERN;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.repository.ReportManagement.DateType#
* mySqlFormat( )
*/
@Override
public String mySqlFormat() {
return "%Y-%m";
}
}
}

View File

@@ -0,0 +1,944 @@
/**
* 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.CustomSoftwareModule;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata_;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.SoftwareModule_;
import org.eclipse.hawkbit.repository.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
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.base.Strings;
import com.google.common.collect.Sets;
/**
* Business facade for managing the deployable {@link SoftwareModule}s.
*
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class SoftwareManagement {
@Autowired
private EntityManager entityManager;
@Autowired
private DistributionSetRepository distributionSetRepository;
@Autowired
private DistributionSetTypeRepository distributionSetTypeRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
@Autowired
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@Autowired
private AuditorAware<String> auditorProvider;
@Autowired
private ArtifactManagement artifactManagement;
/**
* Updates existing {@link SoftwareModule}. Updateable values are
* {@link SoftwareModule#getDescription()}
* {@link SoftwareModule#getVendor()}.
*
* @param sm
* to update
*
* @return the saved {@link Entity}.
*
* @throws NullPointerException
* of {@link SoftwareModule#getId()} is <code>null</code>
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModule updateSoftwareModule(@NotNull final SoftwareModule sm) {
checkNotNull(sm.getId());
final SoftwareModule module = softwareModuleRepository.findOne(sm.getId());
if (null == sm.getDescription() || !sm.getDescription().equals(module.getDescription())) {
module.setDescription(sm.getDescription());
}
if (null == sm.getVendor() || !sm.getVendor().equals(module.getVendor())) {
module.setVendor(sm.getVendor());
}
return softwareModuleRepository.save(module);
}
/**
* Updates existing {@link SoftwareModuleType}. Updatable value is
* {@link SoftwareModuleType#getDescription()} and
* {@link SoftwareModuleType#getColour()}.
*
* @param sm
* to update
* @return updated {@link Entity}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleType updateSoftwareModuleType(@NotNull final SoftwareModuleType sm) {
checkNotNull(sm.getId());
final SoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId());
if (sm.getDescription() != null && !sm.getDescription().equals(type.getDescription())) {
type.setDescription(sm.getDescription());
}
if (sm.getColour() != null && !sm.getColour().equals(type.getColour())) {
type.setColour(sm.getColour());
}
return softwareModuleTypeRepository.save(type);
}
/**
*
* @param swModule
* SoftwareModule to create
* @return SoftwareModule
* @throws EntityAlreadyExistsException
* if a given entity already exists
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public SoftwareModule createSoftwareModule(@NotNull final SoftwareModule swModule) {
if (null != swModule.getId()) {
throw new EntityAlreadyExistsException();
}
return softwareModuleRepository.save(swModule);
}
/**
* Create {@link SoftwareModule}s in the repository.
*
* @param swModules
* {@link SoftwareModule}s to create
* @return SoftwareModule
* @throws EntityAlreadyExistsException
* if a given entity already exists
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<SoftwareModule> createSoftwareModule(@NotNull final Iterable<SoftwareModule> swModules) {
swModules.forEach(swModule -> {
if (null != swModule.getId()) {
throw new EntityAlreadyExistsException();
}
});
return softwareModuleRepository.save(swModules);
}
/**
* retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}
* .
*
* @param pageable
* page parameters
* @param type
* to be filtered on
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Slice<SoftwareModule> findSoftwareModulesByType(@NotNull final Pageable pageable,
@NotNull final SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
Specification<SoftwareModule> spec = SoftwareModuleSpecification.equalType(type);
specList.add(spec);
spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
return findSwModuleByCriteriaAPI(pageable, specList);
}
/**
* Counts {@link SoftwareModule}s with given {@link SoftwareModuleType}.
*
* @param type
* to count
* @return number of found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Long countSoftwareModulesByType(@NotNull final SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
Specification<SoftwareModule> spec = SoftwareModuleSpecification.equalType(type);
specList.add(spec);
spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
return countSwModuleByCriteriaAPI(specList);
}
/**
* Finds {@link SoftwareModule} by given id.
*
* @param id
* to search for
* @return the found {@link SoftwareModule}s or <code>null</code> if not
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public SoftwareModule findSoftwareModuleById(@NotNull final Long id) {
return artifactManagement.findSoftwareModuleById(id);
}
/**
* retrieves {@link SoftwareModule}s by their name AND version.
*
* @param name
* of the {@link SoftwareModule}
* @param version
* of the {@link SoftwareModule}
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<SoftwareModule> findSoftwareModuleByNameAndVersion(@NotEmpty final String name,
@NotEmpty final String version) {
return softwareModuleRepository.findByNameAndVersion(name, version);
}
/**
* Deletes the given {@link SoftwareModule} {@link Entity}.
*
* @param bsm
* is the {@link SoftwareModule} to be deleted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModule(@NotNull final SoftwareModule bsm) {
deleteSoftwareModules(Sets.newHashSet(bsm.getId()));
}
private boolean isUnassigned(final SoftwareModule bsmMerged) {
return distributionSetRepository.findByModules(bsmMerged).isEmpty();
}
private Slice<SoftwareModule> findSwModuleByCriteriaAPI(@NotNull final Pageable pageable,
@NotEmpty final List<Specification<SoftwareModule>> specList) {
Specifications<SoftwareModule> specs = Specifications.where(specList.get(0));
if (specList.size() > 1) {
for (final Specification<SoftwareModule> s : specList.subList(1, specList.size())) {
specs = specs.and(s);
}
}
return criteriaNoCountDao.findAll(specs, pageable, SoftwareModule.class);
}
private Long countSwModuleByCriteriaAPI(@NotEmpty final List<Specification<SoftwareModule>> specList) {
Specifications<SoftwareModule> specs = Specifications.where(specList.get(0));
if (specList.size() > 1) {
for (final Specification<SoftwareModule> s : specList.subList(1, specList.size())) {
specs = specs.and(s);
}
}
return softwareModuleRepository.count(specs);
}
private void deleteGridFsArtifacts(final SoftwareModule swModule) {
for (final LocalArtifact localArtifact : swModule.getLocalArtifacts()) {
artifactManagement.deleteGridFsArtifact(localArtifact);
}
}
/**
* Deletes {@link SoftwareModule}s which is any if the given ids.
*
* @param ids
* of the Software Moduels to be deleted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModules(@NotNull final Iterable<Long> ids) {
final List<SoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
final Set<Long> assignedModuleIds = new HashSet<>();
swModulesToDelete.forEach(swModule -> {
// delete binary data of artifacts
deleteGridFsArtifacts(swModule);
if (isUnassigned(swModule)) {
softwareModuleRepository.delete(swModule);
} else {
assignedModuleIds.add(swModule.getId());
}
});
if (!assignedModuleIds.isEmpty()) {
String currentUser = null;
if (auditorProvider != null) {
currentUser = auditorProvider.getCurrentAuditor();
}
softwareModuleRepository.deleteSoftwareModule(System.currentTimeMillis(), currentUser,
assignedModuleIds.toArray(new Long[0]));
}
}
/**
* Retrieves all software modules. Deleted ones are filtered.
*
* @param pageable
* pagination parameter
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Slice<SoftwareModule> findSoftwareModulesAll(@NotNull final Pageable pageable) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
Specification<SoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
spec = new Specification<SoftwareModule>() {
@Override
public Predicate toPredicate(final Root<SoftwareModule> root, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
if (!query.getResultType().isAssignableFrom(Long.class)) {
root.fetch(SoftwareModule_.type);
}
return cb.conjunction();
}
};
specList.add(spec);
return findSwModuleByCriteriaAPI(pageable, specList);
}
/**
* Count all {@link SoftwareModule}s in the repository that are not marked
* as deleted.
*
* @return number of {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Long countSoftwareModulesAll() {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
final Specification<SoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
return countSwModuleByCriteriaAPI(specList);
}
/**
* Retrieves software module including details (
* {@link SoftwareModule#getArtifacts()}).
*
* @param id
* parameter
* @param isDeleted
* parameter
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) {
return artifactManagement.findSoftwareModuleWithDetails(id);
}
/**
* Retrieves all {@link SoftwareModule}s with a given specification.
*
* @param spec
* the specification to filter the software modules
* @param pageable
* pagination parameter
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModule> findSoftwareModulesByPredicate(@NotNull final Specification<SoftwareModule> spec,
@NotNull final Pageable pageable) {
return softwareModuleRepository.findAll(spec, pageable);
}
/**
* Retrieves all {@link SoftwareModuleType}s with a given specification.
*
* @param spec
* the specification to filter the software modules types
* @param pageable
* pagination parameter
* @return the found {@link SoftwareModuleType}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModuleType> findSoftwareModuleTypesByPredicate(
@NotNull final Specification<SoftwareModuleType> spec, @NotNull final Pageable pageable) {
return softwareModuleTypeRepository.findAll(spec, pageable);
}
/**
* Retrieves all software modules with a given list of ids
* {@link SoftwareModule#getId()}.
*
* @param ids
* to search for
* @return {@link List} of found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<SoftwareModule> findSoftwareModulesById(@NotEmpty final List<Long> ids) {
return softwareModuleRepository.findByIdIn(ids);
}
/**
* Filter {@link SoftwareModule}s with given
* {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()}
* and {@link SoftwareModule#getType()} that are not marked as deleted.
*
* @param pageable
* page parameter
* @param searchText
* to be filtered as "like" on {@link SoftwareModule#getName()}
* @param type
* to be filtered as "like" on {@link SoftwareModule#getType()}
* @return the page of found {@link SoftwareModule}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Slice<SoftwareModule> findSoftwareModuleByFilters(@NotNull final Pageable pageable, final String searchText,
final SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
Specification<SoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
if (!Strings.isNullOrEmpty(searchText)) {
spec = SoftwareModuleSpecification.likeNameOrVersion(searchText);
specList.add(spec);
}
if (null != type) {
spec = SoftwareModuleSpecification.equalType(type);
specList.add(spec);
}
spec = new Specification<SoftwareModule>() {
@Override
public Predicate toPredicate(final Root<SoftwareModule> root, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
if (!query.getResultType().isAssignableFrom(Long.class)) {
root.fetch(SoftwareModule_.type);
}
return cb.conjunction();
}
};
specList.add(spec);
return findSwModuleByCriteriaAPI(pageable, specList);
}
/**
* Filter {@link SoftwareModule}s with given
* {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()}
* and {@link SoftwareModule#getType()} that are not marked as deleted.
*
* @param pageable
* page parameter
* @param orderByDistributionId
* the ID of distribution set to be order by
* @param searchText
* to be filtered as "like" on {@link SoftwareModule#getName()}
* @param type
* to be filtered as "like" on {@link SoftwareModule#getType()}
* @return the page of found {@link SoftwareModule}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Slice<CustomSoftwareModule> findSoftwareModuleOrderByDistribution(@NotNull final Pageable pageable,
@NotNull final Long orderByDistributionId, final String searchText, final SoftwareModuleType type) {
final List<CustomSoftwareModule> resultList = new ArrayList<>();
final int pageSize = pageable.getPageSize();
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
// get the assigned software modules
final CriteriaQuery<SoftwareModule> assignedQuery = cb.createQuery(SoftwareModule.class);
final Root<SoftwareModule> assignedRoot = assignedQuery.from(SoftwareModule.class);
assignedQuery.distinct(true);
final ListJoin<SoftwareModule, DistributionSet> assignedDsJoin = assignedRoot.join(SoftwareModule_.assignedTo);
// build the specifications and then to predicates necessary by the
// given filters
final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, type),
assignedRoot, assignedQuery, cb,
cb.equal(assignedDsJoin.get(DistributionSet_.id), orderByDistributionId));
// if we have some predicates then add it to the where clause of the
// multiselect
assignedQuery.where(specPredicate);
assignedQuery.orderBy(cb.asc(assignedRoot.get(SoftwareModule_.name)),
cb.asc(assignedRoot.get(SoftwareModule_.version)));
// don't page the assigned query on database, we need all assigned
// software modules to filter
// them out in the unassigned query
final List<SoftwareModule> assignedSoftwareModules = entityManager.createQuery(assignedQuery).getResultList();
// map result
if (pageable.getOffset() < assignedSoftwareModules.size()) {
assignedSoftwareModules
.subList(pageable.getOffset(), Math.min(assignedSoftwareModules.size(), pageable.getPageSize()))
.forEach(sw -> resultList.add(new CustomSoftwareModule(sw, true)));
}
if (assignedSoftwareModules.size() >= pageSize) {
return new SliceImpl<>(resultList);
}
// get the unassigned software modules
final CriteriaQuery<SoftwareModule> unassignedQuery = cb.createQuery(SoftwareModule.class);
unassignedQuery.distinct(true);
final Root<SoftwareModule> unassignedRoot = unassignedQuery.from(SoftwareModule.class);
Predicate[] unassignedSpec = null;
if (!assignedSoftwareModules.isEmpty()) {
unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, type), unassignedRoot,
unassignedQuery, cb, cb.not(unassignedRoot.get(SoftwareModule_.id)
.in(assignedSoftwareModules.stream().map(sw -> sw.getId()).collect(Collectors.toList()))));
} else {
unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, type), unassignedRoot,
unassignedQuery, cb);
}
unassignedQuery.where(unassignedSpec);
unassignedQuery.orderBy(cb.asc(unassignedRoot.get(SoftwareModule_.name)),
cb.asc(unassignedRoot.get(SoftwareModule_.version)));
final List<SoftwareModule> unassignedSoftwareModules = entityManager.createQuery(unassignedQuery)
.setFirstResult(Math.max(0, pageable.getOffset() - assignedSoftwareModules.size()))
.setMaxResults(pageSize).getResultList();
// map result
unassignedSoftwareModules.forEach(sw -> resultList.add(new CustomSoftwareModule(sw, false)));
return new SliceImpl<>(resultList);
}
private List<Specification<SoftwareModule>> buildSpecificationList(final String searchText,
final SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
if (!Strings.isNullOrEmpty(searchText)) {
specList.add(SoftwareModuleSpecification.likeNameOrVersion(searchText));
}
if (type != null) {
specList.add(SoftwareModuleSpecification.equalType(type));
}
specList.add(SoftwareModuleSpecification.isDeletedFalse());
return specList;
}
/**
* @param specifications
*/
private Predicate[] specificationsToPredicate(final List<Specification<SoftwareModule>> specifications,
final Root<SoftwareModule> root, final CriteriaQuery<?> query, final CriteriaBuilder cb,
final Predicate... additionalPredicates) {
final List<Predicate> predicates = new ArrayList<>();
specifications.forEach(spec -> predicates.add(spec.toPredicate(root, query, cb)));
for (final Predicate predicate : additionalPredicates) {
predicates.add(predicate);
}
return predicates.toArray(new Predicate[predicates.size()]);
}
/**
* Counts {@link SoftwareModule}s with given
* {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()}
* and {@link SoftwareModule#getType()} that are not marked as deleted.
*
* @param searchText
* to search for in name and version
* @param type
* to filter the result
* @return number of found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Long countSoftwareModuleByFilters(final String searchText, final SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<Specification<SoftwareModule>>();
Specification<SoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
specList.add(spec);
if (!Strings.isNullOrEmpty(searchText)) {
spec = SoftwareModuleSpecification.likeNameOrVersion(searchText);
specList.add(spec);
}
if (null != type) {
spec = SoftwareModuleSpecification.equalType(type);
specList.add(spec);
}
return countSwModuleByCriteriaAPI(specList);
}
/**
* @param pageable
* parameter
* @return all {@link SoftwareModuleType}s in the repository.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModuleType> findSoftwareModuleTypesAll(@NotNull final Pageable pageable) {
return softwareModuleTypeRepository.findByDeleted(pageable, false);
}
/**
* @return number of {@link SoftwareModuleType}s in the repository.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Long countSoftwareModuleTypesAll() {
return softwareModuleTypeRepository.countByDeleted(false);
}
/**
*
* @param key
* to search for
* @return {@link SoftwareModuleType} in the repository with given
* {@link SoftwareModuleType#getKey()}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public SoftwareModuleType findSoftwareModuleTypeByKey(@NotNull final String key) {
return softwareModuleTypeRepository.findByKey(key);
}
/**
*
* @param id
* to search for
* @return {@link SoftwareModuleType} in the repository with given
* {@link SoftwareModuleType#getId()}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public SoftwareModuleType findSoftwareModuleTypeById(@NotNull final Long id) {
return softwareModuleTypeRepository.findOne(id);
}
/**
*
* @param name
* to search for
* @return all {@link SoftwareModuleType}s in the repository with given
* {@link SoftwareModuleType#getName()}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public SoftwareModuleType findSoftwareModuleTypeByName(@NotNull final String name) {
return softwareModuleTypeRepository.findByName(name);
}
/**
* Creates new {@link SoftwareModuleType}.
*
* @param type
* to create
* @return created {@link Entity}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public SoftwareModuleType createSoftwareModuleType(@NotNull final SoftwareModuleType type) {
if (type.getId() != null) {
throw new EntityAlreadyExistsException("Given type contains an Id!");
}
return softwareModuleTypeRepository.save(type);
}
/**
* Creates multiple {@link SoftwareModuleType}s.
*
* @param types
* to create
* @return created {@link Entity}
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<SoftwareModuleType> createSoftwareModuleTypes(@NotNull final Collection<SoftwareModuleType> types) {
return types.stream().map(type -> createSoftwareModuleType(type)).collect(Collectors.toList());
}
/**
* Deletes or markes as delete in case the type is in use.
*
* @param type
* to delete
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModuleType(@NotNull final SoftwareModuleType type) {
if (softwareModuleRepository.countByType(type) > 0
|| distributionSetTypeRepository.countByElementsSmType(type) > 0) {
final SoftwareModuleType toDelete = entityManager.merge(type);
toDelete.setDeleted(true);
softwareModuleTypeRepository.save(toDelete);
} else {
softwareModuleTypeRepository.delete(type.getId());
}
}
/**
* @param pageable
* the page request to page the result set
* @param set
* to search for
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet}.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModule> findSoftwareModuleByAssignedTo(@NotNull final Pageable pageable,
@NotNull final DistributionSet set) {
return softwareModuleRepository.findByAssignedTo(pageable, set);
}
/**
* @param pageable
* the page request to page the result set
* @param set
* to search for
* @param type
* to filter
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet} filtered by {@link SoftwareModuleType}.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModule> findSoftwareModuleByAssignedToAndType(@NotNull final Pageable pageable,
@NotNull final DistributionSet set, @NotNull final SoftwareModuleType type) {
return softwareModuleRepository.findByAssignedToAndType(pageable, set, type);
}
/**
* creates or updates a single software module meta data entry.
*
* @param metadata
* the meta data entry to create or update
* @return the updated or created software module meta data entry
* @throws EntityAlreadyExistsException
* in case the meta data entry already exists for the specific
* key
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleMetadata createSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) {
if (softwareModuleMetadataRepository.exists(metadata.getId())) {
throwMetadataKeyAlreadyExists(metadata.getId().getKey());
}
// merge base software module so optLockRevision gets updated and audit
// log written because
// modifying metadata is modifying the base software module itself for
// auditing purposes.
entityManager.merge(metadata.getSoftwareModule()).setLastModifiedAt(-1L);
return softwareModuleMetadataRepository.save(metadata);
}
/**
* creates a list of software module meta data entries.
*
* @param metadata
* the meta data entries to create or update
* @return the updated or created software module meta data entries
* @throws EntityAlreadyExistsException
* in case one of the meta data entry already exists for the
* specific key
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(
@NotEmpty final Collection<SoftwareModuleMetadata> metadata) {
for (final SoftwareModuleMetadata softwareModuleMetadata : metadata) {
checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(softwareModuleMetadata.getId());
}
metadata.forEach(m -> entityManager.merge(m.getSoftwareModule()).setLastModifiedAt(-1L));
return softwareModuleMetadataRepository.save(metadata);
}
/**
* updates a distribution set meta data value if corresponding entry exists.
*
* @param metadata
* the meta data entry to be updated
* @return the updated meta data entry
* @throws EntityNotFoundException
* in case the meta data entry does not exists and cannot be
* updated
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleMetadata updateSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) {
// check if exists otherwise throw entity not found exception
findOne(metadata.getId());
// touch it to update the lock revision because we are modifying the
// software module
// indirectly
entityManager.merge(metadata.getSoftwareModule()).setLastModifiedAt(-1L);
return softwareModuleMetadataRepository.save(metadata);
}
/**
* deletes a software module meta data entry.
*
* @param id
* the ID of the software module meta data to delete
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public void deleteSoftwareModuleMetadata(@NotNull final SwMetadataCompositeKey id) {
softwareModuleMetadataRepository.delete(id);
}
/**
* finds all meta data by the given software module id.
*
* @param swId
* the software module id to retrieve the meta data from
* @param pageable
* the page request to page the result
* @return a paged result of all meta data entries for a given software
* module id
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(@NotNull final Long swId,
@NotNull final Pageable pageable) {
return softwareModuleMetadataRepository.findBySoftwareModuleId(swId, pageable);
}
/**
* finds all meta data by the given software module id.
*
* @param softwareModuleId
* the software module id to retrieve the meta data from
* @param spec
* the specification to filter the result
* @param pageable
* the page request to page the result
* @return a paged result of all meta data entries for a given software
* module id
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId,
@NotNull final Specification<SoftwareModuleMetadata> spec, @NotNull final Pageable pageable) {
return softwareModuleMetadataRepository.findAll(new Specification<SoftwareModuleMetadata>() {
@Override
public Predicate toPredicate(final Root<SoftwareModuleMetadata> root, final CriteriaQuery<?> query,
final CriteriaBuilder cb) {
return cb.and(cb.equal(root.get(SoftwareModuleMetadata_.softwareModule).get(SoftwareModule_.id),
softwareModuleId), spec.toPredicate(root, query, cb));
}
}, pageable);
}
/**
* finds a single software module meta data by its id.
*
* @param id
* the id of the software module meta data containing the meta
* data key and the ID of the software module
* @return the found SoftwareModuleMetadata or {@code null} if not exits
* @throws EntityNotFoundException
* in case the meta data does not exists for the given key
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public SoftwareModuleMetadata findOne(@NotNull final SwMetadataCompositeKey id) {
final SoftwareModuleMetadata findOne = softwareModuleMetadataRepository.findOne(id);
if (findOne == null) {
throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist");
}
return findOne;
}
private void checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(final SwMetadataCompositeKey metadataId) {
if (softwareModuleMetadataRepository.exists(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
}
}
private void throwMetadataKeyAlreadyExists(final String metadataKey) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SwMetadataCompositeKey;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link SoftwareModuleMetadata} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface SoftwareModuleMetadataRepository
extends PagingAndSortingRepository<SoftwareModuleMetadata, SwMetadataCompositeKey>,
JpaSpecificationExecutor<SoftwareModuleMetadata> {
/**
* Saves all given entities.
*
* @param entities
* @return the saved entities
* @throws IllegalArgumentException
* in case the given entity is (@literal null}.
*/
@Override
<S extends SoftwareModuleMetadata> List<S> save(Iterable<S> entities);
/**
* finds all software module meta data of the given software module id.
*
* @param swId
* the ID of the software module to retrieve the meta data
* @param pageable
* the page request to page the result set
* @return the paged result of all meta data of an given software module id
*/
Page<SoftwareModuleMetadata> findBySoftwareModuleId(final Long swId, Pageable pageable);
}

View File

@@ -0,0 +1,132 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link SoftwareModule} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface SoftwareModuleRepository
extends BaseEntityRepository<SoftwareModule, Long>, JpaSpecificationExecutor<SoftwareModule> {
/**
* Counts all {@link SoftwareModule}s based on the given {@link Type}.
*
* @param type
* to count for
* @return number of {@link SoftwareModule}s
*/
Long countByType(SoftwareModuleType type);
/**
* Retrieves {@link SoftwareModule}s by filtering on name AND version.
*
* @param name
* to be filtered on
* @param version
* to be filtered on
* @return the found {@link SoftwareModule}s with the given name AND verion
*/
List<SoftwareModule> findByNameAndVersion(String name, String version);
/**
* deletes the {@link SoftwareModule}s with the given IDs.
*
* @param modifiedAt
* current timestamp
* @param modifiedBy
* user name of current auditor
* @param ids
* to be deleted
*
*/
@Modifying
@Transactional
@Query("UPDATE SoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.id IN :ids")
void deleteSoftwareModule(@Param("lastModifiedAt") Long modifiedAt, @Param("lastModifiedBy") String modifiedBy,
@Param("ids") final Long... ids);
/**
* @param pageable
* the page request to page the result set
* @param set
* to search for
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet}.
*/
Page<SoftwareModule> findByAssignedTo(Pageable pageable, DistributionSet set);
/**
* @param set
* to search for
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet}
*/
@EntityGraph(value = "SoftwareModule.artifacts", type = EntityGraphType.LOAD)
List<SoftwareModule> findByAssignedTo(DistributionSet set);
/**
* @param pageable
* the page request to page the result set
* @param set
* to search for
* @param type
* to filter
* @return all {@link SoftwareModule}s that are assigned to given
* {@link DistributionSet} filtered by {@link SoftwareModuleType}.
*/
Page<SoftwareModule> findByAssignedToAndType(Pageable pageable, DistributionSet set, SoftwareModuleType type);
/**
* retrieves all software modules with a given {@link SoftwareModuleType}
* and {@link SoftwareModule#getId()}.
*
* @param ids
* to search for
* @param type
* to search for
* @return {@link List} of found {@link SoftwareModule}s
*/
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT sm FROM SoftwareModule sm WHERE sm.id IN ?1 and sm.type = ?2")
List<SoftwareModule> findByIdInAndType(Iterable<Long> ids, SoftwareModuleType type);
@Override
<S extends SoftwareModule> List<S> save(Iterable<S> entities);
/**
* retrieves all software modules with a given
* {@link SoftwareModule#getId()}.
*
* @param ids
* to search for
* @return {@link List} of found {@link SoftwareModule}s
*/
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT sm FROM SoftwareModule sm WHERE sm.id IN ?1")
List<SoftwareModule> findByIdIn(Iterable<Long> ids);
}

View File

@@ -0,0 +1,62 @@
/**
* 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 org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.transaction.annotation.Transactional;
/**
* Repository for {@link SoftwareModuleType}.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface SoftwareModuleTypeRepository
extends BaseEntityRepository<SoftwareModuleType, Long>, JpaSpecificationExecutor<SoftwareModuleType> {
/**
* @param pageable
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* count or all undeleted.
* @return found {@link SoftwareModuleType}s.
*/
Page<SoftwareModuleType> findByDeleted(Pageable pageable, boolean isDeleted);
/**
* @param isDeleted
* to <code>true</code> if only marked as deleted have to be
* count or all undeleted.
* @return number of {@link SoftwareModuleType}s in the repository.
*/
Long countByDeleted(boolean isDeleted);
/**
*
* @param key
* to search for
* @return all {@link SoftwareModuleType}s in the repository with given
* {@link SoftwareModuleType#getKey()}
*/
SoftwareModuleType findByKey(String key);
/**
*
* @param name
* to search for
* @return all {@link SoftwareModuleType}s in the repository with given
* {@link SoftwareModuleType#getName()}
*/
SoftwareModuleType findByName(String name);
}

View File

@@ -0,0 +1,408 @@
/**
* 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.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
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;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Central system management operations of the SP server.
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class SystemManagement implements EnvironmentAware {
@Autowired
private EntityManager entityManager;
@Autowired
private TargetRepository targetRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private DistributionSetRepository distributionSetRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private TenantMetaDataRepository tenantMetaDataRepository;
@Autowired
private DistributionSetTypeRepository distributionSetTypeRepository;
@Autowired
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private DistributionSetTagRepository distributionSetTagRepository;
@Autowired
private ExternalArtifactRepository externalArtifactRepository;
@Autowired
private LocalArtifactRepository artifactRepository;
@Autowired
private ExternalArtifactProviderRepository externalArtifactProviderRepository;
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private TenantAware tenantAware;
@Autowired
private TenancyCacheManager cacheManager;
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
private final ConfigurableConversionService conversionService = new DefaultConversionService();
private Environment environment;
/**
* 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
public CurrentTenantKeyGenerator currentTenantKeyGenerator() {
return new CurrentTenantKeyGenerator();
}
/**
* 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.
*
* @param tenant
* @return
*/
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()")
@Transactional
@Modifying
@NotNull
public TenantMetaData getTenantMetadata(@NotNull final String tenant) {
final TenantMetaData result = tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
// Create if it does not exist
if (result == null) {
try {
createInitialTenant.set(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
return tenantMetaDataRepository.save(new TenantMetaData(createStandardSoftwareDataSetup(), tenant));
} finally {
createInitialTenant.remove();
}
}
return result;
}
/**
*
* @return list of all tenant names in the system.
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
// tenant independent
public List<String> findTenants() {
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
}
/**
* Deletes all data related to a given tenant.
*
* @param tenant
* to delete
*/
@CacheEvict(value = { "tenantMetadata" }, key = "#tenant.toUpperCase()")
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
// tenant independent
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;
}
});
}
/**
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
*/
@Cacheable(value = "tenantMetadata", keyGenerator = "tenantKeyGenerator")
@Transactional
@Modifying
@NotNull
public TenantMetaData getTenantMetadata() {
if (tenantAware.getCurrentTenant() == null) {
throw new IllegalStateException("Tenant not set");
}
return getTenantMetadata(tenantAware.getCurrentTenant());
}
/**
* 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
*/
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator")
// MECS-903 set transaction to not supported, due we call this in
// BaseEntity#prePersist methods
// and it seems that JPA committing the transaction when executing this
// transactional method,
// which then leads that the BaseEntity#prePersist is called again to
// persist the un-persisted
// entity and we landing again in the #currentTenant() method
// suspend the transaction here to do a read-request against the medata
// table, when the current
// tenant is not cached anyway already.
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public String currentTenant() {
final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) {
final TenantMetaData findByTenant = tenantMetaDataRepository
.findByTenantIgnoreCase(tenantAware.getCurrentTenant());
return findByTenant != null ? findByTenant.getTenant() : null;
}
return initialTenantCreation;
}
/**
* Update call for {@link TenantMetaData}.
*
* @param metaData
* to update
* @return updated {@link TenantMetaData} entity
*/
@CachePut(value = "tenantMetadata", key = "#metaData.tenant.toUpperCase()")
@Transactional
@Modifying
@NotNull
public TenantMetaData updateTenantMetadata(@NotNull final TenantMetaData metaData) {
if (!tenantMetaDataRepository.exists(metaData.getId())) {
throw new EntityNotFoundException("Metadata does not exist: " + metaData.getId());
}
return tenantMetaDataRepository.save(metaData);
}
/**
* 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
* the type of the configuration value, e.g. {@code String.class}
* , {@code Integer.class}, etc
* @return the converted configuration value either from the tenant specific
* configuration stored or from the fallback default values or
* {@code null} in case key has not been configured and not default
* value exists
* @throws ConversionFailedException
* if the property cannot be converted to the given
* {@code propertyType}
*/
@Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
public <T> T getConfigurationValue(final TenantConfigurationKey configurationKey, final Class<T> propertyType) {
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
if (tenantConfiguration != null) {
return conversionService.convert(tenantConfiguration.getValue(), propertyType);
} else if (configurationKey.getDefaultKeyName() != null) {
final T defaultKeyNameValue = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType);
return defaultKeyNameValue != null ? defaultKeyNameValue
: conversionService.convert(configurationKey.getDefaultValue(), propertyType);
}
return null;
}
/**
* 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
* @return the added or updated TenantConfiguration
*/
@CacheEvict(value = "tenantConfiguration", key = "#tenantConf.getKey()")
@Transactional
@Modifying
public TenantConfiguration addOrUpdateConfiguration(final TenantConfiguration tenantConf) {
TenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(tenantConf.getKey());
if (tenantConfiguration != null) {
tenantConfiguration.setValue(tenantConf.getValue());
} else {
tenantConfiguration = new TenantConfiguration(tenantConf.getKey(), tenantConf.getValue());
}
return tenantConfigurationRepository.save(tenantConfiguration);
}
/**
* Deletes a specific configuration for the current tenant.
*
* @param configurationKey
* the configuration key to be deleted
*/
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional
@Modifying
public void deleteConfiguration(final TenantConfigurationKey configurationKey) {
tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName());
}
@Transactional
public List<TenantConfiguration> getTenantConfigurations() {
return tenantConfigurationRepository.findAll();
}
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
private DistributionSetType createStandardSoftwareDataSetup() {
// Edge Controller Linux standard setup
final SoftwareModuleType eclApp = softwareModuleTypeRepository.save(new SoftwareModuleType("application",
"ECL Application", "Edge Controller Linux base application type", 1));
final SoftwareModuleType eclOs = softwareModuleTypeRepository
.save(new SoftwareModuleType("os", "ECL OS", "Edge Controller Linux operation system image type", 1));
final SoftwareModuleType eclJvm = softwareModuleTypeRepository.save(
new SoftwareModuleType("runtime", "ECL JVM", "Edge Controller Linux java virtual machine type.", 1));
distributionSetTypeRepository.save(
new DistributionSetType("ecl_os", "OS only", "Standard Edge Controller Linux distribution set type.")
.addMandatoryModuleType(eclOs));
distributionSetTypeRepository.save(new DistributionSetType("ecl_os_app", "OS with optional app",
"Standard Edge Controller Linux distribution set type. OS only.").addMandatoryModuleType(eclOs)
.addOptionalModuleType(eclApp));
final DistributionSetType defaultType = distributionSetTypeRepository
.save(new DistributionSetType("ecl_os_app_jvm", "OS with optional app and jvm",
"Standard Edge Controller Linux distribution set type. OS with optional application.")
.addMandatoryModuleType(eclOs).addOptionalModuleType(eclApp)
.addOptionalModuleType(eclJvm));
return defaultType;
}
/**
* A implementation of the {@link KeyGenerator} to generate a key based on
* either the {@code createInitialTenant} thread local and the
* {@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}.
*
*
*
*/
private class CurrentTenantKeyGenerator implements KeyGenerator {
/*
* (non-Javadoc)
*
* @see
* org.springframework.cache.interceptor.KeyGenerator#generate(java.lang
* .Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object generate(final Object target, final Method method, final Object... params) {
final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) {
return SimpleKeyGenerator.generateKey(tenantAware.getCurrentTenant().toUpperCase(),
tenantAware.getCurrentTenant().toUpperCase());
}
return SimpleKeyGenerator.generateKey(initialTenantCreation.toUpperCase(),
initialTenantCreation.toUpperCase());
}
}
}

View File

@@ -0,0 +1,343 @@
/**
* 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.LinkedList;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
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.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.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;
/**
*
* Mangement service class for {@link Tag}s.
*
*
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class TagManagement {
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private DistributionSetTagRepository distributionSetTagRepository;
@Autowired
private DistributionSetRepository distributionSetRepository;
/**
* Find {@link TargetTag} based on given Name.
*
* @param name
* to look for.
* @return {@link TargetTag} or <code>null</code> if it does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public TargetTag findTargetTag(@NotEmpty final String name) {
return targetTagRepository.findByNameEquals(name);
}
/**
* Creates a new {@link TargetTag}.
*
* @param targetTag
* to be created
*
* @return the new created {@link TargetTag}
*
* @throws EntityAlreadyExistsException
* if given object already exists
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public TargetTag createTargetTag(@NotNull final TargetTag targetTag) {
if (null != targetTag.getId()) {
throw new EntityAlreadyExistsException();
}
if (findTargetTag(targetTag.getName()) != null) {
throw new EntityAlreadyExistsException();
}
return targetTagRepository.save(targetTag);
}
/**
* created multiple {@link TargetTag}s.
*
* @param targetTags
* to be created
* @return the new created {@link TargetTag}s
*
* @throws EntityAlreadyExistsException
* if given object has already an ID.
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<TargetTag> createTargetTags(@NotNull final Iterable<TargetTag> targetTags) {
targetTags.forEach(tag -> {
if (tag.getId() != null) {
throw new EntityAlreadyExistsException();
}
});
return targetTagRepository.save(targetTags);
}
/**
* Deletes {@link TargetTag} with given name.
*
* @param targetTagName
* tag name of the {@link TargetTag} to be deleted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
public void deleteTargetTag(@NotEmpty final String targetTagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(targetTagName);
final List<Target> changed = new LinkedList<Target>();
for (final Target target : targetRepository.findByTag(tag)) {
target.getTags().remove(tag);
changed.add(target);
}
// save association delete
targetRepository.save(changed);
// finally delete the tag itself
targetTagRepository.deleteByName(targetTagName);
}
/**
* returns all {@link TargetTag}s.
*
* @return all {@link TargetTag}s
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public List<TargetTag> findAllTargetTags() {
return targetTagRepository.findAll();
}
/**
* updates the {@link TargetTag}.
*
* @param targetTag
* the {@link TargetTag}
* @return the new {@link TargetTag}
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTag updateTargetTag(@NotNull final TargetTag targetTag) {
checkNotNull(targetTag.getName());
checkNotNull(targetTag.getId());
return targetTagRepository.save(targetTag);
}
/**
* Find {@link DistributionSet} based on given name.
*
* @param name
* to look for.
* @return {@link DistributionSet} or <code>null</code> if it does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public DistributionSetTag findDistributionSetTag(@NotEmpty final String name) {
return distributionSetTagRepository.findByNameEquals(name);
}
/**
* Creates a {@link DistributionSet}.
*
* @param distributionSetTag
* to be created.
* @return the new {@link DistributionSet}
* @throws EntityAlreadyExistsException
* if distributionSetTag already exists
*
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public DistributionSetTag createDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
if (null != distributionSetTag.getId()) {
throw new EntityAlreadyExistsException();
}
if (distributionSetTagRepository.findByNameEquals(distributionSetTag.getName()) != null) {
throw new EntityAlreadyExistsException();
}
return distributionSetTagRepository.save(distributionSetTag);
}
/**
* Creates multiple {@link DistributionSetTag}s.
*
* @param distributionSetTags
* to be created
* @return the new {@link DistributionSetTag}
* @throws EntityAlreadyExistsException
* if a given entity already exists
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public Iterable<DistributionSetTag> createDistributionSetTags(
@NotNull final Iterable<DistributionSetTag> distributionSetTags) {
for (final DistributionSetTag dsTag : distributionSetTags) {
if (dsTag.getId() != null) {
throw new EntityAlreadyExistsException();
}
}
return distributionSetTagRepository.save(distributionSetTags);
}
/**
* Deletes {@link DistributionSetTag} by given
* {@link DistributionSetTag#getName()}.
*
* @param tagNames
* to be deleted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSetTag(@NotEmpty final String tagName) {
final DistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName);
final List<DistributionSet> changed = new LinkedList<DistributionSet>();
for (final DistributionSet set : distributionSetRepository.findByTag(tag)) {
set.getTags().remove(tag);
changed.add(set);
}
// save association delete
distributionSetRepository.save(changed);
distributionSetTagRepository.deleteByName(tagName);
}
/**
* Updates an existing {@link DistributionSetTag}.
*
* @param distributionSetTag
* to be updated
* @return the updated {@link DistributionSet}
* @throws NullPointerException
* of {@link DistributionSetTag#getName()} is <code>null</code>
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetTag updateDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
checkNotNull(distributionSetTag.getName());
checkNotNull(distributionSetTag.getId());
return distributionSetTagRepository.save(distributionSetTag);
}
/**
* returns all {@link DistributionTag}s.
*
* @return all {@link DistributionTag}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public List<DistributionSetTag> findDistributionSetTagsAll() {
return distributionSetTagRepository.findAll();
}
/**
* Finds {@link TargetTag} by given id.
*
* @param id
* to search for
* @return the found {@link TargetTag}s or <code>null</code> if not found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public TargetTag findTargetTagById(@NotNull final Long id) {
return targetTagRepository.findOne(id);
}
/**
* Finds {@link DistributionSetTag} by given id.
*
* @param id
* to search for
* @return the found {@link DistributionSetTag}s or <code>null</code> if not
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public DistributionSetTag findDistributionSetTagById(@NotNull final Long id) {
return distributionSetTagRepository.findOne(id);
}
/**
* returns all {@link TargetTag}s.
*
* @param pageReq
* page parameter
*
* @return all {@link TargetTag}s
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<TargetTag> findAllTargetTags(@NotNull final Pageable pageReq) {
return targetTagRepository.findAll(pageReq);
}
/**
* returns all {@link TargetTag}s.
*
* @param pageReq
* page parameter
* @return all {@link TargetTag}s
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<DistributionSetTag> findDistributionSetTagsAll(@NotNull final Pageable pageReq) {
return distributionSetTagRepository.findAll(pageReq);
}
}

View File

@@ -0,0 +1,180 @@
/**
* 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.ArrayList;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.specifications.TargetFilterQuerySpecification;
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.domain.Specifications;
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.util.Assert;
import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
/**
* Business service facade for managing {@link TargetFilterQuery}s.
*
*
*
*/
@Transactional(readOnly = true)
@Validated
@Service
public class TargetFilterQueryManagement {
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
/**
* creating new {@link TargetFilterQuery}.
*
* @param customTargetFilter
* @return the created {@link TargetFilterQuery}
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public TargetFilterQuery createTargetFilterQuery(@NotNull final TargetFilterQuery customTargetFilter) {
if (targetFilterQueryRepository.findByName(customTargetFilter.getName()) != null) {
throw new EntityAlreadyExistsException(customTargetFilter.getName());
}
final TargetFilterQuery filter = targetFilterQueryRepository.save(customTargetFilter);
return filter;
}
/**
* Delete target filter query.
*
* @param targetFilterQueryId
* IDs of target filter query to be deleted
*/
@Modifying
@Transactional
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
public void deleteTargetFilterQuery(@NotNull final Long targetFilterQueryId) {
targetFilterQueryRepository.delete(targetFilterQueryId);
}
/**
*
* Retrieves all target filter query{@link TargetFilterQuery}.
*
* @param pageable
* pagination parameter
* @return the found {@link TargetFilterQuery}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<TargetFilterQuery> findAllTargetFilterQuery(@NotNull final Pageable pageable) {
return targetFilterQueryRepository.findAll(pageable);
}
/**
* Retrieves all target filter query which {@link TargetFilterQuery}.
*
*
* @param pageable
* pagination parameter
* @param name
* target filter query name
* @return the page with the found {@link TargetFilterQuery}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public Page<TargetFilterQuery> findTargetFilterQueryByFilters(@NotNull final Pageable pageable, final String name) {
final List<Specification<TargetFilterQuery>> specList = new ArrayList<Specification<TargetFilterQuery>>();
if (!Strings.isNullOrEmpty(name)) {
specList.add(TargetFilterQuerySpecification.likeName(name));
}
return findTargetFilterQueryByCriteriaAPI(pageable, specList);
}
/**
*
* @param pageable
* pagination parameter
* @param specList
* list of @link {@link Specification}
* @return the page with the found {@link TargetFilterQuery}
*/
private Page<TargetFilterQuery> findTargetFilterQueryByCriteriaAPI(@NotNull final Pageable pageable,
final List<Specification<TargetFilterQuery>> specList) {
Specifications<TargetFilterQuery> specs = null;
if (!specList.isEmpty()) {
specs = Specifications.where(specList.get(0));
}
if (specList.size() > 1) {
for (final Specification<TargetFilterQuery> s : specList.subList(1, specList.size())) {
specs = specs.and(s);
}
}
if (specs == null) {
return targetFilterQueryRepository.findAll(pageable);
} else {
return targetFilterQueryRepository.findAll(specs, pageable);
}
}
/**
* Find target filter query by name.
*
* @param targetFilterQueryName
* Target filter query name
* @return the found {@link TargetFilterQuery}
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public TargetFilterQuery findTargetFilterQueryByName(@NotNull final String targetFilterQueryName) {
return targetFilterQueryRepository.findByName(targetFilterQueryName);
}
/**
* Find target filter query by id.
*
* @param targetFilterQueryId
* Target filter query id
* @return the found {@link TargetFilterQuery}
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
public TargetFilterQuery findTargetFilterQueryById(@NotNull final Long targetFilterQueryId) {
return targetFilterQueryRepository.findOne(targetFilterQueryId);
}
/**
* updates the {@link TargetFilterQuery}.
*
* @param targetFilterQuery
* to be updated
* @return the updated {@link TargetFilterQuery}
*/
@Modifying
@Transactional
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetFilterQuery updateTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) {
Assert.notNull(targetFilterQuery.getId());
return targetFilterQueryRepository.save(targetFilterQuery);
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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 org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
/**
*
*
*/
@Transactional(readOnly = true)
public interface TargetFilterQueryRepository extends
BaseEntityRepository<TargetFilterQuery, Long>,
JpaSpecificationExecutor<TargetFilterQuery> {
/**
* Find customer target filter by name
*
* @param name
* @return custom target filter
*/
TargetFilterQuery findByName(final String name);
/**
* Find list of all custom target filters.
*/
@Override
Page<TargetFilterQuery> findAll();
@Override
@Modifying
@Transactional
<S extends TargetFilterQuery> S save(S entity);
}

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.repository;
import java.util.Collection;
import java.util.List;
import javax.persistence.Entity;
import javax.transaction.Transactional;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
/**
* Usually a JPA spring data repository to handle {@link TargetInfo} entity.
* However, do to an eclipselink bug with spring boot now a regular interface
* that is implemented by {@link EclipseLinkTargetInfoRepository}.
*
*
*
*/
public interface TargetInfoRepository {
/**
* Sets new TargetUpdateStatus of given target if is not already on that
* value.
*
* @param status
* to set
* @param targets
* to set it for
*/
@Modifying
@Transactional
@Query("update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status")
void setTargetUpdateStatus(@Param("status") TargetUpdateStatus status, @Param("targets") List<Long> targets);
/**
* Save entity and evict cache with it.
*
* @param entity
* to persists
*
* @return persisted or updated {@link Entity}
*/
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
<S extends TargetInfo> S save(S entity);
/**
* Deletes info entries by ID.
*
* @param targetIDs
* to delete
*/
@Modifying
@Transactional
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
void deleteByTargetIdIn(final Collection<Long> targetIDs);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,281 @@
/**
* 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.Collection;
import java.util.List;
import org.eclipse.hawkbit.repository.model.DistributionSet;
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.repository.model.TargetUpdateStatus;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link Target} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface TargetRepository extends BaseEntityRepository<Target, Long>, JpaSpecificationExecutor<Target> {
/**
* Loads {@link Target} including details {@link EntityGraph} by given ID.
*
* @param controllerID
* to search for
* @return found {@link Target} or <code>null</code> if not found.
*/
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
Target findByControllerId(String controllerID);
/**
* Finds targets by given list of {@link Target#getControllerId()}s.
*
* @param controllerIDs
* to serach for
* @return list of found {@link Target}s
*/
List<Target> findByControllerIdIn(String... controllerIDs);
/**
* Deletes the {@link Target}s with the given target IDs.
*
* @param targetIDs
* to be deleted
*/
@Modifying
@Transactional
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM Target t WHERE t.id IN ?1")
void deleteByIdIn(final Collection<Long> targetIDs);
/**
* Finds {@link Target}s by assigned {@link Tag}.
*
* @param tag
* to be found
* @return list of found targets
*/
@Query(value = "SELECT DISTINCT t FROM Target t JOIN t.tags tt WHERE tt = :tag")
List<Target> findByTag(@Param("tag") final TargetTag tag);
/**
* Finds all {@link Target}s based on given {@link Target#getControllerId()}
* list and assigned {@link Tag#getName()}.
*
* @param tag
* to search for
* @param controllerIds
* to search for
* @return {@link List} of found {@link Target}s.
*/
@Query(value = "SELECT DISTINCT t from Target t JOIN t.tags tt WHERE tt.name = :tagname AND t.controllerId IN :targets")
List<Target> findByTagNameAndControllerIdIn(@Param("tagname") final String tag,
@Param("targets") final Collection<String> controllerIds);
/**
* Used by UI to filter based on selected status.
*
* @param pageable
* for page configuration
* @param status
* to filter for
*
* @return found targets
*/
Page<Target> findByTargetInfoUpdateStatus(final Pageable pageable, final TargetUpdateStatus status);
/**
* Finds all targets that have defined {@link DistributionSet} installed.
*
* @param pageable
* for page configuration
* @param set
* is the {@link DistributionSet} to filter for.
*
* @return found targets
*/
Page<Target> findByTargetInfoInstalledDistributionSet(final Pageable pageable, final DistributionSet set);
/**
* retrieves the {@link Target}s which has the {@link DistributionSet}
* installed with the given ID.
*
* @param pageable
* parameter
* @param setID
* the ID of the {@link DistributionSet}
* @return the found {@link Target}s
*/
Page<Target> findByTargetInfoInstalledDistributionSetId(final Pageable pageable, final Long setID);
/**
* Finds all targets that have defined {@link DistributionSet} assigned.
*
* @param pageable
* for page configuration
* @param set
* is the {@link DistributionSet} to filter for.
*
* @return found targets
*/
Page<Target> findByAssignedDistributionSet(final Pageable pageable, final DistributionSet set);
/**
* Saves all given {@link Target}s.
*
* @param entities
* @return the saved entities
* @throws IllegalArgumentException
* in case the given entity is (@literal null}.
*
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
@Override
@Modifying
@Transactional
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
<S extends Target> List<S> save(Iterable<S> entities);
/**
* Saves a given entity. Use the returned instance for further operations as
* the save operation might have changed the entity instance completely.
*
* @param entity
* the target to save
* @return the saved entity
*/
@Override
@Modifying
@Transactional
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
<S extends Target> S save(S entity);
/**
* Finds all targets that have defined {@link DistributionSet} assigned.
*
* @param pageable
* for page configuration
* @param setID
* is the ID of the {@link DistributionSet} to filter for.
*
* @return page of found targets
*/
Page<Target> findByAssignedDistributionSetId(final Pageable pageable, final Long setID);
/**
* Counts number of targets with given
* {@link Target#getAssignedDistributionSet()}.
*
* @param distId
* to search for
*
* @return number of found {@link Target}s.
*/
Long countByAssignedDistributionSetId(final Long distId);
/**
* @param ids
* of count in DB
* @return number of found {@link Target}s with given
* {@link Target#getControllerId()}s
*/
@Query("SELECT COUNT(t) FROM Target t WHERE t.controllerId IN ?1")
Long countByControllerIdIn(final Collection<String> ids);
/**
* Counts number of targets with given
* {@link TargetStatus#getInstalledDistributionSet()}.
*
* @param distId
* to search for
* @return number of found {@link Target}s.
*/
Long countByTargetInfoInstalledDistributionSetId(final Long distId);
/**
* Finds all targets that have defined {@link DistributionSet} assigned or
* installed.
*
* @param pageable
* for page configuration
* @param assigned
* {@link DistributionSet} filter for; please note: must not be
* null
* @param installed
* {@link DistributionSet} filter for; please note: must not be
* null
*
* @return found targets
*/
Page<Target> findByAssignedDistributionSetOrTargetInfoInstalledDistributionSet(final Pageable pageable,
final DistributionSet assigned, final DistributionSet installed);
/**
* Finds all targets that have defined {@link DistributionSet} assigned or
* installed.
*
* @param pageable
* for page configuration
* @param assigned
* {@link DistributionSet} filter for; please note: must not be
* null
* @param installed
* {@link DistributionSet} filter for; please note: must not be
* null
* @return found targets
*/
Page<Target> findByAssignedDistributionSetIdOrTargetInfoInstalledDistributionSetId(final Pageable pageable,
final Long assigned, final Long installed);
/**
* Finds all {@link Target}s in the repository.
*
* @return {@link List} of {@link Target}s
*
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
@Override
List<Target> findAll();
@Override
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("SELECT t FROM Target t WHERE t.id IN ?1")
List<Target> findAll(Iterable<Long> ids);
/**
* Sets {@link Target#getAssignedDistributionSet()}.
*
* @param set
* to use
* @param modifiedAt
* current time
* @param modifiedBy
* current auditor
* @param targets
* to update
*/
@Modifying
@Transactional
@Query("UPDATE Target t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy WHERE t.id IN :targets")
void setAssignedDistributionSet(@Param("set") DistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
}

View File

@@ -0,0 +1,72 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
/**
* Result object for {@link TargetTag} assigments.
*
*
*
*
*/
public class TargetTagAssigmentResult extends AssignmentResult {
private final int unassigned;
private final List<Target> assignedTargets;
private final List<Target> unassignedTargets;
/**
* Constructor.
*
* @param alreadyAssigned
* number of already assigned/ignored elements
* @param assigned
* number of newly assigned elements
* @param unassigned
* number of newly assigned elements
* @param assignedTargets
* {@link List} of assigned {@link Target}s.
* @param unassignedTargets
* {@link List} of unassigned {@link Target}s.
*/
public TargetTagAssigmentResult(final int alreadyAssigned, final int assigned, final int unassigned,
final List<Target> assignedTargets, final List<Target> unassignedTargets) {
super(assigned, alreadyAssigned);
this.unassigned = unassigned;
this.assignedTargets = assignedTargets;
this.unassignedTargets = unassignedTargets;
}
/**
* @return the unassigned
*/
public int getUnassigned() {
return unassigned;
}
/**
* @return the assignedTargets
*/
public List<Target> getAssignedTargets() {
return assignedTargets;
}
/**
* @return the unassignedTargets
*/
public List<Target> getUnassignedTargets() {
return unassignedTargets;
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link TargetTag} repository.
*
*
*
*/
@Transactional(readOnly = true)
public interface TargetTagRepository extends BaseEntityRepository<TargetTag, Long> {
/**
* deletes the {@link TargetTag}s with the given tag names.
*
* @param tagNames
* to be deleted
* @return 1 if tag was deleted
*/
@Modifying
@Transactional
Long deleteByName(final String tagName);
/**
* find {@link TargetTag} by its name.
*
* @param tagName
* to filter on
* @return the {@link TargetTag} if found, otherwise null
*/
TargetTag findByNameEquals(final String tagName);
/**
* Returns all instances of the type.
*
* @return all entities
*/
@Override
List<TargetTag> findAll();
@Override
<S extends TargetTag> List<S> save(Iterable<S> entities);
}

View File

@@ -0,0 +1,65 @@
/**
* 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 org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
/**
* A custom JPA database return value object with the proper constructor so JPA
* is able to parse the result-set directly into this object.
*
*
*
*/
public class TargetWithActionType {
private final String targetId;
private final ActionType actionType;
private final long forceTime;
/**
* @param targetId
* @param actionType
* @param forceTime
*/
public TargetWithActionType(final String targetId, final ActionType actionType, final long forceTime) {
this.targetId = targetId;
this.actionType = actionType;
this.forceTime = forceTime;
}
/**
* @return the actionType
*/
public ActionType getActionType() {
if (actionType != null) {
return actionType;
}
// default value
return ActionType.FORCED;
}
/**
* @return the forceTime
*/
public long getForceTime() {
if (actionType == ActionType.TIMEFORCED) {
return forceTime;
}
return Action.NO_FORCE_TIME;
}
/**
* @return the targetId
*/
public String getTargetId() {
return targetId;
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.springframework.transaction.annotation.Transactional;
/**
* The spring-data repository for the entity {@link TenantConfiguration}.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface TenantConfigurationRepository extends BaseEntityRepository<TenantConfiguration, Long> {
/**
* Finds a specific {@link TenantConfiguration} by the configuration key.
*
* @param configurationKey
* the configuration key to find the configuration for
* @return the found tenant configuration object otherwise {@code null}
*/
TenantConfiguration findByKey(String configurationKey);
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
@Override
List<TenantConfiguration> findAll();
/**
* Deletes a tenant configuration by tenant and key.
*
* @param tenant
* the tenant for this configuration
* @param keyName
* the name of the key to be deleted
*/
void deleteByKey(String keyName);
}

View File

@@ -0,0 +1,43 @@
/**
* 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.lang.reflect.Method;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Service;
/**
* {@link KeyGenerator} for tenant related caches.
*
*
*
*
*/
@Service
public class TenantKeyGenerator implements KeyGenerator {
@Autowired
private TenantAware tenantAware;
/*
* (non-Javadoc)
*
* @see
* org.springframework.cache.interceptor.KeyGenerator#generate(java.lang.
* Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object generate(final Object target, final Method method, final Object... params) {
return tenantAware.getCurrentTenant().toUpperCase();
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.List;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* repository for operations on {@link TenantMetaData} entity.
*
*
*
*
*/
@Transactional(readOnly = true)
public interface TenantMetaDataRepository extends PagingAndSortingRepository<TenantMetaData, Long> {
/**
* Search {@link TenantMetaData} by tenant name.
*
* @param tenant
* to search for
* @return found {@link TenantMetaData} or <code>null</code>
*/
TenantMetaData findByTenantIgnoreCase(String tenant);
/**
* Counts the tenant by the tenant field which is either a count of one or a
* count of zero, this is mostly to check if the tenant exists.
*
* @param tenant
* the name of the tenant to check if it is exists
* @return the count of the tenant by name which is either one or zero
*/
Long countByTenantIgnoreCase(String tenant);
@Override
List<TenantMetaData> findAll();
/**
* @param tenant
*/
void deleteByTenantIgnoreCase(String tenant);
}

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.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,50 @@
/**
* 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 if artifact deletion failed.
*
*
*
*
*/
public final class ArtifactDeleteFailedException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/
public ArtifactDeleteFailedException() {
super(SpServerError.SP_ARTIFACT_DELETE_FAILED);
}
/**
* @param cause
* for the exception
*/
public ArtifactDeleteFailedException(final Throwable cause) {
super(SpServerError.SP_ARTIFACT_DELETE_FAILED, cause);
}
/**
* @param message
* of the error
*/
public ArtifactDeleteFailedException(final String message) {
super(message, SpServerError.SP_ARTIFACT_DELETE_FAILED);
}
}

View File

@@ -0,0 +1,60 @@
/**
* 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;
/**
*
*
*
*/
public final class ArtifactUploadFailedException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/
public ArtifactUploadFailedException() {
super(SpServerError.SP_ARTIFACT_UPLOAD_FAILED);
}
/**
* @param cause
* for the exception
*/
public ArtifactUploadFailedException(final Throwable cause) {
super(SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause);
}
/**
* @param message
* of the error
*/
public ArtifactUploadFailedException(final String message) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED);
}
/**
* @param message
* for the error
* @param cause
* of the error
*/
public ArtifactUploadFailedException(final String message, final Throwable cause) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause);
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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 if cancelation of actions is performened where the action is not
* cancelable, e.g. the action is not active or is already a cancel action.
*
*
*
*
*/
public final class CancelActionNotAllowedException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new CancelActionNotAllowed with
* {@link SpServerError#SP_ACTION_NOT_CANCELABLE} error.
*/
public CancelActionNotAllowedException() {
super(SpServerError.SP_ACTION_NOT_CANCELABLE);
}
/**
* @param cause
* for the exception
*/
public CancelActionNotAllowedException(final Throwable cause) {
super(SpServerError.SP_ACTION_NOT_CANCELABLE, cause);
}
/**
* @param message
* of the error
*/
public CancelActionNotAllowedException(final String message) {
super(message, SpServerError.SP_ACTION_NOT_CANCELABLE);
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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;
/**
* {@link ConcurrentModificationException} is thrown when a given entity in's
* actual and cannot be stored within the current session. Reason could be that
* it has been changed within another session.
*
*
*
*/
public class ConcurrentModificationException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_REPO_CONCURRENT_MODIFICATION;
/**
* Constructor.
*/
public ConcurrentModificationException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public ConcurrentModificationException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* custom error message
* @param cause
* of the exception
*/
public ConcurrentModificationException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* custom error message
*/
public ConcurrentModificationException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,50 @@
/**
* 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 if DS creation failed.
*
*
*
*
*/
public final class DistributionSetCreationFailedMissingMandatoryModuleException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_DS_CREATION_FAILED_MISSING_MODULE} error.
*/
public DistributionSetCreationFailedMissingMandatoryModuleException() {
super(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE);
}
/**
* @param cause
* for the exception
*/
public DistributionSetCreationFailedMissingMandatoryModuleException(final Throwable cause) {
super(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE, cause);
}
/**
* @param message
* of the error
*/
public DistributionSetCreationFailedMissingMandatoryModuleException(final String message) {
super(message, SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE);
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
/**
* Thrown if the user tries to assign modules to a {@link DistributionSet} that
* has to {@link DistributionSetType} defined.
*
*
*
*
*/
public class DistributionSetTypeUndefinedException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_DS_TYPE_UNDEFINED} error.
*/
public DistributionSetTypeUndefinedException() {
super(SpServerError.SP_DS_TYPE_UNDEFINED);
}
/**
* @param cause
* for the exception
*/
public DistributionSetTypeUndefinedException(final Throwable cause) {
super(SpServerError.SP_DS_TYPE_UNDEFINED, cause);
}
/**
* @param message
* of the error
*/
public DistributionSetTypeUndefinedException(final String message) {
super(message, SpServerError.SP_DS_TYPE_UNDEFINED);
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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;
/**
* the {@link EntityAlreadyExistsException} is thrown when a entity is tried to
* be saved which already exists or which violates unique key constraints.
*
*
*
*/
public class EntityAlreadyExistsException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_REPO_ENTITY_ALRREADY_EXISTS;
/**
* Default constructor.
*/
public EntityAlreadyExistsException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public EntityAlreadyExistsException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public EntityAlreadyExistsException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public EntityAlreadyExistsException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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;
/**
* The {@link EntityLockedException} is thrown when an entity has been locked by
* the server to prevent modification.
*
*
*
*/
public class EntityLockedException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_ENTITY_LOCKED;
/**
* Default constructor.
*/
public EntityLockedException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public EntityLockedException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public EntityLockedException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public EntityLockedException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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;
/**
* the {@link EntityNotFoundException} is thrown when a entity is tried find but
* not found.
*
*
*
*/
public class EntityNotFoundException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_REPO_ENTITY_NOT_EXISTS;
/**
* Default constructor.
*/
public EntityNotFoundException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public EntityNotFoundException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public EntityNotFoundException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public EntityNotFoundException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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;
/**
* the {@link EntityReadOnlyException} is thrown when a entity is in read only
* mode and a user tries to change it.
*/
public class EntityReadOnlyException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_REPO_ENTITY_READ_ONLY;
/**
* Default constructor.
*/
public EntityReadOnlyException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public EntityReadOnlyException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public EntityReadOnlyException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public EntityReadOnlyException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,49 @@
/**
* 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;
/**
*
*
*
*/
public final class GridFSDBFileNotFoundException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/
public GridFSDBFileNotFoundException() {
super(SpServerError.SP_ARTIFACT_LOAD_FAILED);
}
/**
* @param cause
* for the exception
*/
public GridFSDBFileNotFoundException(final Throwable cause) {
super(SpServerError.SP_ARTIFACT_LOAD_FAILED, cause);
}
/**
* @param message
* of the error
*/
public GridFSDBFileNotFoundException(final String message) {
super(message, SpServerError.SP_ARTIFACT_LOAD_FAILED);
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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 if a distribution set is assigned to a a target that is incomplete
* (i.e. mandatory modules are missing).
*
*
*
*
*/
public final class IncompleteDistributionSetException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new IncompleteDistributionSetException with
* {@link SpServerError#SP_DS_INCOMPLETE} error.
*/
public IncompleteDistributionSetException() {
super(SpServerError.SP_DS_INCOMPLETE);
}
/**
* @param cause
* for the exception
*/
public IncompleteDistributionSetException(final Throwable cause) {
super(SpServerError.SP_DS_INCOMPLETE, cause);
}
/**
* @param message
* of the error
*/
public IncompleteDistributionSetException(final String message) {
super(message, SpServerError.SP_DS_INCOMPLETE);
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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;
/**
* Exception which is thrown in case the current security context object does
* not hold a required authority/permission.
*
*
*
*/
public class InsufficientPermissionException extends SpServerRtException {
private static final long serialVersionUID = 1L;
/**
* creates new InsufficientPermissionException.
*
* @param cause
* the cause of the exception
*/
public InsufficientPermissionException(final Throwable cause) {
super(SpServerError.SP_INSUFFICIENT_PERMISSION, cause);
}
/**
* creates new InsufficientPermissionException.
*/
public InsufficientPermissionException() {
this(null);
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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 if MD5 checksum check fails.
*
*
*
*
*/
public class InvalidMD5HashException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH} error.
*/
public InvalidMD5HashException() {
super(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH);
}
/**
* @param message
* of the error
* @param cause
* for the exception
*/
public InvalidMD5HashException(final String message, final Throwable cause) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, cause);
}
/**
* @param message
* of the error
*/
public InvalidMD5HashException(final String message) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH);
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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 if SHA1 checksum check fails.
*
*
*
*
*/
public class InvalidSHA1HashException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH} error.
*/
public InvalidSHA1HashException() {
super(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH);
}
/**
* @param message
* of the error
* @param cause
* for the exception
*/
public InvalidSHA1HashException(final String message, final Throwable cause) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH, cause);
}
/**
* @param message
* of the error
*/
public InvalidSHA1HashException(final String message) {
super(message, SpServerError.SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH);
}
}

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.exception;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.exception.SpServerRtException;
/**
* the {@link TenantNotExistException} is thrown when e.g. a controller tries to
* register itself at SP as plug'n play target and the tenant specified in the
* URL for this target does not exist. To avoid that targets could register
* automatically new tenants.
*
*
*
*/
public class TenantNotExistException extends SpServerRtException {
private static final long serialVersionUID = 1L;
private static final SpServerError THIS_ERROR = SpServerError.SP_REPO_TENANT_NOT_EXISTS;
/**
* Default constructor.
*/
public TenantNotExistException() {
super(THIS_ERROR);
}
/**
* Parameterized constructor.
*
* @param cause
* of the exception
*/
public TenantNotExistException(final Throwable cause) {
super(THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
* @param cause
* of the exception
*/
public TenantNotExistException(final String message, final Throwable cause) {
super(message, THIS_ERROR, cause);
}
/**
* Parameterized constructor.
*
* @param message
* of the exception
*/
public TenantNotExistException(final String message) {
super(message, THIS_ERROR);
}
}

View File

@@ -0,0 +1,50 @@
/**
* 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 if too many status entries have been inserted.
*
*
*
*
*/
public final class ToManyAttributeEntriesException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_ATTRIBUTES_TO_MANY_ENTRIES} error.
*/
public ToManyAttributeEntriesException() {
super(SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES);
}
/**
* @param cause
* for the exception
*/
public ToManyAttributeEntriesException(final Throwable cause) {
super(SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES, cause);
}
/**
* @param message
* of the error
*/
public ToManyAttributeEntriesException(final String message) {
super(message, SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES);
}
}

View File

@@ -0,0 +1,50 @@
/**
* 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 if too many status entries have been inserted.
*
*
*
*
*/
public final class ToManyStatusEntriesException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/
public ToManyStatusEntriesException() {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
}
/**
* @param cause
* for the exception
*/
public ToManyStatusEntriesException(final Throwable cause) {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause);
}
/**
* @param message
* of the error
*/
public ToManyStatusEntriesException(final String message) {
super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
/**
* Thrown if user tries to add a {@link SoftwareModule} to a
* {@link DistributionSet} that is not defined by< the
* {@link DistributionSetType}.
*
*
*
*
*/
public class UnsupportedSoftwareModuleForThisDistributionSetException extends SpServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new UnsupportedSoftwareModuleForThisDistributionSetException
* with {@link SpServerError#SP_DS_MODULE_UNSUPPORTED} error.
*/
public UnsupportedSoftwareModuleForThisDistributionSetException() {
super(SpServerError.SP_DS_MODULE_UNSUPPORTED);
}
/**
* @param cause
* for the exception
*/
public UnsupportedSoftwareModuleForThisDistributionSetException(final Throwable cause) {
super(SpServerError.SP_DS_MODULE_UNSUPPORTED, cause);
}
/**
* @param message
* of the error
*/
public UnsupportedSoftwareModuleForThisDistributionSetException(final String message) {
super(message, SpServerError.SP_DS_MODULE_UNSUPPORTED);
}
}

View File

@@ -0,0 +1,395 @@
/**
* 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.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.eclipse.hawkbit.cache.CacheField;
import org.eclipse.hawkbit.cache.CacheKeys;
import org.eclipse.persistence.annotations.CascadeOnDelete;
/**
* <p>
* Applicable transition changes of the software {@link SoftwareModule} state of
* a {@link Target}, e.g. install, uninstall, update, start, stop, and
* preparations for the transition change, i.e. download.
* </p>
*
* <p>
* Actions are managed by the SP server (SPS) and applied to the edge controller
* by the SP controller (SPC). Actions may also be value added commands that are
* nor directly related to SP, e.g. factory reset.
* <p>
*
*
*
*
*
*/
@Table(name = "sp_action", indexes = { @Index(name = "sp_idx_action_01", columnList = "tenant,distribution_set"),
@Index(name = "sp_idx_action_02", columnList = "tenant,target,active"),
@Index(name = "sp_idx_action_prim", columnList = "tenant,id") })
@NamedEntityGraphs({ @NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") }),
@NamedEntityGraph(name = "Action.all", attributeNodes = { @NamedAttributeNode("distributionSet"),
@NamedAttributeNode("target") }) })
@Entity
public class Action extends BaseEntity implements Comparable<Action> {
private static final long serialVersionUID = 1L;
/**
* indicating that target action has no force time {@link #hasForcedTime()}.
*/
public static final long NO_FORCE_TIME = 0L;
/**
* the {@link DistributionSet} which should be installed by this action.
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds") )
private DistributionSet distributionSet;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target") )
private Target target;
@Column(name = "active")
private boolean active;
@Column(name = "action_type", nullable = false)
@Enumerated(EnumType.STRING)
private ActionType actionType;
@Column(name = "forced_time")
private long forcedTime;
@Column(name = "status")
private Status status;
@CascadeOnDelete
@OneToMany(mappedBy = "action", targetEntity = ActionStatus.class, fetch = FetchType.LAZY, cascade = {
CascadeType.REMOVE })
private List<ActionStatus> actionStatus;
/**
* Note: filled only in {@link Status#DOWNLOAD}.
*/
@Transient
@CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT)
private int downloadProgressPercent;
/**
* @return the distributionSet
*/
public DistributionSet getDistributionSet() {
return distributionSet;
}
/**
* @param distributionSet
* the distributionSet to set
*/
public void setDistributionSet(final DistributionSet distributionSet) {
this.distributionSet = distributionSet;
}
public boolean isCancelingOrCanceled() {
return status == Status.CANCELING || status == Status.CANCELED;
}
/**
* @param active
* the active to set
*/
public void setActive(final boolean active) {
this.active = active;
}
/**
* @return the status
*/
public Status getStatus() {
return status;
}
/**
* @param status
* the status to set
*/
public void setStatus(final Status status) {
this.status = status;
}
/**
* @return the downloadProgressPercent
*/
public int getDownloadProgressPercent() {
return downloadProgressPercent;
}
/**
* @param downloadProgressPercent
* the downloadProgressPercent to set
*/
public void setDownloadProgressPercent(final int downloadProgressPercent) {
this.downloadProgressPercent = downloadProgressPercent;
}
/**
* @return the active
*/
public boolean isActive() {
return active;
}
/**
* @param actionType
* the actionType to set
*/
public void setActionType(final ActionType actionType) {
this.actionType = actionType;
}
/**
* @return the actionType
*/
public ActionType getActionType() {
return actionType;
}
/**
* @return the actionStatus
*/
public List<ActionStatus> getActionStatus() {
return actionStatus;
}
/**
* @param target
* the target to set
*/
public void setTarget(final Target target) {
this.target = target;
}
/**
* @return the target
*/
public Target getTarget() {
return target;
}
/**
* @return the forcedTime
*/
public long getForcedTime() {
return forcedTime;
}
/**
* @param forcedTime
* the forcedTime to set
*/
public void setForcedTime(final long forcedTime) {
this.forcedTime = forcedTime;
}
@Override
public int compareTo(final Action o) {
if (super.getId() == null || o == null || o.getId() == null) {
return 0;
}
return super.getId().compareTo(o.getId());
}
/**
* checks if the {@link #forcedTime} is hit by the given
* {@code hitTimeMillis}, by means if the given milliseconds are greater
* than the forcedTime.
*
* @param hitTimeMillis
* the milliseconds, mostly the
* {@link System#currentTimeMillis()}
* @return {@code true} if this {@link #type} is in
* {@link ActionType#TIMEFORCED} and the given {@code hitTimeMillis}
* is greater than the {@link #forcedTime} otherwise {@code false}
*/
public boolean isHitAutoForceTime(final long hitTimeMillis) {
if (actionType == ActionType.TIMEFORCED) {
return hitTimeMillis >= forcedTime;
}
return false;
}
/**
* @return {@code true} if either the {@link #type} is
* {@link ActionType#FORCED} or {@link ActionType#TIMEFORCED} but
* then if the {@link #forcedTime} has been exceeded otherwise
* always {@code false}
*/
public boolean isForce() {
switch (actionType) {
case FORCED:
return true;
case TIMEFORCED:
return isHitAutoForceTime(System.currentTimeMillis());
default:
return false;
}
}
/**
* @return the forced
*/
public boolean isForced() {
return actionType == ActionType.FORCED;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Action [distributionSet=" + distributionSet + ", getId()=" + getId() + "]";
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((actionType == null) ? 0 : actionType.hashCode());
result = prime * result + (active ? 1231 : 1237);
result = prime * result + (int) (forcedTime ^ (forcedTime >>> 32));
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + (isHitAutoForceTime(System.currentTimeMillis()) ? 1231 : 1237);
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is generated
// code
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Action other = (Action) obj;
if (actionType != other.actionType) {
return false;
}
if (active != other.active) {
return false;
}
if (forcedTime != other.forcedTime) {
return false;
}
if (status != other.status) {
return false;
}
return true;
}
/**
* Action status as reported by the controller.
*
* Be aware that JPA is persiting the ordnial 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 {
/**
* Action is finished successfully for this target.
*/
FINISHED,
/**
* Action has failed for this target.
*/
ERROR,
/**
* Action is still running but with warnings.
*/
WARNING,
/**
* Action is still running for this target.
*/
RUNNING,
/**
* Action has been canceled for this target.
*/
CANCELED,
/**
* Action is in canceling state and waiting for controller confirmation.
*/
CANCELING,
/**
* Action has been presented to the target.
*/
RETRIEVED,
/**
* Action needs download by this target which has now started.
*/
DOWNLOAD;
}
/**
* The action type for this action relation.
*
*
*
*
*/
public enum ActionType {
FORCED, SOFT, TIMEFORCED;
}
}

View File

@@ -0,0 +1,169 @@
/**
* 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.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.Table;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import com.google.common.base.Splitter;
/**
* Entity to store the status for a specific action.
*
*
*
*/
@Table(name = "sp_action_status", indexes = { @Index(name = "sp_idx_action_status_01", columnList = "tenant,action"),
@Index(name = "sp_idx_action_status_02", columnList = "tenant,action,status"),
@Index(name = "sp_idx_action_status_prim", columnList = "tenant,id") })
@NamedEntityGraph(name = "ActionStatus.withMessages", attributeNodes = { @NamedAttributeNode("messages") })
@Entity
public class ActionStatus extends BaseEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
@Column(name = "target_occurred_at")
private Long occurredAt;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "action", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action") )
private Action action;
@Column(name = "status")
private Status status;
@CascadeOnDelete
@ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
@CollectionTable(name = "sp_action_status_messages", joinColumns = @JoinColumn(name = "action_status_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat") ) , indexes = {
@Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
@Column(name = "detail_message", length = 512)
private final List<String> messages = new ArrayList<String>();
/**
* Creates a new {@link ActionStatus} object.
*
* @param action
* the action for this action status
* @param status
* the status for this action status
* @param occurredAt
* the occurred timestamp
*/
public ActionStatus(final Action action, final Status status, final Long occurredAt) {
this.action = action;
this.status = status;
this.occurredAt = occurredAt;
}
/**
* Creates a new {@link ActionStatus} object.
*
* @param action
* the action for this action status
* @param status
* the status for this action status
* @param occurredAt
* the occurred timestamp
* @param messages
* the messages which should be added to this action status
*/
public ActionStatus(final Action action, final Status status, final Long occurredAt, final String... messages) {
this.action = action;
this.status = status;
this.occurredAt = occurredAt;
for (final String msg : messages) {
addMessage(msg);
}
}
/**
*
*/
public ActionStatus() {
}
/**
* @return the occurredAt
*/
public Long getOccurredAt() {
return occurredAt;
}
/**
* @param occurredAt
* the occurredAt to set
*/
public void setOccurredAt(final Long occurredAt) {
this.occurredAt = occurredAt;
}
/**
* Adds message.
*
* @param message
* to add
*/
public final void addMessage(final String message) {
Splitter.fixedLength(512).split(message).forEach(chunk -> messages.add(chunk));
}
public List<String> getMessages() {
return messages;
}
/**
* @return the action
*/
public Action getAction() {
return action;
}
/**
* @param action
* the action to set
*/
public void setAction(final Action action) {
this.action = action;
}
/**
* @return the status
*/
public Status getStatus() {
return status;
}
/**
* @param status
* the status to set
*/
public void setStatus(final Status status) {
this.status = status;
}
}

View File

@@ -0,0 +1,169 @@
/**
* 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.model;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
/**
* Custom JPA Model for querying {@link Action} include the count of the
* action's {@link ActionStatus}.
*
*
*
*
*/
public class ActionWithStatusCount {
private final Long actionStatusCount;
private final Long actionId;
private final ActionType actionType;
private final boolean actionActive;
private final long actionForceTime;
private final Status actionStatus;
private final Long actionCreatedAt;
private final Long actionLastModifiedAt;
private final Long dsId;
private final String dsName;
private final String dsVersion;
private final Action action;
/**
* JPA constructor, the parameter are the result set columns of the custom
* query.
*
* @param actionId
* the ID of the action
* @param actionType
* the action type
* @param active
* the active attribute of the action
* @param forcedTime
* the forced time attribute of the action
* @param status
* the status attribute of the action
* @param actionCreatedAt
* the createdAt timestamp of the action
* @param actionLastModifiedAt
* the last modified timestamp of the action
* @param dsId
* the ID of the distributionset
* @param dsName
* the name of the distributionset
* @param dsVersion
* the version of the distributionset
* @param actionStatusCount
* the count of the action status for this action
*/
public ActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active,
final long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt,
final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount) {
this.actionId = actionId;
this.actionType = actionType;
this.actionActive = active;
this.actionForceTime = forcedTime;
this.actionStatus = status;
this.actionCreatedAt = actionCreatedAt;
this.actionLastModifiedAt = actionLastModifiedAt;
this.dsId = dsId;
this.dsName = dsName;
this.dsVersion = dsVersion;
this.actionStatusCount = actionStatusCount;
this.action = new Action();
this.action.setActionType(actionType);
this.action.setActive(actionActive);
this.action.setForcedTime(actionForceTime);
this.action.setStatus(actionStatus);
this.action.setId(actionId);
}
/**
* @return the action
*/
public Action getAction() {
return action;
}
/**
* @return the actionId
*/
public Long getActionId() {
return actionId;
}
/**
* @return the actionType
*/
public ActionType getActionType() {
return actionType;
}
/**
* @return the actionActive
*/
public boolean isActionActive() {
return actionActive;
}
/**
* @return the actionForceTime
*/
public long getActionForceTime() {
return actionForceTime;
}
/**
* @return the actionStatus
*/
public Status getActionStatus() {
return actionStatus;
}
/**
* @return the actionCreatedAt
*/
public Long getActionCreatedAt() {
return actionCreatedAt;
}
/**
* @return the actionLastModifiedAt
*/
public Long getActionLastModifiedAt() {
return actionLastModifiedAt;
}
/**
* @return the dsId
*/
public Long getDsId() {
return dsId;
}
/**
* @return the dsName
*/
public String getDsName() {
return dsName;
}
/**
* @return the dsVersion
*/
public String getDsVersion() {
return dsVersion;
}
/**
* @return the actionStatusCount
*/
public Long getActionStatusCount() {
return actionStatusCount;
}
}

View File

@@ -0,0 +1,82 @@
/**
* 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.model;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
/**
* Tenant specific locally stored artifact representation that is used by
* {@link SoftwareModule} .
*
*
*
*
*/
@MappedSuperclass
public abstract class Artifact extends BaseEntity {
private static final long serialVersionUID = 1L;
@Column(name = "sha1_hash", length = 40, nullable = true)
private String sha1Hash;
@Column(name = "md5_hash", length = 32, nullable = true)
private String md5Hash;
@Column(name = "file_size")
private Long size;
public abstract SoftwareModule getSoftwareModule();
/**
* @return the md5Hash
*/
public String getMd5Hash() {
return md5Hash;
}
/**
* @return the sha1Hash
*/
public String getSha1Hash() {
return sha1Hash;
}
/**
* @param md5Hash
* the md5Hash to set
*/
public void setMd5Hash(final String md5Hash) {
this.md5Hash = md5Hash;
}
/**
* @param sha1Hash
* the sha1Hash to set
*/
public void setSha1Hash(final String sha1Hash) {
this.sha1Hash = sha1Hash;
}
/**
* @return the size
*/
public Long getSize() {
return size;
}
/**
* @param size
* the size to set
*/
public void setSize(final Long size) {
this.size = size;
}
}

View File

@@ -0,0 +1,258 @@
/**
* 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.model;
import java.io.Serializable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.Version;
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
import org.eclipse.persistence.annotations.Multitenant;
import org.eclipse.persistence.annotations.MultitenantType;
import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.hateoas.Identifiable;
/**
* Holder of all base attributes common to all entities.
*
*
*
*
*
*/
@MappedSuperclass
@Access(AccessType.FIELD)
@EntityListeners({ AuditingEntityListener.class, CacheFieldEntityListener.class })
@TenantDiscriminatorColumn(name = "tenant", length = 40)
@Multitenant(MultitenantType.SINGLE_TABLE)
public abstract class BaseEntity implements Serializable, Identifiable<Long> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
private String createdBy;
private String lastModifiedBy;
private Long createdAt;
private Long lastModifiedAt;
@Version
@Column(name = "optlock_revision")
private long optLockRevision;
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
private String tenant;
/**
*
*/
public BaseEntity() {
}
/**
* @param entity
* the entity to copy
*/
public BaseEntity(final BaseEntity entity) {
id = entity.id;
createdAt = entity.createdAt;
createdBy = entity.createdBy;
lastModifiedAt = entity.lastModifiedAt;
lastModifiedBy = entity.lastModifiedBy;
optLockRevision = entity.optLockRevision;
}
/**
* PrePersist listener method for all {@link BaseEntity} entities.
*/
@PrePersist
public void prePersist() {
// before persisting the entity check the current ID of the tenant by
// using the TenantAware
// service
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
if (currentTenant == null) {
throw new TenantNotExistException(
"Tenant " + TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
+ " does not exists, cannot create entity " + this.getClass() + " with id " + id);
}
setTenant(currentTenant.toUpperCase());
}
@Access(AccessType.PROPERTY)
@Column(name = "created_at", insertable = true, updatable = false)
public Long getCreatedAt() {
return createdAt;
}
@Access(AccessType.PROPERTY)
@Column(name = "created_by", insertable = true, updatable = false, length = 40)
public String getCreatedBy() {
return createdBy;
}
@Access(AccessType.PROPERTY)
@Column(name = "last_modified_at", insertable = false, updatable = true)
public Long getLastModifiedAt() {
return lastModifiedAt;
}
@Access(AccessType.PROPERTY)
@Column(name = "last_modified_by", insertable = false, updatable = true, length = 40)
public String getLastModifiedBy() {
return lastModifiedBy;
}
/**
* @param createdBy
* the createdBy to set
*/
@CreatedBy
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}
/**
* @param lastModifiedBy
* the lastModifiedBy to set
*/
@LastModifiedBy
public void setLastModifiedBy(final String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
/**
* @param createdAt
* the createdAt to set
*/
@CreatedDate
public void setCreatedAt(final Long createdAt) {
this.createdAt = createdAt;
}
/**
* @param lastModifiedAt
* the lastModifiedAt to set
*/
@LastModifiedDate
public void setLastModifiedAt(final Long lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
}
public long getOptLockRevision() {
return optLockRevision;
}
/**
* @return the tenant
*/
public String getTenant() {
return tenant;
}
/**
* @param tenant
* the tenant to set
*/
public void setTenant(final String tenant) {
this.tenant = tenant;
}
/**
* @return the id
*/
@Override
public Long getId() {
return id;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BaseEntity [id=" + id + "]";
}
/**
* @param id
* the id to set
*/
public void setId(final Long id) {
this.id = id;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() { // NOSONAR - as this is generated code
final int prime = 31;
int result = 1;
result = prime * result + (id == null ? 0 : id.hashCode());
result = prime * result + (int) (optLockRevision ^ optLockRevision >>> 32);
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@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 BaseEntity other = (BaseEntity) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (optLockRevision != other.optLockRevision) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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.model;
import java.io.Serializable;
/**
* Use to display software modules for the selected distribution.
*
*
*
*
*/
public class CustomSoftwareModule implements Serializable {
private static final long serialVersionUID = 6144585781451168439L;
private final SoftwareModule softwareModule;
private final boolean assigned;
/**
* Constructor.
*
* @param softwareModule
* entity.
* @param assigned
* as true if the software module is assigned and false if not
* assigned.
*/
public CustomSoftwareModule(final SoftwareModule softwareModule, final boolean assigned) {
this.softwareModule = softwareModule;
this.assigned = assigned;
}
/**
* @return the softwareModule
*/
public SoftwareModule getSoftwareModule() {
return softwareModule;
}
/**
* @return the assigned
*/
public boolean isAssigned() {
return assigned;
}
}

View File

@@ -0,0 +1,362 @@
/**
* 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.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.persistence.annotations.CascadeOnDelete;
/**
* <p>
* The {@link DistributionSet} is defined in the SP repository and contains at
* least an OS and an Agent Hub.
* </p>
*
* <p>
* A {@link Target} has exactly one target {@link DistributionSet} assigned.
* </p>
*
*
*
*
*
*/
@Entity
@Table(name = "sp_distribution_set", uniqueConstraints = {
@UniqueConstraint(columnNames = { "name", "version", "tenant" }, name = "uk_distrib_set") }, indexes = {
@Index(name = "sp_idx_distribution_set_01", columnList = "tenant,deleted,name,complete"),
@Index(name = "sp_idx_distribution_set_02", columnList = "tenant,required_migration_step"),
@Index(name = "sp_idx_distribution_set_prim", columnList = "tenant,id") })
@NamedEntityGraph(name = "DistributionSet.detail", attributeNodes = { @NamedAttributeNode("modules"),
@NamedAttributeNode("tags"), @NamedAttributeNode("type") })
public class DistributionSet extends NamedVersionedEntity {
private static final long serialVersionUID = 1L;
@Column(name = "required_migration_step")
private boolean requiredMigrationStep = false;
@ManyToMany(targetEntity = SoftwareModule.class, fetch = FetchType.LAZY)
@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>();
@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>();
@Column(name = "deleted")
private boolean deleted = false;
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = Target.class, fetch = FetchType.LAZY)
private List<Target> assignedToTargets;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = TargetInfo.class, fetch = FetchType.LAZY)
private List<TargetInfo> installedAtTargets;
@OneToMany(mappedBy = "distributionSet", targetEntity = Action.class, fetch = FetchType.LAZY)
private List<Action> actions;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
@JoinColumn(name = "ds_id", insertable = false, updatable = false)
private final List<DistributionSetMetadata> metadata = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds") )
private DistributionSetType type;
@Column(name = "complete")
private boolean complete = false;
/**
* Default constructor.
*/
public DistributionSet() {
super();
}
/**
* Parameterized constructor.
*
* @param name
* of the {@link DistributionSet}
* @param version
* of the {@link DistributionSet}
* @param description
* of the {@link DistributionSet}
* @param type
* of the {@link DistributionSet}
* @param moduleList
* {@link SoftwareModule}s of the {@link DistributionSet}
*/
public DistributionSet(final String name, final String version, final String description,
final DistributionSetType type, final Iterable<SoftwareModule> moduleList) {
super(name, version, description);
this.type = type;
if (moduleList != null) {
moduleList.forEach(module -> addModule(module));
}
complete = type.checkComplete(this);
}
public Set<DistributionSetTag> getTags() {
return tags;
}
/**
* @return the deleted
*/
public boolean isDeleted() {
return deleted;
}
/**
* @return the metadata
*/
public List<DistributionSetMetadata> getMetadata() {
return metadata;
}
/**
* @return the actions
*/
public List<Action> getActions() {
return actions;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof DistributionSet)) {
return false;
}
return true;
}
public boolean isRequiredMigrationStep() {
return requiredMigrationStep;
}
/**
* @param deleted
* the deleted to set
*/
public DistributionSet setDeleted(final boolean deleted) {
this.deleted = deleted;
return this;
}
public DistributionSet setRequiredMigrationStep(final boolean isRequiredMigrationStep) {
requiredMigrationStep = isRequiredMigrationStep;
return this;
}
/**
* @param tags
* the tags to set
*/
public DistributionSet setTags(final Set<DistributionSetTag> tags) {
this.tags = tags;
return this;
}
/**
* @return the assignedTargets
*/
public List<Target> getAssignedTargets() {
return assignedToTargets;
}
/**
* @return the installedTargets
*/
public List<TargetInfo> getInstalledTargets() {
return installedAtTargets;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "DistributionSet [getName()=" + getName() + ", getOptLockRevision()=" + getOptLockRevision()
+ ", getId()=" + getId() + "]";
}
/**
*
* @return unmodifiableSet of {@link SoftwareModule}.
*/
public Set<SoftwareModule> getModules() {
return Collections.unmodifiableSet(modules);
}
public DistributionSetIdName getDistributionSetIdName() {
return new DistributionSetIdName(getId(), getName(), getVersion());
}
/**
* @param softwareModule
* @return <code>true</code> if the module was added and <code>false</code>
* if it already existed in the set
*
*/
public boolean addModule(final SoftwareModule softwareModule) {
// we cannot allow that modules are added without a type defined
if (type == null) {
throw new DistributionSetTypeUndefinedException();
}
// check if it is allowed to such a module to this DS type
if (!type.containsModuleType(softwareModule.getType())) {
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
}
final Optional<SoftwareModule> found = modules.stream()
.filter(module -> module.getId().equals(softwareModule.getId())).findFirst();
if (found.isPresent()) {
return false;
}
final long allready = modules.stream()
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).count();
if (allready >= softwareModule.getType().getMaxAssignments()) {
final Optional<SoftwareModule> sameKey = modules.stream()
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).findFirst();
modules.remove(sameKey.get());
}
if (modules.add(softwareModule)) {
complete = type.checkComplete(this);
return true;
}
return false;
}
/**
* Removed given {@link SoftwareModule} from this DS instance.
*
* @param softwareModule
* to remove
* @return <code>true</code> if element was found and removed
*/
public boolean removeModule(final SoftwareModule softwareModule) {
final Optional<SoftwareModule> found = modules.stream()
.filter(module -> module.getId().equals(softwareModule.getId())).findFirst();
if (found.isPresent()) {
modules.remove(found.get());
complete = type.checkComplete(this);
return true;
}
return false;
}
/**
* 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
* list.
*/
public SoftwareModule findFirstModuleByType(final SoftwareModuleType type) {
final Optional<SoftwareModule> result = modules.stream().filter(module -> module.getType().equals(type))
.findFirst();
if (result.isPresent()) {
return result.get();
}
return null;
}
/**
* @return the type
*/
public DistributionSetType getType() {
return type;
}
/**
* @param type
* the type to set
*/
public void setType(final DistributionSetType type) {
this.type = type;
}
/**
* @return the complete
*/
public boolean isComplete() {
return complete;
}
}

View File

@@ -0,0 +1,111 @@
/**
* 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.model;
import java.io.Serializable;
/**
*
*
*/
public class DistributionSetIdName implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final Long id;
private final String name;
private final String version;
/**
* @param id
* the {@link DistributionSet#getId()}
* @param name
* the {@link DistributionSet#getName()}
* @param version
* the {@link DistributionSet#getVersion()}
*
*/
public DistributionSetIdName(final Long id, final String name, final String version) {
this.id = id;
this.name = name;
this.version = version;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
public String getVersion() {
return version;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() { // NOSONAR - as this is generated
final int prime = 31;
int result = 1;
result = prime * result + (id == null ? 0 : id.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DistributionSetIdName other = (DistributionSetIdName) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// only return the ID because it's used in vaadin for setting the item
// id in the dom
return id.toString();
}
}

View File

@@ -0,0 +1,121 @@
/**
* 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.model;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* Metadata for {@link DistributionSet}.
*
*
*
*
*/
@IdClass(DsMetadataCompositeKey.class)
@Entity
@Table(name = "sp_ds_metadata")
public class DistributionSetMetadata implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@Column(name = "meta_key", length = 128)
private String key;
@Column(name = "meta_value", length = 4000)
@Basic
private String value;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds") )
private DistributionSet distributionSet;
public DistributionSetMetadata() {
}
/**
* Parameter constructor.
*
* @param key
* @param distributionSet
* @param value
*/
public DistributionSetMetadata(final String key, final DistributionSet distributionSet, final String value) {
this.key = key;
this.distributionSet = distributionSet;
this.value = value;
}
public DsMetadataCompositeKey getId() {
return new DsMetadataCompositeKey(distributionSet, key);
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(final String key) {
this.key = key;
}
/**
* @param distributionSet
* the distributionSet to set
*/
public void setDistributionSet(final DistributionSet distributionSet) {
this.distributionSet = distributionSet;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value
* the value to set
*/
public void setValue(final String value) {
this.value = value;
}
/**
* @return the distributionSet
*/
public DistributionSet getDistributionSet() {
return distributionSet;
}
}

View File

@@ -0,0 +1,93 @@
/**
* 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.model;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
/**
* A {@link DistributionSetTag} is used to describe DistributionSet attributes
* and use them also for filtering the DistributionSet list.
*
*
*
*
*
*
*/
@Entity
@Table(name = "sp_distributionset_tag", indexes = {
@Index(name = "sp_idx_distribution_set_tag_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
"name", "tenant" }, name = "uk_ds_tag") )
public class DistributionSetTag extends Tag {
private static final long serialVersionUID = 1L;
/**
* Public constructor.
*
* @param name
* of the {@link DistributionSetTag}
**/
public DistributionSetTag(final String name) {
super(name, null, null);
}
/**
* Public constructor.
*
* @param name
* of the {@link DistributionSetTag}
* @param description
* of the {@link DistributionSetTag}
* @param colour
* of tag in UI
*/
public DistributionSetTag(final String name, final String description, final String colour) {
super(name, description, colour);
}
DistributionSetTag() {
super();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof DistributionSetTag)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,285 @@
/**
* 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.model;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
/**
* A distribution set type defines which software module types can or have to be
* {@link DistributionSet}.
*
*
*
*
*/
@Entity
@Table(name = "sp_distribution_set_type", indexes = {
@Index(name = "sp_idx_distribution_set_type_01", columnList = "tenant,deleted"),
@Index(name = "sp_idx_distribution_set_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_dst_name"),
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_dst_key") })
public class DistributionSetType extends NamedEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
@OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = {
CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "distribution_set_type", insertable = false, updatable = false)
private final Set<DistributionSetTypeElement> elements = new HashSet<>();
@Column(name = "type_key", nullable = false, length = 64)
private String key;
@Column(name = "colour", nullable = true, length = 16)
private String colour;
@Column(name = "deleted")
private boolean deleted = false;
public DistributionSetType() {
}
/**
* Standard constructor.
*
* @param key
* of the type (unique)
* @param name
* of the type (unique)
* @param description
* of the type
*/
public DistributionSetType(final String key, final String name, final String description) {
this(key, name, description, null);
}
/**
* Constructor.
*
* @param key
* of the type
* @param name
* of the type
* @param description
* of the type
* @param color
* of the type. It will be null by default
*/
public DistributionSetType(final String key, final String name, final String description, final String color) {
super(name, description);
this.key = key;
this.colour = color;
}
/**
* @return the deleted
*/
public boolean isDeleted() {
return deleted;
}
/**
* @param deleted
* the deleted to set
*/
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public Set<SoftwareModuleType> getMandatoryModuleTypes() {
return elements.stream().filter(element -> element.isMandatory()).map(element -> element.getSmType())
.collect(Collectors.toSet());
}
public Set<SoftwareModuleType> getOptionalModuleTypes() {
return elements.stream().filter(element -> !element.isMandatory()).map(element -> element.getSmType())
.collect(Collectors.toSet());
}
/**
* Checks if the given {@link SoftwareModuleType} is in this
* {@link DistributionSetType}.
*
* @param softwareModuleType
* search for
* @return <code>true</code> if found
*/
public boolean containsModuleType(final SoftwareModuleType softwareModuleType) {
for (final DistributionSetTypeElement distributionSetTypeElement : elements) {
if (distributionSetTypeElement.getSmType().equals(softwareModuleType)) {
return true;
}
}
return false;
}
/**
* Checks if the given {@link SoftwareModuleType} is in this
* {@link DistributionSetType} and defined as
* {@link DistributionSetTypeElement#isMandatory()}.
*
* @param softwareModuleType
* search for
* @return <code>true</code> if found
*/
public boolean containsMandatoryModuleType(final SoftwareModuleType softwareModuleType) {
return elements.stream().filter(element -> element.isMandatory())
.filter(element -> element.getSmType().equals(softwareModuleType)).findFirst().isPresent();
}
/**
* Checks if the given {@link SoftwareModuleType} is in this
* {@link DistributionSetType} and NOT defined as
* {@link DistributionSetTypeElement#isMandatory()}.
*
* @param softwareModuleType
* search for
* @return <code>true</code> if found
*/
public boolean containsOptionalModuleType(final SoftwareModuleType softwareModuleType) {
return elements.stream().filter(element -> !element.isMandatory())
.filter(element -> element.getSmType().equals(softwareModuleType)).findFirst().isPresent();
}
/**
* Compares the modules of this {@link DistributionSetType} and the given
* one.
*
* @param dsType
* to compare with
* @return <code>true</code> if the lists are identical.
*/
public boolean areModuleEntriesIdentical(final DistributionSetType dsType) {
return new HashSet<DistributionSetTypeElement>(dsType.elements).equals(elements);
}
/**
* Adds {@link SoftwareModuleType} that is optional for the
* {@link DistributionSet}.
*
* @param smType
* to add
* @return updated instance
*/
public DistributionSetType addOptionalModuleType(final SoftwareModuleType smType) {
elements.add(new DistributionSetTypeElement(this, smType, false));
return this;
}
/**
* Adds {@link SoftwareModuleType} that is mandatory for the
* {@link DistributionSet}.
*
* @param smType
* to add
* @return updated instance
*/
public DistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) {
elements.add(new DistributionSetTypeElement(this, smType, true));
return this;
}
/**
* Removes {@link SoftwareModuleType} from the list.
*
* @param smTypeId
* to remove
* @return updated instance
*/
public DistributionSetType removeModuleType(final Long smTypeId) {
// we search by id (standard equals compares also revison)
final Optional<DistributionSetTypeElement> found = elements.stream()
.filter(element -> element.getSmType().getId().equals(smTypeId)).findFirst();
if (found.isPresent()) {
elements.remove(found.get());
}
return this;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(final String key) {
this.key = key;
}
/**
* @param distributionSet
* to check for completeness
* @return <code>true</code> if the all mandatory software module types are
* in the system.
*/
public boolean checkComplete(final DistributionSet distributionSet) {
return distributionSet.getModules().stream().map(module -> module.getType()).collect(Collectors.toList())
.containsAll(getMandatoryModuleTypes());
}
/**
*
* @return the DistributionSet type color
*/
public String getColour() {
return colour;
}
/**
*
* @param colour
* the col
*/
public void setColour(final String colour) {
this.colour = colour;
}
public Set<DistributionSetTypeElement> getElements() {
return elements;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "DistributionSetType [key=" + key + ", isDeleted()=" + isDeleted() + ", getId()=" + getId() + "]";
}
}

View File

@@ -0,0 +1,111 @@
/**
* 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.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Table;
/**
* Relation element between a {@link DistributionSetType} and its
* {@link SoftwareModuleType} elements.
*
*
*
*
*/
@Entity
@Table(name = "sp_ds_type_element")
public class DistributionSetTypeElement implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@EmbeddedId
private DistributionSetTypeElementCompositeKey key;
@Column(name = "mandatory")
private boolean mandatory;
@MapsId("dsType")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_dstype") )
private DistributionSetType dsType;
@MapsId("smType")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "software_module_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype") )
private SoftwareModuleType smType;
/**
* Default constructor.
*/
public DistributionSetTypeElement() {
}
/**
* Standard constructor.
*
* @param dsType
* of the element
* @param smType
* of the element
* @param mandatory
* to <code>true</code> if the {@link SoftwareModuleType} if
* mandatory element in the {@link DistributionSet}.
*/
public DistributionSetTypeElement(final DistributionSetType dsType, final SoftwareModuleType smType,
final boolean mandatory) {
super();
this.key = new DistributionSetTypeElementCompositeKey(dsType, smType);
this.dsType = dsType;
this.smType = smType;
this.mandatory = mandatory;
}
/**
* @return the mandatory
*/
public boolean isMandatory() {
return mandatory;
}
/**
* @return the dsType
*/
public DistributionSetType getDsType() {
return dsType;
}
/**
* @return the smType
*/
public SoftwareModuleType getSmType() {
return smType;
}
/**
* @return the key
*/
public DistributionSetTypeElementCompositeKey getKey() {
return key;
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
/**
* Composite key for {@link DistributionSetTypeElement}.
*
*
*
*
*/
@Embeddable
public class DistributionSetTypeElementCompositeKey implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Column(name = "distribution_set_type", nullable = false)
private Long dsType;
@Column(name = "software_module_type", nullable = false)
private Long smType;
/**
* Default constructor.
*/
DistributionSetTypeElementCompositeKey() {
}
/**
* Constructor.
*
* @param dsType
* in the key
* @param smType
* in the key
*/
DistributionSetTypeElementCompositeKey(final DistributionSetType dsType, final SoftwareModuleType smType) {
super();
this.dsType = dsType.getId();
this.smType = smType.getId();
}
/**
* @return the dsType
*/
public Long getDsType() {
return dsType;
}
/**
* @param dsType
* the dsType to set
*/
public void setDsType(final Long dsType) {
this.dsType = dsType;
}
/**
* @return the smType
*/
public Long getSmType() {
return smType;
}
/**
* @param smType
* the smType to set
*/
public void setSmType(final Long smType) {
this.smType = smType;
}
}

View File

@@ -0,0 +1,126 @@
/**
* 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.model;
import java.io.Serializable;
/**
* The DistributionSet Metadata composite key which contains the meta data key
* and the ID of the DistributionSet itself.
*
*
*
*/
public final class DsMetadataCompositeKey implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String key;
private Long distributionSet;
/**
*
*/
public DsMetadataCompositeKey() {
}
/**
* @param distributionSet
* the distribution set for this meta data
* @param key
* the key of the meta data
*/
public DsMetadataCompositeKey(final DistributionSet distributionSet, final String key) {
this.distributionSet = distributionSet.getId();
this.key = key;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(final String key) {
this.key = key;
}
/**
* @return the distributionSet
*/
public Long getDistributionSet() {
return distributionSet;
}
/**
* @param distributionSet
* the distributionSet to set
*/
public void setDistributionSet(final Long distributionSet) {
this.distributionSet = distributionSet;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((distributionSet == null) ? 0 : distributionSet.hashCode());
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@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 DsMetadataCompositeKey other = (DsMetadataCompositeKey) obj;
if (distributionSet == null) {
if (other.distributionSet != null) {
return false;
}
} else if (!distributionSet.equals(other.distributionSet)) {
return false;
}
if (key == null) {
if (other.key != null) {
return false;
}
} else if (!key.equals(other.key)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,162 @@
/**
* 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.model;
import java.net.URL;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
/**
* External artifact representation with all the necessray informattion to
* generate an artifact {@link URL} at runtime.
*
*
*
*
*/
@Table(name = "sp_external_artifact", indexes = {
@Index(name = "sp_idx_external_artifact_prim", columnList = "id,tenant") })
@Entity
public class ExternalArtifact extends Artifact {
private static final long serialVersionUID = 1L;
@ManyToOne
@JoinColumn(name = "provider", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_art_to_ext_provider") )
private ExternalArtifactProvider externalArtifactProvider;
@Column(name = "url_suffix", length = 512)
private String urlSuffix;
// CascadeType.PERSIST as we register ourself at the BSM
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_external_assigned_sm") )
private SoftwareModule softwareModule;
/**
* Default constructor.
*/
public ExternalArtifact() {
super();
}
/**
* Constructs {@link ExternalArtifact}.
*
* @param externalArtifactProvider
* of the artifact
* @param urlSuffix
* of the artifact
* @param softwareModule
* of the artifact
*/
public ExternalArtifact(@NotNull final ExternalArtifactProvider externalArtifactProvider, final String urlSuffix,
final SoftwareModule softwareModule) {
setSoftwareModule(softwareModule);
this.externalArtifactProvider = externalArtifactProvider;
if (urlSuffix != null) {
this.urlSuffix = urlSuffix;
} else {
this.urlSuffix = externalArtifactProvider.getDefaultSuffix();
}
}
/**
* @return the softwareModule
*/
@Override
public SoftwareModule getSoftwareModule() {
return softwareModule;
}
/**
* @param softwareModule
* the softwareModule to set
*/
public final void setSoftwareModule(final SoftwareModule softwareModule) {
this.softwareModule = softwareModule;
this.softwareModule.addArtifact(this);
}
/**
* @return the externalArtifactProvider
*/
public ExternalArtifactProvider getExternalArtifactProvider() {
return externalArtifactProvider;
}
public String getUrl() {
return new StringBuilder().append(externalArtifactProvider.getBasePath()).append(urlSuffix).toString();
}
/**
* @return the urlSuffix
*/
public String getUrlSuffix() {
return urlSuffix;
}
/**
* @param externalArtifactProvider
* the externalArtifactProvider to set
*/
public void setExternalArtifactProvider(final ExternalArtifactProvider externalArtifactProvider) {
this.externalArtifactProvider = externalArtifactProvider;
}
/**
* @param urlSuffix
* the urlSuffix to set
*/
public void setUrlSuffix(final String urlSuffix) {
this.urlSuffix = urlSuffix;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof ExternalArtifact)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,126 @@
/**
* 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.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
/**
* External repositories for artifact storage. The SP server provides URLs for
* the targets to download rom these external ressources but does not access
* thenm itself.
*
*
*
*
*/
@Table(name = "sp_external_provider", indexes = {
@Index(name = "sp_idx_external_provider_prim", columnList = "tenant,id") })
@Entity
public class ExternalArtifactProvider extends NamedEntity {
private static final long serialVersionUID = 1L;
@Column(name = "base_url", length = 512, nullable = false)
private String basePath;
@Column(name = "default_url_suffix", length = 512, nullable = true)
private String defaultSuffix;
/**
* Constructs {@link ExternalArtifactProvider} based on given properties.
*
* @param name
* of the provided
* @param description
* which is optional
* @param baseURL
* of all {@link ExternalArtifact}s of the provider
* @param defaultUrlSuffix
* that is used if {@link ExternalArtifact#getUrlSuffix()} is
* empty.
*/
public ExternalArtifactProvider(final String name, final String description, final String baseURL,
final String defaultUrlSuffix) {
super(name, description);
basePath = baseURL;
defaultSuffix = defaultUrlSuffix;
}
ExternalArtifactProvider() {
super();
defaultSuffix = "";
basePath = "";
}
/**
* @return the basePath
*/
public String getBasePath() {
return basePath;
}
/**
* @return the defaultSuffix
*/
public String getDefaultSuffix() {
return defaultSuffix;
}
/**
* @param basePath
* the basePath to set
*/
public void setBasePath(final String basePath) {
this.basePath = basePath;
}
/**
* @param defaultSuffix
* the defaultSuffix to set
*/
public void setDefaultSuffix(final String defaultSuffix) {
this.defaultSuffix = defaultSuffix;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof ExternalArtifactProvider)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,139 @@
/**
* 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.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSFile;
/**
* Tenant specific locally stored artifact representation that is used by
* {@link SoftwareModule} . It contains all information that is provided by the
* user while all SP server generated information related to the artifact (hash,
* length) is stored directly with the binary itself.
*
*
*
*/
@Table(name = "sp_artifact", indexes = { @Index(name = "sp_idx_artifact_01", columnList = "tenant,software_module"),
@Index(name = "sp_idx_artifact_prim", columnList = "tenant,id") })
@Entity
public class LocalArtifact extends Artifact {
private static final long serialVersionUID = 1L;
@NotNull
@Column(name = "gridfs_file_name", length = 40)
private String gridFsFileName;
@NotNull
@Column(name = "provided_file_name", length = 256)
private String filename;
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_assigned_sm") )
private SoftwareModule softwareModule;
/**
* Default constructor.
*/
public LocalArtifact() {
super();
}
/**
* Constructs artifact.
*
* @param gridFsFileName
* that is the link to the {@link GridFS} entity.
* @param filename
* that is used by {@link GridFSFile} store.
* @param softwareModule
* of this artifact
*/
public LocalArtifact(@NotNull final String gridFsFileName, @NotNull final String filename,
final SoftwareModule softwareModule) {
setSoftwareModule(softwareModule);
this.gridFsFileName = gridFsFileName;
this.filename = filename;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof LocalArtifact)) {
return false;
}
return true;
}
/**
* @return the softwareModule
*/
@Override
public SoftwareModule getSoftwareModule() {
return softwareModule;
}
/**
* @param softwareModule
* the softwareModule to set
*/
public final void setSoftwareModule(final SoftwareModule softwareModule) {
this.softwareModule = softwareModule;
this.softwareModule.addArtifact(this);
}
/**
* @return the gridFsFileName
*/
public String getGridFsFileName() {
return gridFsFileName;
}
/**
* @return the filename
*/
public String getFilename() {
return filename;
}
}

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.repository.model;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
/**
* {@link BaseEntity} extension for all entities that are named in addition to
* their technical ID.
*
*
*
*
*
*
*/
@MappedSuperclass
public abstract class NamedEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
@Column(name = "name", nullable = false, length = 64)
private String name;
@Column(name = "description", nullable = true, length = 512)
private String description;
/**
* Default constructor.
*/
public NamedEntity() {
super();
}
/**
* Parameterized constructor.
*
* @param name
* of the {@link NamedEntity}
* @param description
* of the {@link NamedEntity}
*/
public NamedEntity(final String name, final String description) {
this.name = name;
this.description = description;
}
public String getDescription() {
return description;
}
public String getName() {
return name;
}
public void setDescription(final String description) {
this.description = description;
}
public void setName(final String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.model;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
/**
* Extension for {@link NamedEntity} that are versioned.
*
*
*
*
*
*
*/
@MappedSuperclass
public abstract class NamedVersionedEntity extends NamedEntity {
private static final long serialVersionUID = 1L;
@Column(name = "version", nullable = false, length = 64)
private String version;
/**
* parameterized constructor.
*
* @param name
* of the entity
* @param version
* of the entity
* @param description
*/
public NamedVersionedEntity(final String name, final String version, final String description) {
super(name, description);
this.version = version;
}
NamedVersionedEntity() {
super();
}
public String getVersion() {
return version;
}
public void setVersion(final String version) {
this.version = version;
}
}

View File

@@ -0,0 +1,293 @@
/**
* 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.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.eclipse.persistence.annotations.CascadeOnDelete;
/**
* Base Software Module that is supported by OS level provisioning mechanism on
* the edge controller, e.g. OS, JVM, AgentHub
*
*
*
*
*
*
*/
@Entity
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
"version", "tenant" }, name = "uk_base_sw_mod") , indexes = {
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
@Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"),
@Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") })
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
public class SoftwareModule extends NamedVersionedEntity {
private static final long serialVersionUID = 1L;
@ManyToOne
@JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type") )
private SoftwareModuleType type;
@ManyToMany(mappedBy = "modules", targetEntity = DistributionSet.class, fetch = FetchType.LAZY)
private final List<DistributionSet> assignedTo = new ArrayList<DistributionSet>();
@Column(name = "deleted")
private boolean deleted = false;
@Column(name = "vendor", nullable = true, length = 256)
private String vendor;
@OneToMany(mappedBy = "softwareModule", cascade = { CascadeType.ALL }, targetEntity = LocalArtifact.class)
private List<LocalArtifact> artifacts;
@OneToMany(mappedBy = "softwareModule", cascade = { CascadeType.ALL }, targetEntity = ExternalArtifact.class)
private List<ExternalArtifact> externalArtifacts;
@CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
@JoinColumn(name = "sw_id", insertable = false, updatable = false)
private final List<SoftwareModuleMetadata> metadata = new ArrayList<>();
/**
* Default constructor.
*/
public SoftwareModule() {
super();
}
/**
* parameterized constructor.
*
* @param type
* of the {@link SoftwareModule}
* @param name
* abstract name of the {@link SoftwareModule}
* @param version
* of the {@link SoftwareModule}
* @param description
* of the {@link SoftwareModule}
* @param vendor
* of the {@link SoftwareModule}
*/
public SoftwareModule(final SoftwareModuleType type, final String name, final String version,
final String description, final String vendor) {
super(name, version, description);
this.vendor = vendor;
this.type = type;
}
/**
* @param artifact
* is added to the assigned {@link Artifact}s.
*/
public void addArtifact(final LocalArtifact artifact) {
if (null == artifacts) {
artifacts = new ArrayList<>(4);
}
if (!artifacts.contains(artifact)) {
artifacts.add(artifact);
}
}
/**
* @param artifact
* is added to the assigned {@link Artifact}s.
*/
public void addArtifact(final ExternalArtifact artifact) {
if (null == externalArtifacts) {
externalArtifacts = new ArrayList<>(4);
}
if (!externalArtifacts.contains(artifact)) {
externalArtifacts.add(artifact);
}
}
/**
* @param artifactId
* to look for
* @return found {@link Artifact}
*/
public Optional<LocalArtifact> getLocalArtifact(final Long artifactId) {
if (null == artifacts) {
return Optional.empty();
}
return artifacts.stream().filter(artifact -> artifact.getId().equals(artifactId)).findFirst();
}
/**
* @param fileName
* to look for
* @return found {@link Artifact}
*/
public Optional<LocalArtifact> getLocalArtifactByFilename(final String fileName) {
if (null == artifacts) {
return Optional.empty();
}
return artifacts.stream().filter(artifact -> artifact.getFilename().equalsIgnoreCase(fileName.trim()))
.findFirst();
}
/**
* @return the artifacts
*/
public List<Artifact> getArtifacts() {
final List<Artifact> result = new ArrayList<Artifact>();
result.addAll(artifacts);
result.addAll(externalArtifacts);
return result;
}
/**
* @return local artifacts only
*/
public List<LocalArtifact> getLocalArtifacts() {
if (artifacts == null) {
return Collections.emptyList();
}
return artifacts;
}
public String getVendor() {
return vendor;
}
/**
* @param artifact
* is removed from the assigned {@link LocalArtifact}s.
*/
public void removeArtifact(final LocalArtifact artifact) {
if (null != artifacts) {
artifacts.remove(artifact);
}
}
/**
* @param artifact
* is removed from the assigned {@link ExternalArtifact}s.
*/
public void removeArtifact(final ExternalArtifact artifact) {
if (null != externalArtifacts) {
externalArtifacts.remove(artifact);
}
}
public void setVendor(final String vendor) {
this.vendor = vendor;
}
public SoftwareModuleType getType() {
return type;
}
/**
* @return the deleted
*/
public boolean isDeleted() {
return deleted;
}
/**
* @param deleted
* the deleted to set
*/
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public void setType(final SoftwareModuleType type) {
this.type = type;
}
/**
* @return the metadata
*/
public List<SoftwareModuleMetadata> getMetadata() {
return metadata;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "SoftwareModule [type=" + type + ", deleted=" + deleted + ", getVersion()=" + getVersion()
+ ", getOptLockRevision()=" + getOptLockRevision() + ", getId()=" + getId() + "]";
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.getClass().getName().hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof SoftwareModule)) {
return false;
}
return true;
}
/**
* @return the assignedTo
*/
public List<DistributionSet> getAssignedTo() {
return assignedTo;
}
}

View File

@@ -0,0 +1,91 @@
/**
* 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.model;
import java.io.Serializable;
/**
* To hold software module name and Id.
*
*
*
*/
public class SoftwareModuleIdName implements Serializable {
private static final long serialVersionUID = -6317413180936148514L;
private final Long id;
private final String name;
/**
* @param id
* if the {@link SoftwareModule}
* @param name
* of the {@link SoftwareModule}
*/
public SoftwareModuleIdName(final Long id, final String name) {
super();
this.id = id;
this.name = name;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {// NOSONAR - as this is generated
final int prime = 31;
int result = 1;
result = prime * result + (id == null ? 0 : id.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {// NOSONAR - as this is generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SoftwareModuleIdName other = (SoftwareModuleIdName) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,124 @@
/**
* 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.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* Metadata for {@link SoftwareModule}.
*
*
*
*
*/
@IdClass(SwMetadataCompositeKey.class)
@Entity
@Table(name = "sp_sw_metadata")
public class SoftwareModuleMetadata implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@Column(name = "meta_key", length = 128)
private String key;
@Column(name = "meta_value", length = 4000)
private String value;
@Id
@ManyToOne(targetEntity = SoftwareModule.class, fetch = FetchType.LAZY)
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw") )
private SoftwareModule softwareModule;
public SoftwareModuleMetadata() {
}
/**
* Standard constructor.
*
* @param key
* of the metadata element
* @param softwareModule
* @param value
* of the metadata element
*/
public SoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) {
this.key = key;
this.softwareModule = softwareModule;
this.value = value;
}
/**
* @return the id
*/
public SwMetadataCompositeKey getId() {
return new SwMetadataCompositeKey(softwareModule, key);
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value
* the value to set
*/
public void setValue(final String value) {
this.value = value;
}
/**
* @return the softwareModule
*/
public SoftwareModule getSoftwareModule() {
return softwareModule;
}
/**
* @param softwareModule
* the softwareModule to set
*/
public void setSoftwareModule(final SoftwareModule softwareModule) {
this.softwareModule = softwareModule;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(final String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,141 @@
/**
* 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.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
/**
* Type of a software modules.
*
*
*
*
*/
@Entity
@Table(name = "sp_software_module_type", indexes = {
@Index(name = "sp_idx_software_module_type_01", columnList = "tenant,deleted"),
@Index(name = "sp_idx_software_module_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_smt_type_key"),
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_smt_name") })
public class SoftwareModuleType extends NamedEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
@Column(name = "type_key", nullable = false, length = 64)
private String key;
@Column(name = "max_ds_assignments", nullable = false)
private int maxAssignments;
@Column(name = "colour", nullable = true, length = 16)
private String colour;
@Column(name = "deleted")
private boolean deleted = false;
/**
* Constructor.
*
* @param key
* of the type
* @param name
* of the type
* @param description
* of the type
* @param maxAssignments
* assignments to a DS
*/
public SoftwareModuleType(final String key, final String name, final String description, final int maxAssignments) {
this(key, name, description, maxAssignments, null);
}
/**
* Constructor.
*
* @param key
* of the type
* @param name
* of the type
* @param description
* of the type
* @param maxAssignments
* assignments to a DS
* @param colour
* of the type. It will be null by default
*/
public SoftwareModuleType(final String key, final String name, final String description, final int maxAssignments,
final String colour) {
super();
this.key = key;
this.maxAssignments = maxAssignments;
setDescription(description);
setName(name);
this.colour = colour;
}
/**
* Default Constructor.
*/
public SoftwareModuleType() {
super();
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @return the max
*/
public int getMaxAssignments() {
return maxAssignments;
}
/**
* @return the deleted
*/
public boolean isDeleted() {
return deleted;
}
/**
* @param deleted
* the deleted to set
*/
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
/**
*
* @return the software type color
*/
public String getColour() {
return colour;
}
/**
*
* @param colour
* the col
*/
public void setColour(final String colour) {
this.colour = colour;
}
}

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